diff --git a/AUTHORS b/AUTHORS index e5f18285..8c56ec44 100644 --- a/AUTHORS +++ b/AUTHORS @@ -2,3 +2,4 @@ Phil Birkelbach Neil Domalik Jean-Manuel Gagnon Garrett Herschleb +Eric Blevins diff --git a/Makefile b/Makefile index 4e43d787..04489100 100644 --- a/Makefile +++ b/Makefile @@ -5,23 +5,48 @@ SHELL := /bin/bash ##################################### I N I T T A R G E T S ##################################### -venv: +venv.marker: python3 -m venv venv + source venv/bin/activate ; pip install --upgrade pip + source venv/bin/activate ; pip install black + source venv/bin/activate ; pip install pytest + source venv/bin/activate ; pip install pytest-qt + source venv/bin/activate ; pip install pytest-env + source venv/bin/activate ; pip install pytest-cov + touch venv.marker + echo -e "\nRun:\nsource venv/bin/activate" +venv: venv.marker .PHONY: venv -init.marker: setup.py - pip install -e .[install] +init.marker: pyproject.toml + source venv/bin/activate ; pip install -e .[install] touch init.marker -init: init.marker +init: venv.marker init.marker .PHONY: init #################################### W H E E L T A R G E T S #################################### init-build.marker: init - pip install -e .[build] + source venv/bin/activate ; pip install -e .[build] touch init-build.marker init-build: init-build.marker .PHONY: init-build wheel: init-build - python -m build --wheel + source venv/bin/activate ; python -m build --wheel + + +test: init + source venv/bin/activate ; pytest + +.PHONY: test + +clean: + rm -rfI venv + rm -fI extras/extras/test_results/*.html + rm -fI extras/extras/test_results/*.png + rm -rfI extras/extras/test_results/htmlcov/ + rm -f init-build.marker + rm -f init.marker + rm -f venv.marker +.PHONY: clean diff --git a/README.rst b/README.rst index a16e86c3..d6da5386 100644 --- a/README.rst +++ b/README.rst @@ -79,6 +79,15 @@ This creates an index.bin file in CIFP directory Update the config file [Screen.PFD] section dbpath and indexpath with the path names of the FAACIFP18 and index.bin files respectively. +Testing +------------ +To run all of the automated tests and code covreage. + +:: + + $ make test + + Distribution ------------ @@ -114,3 +123,13 @@ All CLI options work as defined. --log-config LOG_CONFIG Alternate logger configuration file + +Cleanup +------------ + +To cleanup all of the test files, virtual environemnt and other changes made by the makefile. This is a destructive command, you may want to review what it does before running it. + +:: + + $ make clean + diff --git a/conftest.py b/conftest.py new file mode 100644 index 00000000..1e2b84a4 --- /dev/null +++ b/conftest.py @@ -0,0 +1,99 @@ +import pytest +# Patch pyavtools.fix so it can run in memory without a fix server +import sys +import tests.mock_db.client +import tests.mock_db.scheduler + +sys.modules["pyavtools.fix.client"] = tests.mock_db.client +sys.modules["pyavtools.scheduler"] = tests.mock_db.scheduler + + + +@pytest.fixture +def fix(): + # import fix + import pyavtools.fix as fix + + # Init database + fix.initialize({"main": {"FixServer": "localhost", "FixPort": "3490"}}) + + # TODO Add example setting aux values since needed on many tests + # Define some fix db items we can use in tests + # Define a key: + # key,desc, dtype(as a string), min, max, units, tol, aux + fix.db.define_item("TEST", "Testing key", "float", 0, 100, "", 50000, "") + # Set initial Value + fix.db.set_value("TEST", 50.19) + # Bad and fail are true by default to set them False + fix.db.get_item("TEST").bad = False + fix.db.get_item("TEST").fail = False + + + def create_numbers(key, value, old=False, bad=False, fail=False, annunciate=False): + fix.db.define_item( + key, + "Number", + "float", + 0, + 100, + "degC", + 50000, + "Min,Max,highWarn,highAlarm,lowWarn,lowAlarm", + ) + fix.db.set_value(key, value) + fix.db.get_item(key).old = old + fix.db.get_item(key).bad = bad + fix.db.get_item(key).fail = fail + fix.db.get_item(key).annunciate = annunciate + fix.db.get_item(key).set_aux_value("Min", 0) + fix.db.get_item(key).set_aux_value("lowAlarm", 10) + fix.db.get_item(key).set_aux_value("lowWarn", 20) + fix.db.get_item(key).set_aux_value("highWarn", 80) + fix.db.get_item(key).set_aux_value("highAlarm", 90) + fix.db.get_item(key).set_aux_value("Max", 100) + + + create_numbers("NUMOK", 50.91) + create_numbers("NUMOLD", 51.82, old=True) + create_numbers("NUMBAD", 48.73, bad=True) + create_numbers("NUMFAIL", 65.64, fail=True) + create_numbers("NUMANNUNCIATE", 62.55, annunciate=True) + create_numbers("NUMLOWWARN", 18.46) + create_numbers("NUMLOWALARM", 5.37) + create_numbers("NUMHIGHWARN", 81.28) + create_numbers("NUMHIGHALARM", 95.19) + + # Number with no Aux + fix.db.define_item("NUM", "NUM", "float", 0.0, 100.0, "degC", 50000, "") + + fix.db.define_item("GS", "GS", "float", 0.0, 2000.0, "knots", 50000, "") + fix.db.set_value("GS", 100) + fix.db.get_item("GS").bad = False + fix.db.get_item("GS").fail = False + + fix.db.define_item("TAS", "TAS", "float", 0.0, 2000.0, "knots", 50000, "") + fix.db.set_value("TAS", 105) + fix.db.get_item("TAS").bad = False + fix.db.get_item("TAS").fail = False + + fix.db.define_item( + "IAS", + "Number", + "float", + 0, + 2000.0, + "knots", + 50000, + "Min,Max,V1,V2,Vne,Vfe,Vmc,Va,Vno,Vs,Vs0,Vx,Vy", + ) + fix.db.set_value("IAS", "110") + fix.db.get_item("IAS").bad = False + fix.db.get_item("IAS").fail = False + fix.db.get_item("IAS").set_aux_value("Vs", 45.0) + fix.db.get_item("IAS").set_aux_value("Vs0", 40.0) + fix.db.get_item("IAS").set_aux_value("Vno", 125.0) + fix.db.get_item("IAS").set_aux_value("Vne", 140.0) + fix.db.get_item("IAS").set_aux_value("Vfe", 70.0) + + return fix + diff --git a/pyefis/__init__.py b/extras/extras/test_results/.save similarity index 100% rename from pyefis/__init__.py rename to extras/extras/test_results/.save diff --git a/pyefis/cfg.py b/pyefis/cfg.py deleted file mode 100644 index fcd03411..00000000 --- a/pyefis/cfg.py +++ /dev/null @@ -1,92 +0,0 @@ -import yaml -import os - -def from_yaml(fname,bpath=None,cfg=None,bc=[],preferences=None): - bc.append(fname) - if len(bc) > 500: - import pprint - raise Exception(f"{pprint.pformat(bc)}\nPotential loop detected inside yaml includes, the breadcrumbs above might help detect where the issue is") - - fpath = os.path.dirname(fname) - if not cfg: - # cfg only populated to process nested data - if not bpath: bpath = fpath - with open(fname) as cf: - cfg = yaml.safe_load(cf) - - new = {} - if hasattr(cfg,'items'): - for key, val in cfg.items(): - if key == 'include': - if isinstance(val, str): - files = [ val ] - elif isinstance(val, list): - files = val - else: - raise Exception(f"#include in {fname} must be string or array") - # Process include(s) - for f in files: - # Check if file relative to current file - ifile = fpath + '/' + f - if not os.path.exists(ifile): - # Use base path - ifile = bpath + '/' + f - if not os.path.exists(ifile): - # Check preferences - if 'includes' in preferences: - pfile = preferences['includes'].get(f,False) - if pfile: - ifile = fpath + '/' + pfile - if not os.path.exists(ifile): - ifile = bpath + '/' + pfile - if not os.path.exists(ifile): - raise Exception(f"Cannot find include: {f}") - else: - raise Exception(f"Cannot find include: {f}") - sub = from_yaml(ifile, bpath,bc=bc,preferences=preferences) - if hasattr(sub,'items'): - for k, v in sub.items(): - new[k] = v - else: - raise Exception(f"Include {val} from {fname} is invalid") - elif isinstance(val, dict): - new[key] = from_yaml(fname,bpath,val,bc=bc,preferences=preferences) - elif isinstance(val, list): - new[key] = [] - # Included array elements - for l in val: - if isinstance(l, dict): - if 'include' in l: - ifile = fpath + '/' + l['include'] - if not os.path.exists(ifile): - # Use base path - ifile = bpath + '/' + l['include'] - if not os.path.exists(ifile): - # Check preferences - if 'includes' in preferences: - pfile = preferences['includes'].get(l['include'],False) - if pfile: - ifile = fpath + '/' + pfile - if not os.path.exists(ifile): - ifile = bpath + '/' + pfile - if not os.path.exists(ifile): - raise Exception(f"Cannot find include: {f}") - else: - raise Exception(f"Cannot find include: {f}") - with open(ifile) as cf: - litems = yaml.safe_load(cf) - if 'items' in litems: - if litems['items'] != None: - for a in litems['items']: - new[key].append(a) - else: - raise Exception(f"Error in {ifile}\nWhen including list items they need listed under 'items:' in the include file") - else: - new[key].append(l) - else: - new[key].append(l) - else: - #Save existing - new[key] = val - return new - diff --git a/pyefis/instruments/helpers/__init__.py b/pyefis/instruments/helpers/__init__.py deleted file mode 100644 index d11e841a..00000000 --- a/pyefis/instruments/helpers/__init__.py +++ /dev/null @@ -1,57 +0,0 @@ -from PyQt5.QtWidgets import * -from PyQt5.QtGui import * - -def fit_to_mask(width,height,mask,font,units_mask=None, units_ratio=0.8): - - font_size = height - text_font = QFont(font, int(font_size)) - units_font = QFont(font, int(font_size * units_ratio)) - t = QGraphicsSimpleTextItem(mask) - t.setFont(text_font) - text_width = t.boundingRect().width() - text_height = t.boundingRect().height() - units_width = 0 - units_height = 0 - if units_mask: - u = QGraphicsSimpleTextItem(units_mask) - u.setFont(units_font) - units_width = u.boundingRect().width() - units_height = u.boundingRect().height() - - # IF needed, shrink until it fits - while (text_width + units_width >= width) and font_size > 0.5: - font_size -= 0.2 - text_font.setPointSizeF(font_size) - t.setFont(text_font) - text_width = t.boundingRect().width() - text_height = t.boundingRect().height() - if units_mask: - units_font.setPointSizeF(font_size * units_ratio) - u.setFont(units_font) - units_width = u.boundingRect().width() - units_height = u.boundingRect().height() - - # If needed, grow until it fills - while text_width + units_width <= width: - font_size += 0.2 - text_font.setPointSizeF(font_size) - t.setFont(text_font) - text_width = t.boundingRect().width() - text_height = t.boundingRect().height() - if units_mask: - units_font.setPointSizeF(font_size * units_ratio) - u.setFont(units_font) - units_width = u.boundingRect().width() - units_height = u.boundingRect().height() - - # The above took care of the width, this addresses the height: - while (text_height >= height) and font_size > 0.5: - font_size -= 0.2 - text_font.setPointSizeF(font_size) - t.setFont(text_font) - text_width = t.boundingRect().width() - text_height = t.boundingRect().height() - - return(font_size) - - diff --git a/pyefis/version.py b/pyefis/version.py deleted file mode 100644 index eceef79f..00000000 --- a/pyefis/version.py +++ /dev/null @@ -1,3 +0,0 @@ -VERSION="2.0.19" -print(VERSION) - diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..22521d22 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,81 @@ +[build-system] +requires = [ + "setuptools" +] +build-backend = "setuptools.build_meta" + +#[tool.setuptools] + +[tool.setuptools.dynamic] +version = {attr = "pyefis.__version__"} + +[project] +name = "pyEfis" +dynamic = ["version"] +description = "An Electronic Flight Information System written in Python." +readme = "README.rst" +requires-python = ">=3.8, <4" +authors = [ + { name = "MakerPlane Open Source Aviation", email = "contact@makerplane.org" }, +] +keywords = [ + "aviation", + "efis", + "makerplane", +] +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: X11 Applications :: Qt", + "Intended Audience :: End Users/Desktop", + "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.12", +] +dependencies = [ + "geomag==0.9.2015", + "geopy==2.4.1", +# Until this revision is published to PyPy get it from git: + "pyavtools @ git+https://github.com/makerplane/pyAvTools/@b00273a9016e9a4b975f8efe51d63405103d58fb", + "pycond==20230212", + "PyQt5==5.15.10", + "PyYAML==6.0.1", +] + +[project.optional-dependencies] +build = [ + "build==1.2.1", +] + +[project.scripts] +pyefis = "pyefis.main:main" + +[project.urls] +Homepage = "https://makerplane.org/" +Source = "https://github.com/makerplane/pyEfis" + + +[tool.pytest.ini_options] +qt_api="pyqt5" +env = [ + "QT_QPA_PLATFORM = offscreen:size=1000x1000" +# "QT_QPA_PLATFORM = xcb:size=1000,1000" +] +filterwarnings = [ + # I belive this is warning that pyqt needs updated if you update to python 3.12 + "ignore:sipPyTypeDict.*:DeprecationWarning:.*:" +] +addopts = "-vv --cov=src --cov-report html:extras/extras/test_results/htmlcov/ --cov-report term-missing --strict-markers -rfE" + +[tool.coverage.run] +relative_files = true + +[tool.coverage.paths] +source = [ + "src/" +] + diff --git a/setup.py b/setup.py deleted file mode 100644 index 480df1a6..00000000 --- a/setup.py +++ /dev/null @@ -1,61 +0,0 @@ -"""A setuptools based setup module.""" - -# Always prefer setuptools over distutils -from setuptools import setup, find_packages -import pathlib - -here = pathlib.Path(__file__).parent.resolve() - -# Get the long description from the README file -long_description = (here / "README.rst").read_text(encoding="utf-8") - -setup( - name="pyEfis", - version="0.1.0", - description="An Electronic Flight Information System written in Python.", - long_description=long_description, - long_description_content_type="text/rst", - url="https://github.com/makerplane/pyEfis", - author="MakerPlane Open Source Aviation", - author_email="contact@makerplane.org", - classifiers=[ - "Development Status :: 3 - Alpha", - "Environment :: X11 Applications :: Qt", - "Intended Audience :: End Users/Desktop", - "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3 :: Only", - ], - keywords="aviation, makerplane, efis", - #package_data={ - # "pyefis": ["config/*","config/includes/*", "config/buttons/*"], - #}, - packages=find_packages(exclude=["tests.*", "tests"]), - python_requires=">=3.7, <4", - install_requires=[ - "geomag==0.9.2015", - "PyYAML==6.0.1", - "pyavtools==0.1.0", - "PyQt5==5.15.9", - "pycond==20230212", - "geopy==2.4.1" - ], - extras_require={ - "build": ["build==1.0.3"], - }, - include_package_data= True, - entry_points={ - "console_scripts": [ - "pyefis=pyefis.main:main", - ], - }, - project_urls={ - "Homepage": "https://makerplane.org/", - "Source": "https://github.com/makerplane/pyEfis", - }, -) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 508e2b1f..7e40c266 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -91,10 +91,9 @@ parts: source: . # Use the OS provided pyqt5 to reduce build issues override-build: | - sed -i 's/"PyQt5==5.15.9",//' setup.py - sed -i 's/pyavtools==0.1.0/pyavtools @ git+https:\/\/github.com\/makerplane\/pyAvTools\/@b00273a9016e9a4b975f8efe51d63405103d58fb#egg=pyavtools/' setup.py + sed -i 's/"PyQt5==5.15.10",//' pyproject.toml craftctl default - craftctl set version=$(python3 pyefis/version.py) + craftctl set version=$(python3 src/pyefis/version.py) stage-packages: - libqt5core5t64 - libqt5qml5 diff --git a/src/pyefis/__init__.py b/src/pyefis/__init__.py new file mode 100644 index 00000000..d97f221c --- /dev/null +++ b/src/pyefis/__init__.py @@ -0,0 +1,2 @@ +__version__ = "2.0.20" + diff --git a/src/pyefis/cfg.py b/src/pyefis/cfg.py new file mode 100644 index 00000000..10384a55 --- /dev/null +++ b/src/pyefis/cfg.py @@ -0,0 +1,143 @@ +import yaml +import os + +# allows using include preferences and include: keys to include yaml files inside yaml files +# While it does support including includes within includes, it does not support the include: key being nested deeply. +# +# Rxamples: +# include: some_file.yaml <- supported +# +# test: +# - include: some.yaml <- supported, when inside a list the included yaml needs to return a list inside items {items:[]} +# +# testing: +# include: some.yaml <- supported, the keys inside some.yaml will be nested under testing +# +# testing: +# deep: +# include: some.yaml <- not supported +# +# +# Thi was made to split monolithic files into smaller includable sections +# Allowing users to easily swap sections in/out to configure the screens to their liking + + +def from_yaml(fname, bpath=None, cfg=None, bc=None, preferences=None): + if bc == None: + bc = list() + bc.append(fname) + if len(bc) > 500: + import pprint + + raise RecursionError( + f"{pprint.pformat(bc)}\nPotential loop detected inside yaml includes, the breadcrumbs above might help detect where the issue is" + ) + + fpath = os.path.dirname(fname) + if not cfg and not fpath: + raise SyntaxError( + f"The filename '{fname}', must include the path, not just the filename" + ) + if not cfg: + # cfg only populated to process nested data + if not bpath: + bpath = fpath + with open(fname) as cf: + cfg = yaml.safe_load(cf) + + new = {} + if hasattr(cfg, "items"): + for key, val in cfg.items(): + if key == "include": + if isinstance(val, str): + files = [val] + elif isinstance(val, list): + files = val + else: + raise SyntaxError(f"#include in {fname} must be string or array") + # Process include(s) + for f in files: + # Check if file relative to current file + ifile = fpath + "/" + f + if not os.path.exists(ifile): + # Use base path + ifile = bpath + "/" + f + if not os.path.exists(ifile): + # Check preferences + if preferences != None and "includes" in preferences: + pfile = preferences["includes"].get(f, False) + if pfile: + ifile = fpath + "/" + pfile + if not os.path.exists(ifile): + ifile = bpath + "/" + pfile + if not os.path.exists(ifile): + raise FileNotFoundError( + f"Cannot find include: {f}" + ) + else: + raise FileNotFoundError(f"Cannot find include: {f}") + sub = from_yaml(ifile, bpath, bc=bc, preferences=preferences) + if hasattr(sub, "items"): + for k, v in sub.items(): + new[k] = v + elif isinstance(val, dict): + new[key] = from_yaml(fname, bpath, val, bc=bc, preferences=preferences) + elif isinstance(val, list): + new[key] = [] + # Included array elements + for l in val: + if isinstance(l, dict): + if "include" in l: + ifile = fpath + "/" + l["include"] + if not os.path.exists(ifile): + # Use base path + ifile = bpath + "/" + l["include"] + if not os.path.exists(ifile): + # Check preferences + if "includes" in preferences: + pfile = preferences["includes"].get( + l["include"], False + ) + if pfile: + ifile = fpath + "/" + pfile + if not os.path.exists(ifile): + ifile = bpath + "/" + pfile + if not os.path.exists(ifile): + raise FileNotFoundError( + f"Cannot find include: {pfile}" + ) + else: + raise FileNotFoundError(f"Cannot find include: {ifile}") + # with open(ifile) as cf: + litems = from_yaml( + ifile, bpath, bc=bc, preferences=preferences + ) # yaml.safe_load(cf) + if "items" in litems: + if litems["items"] != None: + for a in litems["items"]: + # What about nested things? + ap = a + if isinstance(a, dict): + ap = from_yaml( + ifile, + bpath, + a, + bc=bc, + preferences=preferences, + ) + if isinstance(litems["items"], dict): + new[key].append({ap: litems["items"][ap]}) + else: + new[key].append(ap) + else: + raise SyntaxError( + f"Error in {ifile}\nWhen including list items they need listed under 'items:' in the include file" + ) + else: + new[key].append(l) + else: + new[key].append(l) + else: + # Save existing + new[key] = val + return new diff --git a/pyefis/common/__init__.py b/src/pyefis/common/__init__.py similarity index 94% rename from pyefis/common/__init__.py rename to src/pyefis/common/__init__.py index fc436d0e..cfa936ca 100644 --- a/pyefis/common/__init__.py +++ b/src/pyefis/common/__init__.py @@ -17,10 +17,10 @@ # Constant definitions -UP = 'up' -DOWN = 'down' -LEFT = 'left' -RIGHT = 'right' +UP = "up" +DOWN = "down" +LEFT = "left" +RIGHT = "right" def bounds(min, max, value): diff --git a/pyefis/config/buttons/auto-pilot-adjust.yaml b/src/pyefis/config/buttons/auto-pilot-adjust.yaml similarity index 100% rename from pyefis/config/buttons/auto-pilot-adjust.yaml rename to src/pyefis/config/buttons/auto-pilot-adjust.yaml diff --git a/pyefis/config/buttons/auto-pilot-flight-plan.yaml b/src/pyefis/config/buttons/auto-pilot-flight-plan.yaml similarity index 100% rename from pyefis/config/buttons/auto-pilot-flight-plan.yaml rename to src/pyefis/config/buttons/auto-pilot-flight-plan.yaml diff --git a/pyefis/config/buttons/auto-pilot-heading-hold.yaml b/src/pyefis/config/buttons/auto-pilot-heading-hold.yaml similarity index 100% rename from pyefis/config/buttons/auto-pilot-heading-hold.yaml rename to src/pyefis/config/buttons/auto-pilot-heading-hold.yaml diff --git a/pyefis/config/buttons/baro-down-invisible.yaml b/src/pyefis/config/buttons/baro-down-invisible.yaml similarity index 100% rename from pyefis/config/buttons/baro-down-invisible.yaml rename to src/pyefis/config/buttons/baro-down-invisible.yaml diff --git a/pyefis/config/buttons/baro-up-invisible.yaml b/src/pyefis/config/buttons/baro-up-invisible.yaml similarity index 100% rename from pyefis/config/buttons/baro-up-invisible.yaml rename to src/pyefis/config/buttons/baro-up-invisible.yaml diff --git a/pyefis/config/buttons/dimmer-down-invisible.yaml b/src/pyefis/config/buttons/dimmer-down-invisible.yaml similarity index 100% rename from pyefis/config/buttons/dimmer-down-invisible.yaml rename to src/pyefis/config/buttons/dimmer-down-invisible.yaml diff --git a/pyefis/config/buttons/dimmer-up-invisible.yaml b/src/pyefis/config/buttons/dimmer-up-invisible.yaml similarity index 100% rename from pyefis/config/buttons/dimmer-up-invisible.yaml rename to src/pyefis/config/buttons/dimmer-up-invisible.yaml diff --git a/pyefis/config/buttons/egt-Lean.yaml b/src/pyefis/config/buttons/egt-Lean.yaml similarity index 100% rename from pyefis/config/buttons/egt-Lean.yaml rename to src/pyefis/config/buttons/egt-Lean.yaml diff --git a/pyefis/config/buttons/egt-Normalize.yaml b/src/pyefis/config/buttons/egt-Normalize.yaml similarity index 100% rename from pyefis/config/buttons/egt-Normalize.yaml rename to src/pyefis/config/buttons/egt-Normalize.yaml diff --git a/pyefis/config/buttons/egt-Peak.yaml b/src/pyefis/config/buttons/egt-Peak.yaml similarity index 100% rename from pyefis/config/buttons/egt-Peak.yaml rename to src/pyefis/config/buttons/egt-Peak.yaml diff --git a/pyefis/config/buttons/egt-reset-peak.yaml b/src/pyefis/config/buttons/egt-reset-peak.yaml similarity index 100% rename from pyefis/config/buttons/egt-reset-peak.yaml rename to src/pyefis/config/buttons/egt-reset-peak.yaml diff --git a/pyefis/config/buttons/leader.yaml b/src/pyefis/config/buttons/leader.yaml similarity index 100% rename from pyefis/config/buttons/leader.yaml rename to src/pyefis/config/buttons/leader.yaml diff --git a/pyefis/config/buttons/mgl/v16/active-rx-status.yaml b/src/pyefis/config/buttons/mgl/v16/active-rx-status.yaml similarity index 100% rename from pyefis/config/buttons/mgl/v16/active-rx-status.yaml rename to src/pyefis/config/buttons/mgl/v16/active-rx-status.yaml diff --git a/pyefis/config/buttons/mgl/v16/active-tx-status.yaml b/src/pyefis/config/buttons/mgl/v16/active-tx-status.yaml similarity index 100% rename from pyefis/config/buttons/mgl/v16/active-tx-status.yaml rename to src/pyefis/config/buttons/mgl/v16/active-tx-status.yaml diff --git a/pyefis/config/buttons/mgl/v16/standby-rx-status.yaml b/src/pyefis/config/buttons/mgl/v16/standby-rx-status.yaml similarity index 100% rename from pyefis/config/buttons/mgl/v16/standby-rx-status.yaml rename to src/pyefis/config/buttons/mgl/v16/standby-rx-status.yaml diff --git a/pyefis/config/buttons/mgl/v16/swap-active-standby.yaml b/src/pyefis/config/buttons/mgl/v16/swap-active-standby.yaml similarity index 100% rename from pyefis/config/buttons/mgl/v16/swap-active-standby.yaml rename to src/pyefis/config/buttons/mgl/v16/swap-active-standby.yaml diff --git a/pyefis/config/buttons/screen-android-pfd.yaml b/src/pyefis/config/buttons/screen-android-pfd.yaml similarity index 100% rename from pyefis/config/buttons/screen-android-pfd.yaml rename to src/pyefis/config/buttons/screen-android-pfd.yaml diff --git a/pyefis/config/buttons/screen-ems-pfd.yaml b/src/pyefis/config/buttons/screen-ems-pfd.yaml similarity index 100% rename from pyefis/config/buttons/screen-ems-pfd.yaml rename to src/pyefis/config/buttons/screen-ems-pfd.yaml diff --git a/pyefis/config/buttons/screen-ems2-pfd.yaml b/src/pyefis/config/buttons/screen-ems2-pfd.yaml similarity index 100% rename from pyefis/config/buttons/screen-ems2-pfd.yaml rename to src/pyefis/config/buttons/screen-ems2-pfd.yaml diff --git a/pyefis/config/buttons/screen-map-pfd.yaml b/src/pyefis/config/buttons/screen-map-pfd.yaml similarity index 100% rename from pyefis/config/buttons/screen-map-pfd.yaml rename to src/pyefis/config/buttons/screen-map-pfd.yaml diff --git a/pyefis/config/buttons/screen-radio-pfd.yaml b/src/pyefis/config/buttons/screen-radio-pfd.yaml similarity index 100% rename from pyefis/config/buttons/screen-radio-pfd.yaml rename to src/pyefis/config/buttons/screen-radio-pfd.yaml diff --git a/pyefis/config/buttons/screen-sixpack-pfd.yaml b/src/pyefis/config/buttons/screen-sixpack-pfd.yaml similarity index 100% rename from pyefis/config/buttons/screen-sixpack-pfd.yaml rename to src/pyefis/config/buttons/screen-sixpack-pfd.yaml diff --git a/pyefis/config/buttons/trim-center-invisible.yaml b/src/pyefis/config/buttons/trim-center-invisible.yaml similarity index 100% rename from pyefis/config/buttons/trim-center-invisible.yaml rename to src/pyefis/config/buttons/trim-center-invisible.yaml diff --git a/pyefis/config/buttons/trim-down-invisible.yaml b/src/pyefis/config/buttons/trim-down-invisible.yaml similarity index 100% rename from pyefis/config/buttons/trim-down-invisible.yaml rename to src/pyefis/config/buttons/trim-down-invisible.yaml diff --git a/pyefis/config/buttons/trim-roll-center-invisible.yaml b/src/pyefis/config/buttons/trim-roll-center-invisible.yaml similarity index 100% rename from pyefis/config/buttons/trim-roll-center-invisible.yaml rename to src/pyefis/config/buttons/trim-roll-center-invisible.yaml diff --git a/pyefis/config/buttons/trim-roll-left-invisible.yaml b/src/pyefis/config/buttons/trim-roll-left-invisible.yaml similarity index 100% rename from pyefis/config/buttons/trim-roll-left-invisible.yaml rename to src/pyefis/config/buttons/trim-roll-left-invisible.yaml diff --git a/pyefis/config/buttons/trim-roll-right-invisible.yaml b/src/pyefis/config/buttons/trim-roll-right-invisible.yaml similarity index 100% rename from pyefis/config/buttons/trim-roll-right-invisible.yaml rename to src/pyefis/config/buttons/trim-roll-right-invisible.yaml diff --git a/pyefis/config/buttons/trim-up-invisible.yaml b/src/pyefis/config/buttons/trim-up-invisible.yaml similarity index 100% rename from pyefis/config/buttons/trim-up-invisible.yaml rename to src/pyefis/config/buttons/trim-up-invisible.yaml diff --git a/pyefis/config/buttons/trim-yaw-center-invisible.yaml b/src/pyefis/config/buttons/trim-yaw-center-invisible.yaml similarity index 100% rename from pyefis/config/buttons/trim-yaw-center-invisible.yaml rename to src/pyefis/config/buttons/trim-yaw-center-invisible.yaml diff --git a/pyefis/config/buttons/trim-yaw-left-invisible.yaml b/src/pyefis/config/buttons/trim-yaw-left-invisible.yaml similarity index 100% rename from pyefis/config/buttons/trim-yaw-left-invisible.yaml rename to src/pyefis/config/buttons/trim-yaw-left-invisible.yaml diff --git a/pyefis/config/buttons/trim-yaw-right-invisible.yaml b/src/pyefis/config/buttons/trim-yaw-right-invisible.yaml similarity index 100% rename from pyefis/config/buttons/trim-yaw-right-invisible.yaml rename to src/pyefis/config/buttons/trim-yaw-right-invisible.yaml diff --git a/pyefis/config/buttons/units.yaml b/src/pyefis/config/buttons/units.yaml similarity index 100% rename from pyefis/config/buttons/units.yaml rename to src/pyefis/config/buttons/units.yaml diff --git a/pyefis/config/databindings/default.yaml b/src/pyefis/config/databindings/default.yaml similarity index 100% rename from pyefis/config/databindings/default.yaml rename to src/pyefis/config/databindings/default.yaml diff --git a/pyefis/config/default.yaml b/src/pyefis/config/default.yaml similarity index 100% rename from pyefis/config/default.yaml rename to src/pyefis/config/default.yaml diff --git a/pyefis/config/examples/beagle.yaml b/src/pyefis/config/examples/beagle.yaml similarity index 100% rename from pyefis/config/examples/beagle.yaml rename to src/pyefis/config/examples/beagle.yaml diff --git a/pyefis/config/examples/ems.yaml b/src/pyefis/config/examples/ems.yaml similarity index 100% rename from pyefis/config/examples/ems.yaml rename to src/pyefis/config/examples/ems.yaml diff --git a/pyefis/config/examples/rotax_ems.yaml b/src/pyefis/config/examples/rotax_ems.yaml similarity index 100% rename from pyefis/config/examples/rotax_ems.yaml rename to src/pyefis/config/examples/rotax_ems.yaml diff --git a/pyefis/config/hmi/encoder_input.yaml b/src/pyefis/config/hmi/encoder_input.yaml similarity index 100% rename from pyefis/config/hmi/encoder_input.yaml rename to src/pyefis/config/hmi/encoder_input.yaml diff --git a/pyefis/config/includes/ahrs/virtual_vfr.yaml b/src/pyefis/config/includes/ahrs/virtual_vfr.yaml similarity index 100% rename from pyefis/config/includes/ahrs/virtual_vfr.yaml rename to src/pyefis/config/includes/ahrs/virtual_vfr.yaml diff --git a/pyefis/config/includes/arcs/vertical/ds1_TACH1-OILP1-HTOT1-VOLT_ds2_TACH1-OILT1-OAT-CURRNT-segmented.yaml b/src/pyefis/config/includes/arcs/vertical/ds1_TACH1-OILP1-HTOT1-VOLT_ds2_TACH1-OILT1-OAT-CURRNT-segmented.yaml similarity index 100% rename from pyefis/config/includes/arcs/vertical/ds1_TACH1-OILP1-HTOT1-VOLT_ds2_TACH1-OILT1-OAT-CURRNT-segmented.yaml rename to src/pyefis/config/includes/arcs/vertical/ds1_TACH1-OILP1-HTOT1-VOLT_ds2_TACH1-OILT1-OAT-CURRNT-segmented.yaml diff --git a/pyefis/config/includes/arcs/vertical/ds1_TACH1-OILP1-HTOT1-VOLT_ds2_TACH1-OILT1-OAT-CURRNT.yaml b/src/pyefis/config/includes/arcs/vertical/ds1_TACH1-OILP1-HTOT1-VOLT_ds2_TACH1-OILT1-OAT-CURRNT.yaml similarity index 100% rename from pyefis/config/includes/arcs/vertical/ds1_TACH1-OILP1-HTOT1-VOLT_ds2_TACH1-OILT1-OAT-CURRNT.yaml rename to src/pyefis/config/includes/arcs/vertical/ds1_TACH1-OILP1-HTOT1-VOLT_ds2_TACH1-OILT1-OAT-CURRNT.yaml diff --git a/pyefis/config/includes/arcs/vertical/four_high_one_state_preferences_ARC9-12.yaml b/src/pyefis/config/includes/arcs/vertical/four_high_one_state_preferences_ARC9-12.yaml similarity index 100% rename from pyefis/config/includes/arcs/vertical/four_high_one_state_preferences_ARC9-12.yaml rename to src/pyefis/config/includes/arcs/vertical/four_high_one_state_preferences_ARC9-12.yaml diff --git a/pyefis/config/includes/arcs/vertical/four_high_two_states_preferences_ARC1-8.yaml b/src/pyefis/config/includes/arcs/vertical/four_high_two_states_preferences_ARC1-8.yaml similarity index 100% rename from pyefis/config/includes/arcs/vertical/four_high_two_states_preferences_ARC1-8.yaml rename to src/pyefis/config/includes/arcs/vertical/four_high_two_states_preferences_ARC1-8.yaml diff --git a/pyefis/config/includes/arcs/vertical/power_temp_VOLT-CURRNT-OAT-CAT.yaml b/src/pyefis/config/includes/arcs/vertical/power_temp_VOLT-CURRNT-OAT-CAT.yaml similarity index 100% rename from pyefis/config/includes/arcs/vertical/power_temp_VOLT-CURRNT-OAT-CAT.yaml rename to src/pyefis/config/includes/arcs/vertical/power_temp_VOLT-CURRNT-OAT-CAT.yaml diff --git a/pyefis/config/includes/bars/vertical/2_CHT.yaml b/src/pyefis/config/includes/bars/vertical/2_CHT.yaml similarity index 100% rename from pyefis/config/includes/bars/vertical/2_CHT.yaml rename to src/pyefis/config/includes/bars/vertical/2_CHT.yaml diff --git a/pyefis/config/includes/bars/vertical/2_EGT.yaml b/src/pyefis/config/includes/bars/vertical/2_EGT.yaml similarity index 100% rename from pyefis/config/includes/bars/vertical/2_EGT.yaml rename to src/pyefis/config/includes/bars/vertical/2_EGT.yaml diff --git a/pyefis/config/includes/bars/vertical/4_CHT.yaml b/src/pyefis/config/includes/bars/vertical/4_CHT.yaml similarity index 100% rename from pyefis/config/includes/bars/vertical/4_CHT.yaml rename to src/pyefis/config/includes/bars/vertical/4_CHT.yaml diff --git a/pyefis/config/includes/bars/vertical/4_EGT.yaml b/src/pyefis/config/includes/bars/vertical/4_EGT.yaml similarity index 100% rename from pyefis/config/includes/bars/vertical/4_EGT.yaml rename to src/pyefis/config/includes/bars/vertical/4_EGT.yaml diff --git a/pyefis/config/includes/bars/vertical/dimmer_control.yaml b/src/pyefis/config/includes/bars/vertical/dimmer_control.yaml similarity index 100% rename from pyefis/config/includes/bars/vertical/dimmer_control.yaml rename to src/pyefis/config/includes/bars/vertical/dimmer_control.yaml diff --git a/pyefis/config/includes/bars/vertical/fuel_info_FUELP1-FUELF1-FUELQT-FUELQ1-FUELQ2-FUELQ3-segmented.yaml b/src/pyefis/config/includes/bars/vertical/fuel_info_FUELP1-FUELF1-FUELQT-FUELQ1-FUELQ2-FUELQ3-segmented.yaml similarity index 100% rename from pyefis/config/includes/bars/vertical/fuel_info_FUELP1-FUELF1-FUELQT-FUELQ1-FUELQ2-FUELQ3-segmented.yaml rename to src/pyefis/config/includes/bars/vertical/fuel_info_FUELP1-FUELF1-FUELQT-FUELQ1-FUELQ2-FUELQ3-segmented.yaml diff --git a/pyefis/config/includes/bars/vertical/fuel_info_FUELP1-FUELF1-FUELQT-FUELQ1-FUELQ2-FUELQ3.yaml b/src/pyefis/config/includes/bars/vertical/fuel_info_FUELP1-FUELF1-FUELQT-FUELQ1-FUELQ2-FUELQ3.yaml similarity index 100% rename from pyefis/config/includes/bars/vertical/fuel_info_FUELP1-FUELF1-FUELQT-FUELQ1-FUELQ2-FUELQ3.yaml rename to src/pyefis/config/includes/bars/vertical/fuel_info_FUELP1-FUELF1-FUELQT-FUELQ1-FUELQ2-FUELQ3.yaml diff --git a/pyefis/config/includes/bars/vertical/six_wide_preferences_BAR1-6.yaml b/src/pyefis/config/includes/bars/vertical/six_wide_preferences_BAR1-6.yaml similarity index 100% rename from pyefis/config/includes/bars/vertical/six_wide_preferences_BAR1-6.yaml rename to src/pyefis/config/includes/bars/vertical/six_wide_preferences_BAR1-6.yaml diff --git a/pyefis/config/includes/buttons/vertical/screen_changing_PFD-EMS-EMS2-ANDROID-RADIO-SIXPACK-Units.yaml b/src/pyefis/config/includes/buttons/vertical/screen_changing_PFD-EMS-EMS2-ANDROID-RADIO-SIXPACK-Units.yaml similarity index 100% rename from pyefis/config/includes/buttons/vertical/screen_changing_PFD-EMS-EMS2-ANDROID-RADIO-SIXPACK-Units.yaml rename to src/pyefis/config/includes/buttons/vertical/screen_changing_PFD-EMS-EMS2-ANDROID-RADIO-SIXPACK-Units.yaml diff --git a/pyefis/config/includes/buttons/vertical/screen_changing_PFD-EMS-MAP-RADIO-AP-Units.yaml b/src/pyefis/config/includes/buttons/vertical/screen_changing_PFD-EMS-MAP-RADIO-AP-Units.yaml similarity index 100% rename from pyefis/config/includes/buttons/vertical/screen_changing_PFD-EMS-MAP-RADIO-AP-Units.yaml rename to src/pyefis/config/includes/buttons/vertical/screen_changing_PFD-EMS-MAP-RADIO-AP-Units.yaml diff --git a/pyefis/config/includes/mgl/v16/radio-volumes-controls-display.yaml b/src/pyefis/config/includes/mgl/v16/radio-volumes-controls-display.yaml similarity index 100% rename from pyefis/config/includes/mgl/v16/radio-volumes-controls-display.yaml rename to src/pyefis/config/includes/mgl/v16/radio-volumes-controls-display.yaml diff --git a/pyefis/config/includes/mgl/v16/rectangle-radio-volume-primary-secondary-display.yaml b/src/pyefis/config/includes/mgl/v16/rectangle-radio-volume-primary-secondary-display.yaml similarity index 100% rename from pyefis/config/includes/mgl/v16/rectangle-radio-volume-primary-secondary-display.yaml rename to src/pyefis/config/includes/mgl/v16/rectangle-radio-volume-primary-secondary-display.yaml diff --git a/pyefis/config/includes/mgl/v16/square-active-radio-display.yaml b/src/pyefis/config/includes/mgl/v16/square-active-radio-display.yaml similarity index 100% rename from pyefis/config/includes/mgl/v16/square-active-radio-display.yaml rename to src/pyefis/config/includes/mgl/v16/square-active-radio-display.yaml diff --git a/pyefis/config/includes/mgl/v16/square-standby-radio-display.yaml b/src/pyefis/config/includes/mgl/v16/square-standby-radio-display.yaml similarity index 100% rename from pyefis/config/includes/mgl/v16/square-standby-radio-display.yaml rename to src/pyefis/config/includes/mgl/v16/square-standby-radio-display.yaml diff --git a/pyefis/config/includes/trim/pitch-slider-button-control.yaml b/src/pyefis/config/includes/trim/pitch-slider-button-control.yaml similarity index 100% rename from pyefis/config/includes/trim/pitch-slider-button-control.yaml rename to src/pyefis/config/includes/trim/pitch-slider-button-control.yaml diff --git a/pyefis/config/includes/trim/pitch-yaw-roll-combined.yaml b/src/pyefis/config/includes/trim/pitch-yaw-roll-combined.yaml similarity index 100% rename from pyefis/config/includes/trim/pitch-yaw-roll-combined.yaml rename to src/pyefis/config/includes/trim/pitch-yaw-roll-combined.yaml diff --git a/pyefis/config/includes/trim/roll-slider-button-control.yaml b/src/pyefis/config/includes/trim/roll-slider-button-control.yaml similarity index 100% rename from pyefis/config/includes/trim/roll-slider-button-control.yaml rename to src/pyefis/config/includes/trim/roll-slider-button-control.yaml diff --git a/pyefis/config/includes/trim/yaw-slider-button-control.yaml b/src/pyefis/config/includes/trim/yaw-slider-button-control.yaml similarity index 100% rename from pyefis/config/includes/trim/yaw-slider-button-control.yaml rename to src/pyefis/config/includes/trim/yaw-slider-button-control.yaml diff --git a/pyefis/config/keybindings/default.yaml b/src/pyefis/config/keybindings/default.yaml similarity index 100% rename from pyefis/config/keybindings/default.yaml rename to src/pyefis/config/keybindings/default.yaml diff --git a/pyefis/config/lists/radio/favorites.yaml b/src/pyefis/config/lists/radio/favorites.yaml similarity index 100% rename from pyefis/config/lists/radio/favorites.yaml rename to src/pyefis/config/lists/radio/favorites.yaml diff --git a/pyefis/config/lists/radio/listbox.yaml b/src/pyefis/config/lists/radio/listbox.yaml similarity index 100% rename from pyefis/config/lists/radio/listbox.yaml rename to src/pyefis/config/lists/radio/listbox.yaml diff --git a/pyefis/config/lists/radio/ohio.yaml b/src/pyefis/config/lists/radio/ohio.yaml similarity index 100% rename from pyefis/config/lists/radio/ohio.yaml rename to src/pyefis/config/lists/radio/ohio.yaml diff --git a/pyefis/config/logging/file-debug.yaml b/src/pyefis/config/logging/file-debug.yaml similarity index 100% rename from pyefis/config/logging/file-debug.yaml rename to src/pyefis/config/logging/file-debug.yaml diff --git a/pyefis/config/logging/stderr-debug.yaml b/src/pyefis/config/logging/stderr-debug.yaml similarity index 100% rename from pyefis/config/logging/stderr-debug.yaml rename to src/pyefis/config/logging/stderr-debug.yaml diff --git a/pyefis/config/logging/stderr-info.yaml b/src/pyefis/config/logging/stderr-info.yaml similarity index 100% rename from pyefis/config/logging/stderr-info.yaml rename to src/pyefis/config/logging/stderr-info.yaml diff --git a/pyefis/config/main/default.yaml b/src/pyefis/config/main/default.yaml similarity index 93% rename from pyefis/config/main/default.yaml rename to src/pyefis/config/main/default.yaml index 05254c70..352f7b58 100644 --- a/pyefis/config/main/default.yaml +++ b/src/pyefis/config/main/default.yaml @@ -16,15 +16,15 @@ # Screen Geometry # Defaults to screen size if screenWidth or screenHeight is not defined - #screenWidth: 1280 - #screenHeight: 720 + screenWidth: 1280 + screenHeight: 720 #screenWidth: 1024 #screenHeight: 768 #screenWidth: 1920 #screenHeight: 1080 # Set EFIS to occupy the entire screen without system border / menu - screenFullSize: True + screenFullSize: False # Screen background color RGB screenColor: (0,0,0) diff --git a/pyefis/config/outputs/default.yaml b/src/pyefis/config/outputs/default.yaml similarity index 100% rename from pyefis/config/outputs/default.yaml rename to src/pyefis/config/outputs/default.yaml diff --git a/pyefis/config/preferences.yaml b/src/pyefis/config/preferences.yaml similarity index 100% rename from pyefis/config/preferences.yaml rename to src/pyefis/config/preferences.yaml diff --git a/pyefis/config/preferences.yaml.custom b/src/pyefis/config/preferences.yaml.custom similarity index 100% rename from pyefis/config/preferences.yaml.custom rename to src/pyefis/config/preferences.yaml.custom diff --git a/pyefis/config/screens/android-left-buttons.yaml b/src/pyefis/config/screens/android-left-buttons.yaml similarity index 100% rename from pyefis/config/screens/android-left-buttons.yaml rename to src/pyefis/config/screens/android-left-buttons.yaml diff --git a/pyefis/config/screens/android.yaml b/src/pyefis/config/screens/android.yaml similarity index 100% rename from pyefis/config/screens/android.yaml rename to src/pyefis/config/screens/android.yaml diff --git a/pyefis/config/screens/default_list.yaml b/src/pyefis/config/screens/default_list.yaml similarity index 100% rename from pyefis/config/screens/default_list.yaml rename to src/pyefis/config/screens/default_list.yaml diff --git a/pyefis/config/screens/ems-left-buttons.yaml b/src/pyefis/config/screens/ems-left-buttons.yaml similarity index 100% rename from pyefis/config/screens/ems-left-buttons.yaml rename to src/pyefis/config/screens/ems-left-buttons.yaml diff --git a/pyefis/config/screens/ems.yaml b/src/pyefis/config/screens/ems.yaml similarity index 100% rename from pyefis/config/screens/ems.yaml rename to src/pyefis/config/screens/ems.yaml diff --git a/pyefis/config/screens/ems2-left-buttons.yaml b/src/pyefis/config/screens/ems2-left-buttons.yaml similarity index 100% rename from pyefis/config/screens/ems2-left-buttons.yaml rename to src/pyefis/config/screens/ems2-left-buttons.yaml diff --git a/pyefis/config/screens/ems2.yaml b/src/pyefis/config/screens/ems2.yaml similarity index 100% rename from pyefis/config/screens/ems2.yaml rename to src/pyefis/config/screens/ems2.yaml diff --git a/pyefis/config/screens/pfd-left-buttons.yaml b/src/pyefis/config/screens/pfd-left-buttons.yaml similarity index 100% rename from pyefis/config/screens/pfd-left-buttons.yaml rename to src/pyefis/config/screens/pfd-left-buttons.yaml diff --git a/pyefis/config/screens/pfd.yaml b/src/pyefis/config/screens/pfd.yaml similarity index 100% rename from pyefis/config/screens/pfd.yaml rename to src/pyefis/config/screens/pfd.yaml diff --git a/pyefis/config/screens/radio-left-buttons.yaml b/src/pyefis/config/screens/radio-left-buttons.yaml similarity index 100% rename from pyefis/config/screens/radio-left-buttons.yaml rename to src/pyefis/config/screens/radio-left-buttons.yaml diff --git a/pyefis/config/screens/radio.yaml b/src/pyefis/config/screens/radio.yaml similarity index 100% rename from pyefis/config/screens/radio.yaml rename to src/pyefis/config/screens/radio.yaml diff --git a/pyefis/config/screens/sixpack-left-buttons.yaml b/src/pyefis/config/screens/sixpack-left-buttons.yaml similarity index 100% rename from pyefis/config/screens/sixpack-left-buttons.yaml rename to src/pyefis/config/screens/sixpack-left-buttons.yaml diff --git a/pyefis/config/screens/sixpack.yaml b/src/pyefis/config/screens/sixpack.yaml similarity index 100% rename from pyefis/config/screens/sixpack.yaml rename to src/pyefis/config/screens/sixpack.yaml diff --git a/pyefis/config/screens/virtualvfr_db.yaml b/src/pyefis/config/screens/virtualvfr_db.yaml similarity index 100% rename from pyefis/config/screens/virtualvfr_db.yaml rename to src/pyefis/config/screens/virtualvfr_db.yaml diff --git a/pyefis/config/weston.ini b/src/pyefis/config/weston.ini similarity index 100% rename from pyefis/config/weston.ini rename to src/pyefis/config/weston.ini diff --git a/pyefis/gui.py b/src/pyefis/gui.py similarity index 100% rename from pyefis/gui.py rename to src/pyefis/gui.py diff --git a/pyefis/hmi/__init__.py b/src/pyefis/hmi/__init__.py similarity index 99% rename from pyefis/hmi/__init__.py rename to src/pyefis/hmi/__init__.py index b68942b0..b3b5fc42 100644 --- a/pyefis/hmi/__init__.py +++ b/src/pyefis/hmi/__init__.py @@ -24,6 +24,7 @@ actions = None from . import menu + def initialize(config): global actions log = logging.getLogger(__name__) diff --git a/pyefis/hmi/actionclass.py b/src/pyefis/hmi/actionclass.py similarity index 64% rename from pyefis/hmi/actionclass.py rename to src/pyefis/hmi/actionclass.py index 84bfff06..9d21d971 100644 --- a/pyefis/hmi/actionclass.py +++ b/src/pyefis/hmi/actionclass.py @@ -37,32 +37,31 @@ class ActionClass(QWidget): doExit = pyqtSignal(object) # arg = ,,,..: - def __init__(self): super(ActionClass, self).__init__() - self.signalMap = {"set airspeed mode":self.setAirspeedMode, - "set egt mode":self.setEgtMode, - "show screen":self.showScreen, - "show next screen":self.showNextScreen, - "show previous screen":self.showPrevScreen, - "set value":functions.setValue, - "change value":functions.changeValue, - "toggle bit":functions.toggleBool, - "activate menu item":self.activateMenuItem, - "activate menu":activateMenu, - "menu encoder":self.menuEncoder, - "set menu focus":self.setMenuFocus, - "set instrument units":self.setInstUnits, - "exit":self.doExit, - "evaluate":eval - } - + self.signalMap = { + "set airspeed mode": self.setAirspeedMode, + "set egt mode": self.setEgtMode, + "show screen": self.showScreen, + "show next screen": self.showNextScreen, + "show previous screen": self.showPrevScreen, + "set value": functions.setValue, + "change value": functions.changeValue, + "toggle bit": functions.toggleBool, + "activate menu item": self.activateMenuItem, + "activate menu": activateMenu, + "menu encoder": self.menuEncoder, + "set menu focus": self.setMenuFocus, + "set instrument units": self.setInstUnits, + "exit": self.doExit, + "evaluate": eval, + } def trigger(self, action, argument=""): a = self.signalMap[action.lower()] if isinstance(a, pyqtBoundSignal): a.emit(argument) - else: # It's not a signal so assume it's a function + else: # It's not a signal so assume it's a function a(argument) def findAction(self, action): diff --git a/pyefis/hmi/data.py b/src/pyefis/hmi/data.py similarity index 100% rename from pyefis/hmi/data.py rename to src/pyefis/hmi/data.py diff --git a/pyefis/hmi/functions.py b/src/pyefis/hmi/functions.py similarity index 96% rename from pyefis/hmi/functions.py rename to src/pyefis/hmi/functions.py index 49e4177f..c05849cc 100644 --- a/pyefis/hmi/functions.py +++ b/src/pyefis/hmi/functions.py @@ -16,9 +16,10 @@ import pyavtools.fix as fix + # Set a value in the FIX database. arg should be "key,value" def setValue(arg): - args = arg.split(',') + args = arg.split(",") i = fix.db.get_item(args[0].strip()) i.value = args[1].strip() # Always output values we set @@ -26,15 +27,16 @@ def setValue(arg): # action and most actions take place outside of pyEFIS i.output_value() + # Change a value in the FIX database by a certain value. arg should be "key,value" def changeValue(arg): - args = arg.split(',') + args = arg.split(",") i = fix.db.get_item(args[0]) i.value += i.dtype(args[1]) i.output_value() + def toggleBool(arg): bit = fix.db.get_item(arg) bit.value = not bit.value bit.output_value() - diff --git a/pyefis/hmi/keys.py b/src/pyefis/hmi/keys.py similarity index 100% rename from pyefis/hmi/keys.py rename to src/pyefis/hmi/keys.py diff --git a/pyefis/hmi/menu.py b/src/pyefis/hmi/menu.py similarity index 100% rename from pyefis/hmi/menu.py rename to src/pyefis/hmi/menu.py diff --git a/pyefis/hooks.py b/src/pyefis/hooks.py similarity index 100% rename from pyefis/hooks.py rename to src/pyefis/hooks.py diff --git a/pyefis/instruments/NumericalDisplay/__init__.py b/src/pyefis/instruments/NumericalDisplay/__init__.py similarity index 69% rename from pyefis/instruments/NumericalDisplay/__init__.py rename to src/pyefis/instruments/NumericalDisplay/__init__.py index af190f62..fbd2bf9b 100644 --- a/pyefis/instruments/NumericalDisplay/__init__.py +++ b/src/pyefis/instruments/NumericalDisplay/__init__.py @@ -18,8 +18,16 @@ from PyQt5.QtCore import * from PyQt5.QtWidgets import * + class NumericalDisplay(QGraphicsView): - def __init__(self, parent=None, total_decimals=3, scroll_decimal=1, font_family="DejaVu Sans Mono", font_size=15): + def __init__( + self, + parent=None, + total_decimals=3, + scroll_decimal=1, + font_family="DejaVu Sans Mono", + font_size=15, + ): super(NumericalDisplay, self).__init__(parent) self.setStyleSheet("border: 0px") self.font_family = font_family @@ -42,23 +50,23 @@ def resizeEvent(self, event): self.w = self.width() self.h = self.height() - t = QGraphicsSimpleTextItem ("9") - t.setFont (self.f) + t = QGraphicsSimpleTextItem("9") + t.setFont(self.f) font_width = t.boundingRect().width() font_height = t.boundingRect().height() while font_width * (self.total_decimals) >= self.w - 0.1: - self.font_size -= 0.1 - self.f.setPointSizeF(self.font_size) - t.setFont (self.f) - font_width = t.boundingRect().width() - font_height = t.boundingRect().height() + self.font_size -= 0.1 + self.f.setPointSizeF(self.font_size) + t.setFont(self.f) + font_width = t.boundingRect().width() + font_height = t.boundingRect().height() while font_width * (self.total_decimals) <= self.w - 0.1: - self.font_size += 0.1 - self.f.setPointSizeF(self.font_size) - t.setFont (self.f) - font_width = t.boundingRect().width() - font_height = t.boundingRect().height() + self.font_size += 0.1 + self.f.setPointSizeF(self.font_size) + t.setFont(self.f) + font_width = t.boundingRect().width() + font_height = t.boundingRect().height() self.font_size = qRound(self.font_size) self.f = QFont(self.font_family, self.font_size) @@ -66,47 +74,53 @@ def resizeEvent(self, event): border_width = 1 top = (self.h - font_height) / 2 rect_pen = QPen(QColor(Qt.white)) - rect_pen.setWidth (border_width) - self.scene.addRect(0, top, self.w, font_height, - rect_pen, QBrush(QColor(Qt.black))) + rect_pen.setWidth(border_width) + self.scene.addRect( + 0, top, self.w, font_height, rect_pen, QBrush(QColor(Qt.black)) + ) self.setScene(self.scene) - self.scrolling_area = NumericalScrollDisplay(self, self.scroll_decimal, - self.font_family, self.font_size) - self.scene.addWidget (self.scrolling_area) + self.scrolling_area = NumericalScrollDisplay( + self, self.scroll_decimal, self.font_family, self.font_size + ) + self.scene.addWidget(self.scrolling_area) self.digit_vertical_spacing = font_height - self.scrolling_area.resize(qRound(font_width*self.scroll_decimal+border_width), self.h) - sax = qRound(self.w-font_width*self.scroll_decimal-border_width) + self.scrolling_area.resize( + qRound(font_width * self.scroll_decimal + border_width), self.h + ) + sax = qRound(self.w - font_width * self.scroll_decimal - border_width) self.scrolling_area.move(sax, 0) - prest = '0' * (self.total_decimals - self.scroll_decimal) + prest = "0" * (self.total_decimals - self.scroll_decimal) if self._bad or self._old: - prest = '' - self.pre_scroll_text = self.scene.addSimpleText (prest, self.f) + prest = "" + self.pre_scroll_text = self.scene.addSimpleText(prest, self.f) self.pre_scroll_text.setPen(QPen(QColor(Qt.white))) self.pre_scroll_text.setBrush(QBrush(QColor(Qt.white))) - - self.pre_scroll_text.setX (0) - self.pre_scroll_text.setY ((self.h-font_height)/2.0) - x = sax-border_width/2 - l = self.scene.addLine (x,0, x,top) + self.pre_scroll_text.setX(0) + self.pre_scroll_text.setY((self.h - font_height) / 2.0) + + x = sax - border_width / 2 + l = self.scene.addLine(x, 0, x, top) l.setPen(rect_pen) top += font_height - l = self.scene.addLine (x,top, x,self.h) + l = self.scene.addLine(x, top, x, self.h) l.setPen(rect_pen) - l = self.scene.addLine (x,0, self.w,0) + l = self.scene.addLine(x, 0, self.w, 0) l.setPen(rect_pen) - l = self.scene.addLine (x,self.h, self.w,self.h) + l = self.scene.addLine(x, self.h, self.w, self.h) l.setPen(rect_pen) # Get a failure scene ready in case it's needed self.fail_scene = QGraphicsScene(0, 0, self.w, self.h) - self.fail_scene.addRect(0,0, self.w, self.h, QPen(QColor(Qt.white)), QBrush(QColor(50,50,50))) + self.fail_scene.addRect( + 0, 0, self.w, self.h, QPen(QColor(Qt.white)), QBrush(QColor(50, 50, 50)) + ) warn_font = QFont(self.font_family, 10, QFont.Bold) t = self.fail_scene.addSimpleText("XXX", warn_font) - t.setPen (QPen(QColor(Qt.red))) - t.setBrush (QBrush(QColor(Qt.red))) + t.setPen(QPen(QColor(Qt.red))) + t.setBrush(QBrush(QColor(Qt.red))) r = t.boundingRect() - t.setPos ((self.w-r.width())/2, (self.h-r.height())/2) + t.setPos((self.w - r.width()) / 2, (self.h - r.height()) / 2) """ Not sure if this is needed: self.bad_text = self.scene.addSimpleText("BAD", warn_font) @@ -128,23 +142,23 @@ def resizeEvent(self, event): self.old_text.hide() """ - def redraw(self): - prevalue = int(self._value / (10 ** self.scroll_decimal)) - scroll_value = self._value - (prevalue * (10 ** self.scroll_decimal)) + prevalue = int(self._value / (10**self.scroll_decimal)) + scroll_value = self._value - (prevalue * (10**self.scroll_decimal)) if self.scroll_decimal > 1: - scroll_value = (scroll_value / (10 ** (self.scroll_decimal-1))) + scroll_value = scroll_value / (10 ** (self.scroll_decimal - 1)) prest = str(prevalue) prelen = self.total_decimals - self.scroll_decimal - prest = "{1:0{0}d}".format(prelen,prevalue) + prest = "{1:0{0}d}".format(prelen, prevalue) if scroll_value < 0: scroll_value = abs(scroll_value) if self._value < 0 and prevalue >= 0: # IF negative ensure the sign it displayed - prest = "-{1:0{0}d}".format(prelen-1,prevalue) + prest = "-{1:0{0}d}".format(prelen - 1, prevalue) if self._bad or self._old: - prest = '' + prest = "" self.pre_scroll_text.setText(prest) + print(dir(self.pre_scroll_text.text)) if not (self._bad or self._old or self._fail): self.scrolling_area.value = scroll_value @@ -159,8 +173,8 @@ def setValue(self, val): value = property(getValue, setValue) def flagDisplay(self): - if (self._bad or self._old or self._fail): - self.pre_scroll_text.setText('') + if self._bad or self._old or self._fail: + self.pre_scroll_text.setText("") self.scrolling_area.hide() else: self.pre_scroll_text.setBrush(QBrush(QColor(Qt.white))) @@ -169,30 +183,35 @@ def flagDisplay(self): def getBad(self): return self._bad + def setBad(self, b): if self._bad != b: self._bad = b - #if b: + # if b: # self.bad_text.show() - #else: + # else: # self.bad_text.hide() self.flagDisplay() + bad = property(getBad, setBad) def getOld(self): return self._old + def setOld(self, b): if self._old != b: self._old = b - #if b: + # if b: # self.old_text.show() - #else: + # else: # self.old_text.hide() self.flagDisplay() + old = property(getOld, setOld) def getFail(self): return self._fail + def setFail(self, b): if self._fail != b: self._fail = b @@ -201,8 +220,10 @@ def setFail(self, b): else: self.setScene(self.scene) self.flagDisplay() + fail = property(getFail, setFail) + class NumericalScrollDisplay(QGraphicsView): def __init__(self, parent=None, scroll_decimal=1, font_family="Sans", font_size=10): super(NumericalScrollDisplay, self).__init__() @@ -219,46 +240,47 @@ def resizeEvent(self, event): self.w = self.width() self.h = self.height() - t = QGraphicsSimpleTextItem ("9") - t.setFont (self.f) + t = QGraphicsSimpleTextItem("9") + t.setFont(self.f) font_width = t.boundingRect().width() font_height = t.boundingRect().height() self.digit_vertical_spacing = font_height * 0.8 nsh = self.digit_vertical_spacing * 12 + self.h - self.scene = QGraphicsScene(0,0,self.w, nsh) - self.scene.addRect(0, 0, self.w, nsh, - QPen(QColor(Qt.black)), QBrush(QColor(Qt.black))) + self.scene = QGraphicsScene(0, 0, self.w, nsh) + self.scene.addRect( + 0, 0, self.w, nsh, QPen(QColor(Qt.black)), QBrush(QColor(Qt.black)) + ) for i in range(20): - y = self.y_offset(i) - font_height/2 + y = self.y_offset(i) - font_height / 2 if y < 0: break - text = str(i%10) + text = str(i % 10) if len(text) < self.scroll_decimal: add0s = self.scroll_decimal - len(text) - text = text + "0"*add0s + text = text + "0" * add0s t = self.scene.addSimpleText(text, self.f) t.setX(2) t.setPen(QPen(QColor(Qt.white))) t.setBrush(QBrush(QColor(Qt.white))) t.setY(y) - for i in range(9,0,-1): - sv = i-10 - y = self.y_offset(sv) - font_height/2 - if y > nsh-font_height: + for i in range(9, 0, -1): + sv = i - 10 + y = self.y_offset(sv) - font_height / 2 + if y > nsh - font_height: break text = str(i) if len(text) < self.scroll_decimal: add0s = self.scroll_decimal - len(text) - text = text + "0"*add0s + text = text + "0" * add0s t = self.scene.addSimpleText(text, self.f) t.setPen(QPen(QColor(Qt.white))) t.setBrush(QBrush(QColor(Qt.white))) t.setX(2) t.setY(y) - self.setScene (self.scene) + self.setScene(self.scene) def y_offset(self, sv): - return ((10.0 - (sv)) * self.digit_vertical_spacing + self.h/2) + return (10.0 - (sv)) * self.digit_vertical_spacing + self.h / 2 def redraw(self): scroll_value = self._value diff --git a/pyefis/instruments/__init__.py b/src/pyefis/instruments/__init__.py similarity index 100% rename from pyefis/instruments/__init__.py rename to src/pyefis/instruments/__init__.py diff --git a/pyefis/instruments/ai/VirtualVfr.py b/src/pyefis/instruments/ai/VirtualVfr.py similarity index 100% rename from pyefis/instruments/ai/VirtualVfr.py rename to src/pyefis/instruments/ai/VirtualVfr.py diff --git a/pyefis/instruments/ai/__init__.py b/src/pyefis/instruments/ai/__init__.py similarity index 100% rename from pyefis/instruments/ai/__init__.py rename to src/pyefis/instruments/ai/__init__.py diff --git a/pyefis/instruments/airspeed/__init__.py b/src/pyefis/instruments/airspeed/__init__.py similarity index 68% rename from pyefis/instruments/airspeed/__init__.py rename to src/pyefis/instruments/airspeed/__init__.py index 613ffbc1..5d6c2769 100644 --- a/pyefis/instruments/airspeed/__init__.py +++ b/src/pyefis/instruments/airspeed/__init__.py @@ -26,9 +26,17 @@ from pyefis.instruments.NumericalDisplay import NumericalDisplay from pyefis.instruments import helpers + class Airspeed(QWidget): FULL_WIDTH = 400 - def __init__(self, parent=None, font_percent=0.07, bg_color=Qt.black, font_family="DejaVu Sans Condensed"): + + def __init__( + self, + parent=None, + font_percent=0.07, + bg_color=Qt.black, + font_family="DejaVu Sans Condensed", + ): super(Airspeed, self).__init__(parent) self.setStyleSheet("border: 0px") self.font_family = font_family @@ -37,22 +45,28 @@ def __init__(self, parent=None, font_percent=0.07, bg_color=Qt.black, font_famil self.bg_color = bg_color self._airspeed = 0 self.item = fix.db.get_item("IAS") + self._airspeed = self.item.value self.item.valueChanged[float].connect(self.setAirspeed) self.item.oldChanged[bool].connect(self.repaint) self.item.badChanged[bool].connect(self.repaint) self.item.failChanged[bool].connect(self.repaint) # V Speeds need to be init before paint - self.Vs = self.item.get_aux_value('Vs') - if self.Vs is None: self.Vs = 0 - self.Vs0 = self.item.get_aux_value('Vs0') - if self.Vs0 is None: self.Vs0 = 0 - self.Vno = self.item.get_aux_value('Vno') - if self.Vno is None: self.Vno = 0 - self.Vne = self.item.get_aux_value('Vne') - if self.Vne is None: self.Vne = 200 - self.Vfe = self.item.get_aux_value('Vfe') - if self.Vfe is None: self.Vfe = 0 + self.Vs = self.item.get_aux_value("Vs") + if self.Vs is None: + self.Vs = 0 + self.Vs0 = self.item.get_aux_value("Vs0") + if self.Vs0 is None: + self.Vs0 = 0 + self.Vno = self.item.get_aux_value("Vno") + if self.Vno is None: + self.Vno = 0 + self.Vne = self.item.get_aux_value("Vne") + if self.Vne is None: + self.Vne = 200 + self.Vfe = self.item.get_aux_value("Vfe") + if self.Vfe is None: + self.Vfe = 0 def getRatio(self): # Return X for 1:x specifying the ratio for this instrument @@ -67,7 +81,7 @@ def paintEvent(self, event): dial = QPainter(self) dial.setRenderHint(QPainter.Antialiasing) - #Draw the Black Background + # Draw the Black Background dial.fillRect(0, 0, w, h, QColor(self.bg_color)) # Setup Pens @@ -77,21 +91,21 @@ def paintEvent(self, event): fontMetrics = QFontMetricsF(f) dialPen = QPen(QColor(Qt.white)) - dialPen.setWidthF(s*0.01) + dialPen.setWidthF(s * 0.01) needleBrush = QBrush(QColor(Qt.white)) vnePen = QPen(QColor(Qt.red)) - vnePen.setWidthF(s*0.025) + vnePen.setWidthF(s * 0.025) vsoPen = QPen(QColor(Qt.white)) - vsoPen.setWidthF(s*0.015) + vsoPen.setWidthF(s * 0.015) vnoPen = QPen(QColor(Qt.green)) - vnoPen.setWidthF(s*0.015) + vnoPen.setWidthF(s * 0.015) yellowPen = QPen(QColor(Qt.yellow)) - yellowPen.setWidthF(s*0.015) + yellowPen.setWidthF(s * 0.015) # Dial Setup @@ -102,22 +116,24 @@ def paintEvent(self, event): Vno_angle = (-(((self.Vno - 30) * 2.5) + 25) + 90) * 16 Vne_angle = (-(((self.Vne - 30) * 2.5) + 25) + 90) * 16 - radius = int(round(min(w,h) * .45)) - diameter = radius*2 + radius = int(round(min(w, h) * 0.45)) + diameter = radius * 2 inner_offset = 3 - center_x = w/2 - center_y = h/2 + center_x = w / 2 + center_y = h / 2 # Vspeeds Arcs dial.setPen(vnoPen) - dial_rect = QRectF(center_x-radius, center_y-radius, - diameter, diameter) + dial_rect = QRectF(center_x - radius, center_y - radius, diameter, diameter) dial.drawArc(dial_rect, qRound(Vs_angle), qRound(-(Vs_angle - Vno_angle))) dial.setPen(vsoPen) - inner_rect = QRectF(center_x-radius+inner_offset, center_y-radius+inner_offset, - diameter-inner_offset*3, diameter-inner_offset*3) - dial.drawArc(inner_rect, - qRound(Vs0_angle), qRound(-(Vs0_angle - Vfe_angle))) + inner_rect = QRectF( + center_x - radius + inner_offset, + center_y - radius + inner_offset, + diameter - inner_offset * 3, + diameter - inner_offset * 3, + ) + dial.drawArc(inner_rect, qRound(Vs0_angle), qRound(-(Vs0_angle - Vfe_angle))) dial.setPen(yellowPen) dial.drawArc(dial_rect, qRound(Vno_angle), qRound(-(Vno_angle - Vne_angle))) dial.save() @@ -128,21 +144,20 @@ def paintEvent(self, event): a_s = 0 while count < 360: if count % 25 == 0 and a_s <= 140: - dial.drawLine(0, -radius, 0, -(radius-15)) + dial.drawLine(0, -radius, 0, -(radius - 15)) x = fontMetrics.width(str(a_s)) / 2 y = f.pixelSize() - dial.drawText(qRound(-x), qRound(-(radius-15 - y)), - str(a_s)) + dial.drawText(qRound(-x), qRound(-(radius - 15 - y)), str(a_s)) a_s += 10 if count == 0: a_s = 30 count = count + 19 dial.rotate(19) elif count % 12.5 == 0 and a_s <= 140: - dial.drawLine(0, -(radius), 0, -(radius-10)) + dial.drawLine(0, -(radius), 0, -(radius - 10)) if count == (-Vne_angle / 16) + 90: dial.setPen(vnePen) - dial.drawLine(0, -(radius), 0, -(radius-15)) + dial.drawLine(0, -(radius), 0, -(radius - 15)) dial.setPen(dialPen) dial.rotate(0.5) count += 0.5 @@ -150,10 +165,10 @@ def paintEvent(self, event): if self.item.fail: warn_font = QFont(self.font_family, 30, QFont.Bold) dial.resetTransform() - dial.setPen (QPen(QColor(Qt.red))) - dial.setBrush (QBrush(QColor(Qt.red))) - dial.setFont (warn_font) - dial.drawText (0,0,w,h, Qt.AlignCenter, "XXX") + dial.setPen(QPen(QColor(Qt.red))) + dial.setBrush(QBrush(QColor(Qt.red))) + dial.setFont(warn_font) + dial.drawText(0, 0, w, h, Qt.AlignCenter, "XXX") dial.restore() return @@ -164,9 +179,10 @@ def paintEvent(self, event): else: dial.setPen(QPen(QColor(Qt.white))) dial.setBrush(QBrush(QColor(Qt.white))) - #Needle Movement - needle = QPolygon([QPoint(5, 0), QPoint(0, +5), QPoint(-5, 0), - QPoint(0, -(radius-15))]) + # Needle Movement + needle = QPolygon( + [QPoint(5, 0), QPoint(0, +5), QPoint(-5, 0), QPoint(0, -(radius - 15))] + ) if self.airspeed <= 30: # Airspeeds Below 30 Knots needle_angle = self._airspeed * 0.83 @@ -203,18 +219,20 @@ def setAirspeed(self, airspeed): airspeed = property(getAirspeed, setAirspeed) - def setAsOld(self,b): + def setAsOld(self, b): pass - def setAsBad(self,b): + def setAsBad(self, b): pass - def setAsFail(self,b): + def setAsFail(self, b): pass class Airspeed_Tape(QGraphicsView): - def __init__(self, parent=None, font_percent=None,font_family="DejaVu Sans Condensed"): + def __init__( + self, parent=None, font_percent=None, font_family="DejaVu Sans Condensed" + ): super(Airspeed_Tape, self).__init__(parent) self.myparent = parent self.font_family = font_family @@ -231,22 +249,27 @@ def __init__(self, parent=None, font_percent=None,font_family="DejaVu Sans Conde self._airspeed = self.item.value # V Speeds - self.Vs = self.item.get_aux_value('Vs') - if self.Vs is None: self.Vs = 0 - self.Vs0 = self.item.get_aux_value('Vs0') - if self.Vs0 is None: self.Vs0 = 0 - self.Vno = self.item.get_aux_value('Vno') - if self.Vno is None: self.Vno = 0 - self.Vne = self.item.get_aux_value('Vne') - if self.Vne is None: self.Vne = 200 - self.Vfe = self.item.get_aux_value('Vfe') - if self.Vfe is None: self.Vfe = 0 - - self.max = int(round(self.Vne*1.25)) + self.Vs = self.item.get_aux_value("Vs") + if self.Vs is None: + self.Vs = 0 + self.Vs0 = self.item.get_aux_value("Vs0") + if self.Vs0 is None: + self.Vs0 = 0 + self.Vno = self.item.get_aux_value("Vno") + if self.Vno is None: + self.Vno = 0 + self.Vne = self.item.get_aux_value("Vne") + if self.Vne is None: + self.Vne = 200 + self.Vfe = self.item.get_aux_value("Vfe") + if self.Vfe is None: + self.Vfe = 0 + + self.max = int(round(self.Vne * 1.25)) self.backgroundOpacity = 0.3 self.foregroundOpacity = 0.6 - self.pph = 10 # Pixels per unit + self.pph = 10 # Pixels per unit self.fontsize = 15 self.majorDiv = 10 self.minorDiv = 5 @@ -261,71 +284,96 @@ def resizeEvent(self, event): self.markWidth = w / 5 f = QFont(self.font_family) if self.font_mask: - self.fontsize = helpers.fit_to_mask(self.width()*.50,self.height()*0.05,self.font_mask,self.font_family) + self.fontsize = helpers.fit_to_mask( + self.width() * 0.50, + self.height() * 0.05, + self.font_mask, + self.font_family, + ) f.setPointSizeF(self.fontsize) else: f.setPixelSize(self.fontsize) tape_height = self.max * self.pph + h - tape_start = self.max * self.pph + h/2 + tape_start = self.max * self.pph + h / 2 dialPen = QPen(QColor(Qt.white)) self.scene = QGraphicsScene(0, 0, w, tape_height) - x = self.scene.addRect(0, 0, w, tape_height, - QPen(QColor(32, 32, 32)), QBrush(QColor(32, 32, 32))) + x = self.scene.addRect( + 0, 0, w, tape_height, QPen(QColor(32, 32, 32)), QBrush(QColor(32, 32, 32)) + ) x.setOpacity(self.backgroundOpacity) # Add Markings # Green Bar - r = QRectF(QPointF(0, -self.Vno * self.pph + tape_start), - QPointF(self.markWidth, -self.Vs0 * self.pph + tape_start)) - x = self.scene.addRect(r, QPen(QColor(0,155,0)), QBrush(QColor(0,155,0))) + r = QRectF( + QPointF(0, -self.Vno * self.pph + tape_start), + QPointF(self.markWidth, -self.Vs0 * self.pph + tape_start), + ) + x = self.scene.addRect(r, QPen(QColor(0, 155, 0)), QBrush(QColor(0, 155, 0))) x.setOpacity(self.foregroundOpacity) # White Bar - r = QRectF(QPointF(self.markWidth / 2, -self.Vfe * self.pph + tape_start), - QPointF(self.markWidth, -self.Vs0 * self.pph + tape_start)) + r = QRectF( + QPointF(self.markWidth / 2, -self.Vfe * self.pph + tape_start), + QPointF(self.markWidth, -self.Vs0 * self.pph + tape_start), + ) x = self.scene.addRect(r, QPen(Qt.white), QBrush(Qt.white)) x.setOpacity(self.foregroundOpacity) - # Yellow Bar - r = QRectF(QPointF(0, -self.Vno * self.pph + tape_start), - QPointF(self.markWidth, -self.Vne * self.pph + tape_start)) + r = QRectF( + QPointF(0, -self.Vno * self.pph + tape_start), + QPointF(self.markWidth, -self.Vne * self.pph + tape_start), + ) x = self.scene.addRect(r, QPen(Qt.yellow), QBrush(Qt.yellow)) x.setOpacity(self.foregroundOpacity) # Draw the little white lines and the text for i in range(self.max, -1, -1): if i % self.majorDiv == 0: - l = self.scene.addLine(0, (- i * self.pph) + tape_start, w / 2, - (- i * self.pph) + tape_start, dialPen) + l = self.scene.addLine( + 0, + (-i * self.pph) + tape_start, + w / 2, + (-i * self.pph) + tape_start, + dialPen, + ) l.setOpacity(self.foregroundOpacity) t = self.scene.addText(str(i)) t.setFont(f) self.scene.setFont(f) t.setDefaultTextColor(QColor(Qt.white)) t.setX(w - t.boundingRect().width()) - t.setY(((- i * self.pph) + tape_start) - - t.boundingRect().height() / 2) + t.setY(((-i * self.pph) + tape_start) - t.boundingRect().height() / 2) t.setOpacity(self.foregroundOpacity) - elif i % self.minorDiv ==0: - l = self.scene.addLine(0, (- i * self.pph) + tape_start, - w / 3, (- i * self.pph) + tape_start, dialPen) + elif i % self.minorDiv == 0: + l = self.scene.addLine( + 0, + (-i * self.pph) + tape_start, + w / 3, + (-i * self.pph) + tape_start, + dialPen, + ) l.setOpacity(self.foregroundOpacity) # Red Line vnePen = QPen(QColor(Qt.red)) vnePen.setWidth(4) - l = self.scene.addLine(0, -self.Vne * self.pph + tape_start, - 30, -self.Vne * self.pph + tape_start, vnePen) + l = self.scene.addLine( + 0, + -self.Vne * self.pph + tape_start, + 30, + -self.Vne * self.pph + tape_start, + vnePen, + ) l.setOpacity(self.foregroundOpacity) self.numerical_display = NumericalDisplay(self) - nbh = w/2 - self.numerical_display.resize (qRound(w/2), qRound(nbh)) - self.numeric_box_pos = QPoint(qRound(w-w/2), qRound(h/2-nbh/2)) + nbh = w / 2 + self.numerical_display.resize(qRound(w / 2), qRound(nbh)) + self.numeric_box_pos = QPoint(qRound(w - w / 2), qRound(h / 2 - nbh / 2)) self.numerical_display.move(self.numeric_box_pos) - self.numeric_box_pos.setY(qRound(self.numeric_box_pos.y()+nbh/2)) + self.numeric_box_pos.setY(qRound(self.numeric_box_pos.y() + nbh / 2)) self.numerical_display.show() self.numerical_display.value = self._airspeed self.setAsOld(self.item.old) @@ -333,8 +381,7 @@ def resizeEvent(self, event): self.setAsFail(self.item.fail) self.setScene(self.scene) - self.centerOn(self.scene.width() / 2, - -self._airspeed * self.pph + tape_start) + self.centerOn(self.scene.width() / 2, -self._airspeed * self.pph + tape_start) self.item.valueChanged[float].connect(self.setAirspeed) self.item.oldChanged[bool].connect(self.setAsOld) self.item.badChanged[bool].connect(self.setAsBad) @@ -343,11 +390,10 @@ def resizeEvent(self, event): def redraw(self): if not self.isVisible(): return - tape_start = self.max * self.pph + self.height()/2 + tape_start = self.max * self.pph + self.height() / 2 self.resetTransform() - self.centerOn(self.scene.width() / 2, - -self._airspeed * self.pph + tape_start) + self.centerOn(self.scene.width() / 2, -self._airspeed * self.pph + tape_start) self.numerical_display.value = self._airspeed # Index Line that doesn't move to make it easy to read the airspeed. @@ -362,10 +408,16 @@ def paintEvent(self, event): p.translate(self.numeric_box_pos.x(), self.numeric_box_pos.y()) p.setPen(marks) p.setBrush(QBrush(Qt.black)) - triangle_size = w/8 - p.drawConvexPolygon(QPolygon([QPoint(0, qRound(-triangle_size-3)), - QPoint(0, qRound(triangle_size-2)), - QPoint(qRound(-triangle_size), -1)])) + triangle_size = w / 8 + p.drawConvexPolygon( + QPolygon( + [ + QPoint(0, qRound(-triangle_size - 3)), + QPoint(0, qRound(triangle_size - 2)), + QPoint(qRound(-triangle_size), -1), + ] + ) + ) def getAirspeed(self): return self._airspeed @@ -377,13 +429,13 @@ def setAirspeed(self, airspeed): airspeed = property(getAirspeed, setAirspeed) - def setAsOld(self,b): + def setAsOld(self, b): self.numerical_display.old = b - def setAsBad(self,b): + def setAsBad(self, b): self.numerical_display.bad = b - def setAsFail(self,b): + def setAsFail(self, b): self.numerical_display.fail = b # We don't want this responding to keystrokes @@ -397,8 +449,9 @@ def wheelEvent(self, event): class Airspeed_Box(QWidget): """Represents a simple numeric display type gauge. The benefit of using this - over a normal text display is that this will change colors properly when - limits are reached or when failures occur""" + over a normal text display is that this will change colors properly when + limits are reached or when failures occur""" + def __init__(self, parent=None, font_family="DejaVu Sans Condensed"): super(Airspeed_Box, self).__init__(parent) self.font_family = font_family @@ -410,7 +463,7 @@ def __init__(self, parent=None, font_family="DejaVu Sans Condensed"): self.fix_item.valueChanged[float].connect(self.setASData) self.alignment = Qt.AlignLeft | Qt.AlignVCenter - self.valueAlignment = Qt.AlignRight | Qt.AlignVCenter + self.valueAlignment = Qt.AlignRight | Qt.AlignVCenter self.small_font_percent = 0.4 self.color = Qt.white self.modeText = self.modes[self._modeIndicator] @@ -423,9 +476,10 @@ def resizeEvent(self, event): self.smallFont.setPixelSize(qRound(self.height() * self.small_font_percent)) qm = QFontMetrics(self.smallFont) - self.modeTextRect = QRectF(0, 0, self.width()-5, self.height()*0.4) - self.valueTextRect = QRectF(0, self.height()*0.5, - self.width()-5, self.height()*0.4) + self.modeTextRect = QRectF(0, 0, self.width() - 5, self.height() * 0.4) + self.valueTextRect = QRectF( + 0, self.height() * 0.5, self.width() - 5, self.height() * 0.4 + ) def paintEvent(self, event): p = QPainter(self) @@ -443,7 +497,7 @@ def paintEvent(self, event): opt = QTextOption(self.alignment) p.drawText(self.modeTextRect, self.modeText, opt) - #Draw Value + # Draw Value p.setFont(self.smallFont) opt = QTextOption(self.valueAlignment) p.drawText(self.valueTextRect, self.valueText, opt) @@ -452,7 +506,8 @@ def setMode(self, Mode): if Mode == "": self.fix_item.valueChanged[float].disconnect(self.setASData) self._modeIndicator += 1 - if self._modeIndicator == 3: self._modeIndicator = 0 + if self._modeIndicator == 3: + self._modeIndicator = 0 else: if Mode != self._modeIndicator: self.fix_item.valueChanged[float].disconnect(self.setASData) diff --git a/pyefis/instruments/altimeter/__init__.py b/src/pyefis/instruments/altimeter/__init__.py similarity index 100% rename from pyefis/instruments/altimeter/__init__.py rename to src/pyefis/instruments/altimeter/__init__.py diff --git a/pyefis/instruments/button/__init__.py b/src/pyefis/instruments/button/__init__.py similarity index 100% rename from pyefis/instruments/button/__init__.py rename to src/pyefis/instruments/button/__init__.py diff --git a/pyefis/instruments/gauges/__init__.py b/src/pyefis/instruments/gauges/__init__.py similarity index 100% rename from pyefis/instruments/gauges/__init__.py rename to src/pyefis/instruments/gauges/__init__.py diff --git a/pyefis/instruments/gauges/abstract.py b/src/pyefis/instruments/gauges/abstract.py similarity index 98% rename from pyefis/instruments/gauges/abstract.py rename to src/pyefis/instruments/gauges/abstract.py index 3b298794..ef43826b 100644 --- a/pyefis/instruments/gauges/abstract.py +++ b/src/pyefis/instruments/gauges/abstract.py @@ -158,7 +158,10 @@ def setValue(self, value): def getValueText(self): if self.fail: - return 'xxx' + if self.font_mask: + return ''.join('X' if x != '.' else x for x in self.font_mask) + else: + return 'xxx' else: if self.encoder_selected and self.encoder_num_mask: if self.encoder_num_blink: @@ -179,7 +182,10 @@ def getValueText(self): return '{0:.{1}f}'.format(float(self.encoder_set_value), self.decimal_places) else: # return properly formatted value - return '{0:.{1}f}'.format(float(self.value), self.decimal_places) + if self.font_mask != None: + return '{0:{1}.{2}f}'.format(float(self.value),len(self.font_mask), self.decimal_places) + else: + return '{0:.{1}f}'.format(float(self.value), self.decimal_places) valueText = property(getValueText) diff --git a/pyefis/instruments/gauges/arc.py b/src/pyefis/instruments/gauges/arc.py similarity index 99% rename from pyefis/instruments/gauges/arc.py rename to src/pyefis/instruments/gauges/arc.py index b54d6841..6a0f1668 100644 --- a/pyefis/instruments/gauges/arc.py +++ b/src/pyefis/instruments/gauges/arc.py @@ -195,7 +195,7 @@ def paintEvent(self, e): if self.name_font_mask: f.setPointSizeF(self.nameFontSize) else: - f.setPixelSize(self.nameFontSize) + f.setPixelSize(int(self.nameFontSize)) fm = QFontMetrics(f) if self.name_font_mask: if self.name_font_ghost_mask: diff --git a/pyefis/instruments/gauges/egt.py b/src/pyefis/instruments/gauges/egt.py similarity index 100% rename from pyefis/instruments/gauges/egt.py rename to src/pyefis/instruments/gauges/egt.py diff --git a/pyefis/instruments/gauges/horizontalBar.py b/src/pyefis/instruments/gauges/horizontalBar.py similarity index 100% rename from pyefis/instruments/gauges/horizontalBar.py rename to src/pyefis/instruments/gauges/horizontalBar.py diff --git a/pyefis/instruments/gauges/numeric.py b/src/pyefis/instruments/gauges/numeric.py similarity index 87% rename from pyefis/instruments/gauges/numeric.py rename to src/pyefis/instruments/gauges/numeric.py index f7e26af8..67143aa4 100644 --- a/pyefis/instruments/gauges/numeric.py +++ b/src/pyefis/instruments/gauges/numeric.py @@ -50,7 +50,7 @@ def resizeEvent(self, event): # TODO Edit to get the units closer to the value self.valueTextRect = QRectF(0, 0, self.width()-unitsWidth-5, self.height()) self.unitsTextRect = QRectF(self.valueTextRect.width(), 0, - self.width()-self.valueTextRect.width(), self.height()) + self.width()-self.valueTextRect.width()-2, self.height() * self.small_font_percent) else: self.valueTextRect = QRectF(0, 0, self.width(), self.height()) @@ -82,4 +82,13 @@ def paintEvent(self, event): if self.show_units: p.setFont(self.smallFont) opt = QTextOption(self.unitsAlignment) + if self.units_font_ghost_mask: + alpha = self.textColor.alpha() + self.textColor.setAlpha(self.font_ghost_alpha) + pen.setColor(self.textColor) + p.setPen(pen) + p.drawText(self.unitsTextRect, self.units_font_ghost_mask , opt) + self.textColor.setAlpha(alpha) + pen.setColor(self.textColor) + p.setPen(pen) p.drawText(self.unitsTextRect, self.units, opt) diff --git a/pyefis/instruments/gauges/verticalBar.py b/src/pyefis/instruments/gauges/verticalBar.py similarity index 99% rename from pyefis/instruments/gauges/verticalBar.py rename to src/pyefis/instruments/gauges/verticalBar.py index f1707cef..5c1fb12d 100644 --- a/pyefis/instruments/gauges/verticalBar.py +++ b/src/pyefis/instruments/gauges/verticalBar.py @@ -290,7 +290,7 @@ def paintEvent(self, event): pen.setWidth(1) p.setPen(pen) p.setBrush(brush) - if self.normalizeMode: + if self.normalizeMode and self.normalize_range > 0: nval = self.peakValue - self.normalizeReference start = self.barTop + self.barHeight / 2 y = start - (nval * self.barHeight / self.normalize_range) @@ -304,7 +304,7 @@ def paintEvent(self, event): brush = QBrush(self.penColor) pen.setWidth(1) p.setBrush(brush) - if self.normalizeMode: + if self.normalizeMode and self.normalize_range > 0: pen.setColor(QColor(Qt.gray)) p.setPen(pen) nval = self._value - self.normalizeReference diff --git a/src/pyefis/instruments/helpers/__init__.py b/src/pyefis/instruments/helpers/__init__.py new file mode 100644 index 00000000..c4eae8d4 --- /dev/null +++ b/src/pyefis/instruments/helpers/__init__.py @@ -0,0 +1,65 @@ +from PyQt5.QtWidgets import * +from PyQt5.QtGui import * + +def fit_to_mask(iwidth,iheight,mask,font,units_mask=None, units_ratio=0.8): + + # Could not get font metrics to work perfectly + # Seems like text it rendered starting slightly to the right + # rsulting in the right side of the text getting cut off + # Fitting to a slightly smaller width and height + # seems to resolve the issue. + width = iwidth * 0.95 + height = iheight * 0.96 + font_size = height + text_font = QFont(font, int(font_size)) + units_font = QFont(font, int(font_size * units_ratio)) + fm = QFontMetrics(text_font) + text_width = fm.tightBoundingRect(mask).width() + text_height = fm.tightBoundingRect(mask).height() + units_width = 0 + units_height = 0 + if units_mask: + fmu = QFontMetrics(units_font) + units_width = fmu.tightBoundingRect(units_mask).width() + units_height = fmu.tightBoundingRect(units_mask).height() + + # IF needed, shrink until it fits + while ( (text_width + units_width > width) or text_height > height ) and font_size > 0.5: + font_size -= 0.2 + print(f"too wide trying: {font_size} text_width:{text_width}, units_width:{units_width}, width:{width}") + text_font.setPointSizeF(font_size) + fm = QFontMetrics(text_font) + text_width = fm.tightBoundingRect(mask).width() + text_height = fm.tightBoundingRect(mask).height() + if units_mask: + units_font.setPointSizeF(font_size * units_ratio) + fmu = QFontMetrics(units_font) + units_width = fmu.tightBoundingRect(units_mask).width() + units_height = fmu.tightBoundingRect(units_mask).height() + # If needed, grow until it fills + while (text_width + units_width < width) and text_height < height: + font_size += 0.2 + print(f"Not wide enough, trying {font_size} text_width:{text_width}, units_width:{units_width}, width:{width}") + text_font.setPointSizeF(font_size) + fm = QFontMetrics(text_font) + text_width = fm.tightBoundingRect(mask).width() + text_height = fm.tightBoundingRect(mask).height() + if units_mask: + units_font.setPointSizeF(font_size * units_ratio) + fmu = QFontMetrics(units_font) + units_width = fmu.tightBoundingRect(units_mask).width() + units_height = fmu.tightBoundingRect(units_mask).height() + + font_size -= 0.2 + + # The above took care of the width, this addresses the height: + while (text_height >= height) and font_size > 0.5: + font_size -= 0.2 + print(f"too high, trying {font_size} text_height:{text_height}, height:{height}") + text_font.setPointSizeF(font_size) + fm = QFontMetrics(text_font) + text_width = fm.tightBoundingRect(mask).width() + + return(font_size) + + diff --git a/pyefis/instruments/hsi/__init__.py b/src/pyefis/instruments/hsi/__init__.py similarity index 100% rename from pyefis/instruments/hsi/__init__.py rename to src/pyefis/instruments/hsi/__init__.py diff --git a/pyefis/instruments/listbox/__init__.py b/src/pyefis/instruments/listbox/__init__.py similarity index 100% rename from pyefis/instruments/listbox/__init__.py rename to src/pyefis/instruments/listbox/__init__.py diff --git a/pyefis/instruments/misc/__init__.py b/src/pyefis/instruments/misc/__init__.py similarity index 90% rename from pyefis/instruments/misc/__init__.py rename to src/pyefis/instruments/misc/__init__.py index 85758078..1de2ca73 100644 --- a/pyefis/instruments/misc/__init__.py +++ b/src/pyefis/instruments/misc/__init__.py @@ -21,11 +21,20 @@ import pyavtools.fix as fix from pyefis.instruments import helpers + class StaticText(QWidget): """Represents a simple static text display. This is very simple and is - really just here to keep the individual screens from having to have - a painter object and a redraw event handler""" - def __init__(self, text="", fontsize=1.0, color=QColor(Qt.white), parent=None, font_family="DejaVu Sans Condensed"): + really just here to keep the individual screens from having to have + a painter object and a redraw event handler""" + + def __init__( + self, + text="", + fontsize=1.0, + color=QColor(Qt.white), + parent=None, + font_family="DejaVu Sans Condensed", + ): super(StaticText, self).__init__(parent) self.font_family = font_family self.font_ghost_mask = None @@ -39,10 +48,12 @@ def __init__(self, text="", fontsize=1.0, color=QColor(Qt.white), parent=None, f def resizeEvent(self, event): self.Font = QFont(self.font_family) if self.font_mask: - self.font_size = helpers.fit_to_mask(self.width(),self.height(),self.font_mask,self.font_family) + self.font_size = helpers.fit_to_mask( + self.width(), self.height(), self.font_mask, self.font_family + ) self.Font.setPointSizeF(self.font_size) else: - self.Font.setPixelSize(qRound(self.height()*self.font_percent)) + self.Font.setPixelSize(qRound(self.height() * self.font_percent)) self.textRect = QRectF(0, 0, self.width(), self.height()) def paintEvent(self, event): @@ -65,7 +76,7 @@ def paintEvent(self, event): p.setPen(pen) p.drawText(self.textRect, self.font_ghost_mask, opt) self.color.setAlpha(alpha) - + pen.setColor(self.color) p.setPen(pen) p.drawText(self.textRect, self.text, opt) @@ -108,14 +119,17 @@ def __init__(self, parent=None, font_family="Open Sans"): # These are the colors that are actually used # for drawing gauge. self.bgColor = self.bgGoodColor - self.textColor = self.textGoodColor # Non value text like units + self.textColor = self.textGoodColor # Non value text like units self.highlightColor = self.highlightGoodColor self.font_mask = None + def resizeEvent(self, event): self.font = QFont(self.font_family) if self.font_mask: - self.font_size = helpers.fit_to_mask(self.width(),self.height(),self.font_mask,self.font_family) + self.font_size = helpers.fit_to_mask( + self.width(), self.height(), self.font_mask, self.font_family + ) self.font.setPointSizeF(self.font_size) else: self.font.setPixelSize(qRound(self.height() * self.font_percent)) @@ -141,13 +155,11 @@ def paintEvent(self, event): p.setPen(pen) p.drawText(self.valueRect, self.font_ghost_mask, opt) self.textColor.setAlpha(alpha) - + pen.setColor(self.textColor) p.setPen(pen) p.drawText(self.valueRect, self.valueText, opt) - - def getValue(self): return self._value @@ -165,7 +177,7 @@ def setValue(self, value): def getValueText(self): if self.fail: - return 'xxx' + return "xxx" else: return str(self.value) @@ -187,7 +199,6 @@ def setDbkey(self, key): dbkey = property(getDbkey, setDbkey) - # This should get called when the gauge is created and then again # anytime a new report of the db item is recieved from the server def setupGauge(self): @@ -206,12 +217,11 @@ def setupGauge(self): self.item.valueChanged[int].disconnect(self.setValue) self.item.valueChanged[bool].disconnect(self.setValue) self.item.valueChanged[str].disconnect(self.setValue) - except: - pass # One will probably fail all the time + except TypeError: + pass # One will probably fail all the time self.item.valueChanged[self.item.dtype].connect(self.setValue) - def setColors(self): if self.bad or self.fail or self.old: self.bgColor = self.bgBadColor diff --git a/pyefis/instruments/pa/__init__.py b/src/pyefis/instruments/pa/__init__.py similarity index 100% rename from pyefis/instruments/pa/__init__.py rename to src/pyefis/instruments/pa/__init__.py diff --git a/pyefis/instruments/tc/__init__.py b/src/pyefis/instruments/tc/__init__.py similarity index 100% rename from pyefis/instruments/tc/__init__.py rename to src/pyefis/instruments/tc/__init__.py diff --git a/pyefis/instruments/vsi/__init__.py b/src/pyefis/instruments/vsi/__init__.py similarity index 100% rename from pyefis/instruments/vsi/__init__.py rename to src/pyefis/instruments/vsi/__init__.py diff --git a/pyefis/instruments/weston/__init__.py b/src/pyefis/instruments/weston/__init__.py similarity index 100% rename from pyefis/instruments/weston/__init__.py rename to src/pyefis/instruments/weston/__init__.py diff --git a/pyefis/main.py b/src/pyefis/main.py similarity index 99% rename from pyefis/main.py rename to src/pyefis/main.py index 28e38d6d..2a594841 100755 --- a/pyefis/main.py +++ b/src/pyefis/main.py @@ -16,7 +16,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. import time -from pyefis import version import sys, os import logging @@ -32,6 +31,7 @@ import hashlib from pyefis import cfg +from pyefis import __version__ if "pyAvTools" not in ''.join(sys.path): neighbor_tools = os.path.join('..', 'pyAvTools') @@ -224,7 +224,7 @@ def main(): log.critical("fix database not fully Initialized yet, ensure you have 'ZZLOADER' created in fixgateway database.yaml") time.sleep(2) pyefis_ver = fix.db.get_item('PYEFIS_VERSION') - pyefis_ver.value = version.VERSION + pyefis_ver.value = __version__ pyefis_ver.output_value() hmi.initialize(config) diff --git a/pyefis/screens/__init__.py b/src/pyefis/screens/__init__.py similarity index 100% rename from pyefis/screens/__init__.py rename to src/pyefis/screens/__init__.py diff --git a/pyefis/screens/ems_sm.py b/src/pyefis/screens/ems_sm.py similarity index 100% rename from pyefis/screens/ems_sm.py rename to src/pyefis/screens/ems_sm.py diff --git a/pyefis/screens/epfd.py b/src/pyefis/screens/epfd.py similarity index 100% rename from pyefis/screens/epfd.py rename to src/pyefis/screens/epfd.py diff --git a/pyefis/screens/pfd.py b/src/pyefis/screens/pfd.py similarity index 100% rename from pyefis/screens/pfd.py rename to src/pyefis/screens/pfd.py diff --git a/pyefis/screens/pfd_sm.py b/src/pyefis/screens/pfd_sm.py similarity index 100% rename from pyefis/screens/pfd_sm.py rename to src/pyefis/screens/pfd_sm.py diff --git a/pyefis/screens/r582_sm.py b/src/pyefis/screens/r582_sm.py similarity index 100% rename from pyefis/screens/r582_sm.py rename to src/pyefis/screens/r582_sm.py diff --git a/pyefis/screens/screenbuilder.py b/src/pyefis/screens/screenbuilder.py similarity index 99% rename from pyefis/screens/screenbuilder.py rename to src/pyefis/screens/screenbuilder.py index fa44e79a..bfa7add5 100644 --- a/pyefis/screens/screenbuilder.py +++ b/src/pyefis/screens/screenbuilder.py @@ -587,7 +587,6 @@ def get_grid_coordinates(self, column, row ): grid_width = ( self.width() - leftm - rightm ) / int(self.layout['columns']) grid_height = ( self.height() - topm - bottomm ) / int(self.layout['rows']) grid_x = leftm + grid_width * (( column )) - #print(f"leftm:{type(leftm)}, grid_width:{type(grid_width)} column:{column}") grid_y = topm + grid_height * (( row )) return grid_x, grid_y, grid_width, grid_height @@ -933,26 +932,26 @@ def paintEvent(self,event): self.textRect = QRectF(int(0), int(0), qRound(grid_width), qRound(grid_height)) while x <= int(self.layout['columns']): grid_x = leftm + grid_width * (x) - painter.setPen(QPen(Qt.red,1)) + painter.setPen(QPen(QColor("#99ff0000"),1)) if (x) % 10 == 0: - painter.setPen(QPen(Qt.green,3)) + painter.setPen(QPen(QColor("#9900ff00"),3)) self.textRect = QRectF(grid_x, grid_y, grid_width*10, grid_height*5) painter.drawText(self.textRect, str(x),QTextOption(Qt.AlignCenter)) - painter.setPen(QPen(Qt.red,3)) + painter.setPen(QPen(QColor("#99ff0000"),3)) painter.drawLine(qRound(grid_x), qRound(grid_y), qRound(grid_x), self.height()) x += 5 y = 0 grid_x = leftm while y <= int(self.layout['rows']): grid_y = topm + grid_height * (y) - painter.setPen(QPen(Qt.red,1)) + painter.setPen(QPen(QColor("#99ff0000"),1)) if (y) % 10 == 0: if y > 0: - painter.setPen(QPen(Qt.green,3)) + painter.setPen(QPen(QColor("#9900ff00"),3)) self.textRect = QRectF(grid_x, grid_y, grid_width*10, grid_height*5) painter.drawText(self.textRect, str(y),QTextOption(Qt.AlignCenter)) - painter.setPen(QPen(Qt.red,3)) + painter.setPen(QPen(QColor("#99ff0000"),3)) painter.drawLine(qRound(grid_x), qRound(grid_y), self.width(), qRound(grid_y)) y += 5 diff --git a/pyefis/screens/sixpack.py b/src/pyefis/screens/sixpack.py similarity index 100% rename from pyefis/screens/sixpack.py rename to src/pyefis/screens/sixpack.py diff --git a/pyefis/screens/test.py b/src/pyefis/screens/test.py similarity index 100% rename from pyefis/screens/test.py rename to src/pyefis/screens/test.py diff --git a/pyefis/user/__init__.py b/src/pyefis/user/__init__.py similarity index 100% rename from pyefis/user/__init__.py rename to src/pyefis/user/__init__.py diff --git a/pyefis/user/hooks/__init__.py b/src/pyefis/user/hooks/__init__.py similarity index 100% rename from pyefis/user/hooks/__init__.py rename to src/pyefis/user/hooks/__init__.py diff --git a/pyefis/user/hooks/composite.py b/src/pyefis/user/hooks/composite.py similarity index 100% rename from pyefis/user/hooks/composite.py rename to src/pyefis/user/hooks/composite.py diff --git a/pyefis/user/hooks/keys.py b/src/pyefis/user/hooks/keys.py similarity index 100% rename from pyefis/user/hooks/keys.py rename to src/pyefis/user/hooks/keys.py diff --git a/pyefis/user/screens/__init__.py b/src/pyefis/user/screens/__init__.py similarity index 100% rename from pyefis/user/screens/__init__.py rename to src/pyefis/user/screens/__init__.py diff --git a/src/pyefis/version.py b/src/pyefis/version.py new file mode 100644 index 00000000..02d15999 --- /dev/null +++ b/src/pyefis/version.py @@ -0,0 +1,3 @@ +import pyefis + +print(pyefis.__version__) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/common/test_init.py b/tests/common/test_init.py new file mode 100644 index 00000000..4d2af0cf --- /dev/null +++ b/tests/common/test_init.py @@ -0,0 +1,26 @@ +import pytest +from pyefis.common import bounds + + +def test_bounds_below_min(): + assert bounds(0, 10, -5) == 0 + + +def test_bounds_above_max(): + assert bounds(0, 10, 15) == 10 + + +def test_bounds_within_range(): + assert bounds(0, 10, 5) == 5 + + +def test_bounds_on_min(): + assert bounds(0, 10, 0) == 0 + + +def test_bounds_on_max(): + assert bounds(0, 10, 10) == 10 + + +if __name__ == "__main__": + pytest.main() diff --git a/tests/data/cfg/sub/sub2/test_list_bpath_preferenceA.yaml b/tests/data/cfg/sub/sub2/test_list_bpath_preferenceA.yaml new file mode 100644 index 00000000..0c8cf43e --- /dev/null +++ b/tests/data/cfg/sub/sub2/test_list_bpath_preferenceA.yaml @@ -0,0 +1,5 @@ +test: + - include: B + - include: C + - 5 + - Ten diff --git a/tests/data/cfg/sub/sub2/test_list_bpath_preferenceB.yaml b/tests/data/cfg/sub/sub2/test_list_bpath_preferenceB.yaml new file mode 100644 index 00000000..b9b87ffb --- /dev/null +++ b/tests/data/cfg/sub/sub2/test_list_bpath_preferenceB.yaml @@ -0,0 +1,2 @@ +items: + b_id: 5 diff --git a/tests/data/cfg/sub/test_array_and_preferencesB.yaml b/tests/data/cfg/sub/test_array_and_preferencesB.yaml new file mode 100644 index 00000000..933ed204 --- /dev/null +++ b/tests/data/cfg/sub/test_array_and_preferencesB.yaml @@ -0,0 +1 @@ +includeB: true diff --git a/tests/data/cfg/sub/test_array_and_preferencesD.yaml b/tests/data/cfg/sub/test_array_and_preferencesD.yaml new file mode 100644 index 00000000..bdcafbc3 --- /dev/null +++ b/tests/data/cfg/sub/test_array_and_preferencesD.yaml @@ -0,0 +1 @@ +include: PREFERENCEB diff --git a/tests/data/cfg/sub/test_preference_file_not_found.yaml b/tests/data/cfg/sub/test_preference_file_not_found.yaml new file mode 100644 index 00000000..26932543 --- /dev/null +++ b/tests/data/cfg/sub/test_preference_file_not_found.yaml @@ -0,0 +1 @@ +include: NOT_FOUND diff --git a/tests/data/cfg/sub/test_preference_file_not_found_list.yaml b/tests/data/cfg/sub/test_preference_file_not_found_list.yaml new file mode 100644 index 00000000..3bff62dc --- /dev/null +++ b/tests/data/cfg/sub/test_preference_file_not_found_list.yaml @@ -0,0 +1,2 @@ +list: + - include: NOT_FOUND diff --git a/tests/data/cfg/sub/test_sub_parent_includesB.yaml b/tests/data/cfg/sub/test_sub_parent_includesB.yaml new file mode 100644 index 00000000..8827cbe8 --- /dev/null +++ b/tests/data/cfg/sub/test_sub_parent_includesB.yaml @@ -0,0 +1,4 @@ +b: + include: test_sub_parent_includesC.yaml +b2: + include: test_sub_parent_includesD.yaml diff --git a/tests/data/cfg/sub/test_sub_parent_includesC.yaml b/tests/data/cfg/sub/test_sub_parent_includesC.yaml new file mode 100644 index 00000000..46296f0b --- /dev/null +++ b/tests/data/cfg/sub/test_sub_parent_includesC.yaml @@ -0,0 +1,2 @@ +c: + done: true diff --git a/tests/data/cfg/test_array_and_preferences.yaml b/tests/data/cfg/test_array_and_preferences.yaml new file mode 100644 index 00000000..145ecd3b --- /dev/null +++ b/tests/data/cfg/test_array_and_preferences.yaml @@ -0,0 +1,5 @@ +ARRAYS: + include: + - test_array_and_preferencesC.yaml + - PREFERENCEA + - sub/test_array_and_preferencesD.yaml diff --git a/tests/data/cfg/test_array_and_preferencesA.yaml b/tests/data/cfg/test_array_and_preferencesA.yaml new file mode 100644 index 00000000..fa77ad86 --- /dev/null +++ b/tests/data/cfg/test_array_and_preferencesA.yaml @@ -0,0 +1 @@ +includeA: true diff --git a/tests/data/cfg/test_array_and_preferencesC.yaml b/tests/data/cfg/test_array_and_preferencesC.yaml new file mode 100644 index 00000000..8bd5f65d --- /dev/null +++ b/tests/data/cfg/test_array_and_preferencesC.yaml @@ -0,0 +1 @@ +includeC: true diff --git a/tests/data/cfg/test_data_type.yaml b/tests/data/cfg/test_data_type.yaml new file mode 100644 index 00000000..00671f3e --- /dev/null +++ b/tests/data/cfg/test_data_type.yaml @@ -0,0 +1 @@ +include: 1 diff --git a/tests/data/cfg/test_list_bpath_preference.yaml b/tests/data/cfg/test_list_bpath_preference.yaml new file mode 100644 index 00000000..a27972d9 --- /dev/null +++ b/tests/data/cfg/test_list_bpath_preference.yaml @@ -0,0 +1 @@ +include: A diff --git a/tests/data/cfg/test_list_bpath_preferenceC.yaml b/tests/data/cfg/test_list_bpath_preferenceC.yaml new file mode 100644 index 00000000..cbad5fbb --- /dev/null +++ b/tests/data/cfg/test_list_bpath_preferenceC.yaml @@ -0,0 +1,2 @@ +items: + - c_id_is: 5 diff --git a/tests/data/cfg/test_list_include_missing_items.yaml b/tests/data/cfg/test_list_include_missing_items.yaml new file mode 100644 index 00000000..d0dd3e26 --- /dev/null +++ b/tests/data/cfg/test_list_include_missing_items.yaml @@ -0,0 +1,3 @@ +list_of_items: + - test: true + - include: test_list_include_missing_itemsA.yaml diff --git a/tests/data/cfg/test_list_include_missing_itemsA.yaml b/tests/data/cfg/test_list_include_missing_itemsA.yaml new file mode 100644 index 00000000..2448b072 --- /dev/null +++ b/tests/data/cfg/test_list_include_missing_itemsA.yaml @@ -0,0 +1 @@ +No_items_at_root: true diff --git a/tests/data/cfg/test_list_include_missing_items_pref.yaml b/tests/data/cfg/test_list_include_missing_items_pref.yaml new file mode 100644 index 00000000..a0a195ac --- /dev/null +++ b/tests/data/cfg/test_list_include_missing_items_pref.yaml @@ -0,0 +1,3 @@ +list_of_items: + - test: true + - include: PREFERENCED diff --git a/tests/data/cfg/test_list_include_missing_items_pref_nested.yaml b/tests/data/cfg/test_list_include_missing_items_pref_nested.yaml new file mode 100644 index 00000000..cc8e68c3 --- /dev/null +++ b/tests/data/cfg/test_list_include_missing_items_pref_nested.yaml @@ -0,0 +1,2 @@ +wrong_items: + - include: PREFERENCEF diff --git a/tests/data/cfg/test_list_include_missing_items_via_preferencesD.yaml b/tests/data/cfg/test_list_include_missing_items_via_preferencesD.yaml new file mode 100644 index 00000000..4477fd43 --- /dev/null +++ b/tests/data/cfg/test_list_include_missing_items_via_preferencesD.yaml @@ -0,0 +1 @@ +no_list_of_items: true diff --git a/tests/data/cfg/test_list_include_missing_items_via_preferences_nestedF.yaml b/tests/data/cfg/test_list_include_missing_items_via_preferences_nestedF.yaml new file mode 100644 index 00000000..6ceb6f9b --- /dev/null +++ b/tests/data/cfg/test_list_include_missing_items_via_preferences_nestedF.yaml @@ -0,0 +1,4 @@ +items: + - test: true + - test2: + - include: PREFERENCEG diff --git a/tests/data/cfg/test_list_include_missing_items_via_preferences_nestedG.yaml b/tests/data/cfg/test_list_include_missing_items_via_preferences_nestedG.yaml new file mode 100644 index 00000000..9d459e8c --- /dev/null +++ b/tests/data/cfg/test_list_include_missing_items_via_preferences_nestedG.yaml @@ -0,0 +1,2 @@ +wrong: + stuff: true diff --git a/tests/data/cfg/test_loop_detection_exception.yaml b/tests/data/cfg/test_loop_detection_exception.yaml new file mode 100644 index 00000000..7f9fab0c --- /dev/null +++ b/tests/data/cfg/test_loop_detection_exception.yaml @@ -0,0 +1,2 @@ +loop1: + include: test_loop_detection_exceptionA.yaml diff --git a/tests/data/cfg/test_loop_detection_exceptionA.yaml b/tests/data/cfg/test_loop_detection_exceptionA.yaml new file mode 100644 index 00000000..0d1c2d1c --- /dev/null +++ b/tests/data/cfg/test_loop_detection_exceptionA.yaml @@ -0,0 +1,2 @@ +loop1a: + include: test_loop_detection_exception.yaml diff --git a/tests/data/cfg/test_no_preferences.yaml b/tests/data/cfg/test_no_preferences.yaml new file mode 100644 index 00000000..32ef8f98 --- /dev/null +++ b/tests/data/cfg/test_no_preferences.yaml @@ -0,0 +1,2 @@ +test: + include: UNDEFINED_INCLUDE diff --git a/tests/data/cfg/test_sub_parent_includesA.yaml b/tests/data/cfg/test_sub_parent_includesA.yaml new file mode 100644 index 00000000..61efb271 --- /dev/null +++ b/tests/data/cfg/test_sub_parent_includesA.yaml @@ -0,0 +1,2 @@ +a: + include: sub/test_sub_parent_includesB.yaml diff --git a/tests/data/cfg/test_sub_parent_includesD.yaml b/tests/data/cfg/test_sub_parent_includesD.yaml new file mode 100644 index 00000000..2156ef05 --- /dev/null +++ b/tests/data/cfg/test_sub_parent_includesD.yaml @@ -0,0 +1,2 @@ +d: + end: true diff --git a/tests/hmi/__init__.py b/tests/hmi/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/hmi/test_actionclass.py b/tests/hmi/test_actionclass.py new file mode 100644 index 00000000..9342890b --- /dev/null +++ b/tests/hmi/test_actionclass.py @@ -0,0 +1,69 @@ +import pytest +from PyQt5.QtCore import pyqtSignal, pyqtBoundSignal +from PyQt5.QtWidgets import QApplication +from unittest import mock # MagicMock, mock.patch +from pyefis.hmi import actionclass # ActionClass + + +# Ensure QApplication instance +@pytest.fixture +def app(qtbot): + test_app = QApplication.instance() + if test_app is None: + test_app = QApplication([]) + return test_app + + +def test_initialization(qtbot): + action_class = actionclass.ActionClass() + assert action_class.signalMap is not None + assert "set airspeed mode" in action_class.signalMap + + +@mock.patch("pyefis.hmi.functions.setValue") +def test_trigger_function(mock_setValue, qtbot): + action_class = actionclass.ActionClass() + # Mocking the entire functions module + mock_setValue.return_value = None + # mock_db.get_item.return_value = None # Mocking pyavtools.fix.db as well + + action_class.trigger("set value", "test") + + # Asserting that setValue is called + mock_setValue.assert_called_once_with("test") + + +def test_trigger_signal(qtbot): + action_class = actionclass.ActionClass() + mock_signal = mock.MagicMock(spec=pyqtBoundSignal) + action_class.signalMap["set airspeed mode"] = ( + mock_signal # mock.patching emit directly + ) + action_class.trigger("set airspeed mode", "test") + mock_signal.emit.assert_called_once_with( + "test" + ) # Use assert_called_once_with on mock_signal directly + + +def test_find_action_exists(qtbot): + action_class = actionclass.ActionClass() + action = action_class.findAction("set airspeed mode") + assert action == action_class.setAirspeedMode + + +def test_find_action_not_exists(qtbot): + action_class = actionclass.ActionClass() + action = action_class.findAction("nonexistent action") + assert action is None + + +def test_trigger_eval(qtbot): + action_class = actionclass.ActionClass() + mock_eval = mock.MagicMock() + action_class.signalMap["evaluate"] = mock_eval + action_class.trigger("evaluate", "print('test')") + mock_eval.assert_called_once_with("print('test')") + + +if __name__ == "__main__": + pytest.main() diff --git a/tests/hmi/test_functions.py b/tests/hmi/test_functions.py new file mode 100644 index 00000000..fe785a63 --- /dev/null +++ b/tests/hmi/test_functions.py @@ -0,0 +1,66 @@ +import pytest +from unittest.mock import MagicMock, patch +import pyefis.hmi.functions + + +# Mock the fix module +@pytest.fixture +def fix_mock(): + with patch("pyefis.hmi.functions.fix") as fix_mock: + yield fix_mock + + +# Test for setValue function +def test_setValue(fix_mock): + # Mocking the arg for setValue + arg = "key,value" + # Setting up the mock for the item + item_mock = MagicMock() + item_mock.value = None + fix_mock.db.get_item.return_value = item_mock + + # Calling the function + pyefis.hmi.functions.setValue(arg) + + # Assertions + fix_mock.db.get_item.assert_called_once_with("key") + assert item_mock.value == "value" + assert item_mock.output_value.called + + +# Test for changeValue function +def test_changeValue(fix_mock): + # Mocking the arg for changeValue + arg = "key,1" + # Setting up the mock for the item + item_mock = MagicMock() + item_mock.value = 1 + item_mock.dtype.return_value = 1 + fix_mock.db.get_item.return_value = item_mock + + # Calling the function + pyefis.hmi.functions.changeValue(arg) + + # Assertions + fix_mock.db.get_item.assert_called_once_with("key") + assert item_mock.value == 2 + assert item_mock.dtype.called + assert item_mock.output_value.called + + +# Test for toggleBool function +def test_toggleBool(fix_mock): + # Mocking the arg for toggleBool + arg = "key" + # Setting up the mock for the item + item_mock = MagicMock() + item_mock.value = False + fix_mock.db.get_item.return_value = item_mock + + # Calling the function + pyefis.hmi.functions.toggleBool(arg) + + # Assertions + fix_mock.db.get_item.assert_called_once_with("key") + assert item_mock.value == True + assert item_mock.output_value.called diff --git a/tests/hmi/test_init.py b/tests/hmi/test_init.py new file mode 100644 index 00000000..7ec2d600 --- /dev/null +++ b/tests/hmi/test_init.py @@ -0,0 +1,56 @@ +import pytest +from unittest.mock import patch, MagicMock +import logging +from pyefis.hmi import initialize, actionclass, data + + +# Fixture to patch and reset global actions variable +@pytest.fixture(autouse=True) +def patch_globals(): + with patch("pyefis.hmi.actions", None): + yield + + +# Fixture to patch the logging module +@pytest.fixture +def log_mock(): + with patch("pyefis.hmi.logging.getLogger") as logger_mock: + yield logger_mock + + +def test_initialize_with_databindings(log_mock): + # Mock the actionclass.ActionClass + with patch( + "pyefis.hmi.actionclass.ActionClass", autospec=True + ) as mock_action_class: + # Mock the data.initialize function + with patch("pyefis.hmi.data.initialize") as mock_data_initialize: + # Sample config with databindings + config = {"databindings": {"key": "value"}} + # Call the initialize function with config + initialize(config) + # Check that logging was called correctly + log_mock.return_value.info.assert_called_once_with("Initializing Actions") + # Check that ActionClass was instantiated + mock_action_class.assert_called_once() + # Check that data.initialize was called with correct config + mock_data_initialize.assert_called_once_with(config["databindings"]) + + +def test_initialize_without_databindings(log_mock): + # Mock the actionclass.ActionClass + with patch( + "pyefis.hmi.actionclass.ActionClass", autospec=True + ) as mock_action_class: + # Mock the data.initialize function + with patch("pyefis.hmi.data.initialize") as mock_data_initialize: + # Sample config without databindings + config = {} + # Call the initialize function with config + initialize(config) + # Check that logging was called correctly + log_mock.return_value.info.assert_called_once_with("Initializing Actions") + # Check that ActionClass was instantiated + mock_action_class.assert_called_once() + # Check that data.initialize was not called + mock_data_initialize.assert_not_called() diff --git a/tests/instruments/__init__.py b/tests/instruments/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/instruments/airspeed/__init__.py b/tests/instruments/airspeed/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/instruments/airspeed/test_airspeed.py b/tests/instruments/airspeed/test_airspeed.py new file mode 100644 index 00000000..9e5965b3 --- /dev/null +++ b/tests/instruments/airspeed/test_airspeed.py @@ -0,0 +1,110 @@ +import pytest +from unittest import mock +from PyQt5.QtWidgets import QApplication +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QColor, QBrush, QPaintEvent +from pyefis.instruments import airspeed +import pyefis.hmi as hmi + + +@pytest.fixture +def app(qtbot): + test_app = QApplication.instance() + if test_app is None: + test_app = QApplication([]) + return test_app + +def test_aux_not_set_for_airspeed(fix, qtbot): + fix.db.get_item("IAS").set_aux_value("Vs", None) + fix.db.get_item("IAS").set_aux_value("Vs0", None) + fix.db.get_item("IAS").set_aux_value("Vno", None) + fix.db.get_item("IAS").set_aux_value("Vne", None) + fix.db.get_item("IAS").set_aux_value("Vfe", None) + widget = airspeed.Airspeed() + assert widget.Vs == 0 + assert widget.Vs0 == 0 + assert widget.Vno == 0 + assert widget.Vne == 200 + assert widget.Vfe == 0 + +def test_aux_not_set_for_airspeed_tape(fix, qtbot): + fix.db.get_item("IAS").set_aux_value("Vs", None) + fix.db.get_item("IAS").set_aux_value("Vs0", None) + fix.db.get_item("IAS").set_aux_value("Vno", None) + fix.db.get_item("IAS").set_aux_value("Vne", None) + fix.db.get_item("IAS").set_aux_value("Vfe", None) + widget = airspeed.Airspeed_Tape() + assert widget.Vs == 0 + assert widget.Vs0 == 0 + assert widget.Vno == 0 + assert widget.Vne == 200 + assert widget.Vfe == 0 + +def test_numerical_airspeed(fix, qtbot): + widget = airspeed.Airspeed() + assert widget.getRatio() == 1 + qtbot.addWidget(widget) + widget.resize(201, 200) + widget.show() + qtbot.waitExposed(widget) + qtbot.wait(500) + assert widget.item.key == "IAS" + assert widget.Vs == 45 + fix.db.get_item("IAS").fail = True + fix.db.get_item("IAS").fail = False + fix.db.get_item("IAS").old = True + fix.db.get_item("IAS").old = False + fix.db.set_value("IAS", "20") + widget.setAsOld(True) + widget.setAsBad(True) + widget.setAsFail(True) + assert widget.getAirspeed() == 20 + qtbot.wait(200) + + +def test_numerical_airspeed_tape(qtbot): + widget = airspeed.Airspeed_Tape(font_percent=0.5) + qtbot.addWidget(widget) + widget.redraw() + widget.resize(50, 200) + widget.show() + event = QPaintEvent(widget.rect()) + widget.paintEvent(event) + assert widget.Vs0 == 40 + assert widget.getAirspeed() == 20 + widget.setAirspeed(40) # redraw() + widget.keyPressEvent(None) + widget.wheelEvent(None) + + +def test_numerical_airspeed_box(fix, qtbot): + hmi.initialize({}) + widget = airspeed.Airspeed_Box() + qtbot.addWidget(widget) + widget.resize(50, 50) + widget.show() + fix.db.set_value("TAS", 100) + widget.setMode(1) + widget.setMode(0) + assert widget.modeText == "TAS" + assert widget.valueText == "100" + fix.db.set_value("GS", 80) + widget.setMode(1) + assert widget.modeText == "GS" + assert widget.valueText == "80" + assert widget._modeIndicator == 1 + fix.db.set_value("IAS", 140) + widget.setMode(2) + assert widget.modeText == "IAS" + assert widget.valueText == "140" + widget.setMode("") + assert widget._modeIndicator == 0 + + fix.db.get_item("TAS").fail = True + fix.db.set_value("TAS", 101) + assert widget.valueText == "XXX" + fix.db.get_item("TAS").fail = False + fix.db.get_item("TAS").bad = True + fix.db.set_value("TAS", 102) + assert widget.valueText == "" + widget.paintEvent(None) diff --git a/tests/instruments/gauges/__init__.py b/tests/instruments/gauges/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/instruments/gauges/test_arc.py b/tests/instruments/gauges/test_arc.py new file mode 100644 index 00000000..6f2b9aa0 --- /dev/null +++ b/tests/instruments/gauges/test_arc.py @@ -0,0 +1,102 @@ +import pytest +from unittest import mock +from PyQt5.QtWidgets import QApplication +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QColor, QBrush, QPen, QPaintEvent, QFontMetrics +from pyefis.instruments import gauges +import pyefis.hmi as hmi +from tests.utils import track_calls + + +@pytest.fixture +def app(qtbot): + test_app = QApplication.instance() + if test_app is None: + test_app = QApplication([]) + return test_app + + +def test_arc_gauge(fix,qtbot): + widget = gauges.ArcGauge() + assert widget.getRatio() == 2 + + # Test with no aux data first + widget.setDbkey("NUM") + widget.setupGauge() + qtbot.addWidget(widget) + widget.resize(300, 200) + widget.show() + qtbot.waitExposed(widget) + widget.paintEvent(None) + assert widget._dbkey == "NUM" + assert widget.isVisible() + assert widget._units == "°C" + widget.resize(200, 220) + widget.resizeEvent(None) + assert widget.tlcx == 0 + assert widget.tlcy == 60 + widget.resize(400, 100) + widget.resizeEvent(None) + assert widget.tlcx == 100 + assert widget.tlcy == 00 + assert int(widget.valueFontSize) == 33 + assert int(widget.unitsFontSize) == 25 + assert int(widget.nameFontSize) == 17 + + widget.font_mask = "00" + widget.units_font_mask = "00" + widget.name_font_mask = "0000" + widget.resizeEvent(None) + assert widget.valueFontSize < 0.5 + assert widget.unitsFontSize < 0.4 + assert widget.nameFontSize < 10 + assert widget.nameFontSize > 9 + widget.name_location = "right" + widget.resizeEvent(None) + assert widget.nameFontSize < 0.4 + # Switch to NUOK with aux data to check the bar colors + widget.setDbkey("NUMOK") + widget.setupGauge() + widget.paintEvent(None) + + widget.segments = 28 + with track_calls(QPen, "setColor") as tracker: + widget.paintEvent(None) + + assert tracker.was_called_with("setColor", QColor(Qt.black)) + assert tracker.was_called_with("setColor", QColor(0, 0, 0, widget.segment_alpha)) + + widget.name_font_ghost_mask = "0000" + with track_calls(QFontMetrics, "width") as tracker: + with track_calls(QColor, "setAlpha") as tracker2: + widget.paintEvent(None) + + assert tracker.was_called_with("width", "0000") + assert tracker2.was_called_with("setAlpha", widget.font_ghost_alpha) + + widget.name_location = "top" + widget.paintEvent(None) + + widget.name_location = "right" + widget.name_font_mask = None + widget.paintEvent(None) + + widget.show_units = True + widget.paintEvent(None) + + # widget.units_font_mask = None + with track_calls(QFontMetrics, "width") as tracker: + widget.paintEvent(None) + assert tracker.was_called_with("width", widget.units_font_mask) + widget.units_font_mask = None + widget.paintEvent(None) + assert tracker.was_called_with("width", widget.units) + + with track_calls(QColor, "setAlpha") as tracker2: + widget.units_font_ghost_mask = "0000" + widget.paintEvent(None) + assert tracker2.was_called_with("setAlpha", widget.font_ghost_alpha) + widget.units_font_ghost_mask = None + widget.font_ghost_mask = "0000" + widget.paintEvent(None) + assert tracker2.was_called_with("setAlpha", widget.font_ghost_alpha) diff --git a/tests/instruments/gauges/test_horizontalBar.py b/tests/instruments/gauges/test_horizontalBar.py new file mode 100644 index 00000000..82d9d7c9 --- /dev/null +++ b/tests/instruments/gauges/test_horizontalBar.py @@ -0,0 +1,71 @@ +import pytest +from unittest import mock +from PyQt5.QtWidgets import QApplication +from PyQt5.QtCore import Qt, qRound +from PyQt5.QtGui import QColor, QBrush, QPen, QFont, QPaintEvent, QFontMetrics +from pyefis.instruments import gauges +import pyefis.hmi as hmi +from tests.utils import track_calls + + +@pytest.fixture +def app(qtbot): + test_app = QApplication.instance() + if test_app is None: + test_app = QApplication([]) + return test_app + + +def test_horizontal_bar_gauge(fix, qtbot): + widget = gauges.HorizontalBar() + assert widget.getRatio() == 2 + widget.setDbkey("NUM") + widget.setupGauge() + qtbot.addWidget(widget) + widget.resize(300, 200) + widget.show() + qtbot.waitExposed(widget) + widget.resizeEvent(None) + with track_calls(QFont, ["setPointSizeF", "setPixelSize"]) as tracker: + widget.resizeEvent(None) + assert tracker.was_not_called("setPointSizeF") + widget.font_mask = "0000" + widget.resizeEvent(None) + assert tracker.was_called("setPointSizeF") + widget.name_font_mask = "0000" + widget.resizeEvent(None) + widget.units_font_mask = "0000" + widget.resizeEvent(None) + + with track_calls(QColor, "setAlpha") as tracker: + widget.name_font_ghost_mask = "0000" + widget.paintEvent(None) + assert tracker.was_called_with("setAlpha", widget.font_ghost_alpha) + + widget.show_units = True + widget.name_font_ghost_mask = None + widget.name_font_mask = None + widget.font_mask = None + with track_calls(QColor, "setAlpha") as tracker: + widget.paintEvent(None) + assert tracker.was_not_called("setAlpha") + widget.units_font_ghost_mask = "0000" + widget.paintEvent(None) + assert tracker.was_called_with("setAlpha", widget.font_ghost_alpha) + + widget.units_font_ghost_mask = None + with track_calls(QColor, "setAlpha") as tracker: + widget.paintEvent(None) + assert tracker.was_not_called("setAlpha") + widget.font_ghost_mask = "0000" + widget.paintEvent(None) + assert tracker.was_called_with("setAlpha", widget.font_ghost_alpha) + widget.setDbkey("NUMOK") + widget.setupGauge() + widget.paintEvent(None) + + widget.segments = 28 + with track_calls(QPen, "setColor") as tracker: + widget.paintEvent(None) + + assert tracker.was_called_with("setColor", QColor(Qt.black)) diff --git a/tests/instruments/gauges/test_numeric.py b/tests/instruments/gauges/test_numeric.py new file mode 100644 index 00000000..9042428c --- /dev/null +++ b/tests/instruments/gauges/test_numeric.py @@ -0,0 +1,43 @@ +import pytest +from unittest import mock +from PyQt5.QtWidgets import QApplication +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QColor, QBrush, QPen, QPaintEvent, QFontMetrics +from pyefis.instruments.gauges import numeric +import pyefis.hmi as hmi +from tests.utils import track_calls + + +@pytest.fixture +def app(qtbot): + test_app = QApplication.instance() + if test_app is None: + test_app = QApplication([]) + return test_app + + +def test_numeric_gauge(fix,qtbot): + widget = numeric.NumericDisplay() + widget.setDbkey("NUM") + widget.setupGauge() + qtbot.addWidget(widget) + widget.resize(300, 200) + widget.show() + qtbot.waitExposed(widget) + widget.resizeEvent(None) + assert widget.font_size == 200 + widget.font_mask = "0000" + widget.resizeEvent(None) + assert widget.font_size != 200 + oldvalueTextRect = widget.valueTextRect + widget.show_units = True + widget.resizeEvent(None) + assert oldvalueTextRect != widget.valueTextRect + widget.font_ghost_mask = "0000" + with track_calls(QColor, "setAlpha") as tracker: + widget.paintEvent(None) + assert tracker.was_called_with("setAlpha", widget.font_ghost_alpha) + widget.font_ghost_mask = None + widget.units_font_ghost_mask = "0000" + widget.paintEvent(None) + assert tracker.was_called_with("setAlpha", widget.font_ghost_alpha) diff --git a/tests/instruments/gauges/test_verticalBar.py b/tests/instruments/gauges/test_verticalBar.py new file mode 100644 index 00000000..49716078 --- /dev/null +++ b/tests/instruments/gauges/test_verticalBar.py @@ -0,0 +1,123 @@ +import pytest +from unittest import mock +from PyQt5.QtWidgets import QApplication +from PyQt5.QtCore import Qt, qRound +from PyQt5.QtGui import QColor, QBrush, QPen, QFont, QPaintEvent, QFontMetrics +from pyefis.instruments import gauges +import pyefis.hmi as hmi +from tests.utils import track_calls + + +@pytest.fixture +def app(qtbot): + test_app = QApplication.instance() + if test_app is None: + test_app = QApplication([]) + return test_app + + +def test_vertical_bar_gauge(fix,qtbot): + widget = gauges.VerticalBar() + assert widget.getRatio() == 0.35 + widget.setDbkey("NUM") + widget.setupGauge() + qtbot.addWidget(widget) + widget.resize(300, 200) + widget.show() + qtbot.waitExposed(widget) + widget.resizeEvent(None) + with track_calls(QFont, ["setPointSizeF", "setPixelSize"]) as tracker: + widget.resizeEvent(None) + assert tracker.was_not_called("setPointSizeF") + widget.font_mask = "0000" + widget.resizeEvent(None) + assert tracker.was_called("setPointSizeF") + widget.name_font_mask = "0000" + widget.resizeEvent(None) + widget.units_font_mask = "0000" + widget.resizeEvent(None) + with track_calls(QColor, "setAlpha") as tracker: + widget.name_font_ghost_mask = "0000" + widget.paintEvent(None) + assert tracker.was_called_with("setAlpha", widget.font_ghost_alpha) + widget.show_units = True + widget.name_font_ghost_mask = None + widget.name_font_mask = None + widget.font_mask = None + with track_calls(QColor, "setAlpha") as tracker: + widget.paintEvent(None) + assert tracker.was_not_called("setAlpha") + widget.units_font_ghost_mask = "0000" + widget.paintEvent(None) + assert tracker.was_called_with("setAlpha", widget.font_ghost_alpha) + widget.units_font_ghost_mask = None + with track_calls(QColor, "setAlpha") as tracker: + widget.paintEvent(None) + assert tracker.was_not_called("setAlpha") + widget.font_ghost_mask = "0000" + widget.paintEvent(None) + assert tracker.was_called_with("setAlpha", widget.font_ghost_alpha) + widget.setDbkey("NUMOK") + widget.setupGauge() + widget.paintEvent(None) + widget.segments = 28 + with track_calls(QPen, "setColor") as tracker: + widget.paintEvent(None) + assert tracker.was_called_with("setColor", QColor(Qt.black)) + widget.setNormalizeMode(True) + assert widget._normalizeMode == True + assert widget.penGoodColor == widget.normalizePenColor + widget.setNormalizeMode(True) + assert widget.penGoodColor == widget.normalizePenColor + widget.setNormalizeMode(False) + assert widget._normalizeMode == False + widget.setPeakMode(True) + assert widget._peakMode == True + widget.setPeakMode(False) + assert widget._peakMode == False + mode = widget._normalizeMode + widget.setMode("normalize") + assert widget.normalizeMode != mode + widget.setMode("normalize") + assert widget.normalizeMode == mode + mode = widget._peakMode + widget.setMode("peak") + assert widget.peakMode != mode + widget.setMode("peak") + assert widget.peakMode == mode + widget.setMode("reset peak") + assert widget.peakValue == widget.value + widget.setMode("lean") + assert widget.peakValue == widget.value + assert widget.normalizeMode == True + assert widget.peakMode == True + widget.setMode("normal") + assert widget.normalizeMode == False + assert widget.peakMode == False + assert widget.barTop != 1 + widget.show_name = False + widget.resizeEvent(None) + assert widget.barTop == 1 + widget.highlight_key = "NUM" + widget.setupGauge() + widget.paintEvent(None) + assert widget.highlight == False + widget.highlight_key = "NUMOK" + widget.setupGauge() + widget.paintEvent(None) + assert widget.highlight == True + widget.setPeakMode(True) + widget.peakValue = 200 + with track_calls(QPen, "setColor") as tracker: + widget.paintEvent(None) + assert tracker.was_called_with("setColor", widget.peakColor) + with track_calls(QPen, "setColor") as tracker: + widget.peakValue = widget.value + widget.paintEvent(None) + assert tracker.was_not_called_with("setColor", widget.peakColor) + widget.units_font_mask = None + widget.paintEvent(None) + widget.setMode("normalize") + widget.paintEvent(None) + widget.normalize_range = 400 + widget.paintEvent(None) diff --git a/tests/instruments/misc/test_misc.py b/tests/instruments/misc/test_misc.py new file mode 100644 index 00000000..5b9eaba3 --- /dev/null +++ b/tests/instruments/misc/test_misc.py @@ -0,0 +1,170 @@ +import pytest +import warnings +from unittest import mock +from PyQt5.QtWidgets import QApplication +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QColor + +from pyefis.instruments.misc import StaticText, ValueDisplay +import os + + +@pytest.fixture +def app(qtbot): + test_app = QApplication.instance() + if test_app is None: + test_app = QApplication([]) + return test_app + + +def test_save_screenshot(fix, qtbot, request): + widget1 = StaticText(text="Testing!") + widget1.font_mask = "XXXXXXXX" + widget1.color = QColor(Qt.black) + widget1.move(0, 0) + widget1.resize(100, 50) + qtbot.addWidget(widget1) + widget1.show() + qtbot.waitExposed(widget1) + path = qtbot.screenshot(widget1, "instruments-misc-test_misc") + qtbot.wait(500) + os.rename( + path, + request.config.rootdir + + "/extras/extras/test_results/instruments-misc-test_misc-StaticText.png", + ) + + +def test_static_text_default(fix, qtbot): + widget = StaticText(text="Testing!") + widget.font_mask = "0.0" + widget.font_ghost_mask = "0.0" + qtbot.addWidget(widget) + + assert widget.text == "Testing!" + assert widget.font_family == "DejaVu Sans Condensed" + assert widget.color == QColor(Qt.white) + widget.resizeEvent(None) + widget.paintEvent(None) + widget.show() + qtbot.waitExposed(widget) + assert widget.isVisible() + assert widget.color.alpha() != 50 + + +def test_static_text_resize(fix, qtbot): + widget = StaticText(text="Test", fontsize=1.0) + qtbot.addWidget(widget) + + widget.resize(200, 100) + widget.resizeEvent(None) + widget.paintEvent(None) + qtbot.waitExposed(widget) + assert widget.width() == 200 + assert widget.height() == 100 + + +def test_value_display_default(fix, qtbot): + widget = ValueDisplay() + qtbot.addWidget(widget) + + assert widget.font_family == "Open Sans" + assert widget._value == 0.0 + assert widget.fail == False + assert widget.bad == False + assert widget.old == False + assert widget.annunciate == False + + widget.show() + assert widget.isVisible() + +def test_value_display_font_mask(fix, qtbot): + widget = ValueDisplay() + widget.font_size = 100 + widget.font_mask = "0.0" + widget.font_ghost_mask = "0.0" + qtbot.addWidget(widget) + widget.resizeEvent(None) + widget.paintEvent(None) + widget.show() + qtbot.waitExposed(widget) + assert widget.isVisible() + assert widget.font_size != 100 + +def test_value_display_set_value(fix, qtbot): + widget = ValueDisplay() + fix.db.set_value("TEST",42.0) + fix.db.get_item("TEST").fail = False + widget.setDbkey("TEST") + qtbot.addWidget(widget) + + assert widget.getValue() == 42.0 + + fix.db.set_value("TEST",3.14) + assert widget.getValue() == 3.14 + + +#@mock.patch("pyefis.instruments.misc.fix") +#def test_value_display_flags(mock_fix, qtbot): +def test_value_display_flags(fix, qtbot): + #mock_item = mock.Mock() + #mock_item.value = 100.0 + #mock_fix.db.get_item.return_value = mock_item + widget = ValueDisplay() + + fix.db.set_value("TEST",100) + widget.setDbkey("TEST") + qtbot.addWidget(widget) + + widget.failFlag(True) + assert widget.fail == True + assert widget.getValue() == 0.0 + + widget.failFlag(False) + assert widget.fail == False + assert widget.getValue() == 100.0 + + widget.oldFlag(True) + assert widget.old == True + assert widget.getValueText() == "100.0" + + widget.badFlag(True) + assert widget.bad == True + assert widget.getValueText() == "100.0" + + widget.failFlag(True) + assert widget.fail == True + assert widget.getValueText() == "xxx" + + widget.failFlag(False) + widget.annunciateFlag(True) + assert widget.annunciate == True + assert widget.textColor == widget.textAnnunciateColor + +@mock.patch("pyefis.instruments.misc.fix") +def test_set_db_key(mock_fix,qtbot): + mock_item = mock.MagicMock() + mock_item.value = 100.0 + mock_item.fail = True + mock_item.bad = False + mock_fix.db.get_item.return_value = mock_item + mock_setupGauge = mock.MagicMock() + widget = ValueDisplay() + qtbot.addWidget(widget) + widget.setDbkey('TEST') + mock_fix.db.get_item.assert_called_once_with("TEST") + mock_item.reportReceived.connect.assert_called_once_with(widget.setupGauge) + mock_item.annunciateChanged.connect.assert_called_once_with(widget.annunciateFlag) + mock_item.oldChanged.connect.assert_called_once_with(widget.oldFlag) + mock_item.badChanged.connect.assert_called_once_with(widget.badFlag) + mock_item.failChanged.connect.assert_called_once_with(widget.failFlag) + assert widget._dbkey == "TEST" + # These would only match if self.setupGauge() was called: + assert widget.fail == True + assert widget.bad == False +# assert mock_item. + + + +if __name__ == "__main__": + pytest.main() diff --git a/tests/instruments/test_numerical_display.py b/tests/instruments/test_numerical_display.py new file mode 100644 index 00000000..2c4fa560 --- /dev/null +++ b/tests/instruments/test_numerical_display.py @@ -0,0 +1,79 @@ +import pytest +from unittest import mock +from PyQt5.QtWidgets import QApplication +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QColor, QBrush +from pyefis.instruments.NumericalDisplay import NumericalDisplay + + +@pytest.fixture +def app(qtbot): + test_app = QApplication.instance() + if test_app is None: + test_app = QApplication([]) + return test_app + + +def test_numerical_display_default(qtbot): + widget = NumericalDisplay(total_decimals=4, scroll_decimal=2) + qtbot.addWidget(widget) + assert widget.total_decimals == 4 + assert widget.scroll_decimal == 2 + assert widget._bad == False + assert widget.value == 0 + widget.show() + qtbot.waitExposed(widget) + assert widget.isVisible() + + +def test_numerical_display_decimals(qtbot): + widget = NumericalDisplay(total_decimals=5, scroll_decimal=2, font_size=20) + qtbot.addWidget(widget) + widget.resize(80, 50) + widget.show() + widget.setValue(val=1234) + qtbot.waitExposed(widget) + assert widget.font_size != 20 + assert widget.pre_scroll_text.text() == "012" + widget.setValue(val=54321) + assert widget.pre_scroll_text.text() == "543" + widget.setBad(True) + widget.setValue(val=43210) + widget.getValue() + assert widget._bad == True + widget.resize(85, 50) + assert widget.pre_scroll_text.text() == "" + assert widget.value == 43210 + assert widget.pre_scroll_text.text() != "432" + a = widget.getValue() + assert a == 43210 + assert widget.pre_scroll_text.text() == "" + assert widget.getBad() == True + widget.setOld(True) + assert widget.getOld() == True + widget.setOld(False) + assert widget.getOld() == False + widget.setFail(True) + assert widget.getFail() == True + widget.setFail(False) + assert widget.getFail() == False + + widget.setBad(False) + widget.setValue(val=54321) + assert widget.pre_scroll_text.brush() == QBrush(QColor(Qt.white)) + + widget.setValue(val=-5432) + assert widget.pre_scroll_text.text() == "-54" + widget.setValue(val=-52) + assert widget.pre_scroll_text.text() == "-00" + + +# Not sure why line 270 is not showing as covered +# when this calls the getValue function: +def test_numerical_get_value(qtbot): + widget = NumericalDisplay(total_decimals=5, scroll_decimal=2, font_size=20) + qtbot.addWidget(widget) + widget.setValue(20) + widget.getValue() + assert widget._value == 20 + assert widget.getValue() == 20 diff --git a/tests/mock_db/__init__.py b/tests/mock_db/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/mock_db/client.py b/tests/mock_db/client.py new file mode 100644 index 00000000..b05a303a --- /dev/null +++ b/tests/mock_db/client.py @@ -0,0 +1,38 @@ +from unittest import mock + + +class SendThread: + def __init__(self, sock, queue): + pass + + def run(self): + pass + + def stop(self): + pass + +class ClientThread: + def __init__(self, host, port, db): + super(ClientThread, self).__init__() + self.sendqueue = mock.MagicMock() + self.db = db # Main FIX database + self.db.init_event.set() + + def handle_value(self, d): + pass + + def handle_request(self, d): + pass + + def run(self): + pass + + def stop(self): + pass + + def join(self): + pass + + def start(Self): + pass + diff --git a/tests/mock_db/scheduler.py b/tests/mock_db/scheduler.py new file mode 100644 index 00000000..fc62bcd1 --- /dev/null +++ b/tests/mock_db/scheduler.py @@ -0,0 +1,48 @@ +class IntervalTimer: + def __init__(self, interval): + self.interval = interval + self.callbacks = [] + + def fire_timer(self): + pass + + def add_callback(self, func): + if func not in self.callbacks: + self.callbacks.append(func) + + def start(self): + pass + + def stop(self): + pass + + def interval(self): + return self.interval + +class ScheduleThread: + def __init__(self): + self.timers = [] + + def run(self): + self.timers.append(IntervalTimer(100)) + self.timers.append(IntervalTimer(500)) + self.timers.append(IntervalTimer(1000)) + + def stop(self): + pass + + def getTimer(self, interval): + for each in self.timers: + if each.timer.interval() == interval: + return each + + def start(self): + pass + +initialized=False +def initialize(): + global initialized + scheduler = ScheduleThread() + scheduler.start() + initialized=True + diff --git a/tests/screenshots/__init__.py b/tests/screenshots/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/screenshots/configs/pyefis.instruments.gauges.NumericDisplay.yaml b/tests/screenshots/configs/pyefis.instruments.gauges.NumericDisplay.yaml new file mode 100644 index 00000000..f5f3876d --- /dev/null +++ b/tests/screenshots/configs/pyefis.instruments.gauges.NumericDisplay.yaml @@ -0,0 +1,189 @@ +main: + nodeID: 1 + screenWidth: 1000 + screenHeight: 1000 + screenFullSize: True + screenColor: (0,0,0) + +screens: + TEST: + layout: + rows: 100 + columns: 100 + draw_grid: true + instruments: + - type: ganged_numeric_display + gang_type: vertical + row: 10 + column: 10 + span: + rows: 90 + columns: 30 + groups: + - name: ROW1 + gap: 0 + common_options: + dbkey: NUMOK + decimal_places: 2 + show_units: false + font_family: DejaVu Sans Mono + instruments: + - + options: + show_units: false + - + options: + font_mask: "00.00" + - + options: + font_mask: "00.00" + font_family: "DSEG14 Classic" + #units_font_mask: '~~' + font_ghost_mask: "~~.~~" + #units_font_ghost_mask: "~~" + - + options: + font_mask: "00.00" + dbkey: NUMOLD + - + options: + font_mask: "00.00" + dbkey: NUMBAD + - + options: + font_mask: "00.00" + dbkey: NUMFAIL + - + options: + font_mask: "00.00" + dbkey: NUMANNUNCIATE + - + options: + font_mask: "00.00" + dbkey: NUMLOWWARN + - + options: + font_mask: "00.00" + dbkey: NUMLOWALARM + + - type: ganged_numeric_display + gang_type: vertical + row: 10 + column: 45 + span: + rows: 90 + columns: 30 + groups: + - name: ROW1 + gap: 0 + common_options: + dbkey: NUMOK + decimal_places: 2 + show_units: true + font_family: DejaVu Sans Mono + instruments: + - + options: + show_units: false + - + options: + font_mask: "00.00" + units_font_mask: '~~' + - + options: + font_mask: "00.00" + font_family: "DSEG14 Classic" + units_font_mask: '~~' + font_ghost_mask: "~~.~~" + units_font_ghost_mask: "~~" + - + options: + font_mask: "00.00" + dbkey: NUMOLD + units_font_mask: '~~' + - + options: + font_mask: "00.00" + dbkey: NUMBAD + units_font_mask: '~~' + - + options: + font_mask: "00.00" + dbkey: NUMFAIL + units_font_mask: '~~' + - + options: + font_mask: "00.00" + dbkey: NUMANNUNCIATE + units_font_mask: '~~' + - + options: + font_mask: "00.00" + dbkey: NUMLOWWARN + units_font_mask: '~~' + - + options: + font_mask: "00.00" + dbkey: NUMLOWALARM + units_font_mask: '~~' + + + + + + + - type: ganged_numeric_display + gang_type: vertical + row: 10 + column: 80 + span: + rows: 90 + columns: 20 + groups: + - name: ROW1 + gap: 0 + common_options: + dbkey: NUMOK + decimal_places: 2 + show_units: false + font_family: DejaVu Sans Mono + + instruments: + - + options: + show_units: false + - + options: + font_mask: "00.00" + - + options: + font_mask: "00.00" + font_family: "DSEG14 Classic" + #units_font_mask: '~~' + font_ghost_mask: "~~.~~" + #units_font_ghost_mask: "~~" + - + options: + font_mask: "00.00" + dbkey: NUMOLD + - + options: + font_mask: "00.00" + dbkey: NUMBAD + - + options: + font_mask: "00.00" + dbkey: NUMFAIL + - + options: + font_mask: "00.00" + dbkey: NUMANNUNCIATE + - + options: + font_mask: "00.00" + dbkey: NUMLOWWARN + - + options: + font_mask: "00.00" + dbkey: NUMLOWALARM + diff --git a/tests/screenshots/generate_screenshots.py b/tests/screenshots/generate_screenshots.py new file mode 100644 index 00000000..587ec947 --- /dev/null +++ b/tests/screenshots/generate_screenshots.py @@ -0,0 +1,33 @@ +import pytest +import warnings +import importlib +from unittest import mock +from PyQt5.QtWidgets import QApplication +from PyQt5.QtCore import Qt, QObject +from PyQt5.QtGui import QColor +from tests.screenshots import gui +import yaml +import os + +@pytest.fixture +def app(qtbot): + test_app = QApplication.instance() + if test_app is None: + test_app = QApplication([]) + return test_app + + +def test_save_screenshot_numeric_display(qtbot, request): + with open("tests/screenshots/configs/pyefis.instruments.gauges.NumericDisplay.yaml") as cf: + config = yaml.safe_load(cf) + #widget = Screen("TEST", "pyefis.screens.screenbuilder", config) + gui.initialize(config,"tests",{}) + qtbot.waitExposed(gui.mainWindow.scr.module) + path = qtbot.screenshot(gui.mainWindow, "numeric_display") + qtbot.wait(500) + os.rename( + path, + request.config.rootdir + + "/extras/extras/test_results/numeric_display.png", + ) + qtbot.wait(10000) diff --git a/tests/screenshots/gui.py b/tests/screenshots/gui.py new file mode 100644 index 00000000..b0052b04 --- /dev/null +++ b/tests/screenshots/gui.py @@ -0,0 +1,166 @@ +# Copyright (c) 2016 Phil Birkelbach +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +from PyQt5.QtGui import * +from PyQt5.QtCore import * +from PyQt5.QtWidgets import * + +import time +import importlib +import logging +import sys +import os +from pyefis import hooks +from pyefis import hmi +import pyavtools.fix as fix +import pyavtools.scheduler as scheduler + + +# This class is just a structure to hold information about a single +# screen that will be loaded. +class Screen(QObject): + screenShow = pyqtSignal() + screenHide = pyqtSignal() + + def __init__(self, name, module, config): + super(Screen, self).__init__() + self.name = name + self.module = importlib.import_module(module) + self.config = config + + # This would hold the instantiated Screen object from the module. + self.object = None + self.default = False + + def show(self): + self.object.show() + self.screenShow.emit() + + def hide(self): + self.object.hide() + self.screenHide.emit() + + +class Main(QMainWindow): + keyPress = pyqtSignal(QEvent) + keyRelease = pyqtSignal(QEvent) + windowShow = pyqtSignal(QEvent) + windowClose = pyqtSignal(QEvent) + #change_asd_mode = pyqtSignal(QEvent) + def __init__(self, config, config_path, preferences, scr, parent=None): + super(Main, self).__init__(parent) + self.preferences = preferences + self.config_path = config_path + self.scr = scr + self.screenWidth = int(config["main"].get("screenWidth", False)) + self.screenHeight = int(config["main"].get("screenHeight", False)) + if not ( self.screenWidth and self.screenHeight ): + # screenWidth and screenHeight are not defined in the config file + # go full screen + pscreen = QApplication.primaryScreen() + screensize = pscreen.size() + self.screenWidth = screensize.width() + self.screenHeight = screensize.height() + + self.screenColor = config["main"]["screenColor"] + self.nodeID = config["main"].get('nodeID', 1) + + self.setObjectName("EFIS") + self.resize(self.screenWidth, self.screenHeight) + w = QWidget(self) + w.setGeometry(0, 0, self.screenWidth, self.screenHeight) + + p = w.palette() + if self.screenColor: + p.setColor(w.backgroundRole(), QColor(self.screenColor)) + w.setPalette(p) + w.setAutoFillBackground(True) + + scr.object = self.scr.module.Screen(self) + scr.object.resize(self.width(), self.height()) + scr.object.move(0,0) + scr.show() + + + #def doExit(self, s=""): + # Ensure external processes are terminated before exiting + # For example waydroid/weston if they are in use + #for s in screens: + # s.object.close() + # Close down fix connections + # This needs done before the main event loop is stopped below + + # We send signals for these events so everybody can play. + def showEvent(self, event): + self.windowShow.emit(event) + + def closeEvent(self, event): + log.debug("Window Close event received") + self.windowClose.emit(event) + + def keyPressEvent(self, event): + self.keyPress.emit(event) + + def keyReleaseEvent(self, event): + self.keyRelease.emit(event) + + # def change_asd_mode_event (self, event): + # self.change_asd_mode.emit(event) + + def get_config_item(self, child, key): + print(f"child:{child,} key:{key}") + if self.scr.object == child: + print("match") + print(self.scr.config) + return self.scr.config.get(key) + else: + return None + + +def initialize(config,config_path,preferences): + global mainWindow + global log + log = logging.getLogger(__name__) + log.info("Initializing Graphics") + module = "pyefis.screens.screenbuilder" + + scr = Screen("TEST", module, config['screens']["TEST"]) + print(scr) + mainWindow = Main(config,config_path,preferences,scr) + + mainWindow.showFullScreen() + + def button_timeout(): + # set MENUTIMEOUT True + button_key.value = True + + def button_timeout_reset(): + if not button_key.value: + #When set to False reset timer + button_timer.start() + + if config['main'].get('button_timeout', False): + scheduler.initialize() + button_timer = scheduler.scheduler.getTimer(config['main']['button_timeout']) + if not button_timer: + scheduler.scheduler.timers.append(scheduler.IntervalTimer(config['main']['button_timeout'])) + scheduler.scheduler.timers[-1].start() + button_timer = scheduler.scheduler.getTimer(config['main']['button_timeout']) + button_key = fix.db.get_item('HIDEBUTTON') + button_timer.add_callback(button_timeout) + button_key.valueWrite[bool].connect(button_timeout_reset) + + diff --git a/tests/test_cfg.py b/tests/test_cfg.py new file mode 100644 index 00000000..7fe4af24 --- /dev/null +++ b/tests/test_cfg.py @@ -0,0 +1,109 @@ +import pytest +import os +from unittest.mock import MagicMock, patch +from pyefis.cfg import from_yaml + + +def test_missing_path(): + with pytest.raises(SyntaxError): + d = from_yaml(fname="missing_path.yaml") + + +def test_sub_parent_includes(): + a = from_yaml("tests/data/cfg/test_sub_parent_includesA.yaml") + assert a == {"a": {"b": {"c": {"done": True}}, "b2": {"d": {"end": True}}}} + + +def test_array_and_preferences(): + b = from_yaml( + "tests/data/cfg/test_array_and_preferences.yaml", + preferences={ + "includes": { + "PREFERENCEA": "test_array_and_preferencesA.yaml", + "PREFERENCEB": "test_array_and_preferencesB.yaml", + } + }, + ) + assert b == {"ARRAYS": {"includeC": True, "includeA": True, "includeB": True}} + + +def test_data_type(): + with pytest.raises(SyntaxError): + c = from_yaml("tests/data/cfg/test_data_type.yaml") + + +def test_loop_detection_exception(): + with pytest.raises(RecursionError): + d = from_yaml("tests/data/cfg/test_loop_detection_exception.yaml") + + +def test_preference_file_not_found(): + with pytest.raises(FileNotFoundError): + e = from_yaml( + "tests/data/cfg/sub/test_preference_file_not_found.yaml", + preferences={"includes": {"NOT_FOUND": "preference_not_found.yaml"}}, + ) + +def test_preference_file_not_found_list(): + with pytest.raises(FileNotFoundError): + e = from_yaml( + "tests/data/cfg/sub/test_preference_file_not_found_list.yaml", + preferences={"includes": {"NOT_FOUND": "preference_not_found.yaml"}}, + ) + +def test_preference_file_not_found_list_no_includes(): + with pytest.raises(FileNotFoundError): + e = from_yaml( + "tests/data/cfg/sub/test_preference_file_not_found_list.yaml", + preferences={"includes_typo": {"NOT_FOUND": "preference_not_found.yaml"}}, + ) + + +def test_no_preferences(): + with pytest.raises(FileNotFoundError): + e = from_yaml("tests/data/cfg/test_no_preferences.yaml") + + +def test_list_include_missing_items(): + with pytest.raises(SyntaxError): + e = from_yaml("tests/data/cfg/test_list_include_missing_items.yaml") + + +def test_list_include_missing_items_via_preferences(): + with pytest.raises(SyntaxError): + e = from_yaml( + "tests/data/cfg/test_list_include_missing_items_pref.yaml", + preferences={ + "includes": { + "PREFERENCED": "test_list_include_missing_items_via_preferencesD.yaml", + "PREFERENCEE": "preferencee.yaml", + } + }, + ) + + +def test_list_include_missing_items_via_preferences_nested(): + with pytest.raises(SyntaxError): + e = from_yaml( + "tests/data/cfg/test_list_include_missing_items_pref_nested.yaml", + preferences={ + "includes": { + "PREFERENCEF": "test_list_include_missing_items_via_preferences_nestedF.yaml", + "PREFERENCEG": "test_list_include_missing_items_via_preferences_nestedG.yaml", + } + }, + ) + + +def test_list_bpath_preference(): + e = from_yaml( + "tests/data/cfg/test_list_bpath_preference.yaml", + preferences={ + "includes": { + "A": "sub/sub2/test_list_bpath_preferenceA.yaml", + "B": "test_list_bpath_preferenceB.yaml", + "C": "test_list_bpath_preferenceC.yaml", + } + }, + ) + assert e == {"test": [{"b_id": 5}, {"c_id_is": 5}, 5, "Ten"]} diff --git a/tests/test_version.py b/tests/test_version.py new file mode 100644 index 00000000..de8dc2eb --- /dev/null +++ b/tests/test_version.py @@ -0,0 +1,5 @@ +import pyefis.version + + +def test_pyefis_version(): + assert pyefis.__version__ is not None diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 00000000..6f6e23ab --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,35 @@ +from contextlib import contextmanager + +# Used in tests to wrap a function and verify +# it was calld with specific arguments or not +class CallTracker: + def __init__(self): + self.calls = [] + + def track(self, obj, method_name): + method = getattr(obj, method_name) + def wrapper(obj, *args, **kwargs): + self.calls.append((method_name, args, kwargs)) + return method(obj, *args, **kwargs) + + setattr(obj, method_name, wrapper) + def was_called(self, method_name): + return any( call[0] == method_name for call in self.calls) + + def was_not_called(self, method_name): + return not any( call[0] == method_name for call in self.calls) + + def was_called_with(self, method_name, *args, **kwargs): + return any(call[0] == method_name and call[1] == args for call in self.calls) + + def was_not_called_with(self, method_name, *args, **kwargs): + return not any(call[0] == method_name and call[1] == args for call in self.calls) +@contextmanager +def track_calls(obj, method_name): + if not isinstance(method_name, list): + method_name = [method_name] + tracker = CallTracker() + for m in method_name: + tracker.track(obj, m) + yield tracker +