├── source ├── pypi │ ├── __init__.py │ ├── pyproject.toml │ ├── setup.py.tpl │ └── README.md ├── pcm │ ├── icon.png │ └── metadata-template.json ├── kivar_icon_dark.png ├── kivar_icon_dark_24.png ├── kivar_icon_light.png ├── kivar_icon_light_24.png ├── kivar_version.py ├── kivar_gui_custom.py ├── release.sh ├── __init__.py ├── kivar_gui.py ├── kivar_forms.py └── kivar_cli.py ├── doc ├── pcb-change.png ├── pcb-nochange.png ├── plugin-empty.png ├── plugin-changes.png ├── examples │ └── README.md ├── gen │ └── render.sh └── kivar-icon.inkscape.svg ├── .gitignore ├── .github └── ISSUE_TEMPLATE │ ├── question.md │ ├── bug_report.md │ └── feature_request.md ├── demo ├── kivar-demo.kicad_dru ├── kivar-demo.kivar_vdt.csv ├── kivar-demo.kicad_sch ├── rule_processing.kicad_sch ├── kivar-demo.kicad_pro └── example_3.kicad_sch └── LICENSE /source/pypi/__init__.py: -------------------------------------------------------------------------------- 1 | from .kivar_cli import main 2 | -------------------------------------------------------------------------------- /doc/pcb-change.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markh-de/KiVar/HEAD/doc/pcb-change.png -------------------------------------------------------------------------------- /source/pcm/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markh-de/KiVar/HEAD/source/pcm/icon.png -------------------------------------------------------------------------------- /doc/pcb-nochange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markh-de/KiVar/HEAD/doc/pcb-nochange.png -------------------------------------------------------------------------------- /doc/plugin-empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markh-de/KiVar/HEAD/doc/plugin-empty.png -------------------------------------------------------------------------------- /doc/plugin-changes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markh-de/KiVar/HEAD/doc/plugin-changes.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /release/ 2 | *-backups/ 3 | *~ 4 | fp-info-cache 5 | *.kicad_prl 6 | __pycache__/ 7 | 8 | -------------------------------------------------------------------------------- /source/kivar_icon_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markh-de/KiVar/HEAD/source/kivar_icon_dark.png -------------------------------------------------------------------------------- /source/kivar_icon_dark_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markh-de/KiVar/HEAD/source/kivar_icon_dark_24.png -------------------------------------------------------------------------------- /source/kivar_icon_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markh-de/KiVar/HEAD/source/kivar_icon_light.png -------------------------------------------------------------------------------- /source/kivar_icon_light_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markh-de/KiVar/HEAD/source/kivar_icon_light_24.png -------------------------------------------------------------------------------- /source/kivar_version.py: -------------------------------------------------------------------------------- 1 | def version(): 2 | return '0.5.2' 3 | 4 | if __name__ == "__main__": 5 | print(version()) 6 | -------------------------------------------------------------------------------- /source/pypi/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools >= 42.0.0"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Usage question 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Ask your question here. 11 | -------------------------------------------------------------------------------- /demo/kivar-demo.kicad_dru: -------------------------------------------------------------------------------- 1 | (version 1) 2 | 3 | (rule "Allow overlapping courtyards for DNP parts" 4 | (condition "A.Type == 'Footprint' && B.Type == 'Footprint' && A.Do_not_populate") 5 | (constraint courtyard_clearance (min -1mm)) 6 | ) 7 | -------------------------------------------------------------------------------- /demo/kivar-demo.kivar_vdt.csv: -------------------------------------------------------------------------------- 1 | ,"BOOT_SRC","IOEXP_TYPE/ADDR","EEPROM_ADDR","I_LED_MA","ISL91127" 2 | "Series 1000","NAND","9535/0x20","0x54","50","IRNZ" 3 | "Series 3000 Basic","NAND","9535/0x24","0x54","70","IRNZ" 4 | "Series 3000 Pro","SD","9535/0x24","0x54","100","IRAZ" 5 | "Series 5000 Basic","EMMC","9539/0x74","0x55","80","IRNZ" 6 | "Series 5000 Pro","SD","9539/0x74","0x55","110","IRAZ" 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. ... 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. Linux] 25 | - Version [e.g. 0.0.1] 26 | 27 | **Additional context** 28 | Add any other context about the problem here. 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /source/pypi/setup.py.tpl: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | import os 3 | 4 | with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'README.md'), 'r') as f: 5 | long_descr = f.read() 6 | 7 | setup( 8 | name='kivar', 9 | description='PCB Assembly Variants for KiCad', 10 | long_description=long_descr, 11 | long_description_content_type='text/markdown', 12 | license='MIT', 13 | url='https://github.com/markh-de/KiVar', 14 | author='Mark Hämmerling', 15 | author_email='dev@markh.de', 16 | version="<>", 17 | packages=["kivar"], 18 | install_requires=[], 19 | entry_points={ 20 | "console_scripts": [ 21 | "kivar = kivar:main" 22 | ] 23 | } 24 | ) 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Mark Hämmerling 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /doc/examples/README.md: -------------------------------------------------------------------------------- 1 | # How to Create Documentation Figures 2 | 3 | ## Schematic Snippet SVGs 4 | 5 | Plot all sheets as SVG using the following command (in the `demo/` directory): 6 | 7 | ``` 8 | kicad-cli sch export svg -o plot -e kivar-demo.kicad_sch 9 | ``` 10 | 11 | Then, open each SVG in Inkscape and 12 | 13 | * change the document scale to *0.5 user units per mm* (in Document Properties), 14 | * select the Documentation Figure Frame, 15 | * press *Ctrl+Shift+R* (Fit Page to Selection), 16 | * press *Ctrl+Shift+S* and save as *Plain SVG* to "examples/${number}.svg". 17 | 18 | ## 3D Images 19 | 20 | Resize the 3D viewer window accordingly (max. resolution). 21 | 22 | Copy image clipboard, paste to new image in GIMP, export as PNG. 23 | 24 | ## Vector Screenshots 25 | 26 | Used GTK Theme: "Adapta" 27 | 28 | Use Vector Screenshot tool to create screenshots of the selection dialog window. 29 | 30 | Open each resulting SVG in Inkscape and 31 | 32 | * change the document scale to *1.4 user units per mm* (in Document Properties), 33 | * select the base rectangle (enter group), 34 | * add stroke (RGBA: `4d4d4dff` = "70% Gray", *1.5pt* width), 35 | * press *Ctrl+Shift+R* (Fit Page to Selection), 36 | * press *Ctrl+Shift+S* and save as *Plain SVG*. 37 | -------------------------------------------------------------------------------- /doc/gen/render.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | fixup_pcb() { 4 | # kicad-cli pcb render does not honor the "DNP" or "NotInPos" 5 | # attributes, hence we hide all DNP 3D models prior to rendering: 6 | python - "$1" <<__PARROT__ 7 | import pcbnew 8 | import sys 9 | board = pcbnew.LoadBoard(sys.argv[1]) 10 | for fp in board.GetFootprints(): 11 | if fp.IsDNP(): 12 | for index, model in enumerate(fp.Models()): 13 | fp.Models()[index].m_Show = False 14 | print(f"Hide 3D model #'{index+1}' of {fp.GetReference()}.") 15 | pcbnew.SaveBoard(sys.argv[1], board) 16 | __PARROT__ 17 | } 18 | 19 | render() { 20 | kicad-cli pcb render -o "$2" --quality basic --zoom 1.44 --width 2500 --height 1800 "$1" 21 | } 22 | 23 | kivar_cli="$(dirname "$0")/../../source/kivar_cli.py" 24 | pcb_orig="$(dirname "$0")/../../demo/kivar-demo.kicad_pcb" 25 | pcb_nochange=$(mktemp --suffix .kicad_pcb) 26 | pcb_change=$(mktemp --suffix .kicad_pcb) 27 | 28 | python "$kivar_cli" set -v -V 'Series 1000' -o "$pcb_nochange" "$pcb_orig" 29 | fixup_pcb "$pcb_nochange" 30 | render "$pcb_nochange" "$(dirname "$0")/../pcb-nochange.png" 31 | 32 | python "$kivar_cli" set -v -V 'Series 5000 Basic' -o "$pcb_change" "$pcb_orig" 33 | fixup_pcb "$pcb_change" 34 | render "$pcb_change" "$(dirname "$0")/../pcb-change.png" 35 | 36 | rm "$pcb_nochange" "$pcb_change" 37 | -------------------------------------------------------------------------------- /source/kivar_gui_custom.py: -------------------------------------------------------------------------------- 1 | import wx 2 | 3 | """ Custom widgets referenced in the code generated by wxFB """ 4 | 5 | class MenuButton(wx.Button): 6 | def __init__(self, parent, id=wx.ID_ANY, label='', pos=wx.DefaultPosition, size=wx.DefaultSize, style=0, validator=wx.DefaultValidator, name=wx.ButtonNameStr): 7 | super().__init__(parent=parent, id=id, label=label, pos=pos, size=size, style=style, validator=validator, name=name) 8 | self.menu = None 9 | self.menu_update_callback = None 10 | self.parent = parent 11 | self.Bind(wx.EVT_BUTTON, self.on_show_menu) 12 | 13 | def set_menu_config(self, menu=None, menu_update_callback=None): 14 | self.menu = menu 15 | self.menu_update_callback = menu_update_callback 16 | 17 | def on_show_menu(self, event): 18 | if self.menu is not None: 19 | if self.menu_update_callback is not None: 20 | self.menu_update_callback(self.menu) 21 | btn_size = self.GetSize() 22 | self.PopupMenu(self.menu, (0, btn_size.y)) 23 | event.Skip() 24 | 25 | class PcbItemListBox(wx.ListBox): 26 | def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, choices=[], style=0, validator=wx.DefaultValidator, name=''): 27 | super().__init__(parent=parent, id=id, pos=pos, size=size, choices=choices, style=style, validator=validator, name=name) 28 | self.select_handler = None 29 | self.uuids = [] 30 | self.Bind(wx.EVT_LISTBOX, self.on_item_selected) 31 | 32 | def set_item_list(self, item_list): 33 | self.uuids = [] 34 | self.Clear() 35 | for item in item_list: 36 | self.uuids.append(item[0]) 37 | self.Append(item[2]) 38 | 39 | def set_select_handler(self, handler): 40 | self.select_handler = handler 41 | 42 | def on_item_selected(self, event): 43 | if self.select_handler is not None: 44 | self.select_handler(self.uuids[self.GetSelection()]) 45 | event.Skip() 46 | -------------------------------------------------------------------------------- /source/pcm/metadata-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://go.kicad.org/pcm/schemas/v1", 3 | "name": "KiVar", 4 | "description": "PCB Assembly Variants for KiCad", 5 | "description_full": "This plugin provides multi-aspect PCB assembly variant selection.\nIt assigns component values, field content, footprint attributes (such as 'Do not populate') and features (e.g. solder paste application or individual 3D model visibility) according to variation rules specified in component fields.\n\u2014 Key Concepts \u2014\n\u2022 Designs may contain multiple independent variation aspects (i.e. scopes/dimensions/degrees of freedom).\n\u2022 Variation rules are fully contained in the native design files (no external files) and portable (copying parts to another design keeps variation specification intact).\n\u2022 Component data is modified in place, enabling compatibility with any exporter.\n\u2022 No external state information is stored; currently matching variation choices are detected automatically.\n\u2022 Optional external variant definition table in independent file format (CSV) enables switching of aspect groups based on a single variant name.\n\u2014 Documentation and Demo Project \u2014\nThe variation expression syntax and variant table format specification including examples can be found under the 'Documentation' resource link.\nA demo KiCad project is contained in the source code repository, which can be downloaded as an archive from the 'Source and Demo' resource link.\n\u2014 Support \u2014\nIf you find bugs or want to discuss ideas or questions, please follow the 'Issue Tracker' resource link.", 6 | "identifier": "de.markh.kivar", 7 | "type": "plugin", 8 | "author": { 9 | "name": "Mark H\u00e4mmerling", 10 | "contact": { 11 | "github": "https://github.com/markh-de" 12 | } 13 | }, 14 | "license": "MIT", 15 | "resources": { 16 | "Documentation": "https://github.com/markh-de/KiVar/blob/v<>/README.md", 17 | "Source and Demo": "https://github.com/markh-de/KiVar/archive/refs/tags/v<>.zip", 18 | "Issue Tracker": "https://github.com/markh-de/KiVar/issues", 19 | "License": "https://raw.githubusercontent.com/markh-de/KiVar/v<>/LICENSE" 20 | }, 21 | "keep_on_update": [ 22 | ".*/kivar_config\\.json$" 23 | ], 24 | "versions": [ 25 | { 26 | "version": "<>", 27 | "status": "stable", 28 | "kicad_version": "8.0" 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /source/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | release_plugin_pcm() { 4 | out_base_name="${RELEASE_NAME}-${VERSION}-pcm.zip" 5 | meta_version="$VERSION" 6 | meta_status="stable" 7 | meta_kicad_version="8.0" 8 | meta_download_url="https://pcm.kivar.markh.de/${out_base_name}" 9 | 10 | release_dir="$RELEASE_DIR" 11 | src_pcm_dir="$SRC_DIR/pcm" 12 | tmp_dir=$(mktemp -d) 13 | out_file="$release_dir/${out_base_name}" 14 | 15 | mkdir -p "$tmp_dir/plugins" 16 | 17 | cp "$SRC_DIR/__init__.py" \ 18 | "$SRC_DIR/kivar_engine.py" \ 19 | "$SRC_DIR/kivar_forms.py" \ 20 | "$SRC_DIR/kivar_gui.py" \ 21 | "$SRC_DIR/kivar_gui_custom.py" \ 22 | "$SRC_DIR/kivar_version.py" \ 23 | "$SRC_DIR/kivar_icon_light.png" \ 24 | "$SRC_DIR/kivar_icon_light_24.png" \ 25 | "$SRC_DIR/kivar_icon_dark.png" \ 26 | "$SRC_DIR/kivar_icon_dark_24.png" \ 27 | "$tmp_dir/plugins/" 28 | 29 | mkdir -p "$tmp_dir/resources" 30 | cp "$src_pcm_dir/icon.png" \ 31 | "$tmp_dir/resources/" 32 | 33 | sed "s/<>/$VERSION/g" "$src_pcm_dir/metadata-template.json" > "$tmp_dir/metadata.json" 34 | 35 | rm -f "$out_file" 36 | cd "$tmp_dir" 37 | zip -r "$out_file" . 38 | cd - >/dev/null 39 | 40 | rm -rf "$tmp_dir" 41 | 42 | echo '' 43 | echo '<<< PCM Package Info' 44 | cat <>>' 56 | } 57 | 58 | release_cli_pypi() { 59 | release_dir="$RELEASE_DIR" 60 | pypi_meta_dir="$SRC_DIR/pypi" 61 | tmp_dir=$(mktemp -d) 62 | 63 | sed "s/<>/$VERSION/g" "$pypi_meta_dir/setup.py.tpl" > "$tmp_dir/setup.py" 64 | 65 | mkdir -p "$tmp_dir/$RELEASE_NAME/" 66 | 67 | cp "$pypi_meta_dir/__init__.py" \ 68 | "$SRC_DIR/kivar_cli.py" \ 69 | "$SRC_DIR/kivar_engine.py" \ 70 | "$SRC_DIR/kivar_version.py" \ 71 | "$tmp_dir/$RELEASE_NAME/" 72 | 73 | cp "$pypi_meta_dir/README.md" \ 74 | "$pypi_meta_dir/pyproject.toml" \ 75 | "$tmp_dir/" 76 | 77 | cd "$tmp_dir/" 78 | python3 ./setup.py sdist 79 | cd - >/dev/null 80 | 81 | cp "$tmp_dir/dist/"* \ 82 | "$release_dir" 83 | 84 | rm -rf "$tmp_dir" 85 | } 86 | 87 | # main 88 | 89 | SRC_DIR=$(readlink -f "$(dirname "$0")") 90 | VERSION=$(python3 ${SRC_DIR}/kivar_version.py) 91 | RELEASE_NAME='kivar' 92 | ID='de_markh_kivar' 93 | RELEASE_DIR=$(readlink -f "$(dirname "$0")")"/../release/v$VERSION" 94 | 95 | echo "Creating KiVar release $VERSION:" 96 | echo '' 97 | 98 | rm -rf "$RELEASE_DIR/" 99 | mkdir -p "$RELEASE_DIR/" 100 | 101 | echo "--- PyPI ---" 102 | release_cli_pypi 103 | echo '' 104 | 105 | echo "--- PCM ---" 106 | release_plugin_pcm 107 | echo '' 108 | 109 | echo 'Release files created.' 110 | -------------------------------------------------------------------------------- /source/__init__.py: -------------------------------------------------------------------------------- 1 | import pcbnew 2 | import wx 3 | import os 4 | import json 5 | 6 | from .kivar_version import version 7 | from . import kivar_gui as gui 8 | from . import kivar_engine as engine 9 | 10 | DEBUG = False 11 | def create_icon(source_icon, cached_icon, icon_size, max_size=64): 12 | if not os.path.exists(cached_icon): 13 | try: 14 | image = wx.Image(source_icon, wx.BITMAP_TYPE_PNG) 15 | image = image.Scale(icon_size, icon_size, wx.IMAGE_QUALITY_HIGH) 16 | image.SaveFile(cached_icon, wx.BITMAP_TYPE_PNG) 17 | except (PermissionError, FileNotFoundError): 18 | pass 19 | 20 | def get_icon_size(): 21 | icon_size = 24 # default size 22 | scale = wx.Display().GetScaleFactor() # use default display 23 | version_major_minor = pcbnew.GetMajorMinorVersion() 24 | config_dir = None 25 | manual_home = os.environ.get("KICAD_CONFIG_HOME") 26 | if manual_home is not None and manual_home != '': 27 | config_dir = manual_home 28 | else: 29 | platform = wx.Platform 30 | if platform == '__WXGTK__': 31 | config_home = os.environ.get('XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), '.config')) 32 | config_dir = os.path.join(config_home, 'kicad') 33 | elif platform == '__WXMAC__': 34 | config_home = os.environ.get('XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), 'Library', 'Preferences')) 35 | config_dir = os.path.join(config_home, 'kicad') 36 | elif platform == '__WXMSW__': 37 | appdata = os.environ.get('appdata', os.path.join(os.path.expanduser('~'), 'AppData', 'Roaming')) 38 | config_dir = os.path.join(appdata, 'kicad') 39 | if config_dir is not None: 40 | config_file = os.path.join(config_dir, version_major_minor, 'kicad_common.json') 41 | with open(config_file, 'r') as f: 42 | config = json.load(f) 43 | icon_size = config.get("appearance", {}).get("toolbar_icon_size", icon_size) 44 | return int(icon_size * scale) 45 | 46 | class KiVarPlugin(pcbnew.ActionPlugin): 47 | def defaults(self): 48 | plugin_dir = os.path.dirname(__file__) 49 | 50 | if DEBUG: 51 | import sys 52 | log_file = os.path.join(plugin_dir, 'kivar_debug.txt') 53 | sys.stderr = open(log_file, "w") 54 | sys.stdout = sys.stderr 55 | 56 | icon_size = get_icon_size() 57 | icon_source_light = os.path.join(plugin_dir, 'kivar_icon_light.png') 58 | icon_source_dark = os.path.join(plugin_dir, 'kivar_icon_dark.png') 59 | icon_scaled_light = os.path.join(plugin_dir, f'kivar_icon_light_{icon_size}.png') 60 | icon_scaled_dark = os.path.join(plugin_dir, f'kivar_icon_dark_{icon_size}.png') 61 | create_icon(icon_source_light, icon_scaled_light, icon_size) 62 | create_icon(icon_source_dark, icon_scaled_dark, icon_size) 63 | 64 | self.name = 'KiVar: Switch Assembly Variants' 65 | self.category = 'Assembly Variants' 66 | self.description = 'Switch between predefined assembly variant choices' 67 | self.icon_file_name = icon_scaled_light 68 | self.dark_icon_file_name = icon_scaled_dark 69 | self.show_toolbar_button = True 70 | 71 | def Run(self): 72 | if DEBUG: 73 | import sys 74 | log_file = os.path.join(os.path.dirname(__file__), 'kivar_debug.txt') 75 | sys.stderr = open(log_file, "w") 76 | sys.stdout = sys.stderr 77 | compatibility_problem = engine.pcbnew_compatibility_error() 78 | if compatibility_problem is not None: 79 | wx.MessageBox(message=compatibility_problem, caption=f'Compatibility Problem | KiVar {version()}', style=wx.ICON_ERROR) 80 | return 81 | board = pcbnew.GetBoard() 82 | fpdict = engine.build_fpdict(board) 83 | vardict, errors = engine.build_vardict(fpdict, engine.field_ids(board)) 84 | if len(errors) > 0: 85 | gui.show_error_dialog(errors, board) 86 | elif len(vardict) == 0: 87 | gui.show_missing_rules_dialog(engine.legacy_expressions_found(fpdict)) 88 | else: 89 | gui.show_selection_dialog(board, fpdict, vardict) 90 | 91 | KiVarPlugin().register() 92 | -------------------------------------------------------------------------------- /source/pypi/README.md: -------------------------------------------------------------------------------- 1 | # KiVar − PCB Assembly Variants for KiCad 2 | 3 | ## Introduction 4 | 5 | KiVar is a tool for **KiCad PCB Assembly Variant selection**, provided as platform-independent 6 | 7 | * **Command Line Application** (this package) and 8 | * **KiCad Action Plugin** (available in KiCad PCM). 9 | 10 | PCB component variation rules for multiple limited design scopes are defined in component (i.e. symbol or footprint) fields. This allows for the complete low-level variation configuration to be contained in the schematic and board files without requiring external data outside the native KiCad design. 11 | 12 | In addition, variant definition tables (KiCad-independent CSV format) can be used to summarize the configuration of these low-level scopes in classic flat variants, which can then be switched between. 13 | 14 | ## Features 15 | 16 | KiVar assigns PCB component **values**, **field content**, **attributes** (such as _Do not populate_, _Not in position files_ or _Not in BoM_) and **features** (such as _individual 3D model visibility_ or _solder paste application_) according to variation rules specified in footprint fields. When applying those rules, components are modified **in place**, allowing for immediate update of the PCB design as well as the 3D view and enabling compatibility with _any_ exporter. 17 | 18 | Back-propagation of modified component data from the PCB to the schematic can be done in an extra step. 19 | 20 | ## What to Expect 21 | 22 | Example usage of the **KiVar Command Line Interface app**: 23 | 24 | ``` 25 | $ kivar list --selection kivar-demo.kicad_pcb 26 | ~: 'Series 1000' 'Series 3000 Basic' ['Series 3000 Pro'] 'Series 5000 Basic' 'Series 5000 Pro' 27 | BOOT_SRC~: EMMC JP NAND [SD] 28 | IOEXP_TYPE/ADDR~: 9535/0x20 [9535/0x24] 9539/0x74 29 | EEPROM_ADDR~: [0x54] 0x55 30 | I_LED_MA~: 10 20 30 40 50 60 70 80 90 [100] 110 120 130 140 150 JP 31 | ISL91127~: [IRAZ] IRNZ 32 | UVLO_LO/HI: 2.41V/3.40V [3.15V/3.57V] 33 | VOUT: 1.2V [1.8V] 2.5V 3.3V 34 | 35 | $ kivar set --variant 'Series 5000 Pro' --verbose kivar-demo.kicad_pcb 36 | Changes (19): 37 | Change R1 'Do not populate' from 'true' to 'false' (EEPROM_ADDR=0x55). 38 | Change R1 'Exclude from bill of materials' from 'true' to 'false' (EEPROM_ADDR=0x55). 39 | Change R1 'Exclude from position files' from 'true' to 'false' (EEPROM_ADDR=0x55). 40 | Change R2 'Do not populate' from 'false' to 'true' (EEPROM_ADDR=0x55). 41 | Change R2 'Exclude from bill of materials' from 'false' to 'true' (EEPROM_ADDR=0x55). 42 | Change R2 'Exclude from position files' from 'false' to 'true' (EEPROM_ADDR=0x55). 43 | Change R21 field 'VarID' from 'A' to 'B' (I_LED_MA=110). 44 | Change R30 'Do not populate' from 'true' to 'false' (I_LED_MA=110). 45 | Change R30 'Exclude from bill of materials' from 'true' to 'false' (I_LED_MA=110). 46 | Change R30 'Exclude from position files' from 'true' to 'false' (I_LED_MA=110). 47 | Change U1 field 'I2C Address' from '0x54' to '0x55' (EEPROM_ADDR=0x55). 48 | Change U1 field 'VarID' from '54' to '55' (EEPROM_ADDR=0x55). 49 | Change U4 value from 'TCA9535PWR' to 'TCA9539PWR' (IOEXP_TYPE/ADDR=9539/0x74). 50 | Change U4 visibility of 3D model #1 from 'true' to 'false' (IOEXP_TYPE/ADDR=9539/0x74). 51 | Change U4 visibility of 3D model #2 from 'false' to 'true' (IOEXP_TYPE/ADDR=9539/0x74). 52 | Change U4 field 'I2C Address' from '0x24' to '0x74' (IOEXP_TYPE/ADDR=9539/0x74). 53 | Change U4 field 'VarID' from '24' to '74' (IOEXP_TYPE/ADDR=9539/0x74). 54 | Change U4 field 'Datasheet' from 'http://www.ti.com/lit/ds/symlink/tca9535.pdf' to 'http://www.ti.com/lit/ds/symlink/tca9539.pdf' (IOEXP_TYPE/ADDR=9539/0x74). 55 | Change U4 field 'MPN' from 'TCA9535PWR' to 'TCA9539PWR' (IOEXP_TYPE/ADDR=9539/0x74). 56 | Board saved to file "kivar-demo.kicad_pcb". 57 | 58 | $ kivar list --selection kivar-demo.kicad_pcb 59 | ~: 'Series 1000' 'Series 3000 Basic' 'Series 3000 Pro' 'Series 5000 Basic' ['Series 5000 Pro'] 60 | BOOT_SRC~: EMMC JP NAND [SD] 61 | IOEXP_TYPE/ADDR~: 9535/0x20 9535/0x24 [9539/0x74] 62 | EEPROM_ADDR~: 0x54 [0x55] 63 | I_LED_MA~: 10 20 30 40 50 60 70 80 90 100 [110] 120 130 140 150 JP 64 | ISL91127~: [IRAZ] IRNZ 65 | UVLO_LO/HI: 2.41V/3.40V [3.15V/3.57V] 66 | VOUT: 1.2V [1.8V] 2.5V 3.3V 67 | 68 | $ kivar check kivar-demo.kicad_pcb 69 | Check passed. Matching variant and choices found for complete set of 7 aspect(s). 70 | 71 | $ kivar state --query EEPROM_ADDR --query BOOT_SRC kivar-demo.kicad_pcb 72 | 0x55 73 | SD 74 | ``` 75 | 76 | The KiVar CLI application provides support for 77 | 78 | * setting, 79 | * querying, 80 | * listing and 81 | * analyzing 82 | 83 | low-level and high-level variant data and current settings of a PCB. It can also be used in Continuous Integration services. 84 | 85 | ## Concepts 86 | 87 | Key concepts of KiVar are: 88 | 89 | * Designs may contain **multiple** independent variation **aspects** (i.e. scopes/dimensions/degrees of freedom). 90 | * Variation rules are **fully contained** in component fields of native design files (no external configuration files involved) and **portable** (i.e. copying components to another design keeps their variation specification intact). 91 | * Component values, fields, attributes and features are modified **in place** with immediate effect, enabling compatibility with all exporters that work on the actual component data. 92 | * **No external state information** is stored; currently matching variation choices are detected automatically. 93 | * Optional external **variant** definition table in independent file format (CSV) enables switching of aspect groups based on a single variant name. 94 | 95 | ## Supported KiCad Versions 96 | 97 | KiVar requires the _pcbnew_ Python module of at least **KiCad release 8**. 98 | 99 | ## Project Page 100 | 101 | For the usage manual and a demo project check out the [KiVar Project Page](https://github.com/markh-de/KiVar). 102 | -------------------------------------------------------------------------------- /demo/kivar-demo.kicad_sch: -------------------------------------------------------------------------------- 1 | (kicad_sch 2 | (version 20231120) 3 | (generator "eeschema") 4 | (generator_version "8.0") 5 | (uuid "cd745a5c-45fb-4106-85c8-c86c130b6ee7") 6 | (paper "A4") 7 | (title_block 8 | (title "KiVar Demo") 9 | (date "2025-04-30") 10 | (rev "0.5.0") 11 | (company "Author: Mark Hämmerling ") 12 | (comment 1 "https://kivar.markh.de") 13 | (comment 4 "Root Sheet") 14 | ) 15 | (lib_symbols) 16 | (sheet 17 | (at 46.99 125.73) 18 | (size 92.075 12.7) 19 | (fields_autoplaced yes) 20 | (stroke 21 | (width 0.1524) 22 | (type solid) 23 | (color 125 147 158 1) 24 | ) 25 | (fill 26 | (color 181 212 228 1.0000) 27 | ) 28 | (uuid "0e980ce9-433f-4ae1-b84f-fd5b1f452ede") 29 | (property "Sheetname" "Example 5: IC Type and Address Selection" 30 | (at 46.99 124.7644 0) 31 | (effects 32 | (font 33 | (size 1.778 1.778) 34 | (bold yes) 35 | (color 146 108 108 1) 36 | ) 37 | (justify left bottom) 38 | ) 39 | ) 40 | (property "Sheetfile" "example_5.kicad_sch" 41 | (at 46.99 139.0146 0) 42 | (effects 43 | (font 44 | (size 1.27 1.27) 45 | (italic yes) 46 | (color 194 194 194 1) 47 | ) 48 | (justify left top) 49 | ) 50 | ) 51 | (instances 52 | (project "kivar-demo" 53 | (path "/cd745a5c-45fb-4106-85c8-c86c130b6ee7" 54 | (page "6") 55 | ) 56 | ) 57 | ) 58 | ) 59 | (sheet 60 | (at 161.29 24.13) 61 | (size 91.44 12.7) 62 | (fields_autoplaced yes) 63 | (stroke 64 | (width 0.1524) 65 | (type solid) 66 | (color 151 166 142 1) 67 | ) 68 | (fill 69 | (color 208 229 196 1.0000) 70 | ) 71 | (uuid "133d4058-5b5c-4077-bb0d-59323121a93a") 72 | (property "Sheetname" "Rule Processing Illustration" 73 | (at 161.29 23.1644 0) 74 | (effects 75 | (font 76 | (size 1.778 1.778) 77 | (bold yes) 78 | (color 146 108 108 1) 79 | ) 80 | (justify left bottom) 81 | ) 82 | ) 83 | (property "Sheetfile" "rule_processing.kicad_sch" 84 | (at 161.29 37.4146 0) 85 | (effects 86 | (font 87 | (size 1.27 1.27) 88 | (italic yes) 89 | (color 194 194 194 1) 90 | ) 91 | (justify left top) 92 | ) 93 | ) 94 | (instances 95 | (project "kivar-demo" 96 | (path "/cd745a5c-45fb-4106-85c8-c86c130b6ee7" 97 | (page "9") 98 | ) 99 | ) 100 | ) 101 | ) 102 | (sheet 103 | (at 46.99 176.53) 104 | (size 92.075 12.7) 105 | (fields_autoplaced yes) 106 | (stroke 107 | (width 0.1524) 108 | (type solid) 109 | (color 125 147 158 1) 110 | ) 111 | (fill 112 | (color 181 212 228 1.0000) 113 | ) 114 | (uuid "6cc89fe7-613b-41f0-9c47-85b72f1b3014") 115 | (property "Sheetname" "Example 7: LDO Output Voltage Selection" 116 | (at 46.99 175.5644 0) 117 | (effects 118 | (font 119 | (size 1.778 1.778) 120 | (bold yes) 121 | (color 146 108 108 1) 122 | ) 123 | (justify left bottom) 124 | ) 125 | ) 126 | (property "Sheetfile" "example_7.kicad_sch" 127 | (at 46.99 189.8146 0) 128 | (effects 129 | (font 130 | (size 1.27 1.27) 131 | (italic yes) 132 | (color 194 194 194 1) 133 | ) 134 | (justify left top) 135 | ) 136 | ) 137 | (instances 138 | (project "kivar-demo" 139 | (path "/cd745a5c-45fb-4106-85c8-c86c130b6ee7" 140 | (page "8") 141 | ) 142 | ) 143 | ) 144 | ) 145 | (sheet 146 | (at 46.99 24.13) 147 | (size 92.075 12.7) 148 | (fields_autoplaced yes) 149 | (stroke 150 | (width 0.1524) 151 | (type solid) 152 | (color 125 147 158 1) 153 | ) 154 | (fill 155 | (color 181 212 228 1.0000) 156 | ) 157 | (uuid "819719b2-602e-433d-a771-1f2596d21337") 158 | (property "Sheetname" "Example 1: I²C Device Address Selection" 159 | (at 46.99 23.1644 0) 160 | (effects 161 | (font 162 | (size 1.778 1.778) 163 | (bold yes) 164 | (color 146 108 108 1) 165 | ) 166 | (justify left bottom) 167 | ) 168 | ) 169 | (property "Sheetfile" "example_1.kicad_sch" 170 | (at 46.99 37.4146 0) 171 | (effects 172 | (font 173 | (size 1.27 1.27) 174 | (italic yes) 175 | (color 194 194 194 1) 176 | ) 177 | (justify left top) 178 | ) 179 | ) 180 | (instances 181 | (project "kivar-demo" 182 | (path "/cd745a5c-45fb-4106-85c8-c86c130b6ee7" 183 | (page "2") 184 | ) 185 | ) 186 | ) 187 | ) 188 | (sheet 189 | (at 46.99 74.93) 190 | (size 92.075 12.7) 191 | (fields_autoplaced yes) 192 | (stroke 193 | (width 0.1524) 194 | (type solid) 195 | (color 125 147 158 1) 196 | ) 197 | (fill 198 | (color 181 212 228 1.0000) 199 | ) 200 | (uuid "98917921-089a-4f53-9391-423838589d96") 201 | (property "Sheetname" "Example 3: Undervoltage Trip Points" 202 | (at 46.99 73.9644 0) 203 | (effects 204 | (font 205 | (size 1.778 1.778) 206 | (bold yes) 207 | (color 146 108 108 1) 208 | ) 209 | (justify left bottom) 210 | ) 211 | ) 212 | (property "Sheetfile" "example_3.kicad_sch" 213 | (at 46.99 88.2146 0) 214 | (effects 215 | (font 216 | (size 1.27 1.27) 217 | (italic yes) 218 | (color 194 194 194 1) 219 | ) 220 | (justify left top) 221 | ) 222 | ) 223 | (instances 224 | (project "kivar-demo" 225 | (path "/cd745a5c-45fb-4106-85c8-c86c130b6ee7" 226 | (page "4") 227 | ) 228 | ) 229 | ) 230 | ) 231 | (sheet 232 | (at 46.99 100.33) 233 | (size 92.075 12.7) 234 | (fields_autoplaced yes) 235 | (stroke 236 | (width 0.1524) 237 | (type solid) 238 | (color 125 147 158 1) 239 | ) 240 | (fill 241 | (color 181 212 228 1.0000) 242 | ) 243 | (uuid "b90e2ece-31a9-44a1-a7e2-4f98d0159cd7") 244 | (property "Sheetname" "Example 4: IC Variant Selection" 245 | (at 46.99 99.3644 0) 246 | (effects 247 | (font 248 | (size 1.778 1.778) 249 | (bold yes) 250 | (color 146 108 108 1) 251 | ) 252 | (justify left bottom) 253 | ) 254 | ) 255 | (property "Sheetfile" "example_4.kicad_sch" 256 | (at 46.99 113.6146 0) 257 | (effects 258 | (font 259 | (size 1.27 1.27) 260 | (italic yes) 261 | (color 194 194 194 1) 262 | ) 263 | (justify left top) 264 | ) 265 | ) 266 | (instances 267 | (project "kivar-demo" 268 | (path "/cd745a5c-45fb-4106-85c8-c86c130b6ee7" 269 | (page "5") 270 | ) 271 | ) 272 | ) 273 | ) 274 | (sheet 275 | (at 46.99 151.13) 276 | (size 92.075 12.7) 277 | (fields_autoplaced yes) 278 | (stroke 279 | (width 0.1524) 280 | (type solid) 281 | (color 125 147 158 1) 282 | ) 283 | (fill 284 | (color 181 212 228 1.0000) 285 | ) 286 | (uuid "efd04459-986c-49c2-bdc3-96a700b4a724") 287 | (property "Sheetname" "Example 6: Maximum LED Backlight Drive Current Selection" 288 | (at 46.99 150.1644 0) 289 | (effects 290 | (font 291 | (size 1.778 1.778) 292 | (bold yes) 293 | (color 146 108 108 1) 294 | ) 295 | (justify left bottom) 296 | ) 297 | ) 298 | (property "Sheetfile" "example_6.kicad_sch" 299 | (at 46.99 164.4146 0) 300 | (effects 301 | (font 302 | (size 1.27 1.27) 303 | (italic yes) 304 | (color 194 194 194 1) 305 | ) 306 | (justify left top) 307 | ) 308 | ) 309 | (instances 310 | (project "kivar-demo" 311 | (path "/cd745a5c-45fb-4106-85c8-c86c130b6ee7" 312 | (page "7") 313 | ) 314 | ) 315 | ) 316 | ) 317 | (sheet 318 | (at 46.99 49.53) 319 | (size 92.075 12.7) 320 | (fields_autoplaced yes) 321 | (stroke 322 | (width 0.1524) 323 | (type solid) 324 | (color 125 147 158 1) 325 | ) 326 | (fill 327 | (color 181 212 228 1.0000) 328 | ) 329 | (uuid "f1bdcd00-da00-4cca-b21f-4862aa5c3200") 330 | (property "Sheetname" "Example 2: Boot Source Selection" 331 | (at 46.99 48.5644 0) 332 | (effects 333 | (font 334 | (size 1.778 1.778) 335 | (bold yes) 336 | (color 146 108 108 1) 337 | ) 338 | (justify left bottom) 339 | ) 340 | ) 341 | (property "Sheetfile" "example_2.kicad_sch" 342 | (at 46.99 62.8146 0) 343 | (effects 344 | (font 345 | (size 1.27 1.27) 346 | (italic yes) 347 | (color 194 194 194 1) 348 | ) 349 | (justify left top) 350 | ) 351 | ) 352 | (instances 353 | (project "kivar-demo" 354 | (path "/cd745a5c-45fb-4106-85c8-c86c130b6ee7" 355 | (page "3") 356 | ) 357 | ) 358 | ) 359 | ) 360 | (sheet_instances 361 | (path "/" 362 | (page "1") 363 | ) 364 | ) 365 | ) 366 | -------------------------------------------------------------------------------- /demo/rule_processing.kicad_sch: -------------------------------------------------------------------------------- 1 | (kicad_sch 2 | (version 20231120) 3 | (generator "eeschema") 4 | (generator_version "8.0") 5 | (uuid "55cc74f3-a76a-489e-86d1-364a3afd14d4") 6 | (paper "A5") 7 | (title_block 8 | (title "KiVar Demo") 9 | (date "2025-04-30") 10 | (rev "0.5.0") 11 | (company "Author: Mark Hämmerling ") 12 | (comment 1 "https://kivar.markh.de") 13 | (comment 4 "KiVar Rule Processing") 14 | ) 15 | (lib_symbols) 16 | (polyline 17 | (pts 18 | (xy 101.6 53.34) (xy 102.235 54.61) 19 | ) 20 | (stroke 21 | (width 0) 22 | (type default) 23 | (color 188 126 122 1) 24 | ) 25 | (uuid "10b7d359-0861-4c9e-953e-fe4804005652") 26 | ) 27 | (polyline 28 | (pts 29 | (xy 81.28 64.135) (xy 81.28 67.945) 30 | ) 31 | (stroke 32 | (width 0) 33 | (type default) 34 | (color 188 126 122 1) 35 | ) 36 | (uuid "1cd31313-c47e-4b96-901e-7f7829a37352") 37 | ) 38 | (polyline 39 | (pts 40 | (xy 123.825 66.675) (xy 123.19 67.945) 41 | ) 42 | (stroke 43 | (width 0) 44 | (type default) 45 | (color 188 126 122 1) 46 | ) 47 | (uuid "1fd90198-0a28-46a3-921c-cf4588d3f82d") 48 | ) 49 | (polyline 50 | (pts 51 | (xy 123.19 64.135) (xy 123.19 67.945) 52 | ) 53 | (stroke 54 | (width 0) 55 | (type default) 56 | (color 188 126 122 1) 57 | ) 58 | (uuid "2a6ccc75-e8dd-4588-949a-3e24735fbc5f") 59 | ) 60 | (polyline 61 | (pts 62 | (xy 123.825 53.34) (xy 123.19 54.61) 63 | ) 64 | (stroke 65 | (width 0) 66 | (type default) 67 | (color 188 126 122 1) 68 | ) 69 | (uuid "5c7feb87-83db-47ca-aff0-1fce0d712917") 70 | ) 71 | (polyline 72 | (pts 73 | (xy 102.87 53.34) (xy 102.235 54.61) 74 | ) 75 | (stroke 76 | (width 0) 77 | (type default) 78 | (color 188 126 122 1) 79 | ) 80 | (uuid "606f9e25-2662-4e24-a5df-c02754a9225a") 81 | ) 82 | (polyline 83 | (pts 84 | (xy 80.645 66.675) (xy 81.28 67.945) 85 | ) 86 | (stroke 87 | (width 0) 88 | (type default) 89 | (color 188 126 122 1) 90 | ) 91 | (uuid "64e73ba7-9e23-4b54-a327-8f6d10ff7990") 92 | ) 93 | (polyline 94 | (pts 95 | (xy 122.555 53.34) (xy 123.19 54.61) 96 | ) 97 | (stroke 98 | (width 0) 99 | (type default) 100 | (color 188 126 122 1) 101 | ) 102 | (uuid "7b0d524b-36a0-4e16-8b29-f4f0b7fcb0ce") 103 | ) 104 | (polyline 105 | (pts 106 | (xy 102.87 66.675) (xy 102.235 67.945) 107 | ) 108 | (stroke 109 | (width 0) 110 | (type default) 111 | (color 188 126 122 1) 112 | ) 113 | (uuid "a5870322-4fab-4b61-a7be-e5e5a5613b1e") 114 | ) 115 | (polyline 116 | (pts 117 | (xy 123.19 50.8) (xy 123.19 54.61) 118 | ) 119 | (stroke 120 | (width 0) 121 | (type default) 122 | (color 188 126 122 1) 123 | ) 124 | (uuid "b073ff6d-af75-497c-ae2d-1534c97b2685") 125 | ) 126 | (polyline 127 | (pts 128 | (xy 102.235 64.135) (xy 102.235 67.945) 129 | ) 130 | (stroke 131 | (width 0) 132 | (type default) 133 | (color 188 126 122 1) 134 | ) 135 | (uuid "bdbca326-dd07-4ed3-a930-9f8eef90c22e") 136 | ) 137 | (polyline 138 | (pts 139 | (xy 102.235 50.8) (xy 102.235 54.61) 140 | ) 141 | (stroke 142 | (width 0) 143 | (type default) 144 | (color 188 126 122 1) 145 | ) 146 | (uuid "c22e554f-b69b-4ebf-94fd-48be23cdef5c") 147 | ) 148 | (polyline 149 | (pts 150 | (xy 81.915 66.675) (xy 81.28 67.945) 151 | ) 152 | (stroke 153 | (width 0) 154 | (type default) 155 | (color 188 126 122 1) 156 | ) 157 | (uuid "c357f0e9-5f93-4371-8fd7-be5418d5f123") 158 | ) 159 | (polyline 160 | (pts 161 | (xy 81.28 50.8) (xy 81.28 54.61) 162 | ) 163 | (stroke 164 | (width 0) 165 | (type default) 166 | (color 188 126 122 1) 167 | ) 168 | (uuid "c9648ff4-a141-4a50-a7a3-05d3eab8826c") 169 | ) 170 | (polyline 171 | (pts 172 | (xy 81.915 53.34) (xy 81.28 54.61) 173 | ) 174 | (stroke 175 | (width 0) 176 | (type default) 177 | (color 188 126 122 1) 178 | ) 179 | (uuid "cec418f9-06bd-4437-81b6-94607425d0fa") 180 | ) 181 | (polyline 182 | (pts 183 | (xy 80.645 53.34) (xy 81.28 54.61) 184 | ) 185 | (stroke 186 | (width 0) 187 | (type default) 188 | (color 188 126 122 1) 189 | ) 190 | (uuid "cfcea8ea-a86f-432d-967c-97d3231e1710") 191 | ) 192 | (polyline 193 | (pts 194 | (xy 101.6 66.675) (xy 102.235 67.945) 195 | ) 196 | (stroke 197 | (width 0) 198 | (type default) 199 | (color 188 126 122 1) 200 | ) 201 | (uuid "d0597db5-ec96-4168-b579-e7ec3ada5c5c") 202 | ) 203 | (polyline 204 | (pts 205 | (xy 122.555 66.675) (xy 123.19 67.945) 206 | ) 207 | (stroke 208 | (width 0) 209 | (type default) 210 | (color 188 126 122 1) 211 | ) 212 | (uuid "d3ee2723-c8ad-437c-b250-1cbc03be3e70") 213 | ) 214 | (rectangle 215 | (start 48.26 33.02) 216 | (end 135.89 83.82) 217 | (stroke 218 | (width 0.254) 219 | (type default) 220 | (color 72 72 72 1) 221 | ) 222 | (fill 223 | (type none) 224 | ) 225 | (uuid f8401ade-3d65-4dd1-b16c-f66112011d93) 226 | ) 227 | (text_box "C1" 228 | (exclude_from_sim no) 229 | (at 92.71 67.945 0) 230 | (size 19.05 12.7) 231 | (stroke 232 | (width -0.0001) 233 | (type default) 234 | (color 0 194 194 1) 235 | ) 236 | (fill 237 | (type color) 238 | (color 227 190 165 1) 239 | ) 240 | (effects 241 | (font 242 | (size 1.27 1.27) 243 | (thickness 0.254) 244 | (bold yes) 245 | (italic yes) 246 | (color 177 148 129 1) 247 | ) 248 | (justify top) 249 | ) 250 | (uuid "345334cb-8809-4a48-85da-532752a9b19b") 251 | ) 252 | (text_box "R1" 253 | (exclude_from_sim no) 254 | (at 71.755 67.945 0) 255 | (size 19.05 12.7) 256 | (stroke 257 | (width -0.0001) 258 | (type default) 259 | (color 0 194 194 1) 260 | ) 261 | (fill 262 | (type color) 263 | (color 227 190 165 1) 264 | ) 265 | (effects 266 | (font 267 | (size 1.27 1.27) 268 | (thickness 0.254) 269 | (bold yes) 270 | (italic yes) 271 | (color 177 148 129 1) 272 | ) 273 | (justify top) 274 | ) 275 | (uuid "35ff6598-4048-4b9e-aa41-7a0666c4bb61") 276 | ) 277 | (text_box "C1" 278 | (exclude_from_sim no) 279 | (at 92.71 40.005 0) 280 | (size 19.05 10.795) 281 | (stroke 282 | (width -0.0001) 283 | (type default) 284 | (color 0 194 194 1) 285 | ) 286 | (fill 287 | (type color) 288 | (color 201 231 219 1) 289 | ) 290 | (effects 291 | (font 292 | (size 1.27 1.27) 293 | (thickness 0.254) 294 | (bold yes) 295 | (italic yes) 296 | (color 153 176 167 1) 297 | ) 298 | (justify top) 299 | ) 300 | (uuid "3aa90bca-20c0-4541-8c2d-3178392d24bd") 301 | ) 302 | (text_box "R1" 303 | (exclude_from_sim no) 304 | (at 71.755 40.005 0) 305 | (size 19.05 10.795) 306 | (stroke 307 | (width -0.0001) 308 | (type default) 309 | (color 0 194 194 1) 310 | ) 311 | (fill 312 | (type color) 313 | (color 201 231 219 1) 314 | ) 315 | (effects 316 | (font 317 | (size 1.27 1.27) 318 | (thickness 0.254) 319 | (bold yes) 320 | (italic yes) 321 | (color 153 176 167 1) 322 | ) 323 | (justify top) 324 | ) 325 | (uuid "54760d29-0db6-463a-a2f5-f7c82d04c02d") 326 | ) 327 | (text_box "KiVar Variation Rule Processor" 328 | (exclude_from_sim no) 329 | (at 71.755 54.61 0) 330 | (size 60.96 9.525) 331 | (stroke 332 | (width -0.0001) 333 | (type default) 334 | ) 335 | (fill 336 | (type color) 337 | (color 188 198 249 1) 338 | ) 339 | (effects 340 | (font 341 | (size 1.27 1.27) 342 | (thickness 0.254) 343 | (bold yes) 344 | (italic yes) 345 | (color 134 141 177 1) 346 | ) 347 | (justify top) 348 | ) 349 | (uuid "c03eb24b-a96a-4516-a7fc-0527fa5a7732") 350 | ) 351 | (text_box "L1" 352 | (exclude_from_sim no) 353 | (at 113.665 67.945 0) 354 | (size 19.05 12.7) 355 | (stroke 356 | (width -0.0001) 357 | (type default) 358 | (color 0 194 194 1) 359 | ) 360 | (fill 361 | (type color) 362 | (color 227 190 165 1) 363 | ) 364 | (effects 365 | (font 366 | (size 1.27 1.27) 367 | (thickness 0.254) 368 | (bold yes) 369 | (italic yes) 370 | (color 177 148 129 1) 371 | ) 372 | (justify top) 373 | ) 374 | (uuid "e7bfe24a-0f09-448b-bdbd-f4333151be35") 375 | ) 376 | (text_box "L1" 377 | (exclude_from_sim no) 378 | (at 113.665 40.005 0) 379 | (size 19.05 10.795) 380 | (stroke 381 | (width -0.0001) 382 | (type default) 383 | (color 0 194 194 1) 384 | ) 385 | (fill 386 | (type color) 387 | (color 201 231 219 1) 388 | ) 389 | (effects 390 | (font 391 | (size 1.27 1.27) 392 | (thickness 0.254) 393 | (bold yes) 394 | (italic yes) 395 | (color 153 176 167 1) 396 | ) 397 | (justify top) 398 | ) 399 | (uuid "e9ffcd0d-541a-475f-8ba0-9c6ae4208948") 400 | ) 401 | (text "One: 10mH\nTwo: 10mH\nThree: 1mH unfit\nFour: 10mH unfit\n" 402 | (exclude_from_sim no) 403 | (at 114.3 72.39 0) 404 | (effects 405 | (font 406 | (size 1.27 1.27) 407 | (color 113 86 77 1) 408 | ) 409 | (justify left top) 410 | ) 411 | (uuid "1ba64b71-db13-4aff-af22-c68e411d34a5") 412 | ) 413 | (text "Two(33pF)\nFour(100pF -f)\n?(470pF)" 414 | (exclude_from_sim no) 415 | (at 93.345 44.45 0) 416 | (effects 417 | (font 418 | (size 1.27 1.27) 419 | (color 72 101 132 1) 420 | ) 421 | (justify left top) 422 | ) 423 | (uuid "2b0eb2a0-6cf5-45ec-b422-8965bc4f759f") 424 | ) 425 | (text "One: 470pF\nTwo: 33pF\nThree: 470pF\nFour: 100pF unfit" 426 | (exclude_from_sim no) 427 | (at 93.345 72.39 0) 428 | (effects 429 | (font 430 | (size 1.27 1.27) 431 | (color 113 86 77 1) 432 | ) 433 | (justify left top) 434 | ) 435 | (uuid "4e726c43-feea-4e83-b64b-5f1f63afd715") 436 | ) 437 | (text "Three(1mH -f)\nFour(-f)\n*(10mH)" 438 | (exclude_from_sim no) 439 | (at 114.3 44.45 0) 440 | (effects 441 | (font 442 | (size 1.27 1.27) 443 | (color 72 101 132 1) 444 | ) 445 | (justify left top) 446 | ) 447 | (uuid "6a895d36-352c-49b6-b695-6522fee58438") 448 | ) 449 | (text "Figure" 450 | (exclude_from_sim no) 451 | (at 48.26 85.09 0) 452 | (effects 453 | (font 454 | (size 1.27 1.27) 455 | (italic yes) 456 | (color 72 72 72 1) 457 | ) 458 | (justify left top) 459 | ) 460 | (uuid "8b4bd913-bcf6-412b-b1f2-82d6e683287d") 461 | ) 462 | (text "One, Two, Three, Four" 463 | (exclude_from_sim no) 464 | (at 102.235 62.865 0) 465 | (effects 466 | (font 467 | (size 1.27 1.27) 468 | (color 72 101 132 1) 469 | ) 470 | ) 471 | (uuid "960f15b2-96fb-44f9-baaa-4d34f7675cd9") 472 | ) 473 | (text "One: 0Ω unfit\nTwo: 10kΩ\nThree: 10kΩ unfit\nFour: 10kΩ unfit" 474 | (exclude_from_sim no) 475 | (at 72.39 72.39 0) 476 | (effects 477 | (font 478 | (size 1.27 1.27) 479 | (color 113 86 77 1) 480 | ) 481 | (justify left top) 482 | ) 483 | (uuid "a9c957b8-9f45-47b1-93c2-17c607c6b3ba") 484 | ) 485 | (text "All choices apply to the same example aspect." 486 | (exclude_from_sim no) 487 | (at 71.755 36.195 0) 488 | (effects 489 | (font 490 | (size 1.27 1.27) 491 | (italic yes) 492 | (color 67 93 122 1) 493 | ) 494 | (justify left top) 495 | ) 496 | (uuid "ae1d8ec5-2f8d-4198-ba33-cf738998e168") 497 | ) 498 | (text "Resulting Content\nand Property\nassignments:" 499 | (exclude_from_sim no) 500 | (at 69.215 68.58 0) 501 | (effects 502 | (font 503 | (size 1.27 1.27) 504 | (italic yes) 505 | (color 134 141 177 1) 506 | ) 507 | (justify right top) 508 | ) 509 | (uuid "cab26815-2b7f-43ec-a032-513ef3ed2966") 510 | ) 511 | (text "Choice Expressions:" 512 | (exclude_from_sim no) 513 | (at 69.215 40.64 0) 514 | (effects 515 | (font 516 | (size 1.27 1.27) 517 | (italic yes) 518 | (color 134 141 177 1) 519 | ) 520 | (justify right top) 521 | ) 522 | (uuid "cf54357c-5c54-408d-8bec-2a66c13474a6") 523 | ) 524 | (text "One(0Ω)\nTwo(+f)\n*(10kΩ -f)" 525 | (exclude_from_sim no) 526 | (at 72.39 44.45 0) 527 | (effects 528 | (font 529 | (size 1.27 1.27) 530 | (color 72 101 132 1) 531 | ) 532 | (justify left top) 533 | ) 534 | (uuid "ddae87ba-535d-499e-b382-4d5a34b9279e") 535 | ) 536 | (text "Declared choices:" 537 | (exclude_from_sim no) 538 | (at 102.235 60.325 0) 539 | (effects 540 | (font 541 | (size 1.27 1.27) 542 | (italic yes) 543 | (color 134 141 177 1) 544 | ) 545 | ) 546 | (uuid "ef6cf0e5-03f1-4e08-99dd-48f6a1ecc4c1") 547 | ) 548 | ) 549 | -------------------------------------------------------------------------------- /demo/kivar-demo.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "3dviewports": [], 4 | "design_settings": { 5 | "defaults": { 6 | "apply_defaults_to_fp_fields": false, 7 | "apply_defaults_to_fp_shapes": false, 8 | "apply_defaults_to_fp_text": false, 9 | "board_outline_line_width": 0.1, 10 | "copper_line_width": 0.2, 11 | "copper_text_italic": false, 12 | "copper_text_size_h": 1.5, 13 | "copper_text_size_v": 1.5, 14 | "copper_text_thickness": 0.3, 15 | "copper_text_upright": false, 16 | "courtyard_line_width": 0.05, 17 | "dimension_precision": 4, 18 | "dimension_units": 3, 19 | "dimensions": { 20 | "arrow_length": 1270000, 21 | "extension_offset": 500000, 22 | "keep_text_aligned": true, 23 | "suppress_zeroes": false, 24 | "text_position": 0, 25 | "units_format": 1 26 | }, 27 | "fab_line_width": 0.1, 28 | "fab_text_italic": false, 29 | "fab_text_size_h": 1.0, 30 | "fab_text_size_v": 1.0, 31 | "fab_text_thickness": 0.15, 32 | "fab_text_upright": false, 33 | "other_line_width": 0.15, 34 | "other_text_italic": false, 35 | "other_text_size_h": 1.0, 36 | "other_text_size_v": 1.0, 37 | "other_text_thickness": 0.15, 38 | "other_text_upright": false, 39 | "pads": { 40 | "drill": 0.762, 41 | "height": 1.524, 42 | "width": 1.524 43 | }, 44 | "silk_line_width": 0.15, 45 | "silk_text_italic": false, 46 | "silk_text_size_h": 1.0, 47 | "silk_text_size_v": 1.0, 48 | "silk_text_thickness": 0.15, 49 | "silk_text_upright": false, 50 | "zones": { 51 | "min_clearance": 0.5 52 | } 53 | }, 54 | "diff_pair_dimensions": [], 55 | "drc_exclusions": [], 56 | "meta": { 57 | "version": 2 58 | }, 59 | "rule_severities": { 60 | "annular_width": "error", 61 | "clearance": "error", 62 | "connection_width": "warning", 63 | "copper_edge_clearance": "error", 64 | "copper_sliver": "warning", 65 | "courtyards_overlap": "error", 66 | "diff_pair_gap_out_of_range": "error", 67 | "diff_pair_uncoupled_length_too_long": "error", 68 | "drill_out_of_range": "error", 69 | "duplicate_footprints": "warning", 70 | "extra_footprint": "warning", 71 | "footprint": "error", 72 | "footprint_symbol_mismatch": "warning", 73 | "footprint_type_mismatch": "ignore", 74 | "hole_clearance": "error", 75 | "hole_near_hole": "error", 76 | "invalid_outline": "error", 77 | "isolated_copper": "warning", 78 | "item_on_disabled_layer": "error", 79 | "items_not_allowed": "error", 80 | "length_out_of_range": "error", 81 | "lib_footprint_issues": "warning", 82 | "lib_footprint_mismatch": "warning", 83 | "malformed_courtyard": "error", 84 | "microvia_drill_out_of_range": "error", 85 | "missing_courtyard": "ignore", 86 | "missing_footprint": "warning", 87 | "net_conflict": "warning", 88 | "npth_inside_courtyard": "ignore", 89 | "padstack": "warning", 90 | "pth_inside_courtyard": "ignore", 91 | "shorting_items": "error", 92 | "silk_edge_clearance": "warning", 93 | "silk_over_copper": "warning", 94 | "silk_overlap": "ignore", 95 | "skew_out_of_range": "error", 96 | "solder_mask_bridge": "ignore", 97 | "starved_thermal": "error", 98 | "text_height": "warning", 99 | "text_thickness": "warning", 100 | "through_hole_pad_without_hole": "error", 101 | "too_many_vias": "error", 102 | "track_dangling": "warning", 103 | "track_width": "error", 104 | "tracks_crossing": "error", 105 | "unconnected_items": "error", 106 | "unresolved_variable": "error", 107 | "via_dangling": "warning", 108 | "zones_intersect": "error" 109 | }, 110 | "rules": { 111 | "max_error": 0.005, 112 | "min_clearance": 0.0, 113 | "min_connection": 0.0, 114 | "min_copper_edge_clearance": 0.0, 115 | "min_hole_clearance": 0.25, 116 | "min_hole_to_hole": 0.25, 117 | "min_microvia_diameter": 0.2, 118 | "min_microvia_drill": 0.1, 119 | "min_resolved_spokes": 2, 120 | "min_silk_clearance": 0.0, 121 | "min_text_height": 0.8, 122 | "min_text_thickness": 0.08, 123 | "min_through_hole_diameter": 0.3, 124 | "min_track_width": 0.0, 125 | "min_via_annular_width": 0.1, 126 | "min_via_diameter": 0.5, 127 | "solder_mask_clearance": 0.0, 128 | "solder_mask_min_width": 0.0, 129 | "solder_mask_to_copper_clearance": 0.0, 130 | "use_height_for_length_calcs": true 131 | }, 132 | "teardrop_options": [ 133 | { 134 | "td_onpadsmd": true, 135 | "td_onroundshapesonly": false, 136 | "td_ontrackend": false, 137 | "td_onviapad": true 138 | } 139 | ], 140 | "teardrop_parameters": [ 141 | { 142 | "td_allow_use_two_tracks": true, 143 | "td_curve_segcount": 0, 144 | "td_height_ratio": 1.0, 145 | "td_length_ratio": 0.5, 146 | "td_maxheight": 2.0, 147 | "td_maxlen": 1.0, 148 | "td_on_pad_in_zone": false, 149 | "td_target_name": "td_round_shape", 150 | "td_width_to_size_filter_ratio": 0.9 151 | }, 152 | { 153 | "td_allow_use_two_tracks": true, 154 | "td_curve_segcount": 0, 155 | "td_height_ratio": 1.0, 156 | "td_length_ratio": 0.5, 157 | "td_maxheight": 2.0, 158 | "td_maxlen": 1.0, 159 | "td_on_pad_in_zone": false, 160 | "td_target_name": "td_rect_shape", 161 | "td_width_to_size_filter_ratio": 0.9 162 | }, 163 | { 164 | "td_allow_use_two_tracks": true, 165 | "td_curve_segcount": 0, 166 | "td_height_ratio": 1.0, 167 | "td_length_ratio": 0.5, 168 | "td_maxheight": 2.0, 169 | "td_maxlen": 1.0, 170 | "td_on_pad_in_zone": false, 171 | "td_target_name": "td_track_end", 172 | "td_width_to_size_filter_ratio": 0.9 173 | } 174 | ], 175 | "track_widths": [], 176 | "tuning_pattern_settings": { 177 | "diff_pair_defaults": { 178 | "corner_radius_percentage": 100, 179 | "corner_style": 1, 180 | "max_amplitude": 1.0, 181 | "min_amplitude": 0.1, 182 | "single_sided": false, 183 | "spacing": 0.6 184 | }, 185 | "diff_pair_skew_defaults": { 186 | "corner_radius_percentage": 100, 187 | "corner_style": 1, 188 | "max_amplitude": 1.0, 189 | "min_amplitude": 0.1, 190 | "single_sided": false, 191 | "spacing": 0.6 192 | }, 193 | "single_track_defaults": { 194 | "corner_radius_percentage": 100, 195 | "corner_style": 1, 196 | "max_amplitude": 1.0, 197 | "min_amplitude": 0.1, 198 | "single_sided": false, 199 | "spacing": 0.6 200 | } 201 | }, 202 | "via_dimensions": [], 203 | "zones_allow_external_fillets": false 204 | }, 205 | "ipc2581": { 206 | "dist": "", 207 | "distpn": "", 208 | "internal_id": "", 209 | "mfg": "", 210 | "mpn": "" 211 | }, 212 | "layer_presets": [], 213 | "viewports": [] 214 | }, 215 | "boards": [], 216 | "cvpcb": { 217 | "equivalence_files": [] 218 | }, 219 | "erc": { 220 | "erc_exclusions": [], 221 | "meta": { 222 | "version": 0 223 | }, 224 | "pin_map": [ 225 | [ 226 | 0, 227 | 0, 228 | 0, 229 | 0, 230 | 0, 231 | 0, 232 | 1, 233 | 0, 234 | 0, 235 | 0, 236 | 0, 237 | 2 238 | ], 239 | [ 240 | 0, 241 | 2, 242 | 0, 243 | 1, 244 | 0, 245 | 0, 246 | 1, 247 | 0, 248 | 2, 249 | 2, 250 | 2, 251 | 2 252 | ], 253 | [ 254 | 0, 255 | 0, 256 | 0, 257 | 0, 258 | 0, 259 | 0, 260 | 1, 261 | 0, 262 | 1, 263 | 0, 264 | 1, 265 | 2 266 | ], 267 | [ 268 | 0, 269 | 1, 270 | 0, 271 | 0, 272 | 0, 273 | 0, 274 | 1, 275 | 1, 276 | 2, 277 | 1, 278 | 1, 279 | 2 280 | ], 281 | [ 282 | 0, 283 | 0, 284 | 0, 285 | 0, 286 | 0, 287 | 0, 288 | 1, 289 | 0, 290 | 0, 291 | 0, 292 | 0, 293 | 2 294 | ], 295 | [ 296 | 0, 297 | 0, 298 | 0, 299 | 0, 300 | 0, 301 | 0, 302 | 0, 303 | 0, 304 | 0, 305 | 0, 306 | 0, 307 | 2 308 | ], 309 | [ 310 | 1, 311 | 1, 312 | 1, 313 | 1, 314 | 1, 315 | 0, 316 | 1, 317 | 1, 318 | 1, 319 | 1, 320 | 1, 321 | 2 322 | ], 323 | [ 324 | 0, 325 | 0, 326 | 0, 327 | 1, 328 | 0, 329 | 0, 330 | 1, 331 | 0, 332 | 0, 333 | 0, 334 | 0, 335 | 2 336 | ], 337 | [ 338 | 0, 339 | 2, 340 | 1, 341 | 2, 342 | 0, 343 | 0, 344 | 1, 345 | 0, 346 | 2, 347 | 2, 348 | 2, 349 | 2 350 | ], 351 | [ 352 | 0, 353 | 2, 354 | 0, 355 | 1, 356 | 0, 357 | 0, 358 | 1, 359 | 0, 360 | 2, 361 | 0, 362 | 0, 363 | 2 364 | ], 365 | [ 366 | 0, 367 | 2, 368 | 1, 369 | 1, 370 | 0, 371 | 0, 372 | 1, 373 | 0, 374 | 2, 375 | 0, 376 | 0, 377 | 2 378 | ], 379 | [ 380 | 2, 381 | 2, 382 | 2, 383 | 2, 384 | 2, 385 | 2, 386 | 2, 387 | 2, 388 | 2, 389 | 2, 390 | 2, 391 | 2 392 | ] 393 | ], 394 | "rule_severities": { 395 | "bus_definition_conflict": "error", 396 | "bus_entry_needed": "error", 397 | "bus_to_bus_conflict": "error", 398 | "bus_to_net_conflict": "error", 399 | "conflicting_netclasses": "error", 400 | "different_unit_footprint": "error", 401 | "different_unit_net": "error", 402 | "duplicate_reference": "error", 403 | "duplicate_sheet_names": "error", 404 | "endpoint_off_grid": "warning", 405 | "extra_units": "error", 406 | "global_label_dangling": "warning", 407 | "hier_label_mismatch": "error", 408 | "label_dangling": "error", 409 | "lib_symbol_issues": "warning", 410 | "missing_bidi_pin": "warning", 411 | "missing_input_pin": "warning", 412 | "missing_power_pin": "error", 413 | "missing_unit": "warning", 414 | "multiple_net_names": "warning", 415 | "net_not_bus_member": "warning", 416 | "no_connect_connected": "warning", 417 | "no_connect_dangling": "warning", 418 | "pin_not_connected": "error", 419 | "pin_not_driven": "error", 420 | "pin_to_pin": "error", 421 | "power_pin_not_driven": "error", 422 | "similar_labels": "warning", 423 | "simulation_model_issue": "ignore", 424 | "unannotated": "error", 425 | "unit_value_mismatch": "error", 426 | "unresolved_variable": "error", 427 | "wire_dangling": "error" 428 | } 429 | }, 430 | "libraries": { 431 | "pinned_footprint_libs": [], 432 | "pinned_symbol_libs": [] 433 | }, 434 | "meta": { 435 | "filename": "kivar-demo.kicad_pro", 436 | "version": 1 437 | }, 438 | "net_settings": { 439 | "classes": [ 440 | { 441 | "bus_width": 12, 442 | "clearance": 0.2, 443 | "diff_pair_gap": 0.25, 444 | "diff_pair_via_gap": 0.25, 445 | "diff_pair_width": 0.2, 446 | "line_style": 0, 447 | "microvia_diameter": 0.3, 448 | "microvia_drill": 0.1, 449 | "name": "Default", 450 | "pcb_color": "rgba(0, 0, 0, 0.000)", 451 | "schematic_color": "rgba(0, 0, 0, 0.000)", 452 | "track_width": 0.25, 453 | "via_diameter": 0.8, 454 | "via_drill": 0.4, 455 | "wire_width": 6 456 | } 457 | ], 458 | "meta": { 459 | "version": 3 460 | }, 461 | "net_colors": null, 462 | "netclass_assignments": null, 463 | "netclass_patterns": [] 464 | }, 465 | "pcbnew": { 466 | "last_paths": { 467 | "gencad": "", 468 | "idf": "", 469 | "netlist": "", 470 | "plot": "", 471 | "pos_files": "", 472 | "specctra_dsn": "", 473 | "step": "", 474 | "svg": "", 475 | "vrml": "" 476 | }, 477 | "page_layout_descr_file": "" 478 | }, 479 | "schematic": { 480 | "annotate_start_num": 0, 481 | "bom_fmt_presets": [], 482 | "bom_fmt_settings": { 483 | "field_delimiter": ",", 484 | "keep_line_breaks": false, 485 | "keep_tabs": false, 486 | "name": "CSV", 487 | "ref_delimiter": ",", 488 | "ref_range_delimiter": "", 489 | "string_delimiter": "\"" 490 | }, 491 | "bom_presets": [], 492 | "bom_settings": { 493 | "exclude_dnp": false, 494 | "fields_ordered": [ 495 | { 496 | "group_by": false, 497 | "label": "Reference", 498 | "name": "Reference", 499 | "show": true 500 | }, 501 | { 502 | "group_by": false, 503 | "label": "Value", 504 | "name": "Value", 505 | "show": true 506 | }, 507 | { 508 | "group_by": false, 509 | "label": "Datasheet", 510 | "name": "Datasheet", 511 | "show": false 512 | }, 513 | { 514 | "group_by": false, 515 | "label": "Footprint", 516 | "name": "Footprint", 517 | "show": false 518 | }, 519 | { 520 | "group_by": false, 521 | "label": "Description", 522 | "name": "Description", 523 | "show": false 524 | }, 525 | { 526 | "group_by": false, 527 | "label": "#", 528 | "name": "${ITEM_NUMBER}", 529 | "show": false 530 | }, 531 | { 532 | "group_by": false, 533 | "label": "Qty", 534 | "name": "${QUANTITY}", 535 | "show": false 536 | }, 537 | { 538 | "group_by": false, 539 | "label": "Var", 540 | "name": "Var", 541 | "show": true 542 | }, 543 | { 544 | "group_by": false, 545 | "label": "ChoiceText.Var", 546 | "name": "ChoiceText.Var", 547 | "show": false 548 | }, 549 | { 550 | "group_by": false, 551 | "label": "Datasheet.Var(*)", 552 | "name": "Datasheet.Var(*)", 553 | "show": false 554 | }, 555 | { 556 | "group_by": false, 557 | "label": "Datasheet.Var(9539/0x74)", 558 | "name": "Datasheet.Var(9539/0x74)", 559 | "show": false 560 | }, 561 | { 562 | "group_by": false, 563 | "label": "I2C Address", 564 | "name": "I2C Address", 565 | "show": false 566 | }, 567 | { 568 | "group_by": false, 569 | "label": "I2C Address.Var", 570 | "name": "I2C Address.Var", 571 | "show": false 572 | }, 573 | { 574 | "group_by": false, 575 | "label": "MPN", 576 | "name": "MPN", 577 | "show": false 578 | }, 579 | { 580 | "group_by": false, 581 | "label": "MPN.Var(*)", 582 | "name": "MPN.Var(*)", 583 | "show": false 584 | }, 585 | { 586 | "group_by": false, 587 | "label": "MPN.Var(9539/0x74)", 588 | "name": "MPN.Var(9539/0x74)", 589 | "show": false 590 | }, 591 | { 592 | "group_by": false, 593 | "label": "Manufacturer", 594 | "name": "Manufacturer", 595 | "show": false 596 | }, 597 | { 598 | "group_by": false, 599 | "label": "ChoiceText", 600 | "name": "ChoiceText", 601 | "show": false 602 | }, 603 | { 604 | "group_by": false, 605 | "label": "Var.Aspect", 606 | "name": "Var.Aspect", 607 | "show": true 608 | }, 609 | { 610 | "group_by": false, 611 | "label": "MPN.Var", 612 | "name": "MPN.Var", 613 | "show": false 614 | }, 615 | { 616 | "group_by": false, 617 | "label": "Info.Var", 618 | "name": "Info.Var", 619 | "show": false 620 | }, 621 | { 622 | "group_by": false, 623 | "label": "Var(JP)", 624 | "name": "Var(JP)", 625 | "show": false 626 | }, 627 | { 628 | "group_by": false, 629 | "label": "Info", 630 | "name": "Info", 631 | "show": false 632 | } 633 | ], 634 | "filter_string": "", 635 | "group_symbols": true, 636 | "name": "", 637 | "sort_asc": false, 638 | "sort_field": "Var" 639 | }, 640 | "connection_grid_size": 50.0, 641 | "drawing": { 642 | "dashed_lines_dash_length_ratio": 12.0, 643 | "dashed_lines_gap_length_ratio": 3.0, 644 | "default_line_thickness": 6.0, 645 | "default_text_size": 50.0, 646 | "field_names": [ 647 | { 648 | "name": "Var", 649 | "url": false, 650 | "visible": true 651 | } 652 | ], 653 | "intersheets_ref_own_page": false, 654 | "intersheets_ref_prefix": "", 655 | "intersheets_ref_short": false, 656 | "intersheets_ref_show": false, 657 | "intersheets_ref_suffix": "", 658 | "junction_size_choice": 3, 659 | "label_size_ratio": 0.375, 660 | "operating_point_overlay_i_precision": 3, 661 | "operating_point_overlay_i_range": "~A", 662 | "operating_point_overlay_v_precision": 3, 663 | "operating_point_overlay_v_range": "~V", 664 | "overbar_offset_ratio": 1.23, 665 | "pin_symbol_size": 25.0, 666 | "text_offset_ratio": 0.15 667 | }, 668 | "legacy_lib_dir": "", 669 | "legacy_lib_list": [], 670 | "meta": { 671 | "version": 1 672 | }, 673 | "net_format_name": "", 674 | "page_layout_descr_file": "", 675 | "plot_directory": "", 676 | "spice_current_sheet_as_root": false, 677 | "spice_external_command": "spice \"%I\"", 678 | "spice_model_current_sheet_as_root": true, 679 | "spice_save_all_currents": false, 680 | "spice_save_all_dissipations": false, 681 | "spice_save_all_voltages": false, 682 | "subpart_first_id": 65, 683 | "subpart_id_separator": 0 684 | }, 685 | "sheets": [ 686 | [ 687 | "cd745a5c-45fb-4106-85c8-c86c130b6ee7", 688 | "Root" 689 | ], 690 | [ 691 | "819719b2-602e-433d-a771-1f2596d21337", 692 | "Example 1: I²C Device Address Selection" 693 | ], 694 | [ 695 | "f1bdcd00-da00-4cca-b21f-4862aa5c3200", 696 | "Example 2: Boot Source Selection" 697 | ], 698 | [ 699 | "98917921-089a-4f53-9391-423838589d96", 700 | "Example 3: Undervoltage Trip Points" 701 | ], 702 | [ 703 | "b90e2ece-31a9-44a1-a7e2-4f98d0159cd7", 704 | "Example 4: IC Variant Selection" 705 | ], 706 | [ 707 | "0e980ce9-433f-4ae1-b84f-fd5b1f452ede", 708 | "Example 5: IC Type and Address Selection" 709 | ], 710 | [ 711 | "efd04459-986c-49c2-bdc3-96a700b4a724", 712 | "Example 6: Maximum LED Backlight Drive Current Selection" 713 | ], 714 | [ 715 | "6cc89fe7-613b-41f0-9c47-85b72f1b3014", 716 | "Example 7: LDO Output Voltage Selection" 717 | ], 718 | [ 719 | "133d4058-5b5c-4077-bb0d-59323121a93a", 720 | "Rule Processing Illustration" 721 | ] 722 | ], 723 | "text_variables": {} 724 | } 725 | -------------------------------------------------------------------------------- /source/kivar_gui.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import wx 4 | import pcbnew 5 | 6 | from .kivar_engine import * 7 | from .kivar_version import version 8 | from . import kivar_gui_custom as custom 9 | from . import kivar_forms as forms 10 | 11 | # Note: This GUI can (currently) only be used from within a running pcbnew session. 12 | 13 | def doc_vcs_ref(): 14 | return f'v{version()}' 15 | 16 | def doc_base_url(): 17 | return f'https://doc.kivar.markh.de/{doc_vcs_ref()}/README.md' 18 | 19 | def window_suffix(): 20 | return f' | KiVar {version()}' 21 | 22 | class Config: 23 | MAIN_WIN = 'main_window' 24 | ERROR_WIN = 'error_window' 25 | WIN_SIZE = 'size' 26 | 27 | def __init__(self): 28 | self.config_file = os.path.join(os.path.dirname(__file__), 'kivar_config.json') 29 | self.config_data = {} 30 | if os.path.exists(self.config_file): 31 | with open(self.config_file, 'r') as f: 32 | self.config_data = json.load(f) 33 | self.inherit_defaults( 34 | { 35 | Config.MAIN_WIN: { Config.WIN_SIZE: [900, 680] }, 36 | Config.ERROR_WIN: { Config.WIN_SIZE: [800, 480] } 37 | } 38 | ) 39 | 40 | def inherit_defaults(self, default_data): 41 | Config._extend_config(self.config_data, default_data) 42 | 43 | @staticmethod 44 | def _extend_config(config_data, default_data): 45 | for key, default_value in default_data.items(): 46 | if not key in config_data: 47 | config_data[key] = default_value 48 | elif isinstance(config_data[key], dict) and isinstance(default_value, dict): 49 | Config._extend_config(config_data[key], default_value) 50 | 51 | def get_window_size(self, window_key): 52 | return wx.Size(self.config_data[window_key][Config.WIN_SIZE]) 53 | 54 | def set_window_size(self, window_key, window_size): 55 | self.config_data[window_key][Config.WIN_SIZE] = [window_size.GetWidth(), window_size.GetHeight()] 56 | return self 57 | 58 | def save(self): 59 | with open(self.config_file, 'w') as f: 60 | json.dump(dict(sorted(self.config_data.items())), f) 61 | 62 | def help_url(): 63 | return f'{doc_base_url()}#usage' 64 | 65 | def help_migrate_url(): 66 | return f'{doc_base_url()}#migrate' 67 | 68 | def platform_str(): 69 | return wx.PlatformInformation().GetPortIdShortName() 70 | 71 | def unset_str(): 72 | return '-- unspecified --' 73 | 74 | def pcbnew_parent_window(): 75 | return wx.FindWindowByName('PcbFrame') 76 | 77 | def dialog_base_config(dialog): 78 | # uses the hires icon, relies on internal down-scaling 79 | theme = 'dark' if dialog.GetBackgroundColour().GetLuminance() < 0.5 else 'light' 80 | dialog.SetIcon(wx.Icon(os.path.join(os.path.dirname(__file__), f'kivar_icon_{theme}.png'), wx.BITMAP_TYPE_PNG)) 81 | dialog.SetTitle(dialog.GetTitle() + window_suffix()) 82 | 83 | def show_selection_dialog(board, fpdict, vardict, parent=pcbnew_parent_window()): 84 | exit_ui = False 85 | rect = None 86 | while not exit_ui: 87 | variant_info = VariantInfo(board.GetFileName()) 88 | errors = variant_info.read_csv(get_choice_dict(vardict)) 89 | if errors: 90 | show_error_dialog([['', '', 'Variant Table: '+error] for error in errors]) 91 | exit_ui = True 92 | else: 93 | dialog = GuiVariantDialog(parent, board, fpdict, vardict, variant_info) 94 | if rect is not None: 95 | dialog.SetRect(rect) 96 | else: 97 | dialog.SetSize(Config().get_window_size(Config.MAIN_WIN)) 98 | dialog.CentreOnParent() 99 | result = dialog.ShowModal() 100 | rect = dialog.GetRect() 101 | Config().set_window_size(Config.MAIN_WIN, dialog.GetSize()).save() 102 | dialog.Destroy() 103 | if result == wx.ID_OK: 104 | apply_selection(fpdict, vardict, dialog.selections()) 105 | store_fpdict(board, fpdict) 106 | pcbnew.Refresh() 107 | if result != wx.ID_REFRESH: 108 | exit_ui = True 109 | 110 | def show_error_dialog(errors, board=None, parent=pcbnew_parent_window()): 111 | dialog = GuiErrorListDialog(parent, errors=sorted(errors, key=lambda x: natural_sort_key(x[1])), board=board) # sort by text 112 | dialog.SetSize(Config().get_window_size(Config.ERROR_WIN)) 113 | dialog.CentreOnParent() 114 | dialog.ShowModal() 115 | Config().set_window_size(Config.ERROR_WIN, dialog.GetSize()).save() 116 | dialog.Destroy() 117 | 118 | def show_missing_rules_dialog(legacy_found=0, parent=pcbnew_parent_window()): 119 | dialog = GuiMissingRulesDialog(parent, legacy_found) 120 | dialog.ShowModal() 121 | dialog.Destroy() 122 | 123 | class GuiVariantDialog(forms.VariantDialog): 124 | def __init__(self, parent, board, fpdict, vardict, variant_info): 125 | super().__init__(parent=parent) 126 | dialog_base_config(self) 127 | 128 | self.board = board 129 | self.fpdict = fpdict 130 | self.vardict = vardict 131 | self.variant_info = variant_info 132 | 133 | choice_dict = get_choice_dict(self.vardict) 134 | self.preselect = detect_current_choices(self.fpdict, self.vardict) 135 | 136 | self.chc_variant.SetItems([unset_str()] + self.variant_info.variants()) 137 | self.chc_variant.SetSelection(0) 138 | self.chc_variant.Enable(self.variant_info.is_loaded()) 139 | 140 | self.bt_var_menu.set_menu_config(self.menu_var, self.on_menu_update) 141 | 142 | # TODO FUTURE when creating a table, have a checkbox in the naming dialog 143 | # "Add unspecified aspects for sort order customization?" 144 | 145 | all_aspects = sorted(choice_dict, key=natural_sort_key) 146 | bound_aspects = self.variant_info.aspects() 147 | free_aspects = [aspect for aspect in all_aspects if aspect not in bound_aspects] 148 | enum_aspects = bound_aspects + free_aspects 149 | 150 | self.aspects_gui = {} 151 | for aspect in enum_aspects: 152 | is_bound = aspect in bound_aspects 153 | panel = self.pnl_bound if is_bound else self.pnl_free 154 | label = wx.StaticText(panel, wx.ID_ANY, aspect, wx.DefaultPosition, wx.DefaultSize, 0) 155 | marking = wx.StaticText(panel, wx.ID_ANY, '', wx.DefaultPosition, wx.DefaultSize, 0) 156 | sorted_choices = sorted(choice_dict[aspect], key=natural_sort_key) 157 | choice = wx.Choice(panel, choices=[unset_str()] + sorted_choices) 158 | sel_choice = self.preselect[aspect] 159 | sel_index = 0 if sel_choice is None else sorted_choices.index(sel_choice) + 1 160 | choice.SetSelection(sel_index) 161 | choice.Bind(wx.EVT_CHOICE, self.on_aspect_change) 162 | panel.GetSizer().Add(label, 1, wx.ALIGN_CENTRE_VERTICAL | wx.ALIGN_RIGHT | wx.EXPAND) 163 | panel.GetSizer().Add(marking, 1, wx.ALIGN_CENTRE_VERTICAL | wx.ALIGN_CENTRE_HORIZONTAL) 164 | panel.GetSizer().Add(choice, 1, wx.ALIGN_CENTRE_VERTICAL | wx.ALIGN_LEFT | wx.EXPAND) 165 | self.aspects_gui[aspect] = (label, marking, choice, panel) 166 | 167 | self.highlight_changed_aspects() 168 | self.lbx_changes.set_select_handler(self.on_change_item_selected) 169 | self.update_changes_list() 170 | 171 | # Bottom (help link and buttons) 172 | legacy_rules = legacy_expressions_found(self.fpdict) 173 | if legacy_rules: 174 | target_url = help_migrate_url() 175 | link_text = f'How to migrate {legacy_rules} found legacy rule(s).' 176 | else: 177 | target_url = help_url() 178 | link_text = 'Help' 179 | self.link_help.SetLabel(link_text) 180 | self.link_help.SetURL(target_url) 181 | 182 | self.sdbszOK.SetLabel('Update PCB') 183 | self.sdbszCancel.SetLabel("Close") 184 | 185 | self.select_matching_variant() 186 | 187 | self.sdbszOK.SetFocus() 188 | 189 | def on_change_item_selected(self, uuid): 190 | if self.board is not None and uuid is not None: 191 | fp = uuid_to_fp(self.board, uuid) 192 | if fp is not None: 193 | pcbnew.FocusOnItem(fp) 194 | 195 | def on_menu_update(self, menu): 196 | vi = self.variant_info 197 | file_path = vi.file_path() 198 | can_edit_file = file_path is not None and os.path.exists(file_path) and os.access(file_path, os.W_OK) 199 | bound = self.variant_info.aspects() 200 | all_bound_specified = True 201 | none_specified = True 202 | for aspect, (label, marking, choice, panel) in self.aspects_gui.items(): 203 | if choice.GetSelection() > 0: 204 | none_specified = False 205 | else: 206 | if aspect in bound: all_bound_specified = False 207 | 208 | self.mi_create_defs.Enable((file_path is not None) and (not none_specified) and (not vi.is_loaded())) 209 | self.mi_add_def.Enable(all_bound_specified and vi.is_loaded() and self.chc_variant.GetSelection() == 0) 210 | self.mi_edit_defs.Enable(can_edit_file) 211 | self.mi_del_def.Enable(vi.is_loaded() and self.chc_variant.GetSelection() > 0) 212 | 213 | def on_mi_create_defs(self, event): 214 | if self.variant_info.file_path() is None: return # double-check 215 | sel = {} 216 | for aspect, (label, marking, choice, panel) in self.aspects_gui.items(): 217 | if choice.GetSelection() > 0: 218 | sel[aspect] = choice.GetStringSelection() 219 | if len(sel) == 0: return # double-check 220 | dialog = GuiCreateTableDialog(self, sel) 221 | result = dialog.ShowModal() 222 | if result == wx.ID_OK: 223 | varid = dialog.txc_varid.GetValue() 224 | if self.variant_file_write_allowed(): 225 | self.variant_info.create_table(varid, sorted(sel, key=natural_sort_key), sel) 226 | self.variant_info.write_csv() 227 | self.EndModal(wx.ID_REFRESH) 228 | dialog.Destroy() 229 | 230 | def on_mi_add_def(self, event): 231 | if not self.variant_info.is_loaded(): return # double-check 232 | bound = self.variant_info.aspects() 233 | sel = {} 234 | for aspect, (label, marking, choice, panel) in self.aspects_gui.items(): 235 | if aspect in bound: 236 | if choice.GetSelection() > 0: 237 | sel[aspect] = choice.GetStringSelection() 238 | else: 239 | return # all bound choices must be specified, double-check 240 | dialog = GuiAddVariantDialog(self, sel) 241 | dialog.set_existing_varids(self.variant_info.variants()) 242 | result = dialog.ShowModal() 243 | if result == wx.ID_OK: 244 | if not dialog.entered_varid_exists(): # double-check 245 | if self.variant_file_write_allowed(): 246 | self.variant_info.add_variant(dialog.entered_varid(), sel) 247 | self.variant_info.write_csv() 248 | self.EndModal(wx.ID_REFRESH) 249 | dialog.Destroy() 250 | 251 | def on_mi_edit_defs(self, event): 252 | wx.LaunchDefaultApplication(self.variant_info.file_path()) 253 | 254 | def on_mi_del_def(self, event): 255 | if self.chc_variant.GetSelection() == 0: return # double-check 256 | varid = self.chc_variant.GetStringSelection() 257 | if self.variant_info.variants() == [varid]: 258 | dialog = wx.MessageDialog(self, f'Deleting the definition of the last variant identifier will cause the table file to be removed as well.\nYou will therefore lose all your aspect bindings.\n\nAre you sure you want to remove the variant identifier "{varid}" and all aspect bindings?', 'Delete Last Variant Definition', style=wx.YES_NO|wx.NO_DEFAULT|wx.ICON_WARNING) 259 | else: 260 | dialog = wx.MessageDialog(self, f'Are you sure you want to remove the variant identifier "{varid}"?', 'Delete Variant Definition', style=wx.YES_NO|wx.NO_DEFAULT|wx.ICON_WARNING) 261 | answer = dialog.ShowModal() 262 | dialog.Destroy() 263 | if answer == wx.ID_YES: 264 | if self.variant_file_write_allowed(): 265 | self.variant_info.delete_variant(varid) 266 | self.variant_info.write_csv() 267 | self.EndModal(wx.ID_REFRESH) 268 | 269 | def on_mi_reload(self, event): 270 | self.EndModal(wx.ID_REFRESH) 271 | 272 | def selections(self): 273 | sel = {} 274 | for aspect, (label, marking, choice, panel) in self.aspects_gui.items(): 275 | index = choice.GetSelection() 276 | sel[aspect] = choice.GetStringSelection() if index > 0 else None 277 | return sel 278 | 279 | def highlight_changed_aspects(self): 280 | changed = False 281 | mark_modified = u"\u25CF" 282 | mark_original = '' 283 | for aspect, (label, marking, choice, panel) in self.aspects_gui.items(): 284 | preselect = self.preselect[aspect] 285 | selected = choice.GetSelection() 286 | selected_str = choice.GetStringSelection() 287 | new_modified = selected_str != preselect if selected > 0 else False 288 | current_modified = marking.GetLabel() == mark_modified 289 | if new_modified != current_modified: 290 | if new_modified: 291 | marking.SetLabelText(mark_modified) 292 | if preselect is not None: 293 | marking.SetToolTip(f"Modified (from '{preselect}')") 294 | else: 295 | marking.SetToolTip('Modified') 296 | else: 297 | marking.SetLabelText(mark_original) 298 | marking.SetToolTip(None) 299 | 300 | panel.Refresh() 301 | changed = True 302 | 303 | # GTK and macOS issue: 304 | # changing label texts requires re-fitting the scroll-windows for manual window width reduction to work properly afterwards 305 | if (wx.Platform == '__WXGTK__' or wx.Platform == '__WXMAC__') and changed: 306 | self.scw_bound.Fit() 307 | self.scw_free.Fit() 308 | 309 | def variant_file_write_allowed(self): 310 | if self.variant_info.file_has_changed(): 311 | # TODO use yesnocancel and set labels for buttons accordingly: Overwrite / Reload and Reset / Cancel 312 | dialog = wx.MessageDialog(self, 'File content on disk has changed since loading.\nYou should either reload the file using the menu option "Reload and Reset", or revert to the known file state externally.\n\nDo you want to continue and overwrite the external changes?', 'File Changed on Disk', style=wx.YES_NO|wx.NO_DEFAULT|wx.ICON_QUESTION) 313 | answer = dialog.ShowModal() 314 | dialog.Destroy() 315 | return answer == wx.ID_YES 316 | else: 317 | return True 318 | 319 | def on_variant_change(self, event): 320 | index = self.chc_variant.GetSelection() 321 | if index > 0: 322 | index -= 1 323 | variant = self.variant_info.variants()[index] 324 | choices = self.variant_info.choices()[variant] 325 | for aspect_index, aspect in enumerate(self.variant_info.aspects()): 326 | choice_str = choices[aspect_index] 327 | choice_gui = self.aspects_gui[aspect][2] 328 | choice_idx = choice_gui.FindString(choice_str, caseSensitive=True) 329 | choice_gui.SetSelection(choice_idx) 330 | self.on_aspect_change(None) 331 | else: 332 | self.select_matching_variant() 333 | self.Layout() 334 | 335 | def select_matching_variant(self): 336 | sel = self.selections() 337 | match = self.variant_info.match_variant(sel) 338 | if match is None: 339 | self.chc_variant.SetSelection(0) 340 | else: 341 | sel_index = self.chc_variant.FindString(match, caseSensitive=True) 342 | self.chc_variant.SetSelection(sel_index) 343 | 344 | def on_aspect_change(self, event): 345 | if event is not None: # if None, we were called manually, avoid recursion! 346 | self.select_matching_variant() 347 | self.highlight_changed_aspects() 348 | self.update_changes_list() 349 | self.Layout() 350 | 351 | def update_changes_list(self): 352 | changes = apply_selection(self.fpdict, self.vardict, self.selections(), True) 353 | self.lbx_changes.set_item_list(sorted(changes, key=lambda x: natural_sort_key(x[1]))) 354 | self.Layout() 355 | 356 | class GuiCreateTableDialog(forms.CreateTableDialog): 357 | def __init__(self, parent, choice_dict): 358 | super().__init__(parent=parent) 359 | # child dialog, hence no base config 360 | sel = [] 361 | for aspect in sorted(choice_dict, key=natural_sort_key): 362 | sel.append(u"\u2022" + f' {aspect} = {choice_dict[aspect]}') 363 | self.txt_aspects.SetLabelText('\n'.join(sel)) 364 | self.Fit() 365 | self.CentreOnParent() 366 | 367 | def on_confirm(self, event): 368 | if not self.entered_varid().strip(): 369 | wx.MessageBox(f'Please choose a valid variant identifier name.', 'Invalid Variant Identifier', style=wx.ICON_ERROR, parent=self) 370 | else: 371 | self.EndModal(wx.ID_OK) 372 | 373 | def entered_varid(self): 374 | return self.txc_varid.GetValue() 375 | 376 | class GuiAddVariantDialog(forms.AddVariantDialog): 377 | def __init__(self, parent, choice_dict): 378 | super().__init__(parent=parent) 379 | # child dialog, hence no base config 380 | sel = [] 381 | self.existing_varids = [] 382 | for aspect in sorted(choice_dict, key=natural_sort_key): 383 | sel.append(u"\u2022" + f' {aspect} = {choice_dict[aspect]}') 384 | self.txt_aspects.SetLabelText('\n'.join(sel)) 385 | self.Fit() 386 | self.CentreOnParent() 387 | 388 | def set_existing_varids(self, varids): 389 | self.existing_varids = varids 390 | 391 | def entered_varid_exists(self): 392 | return self.entered_varid() in self.existing_varids 393 | 394 | def on_confirm(self, event): 395 | if not self.entered_varid().strip(): 396 | wx.MessageBox(f'Please choose a valid variant identifier name.', 'Invalid Variant Identifier', style=wx.ICON_ERROR, parent=self) 397 | elif self.entered_varid_exists(): 398 | wx.MessageBox(f'The variant identifier "{self.entered_varid()}" already exists.\nPlease choose a different variant identifier name.', 'Conflicting Variant Identifier', style=wx.ICON_ERROR, parent=self) 399 | else: 400 | self.EndModal(wx.ID_OK) 401 | 402 | def entered_varid(self): 403 | return self.txc_varid.GetValue() 404 | 405 | class GuiMissingRulesDialog(forms.MissingRulesDialog): 406 | def __init__(self, parent, legacy_found=0): 407 | super().__init__(parent=parent) 408 | dialog_base_config(self) 409 | if legacy_found: # override text and URL 410 | self.txt_info.SetLabelMarkup(f'KiVar could not find any rules in the current format.\n\nHowever, there were found {legacy_found} rule(s) in the legacy format,\nwhich is not supported anymore.\n\nPlease consult the KiVar documentation to learn how to migrate\nthe rules of your existing designs to the current format.') 411 | self.link_help.SetLabel('KiVar Migration Guide') 412 | self.link_help.SetURL(help_migrate_url()) 413 | else: 414 | self.link_help.SetLabel('KiVar Usage Guide') 415 | self.link_help.SetURL(help_url()) 416 | self.btn_close.SetFocus() 417 | self.Fit() 418 | self.CentreOnParent() 419 | 420 | class GuiErrorListDialog(forms.ErrorListDialog): 421 | def __init__(self, parent, errors=None, board=None): 422 | super().__init__(parent=parent) 423 | dialog_base_config(self) 424 | self.board = board 425 | self.lbx_errors.set_item_list(errors) 426 | self.lbx_errors.set_select_handler(self.on_item_selected) 427 | self.btn_close.SetFocus() 428 | self.Fit() 429 | self.CentreOnParent() 430 | 431 | def on_item_selected(self, uuid): 432 | if self.board is not None and uuid is not None: 433 | fp = uuid_to_fp(self.board, uuid) 434 | if fp is not None: 435 | pcbnew.FocusOnItem(fp) 436 | -------------------------------------------------------------------------------- /source/kivar_forms.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ########################################################################### 4 | ## Python code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6-dirty) 5 | ## http://www.wxformbuilder.org/ 6 | ## 7 | ## PLEASE DO *NOT* EDIT THIS FILE! 8 | ########################################################################### 9 | 10 | from .kivar_gui_custom import MenuButton 11 | from .kivar_gui_custom import PcbItemListBox 12 | import wx 13 | import wx.xrc 14 | import wx.adv 15 | 16 | ########################################################################### 17 | ## Class VariantDialog 18 | ########################################################################### 19 | 20 | class VariantDialog ( wx.Dialog ): 21 | 22 | def __init__( self, parent ): 23 | wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"Variant Selection", pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_DIALOG_STYLE|wx.MAXIMIZE_BOX|wx.RESIZE_BORDER, name = u"kivar_sel" ) 24 | 25 | self.SetSizeHints( wx.Size( 700,350 ), wx.DefaultSize ) 26 | 27 | sz_main = wx.BoxSizer( wx.VERTICAL ) 28 | 29 | sz_main.SetMinSize( wx.Size( 700,350 ) ) 30 | sz_var_hor = wx.BoxSizer( wx.HORIZONTAL ) 31 | 32 | sz_var_hor.SetMinSize( wx.Size( 700,100 ) ) 33 | sz_var_left = wx.BoxSizer( wx.VERTICAL ) 34 | 35 | sz_var_left.SetMinSize( wx.Size( 300,100 ) ) 36 | sz_variant = wx.BoxSizer( wx.HORIZONTAL ) 37 | 38 | self.lbl_variant = wx.StaticText( self, wx.ID_ANY, u"Variant:", wx.DefaultPosition, wx.DefaultSize, 0 ) 39 | self.lbl_variant.Wrap( -1 ) 40 | 41 | sz_variant.Add( self.lbl_variant, 0, wx.ALIGN_CENTER_VERTICAL, 5 ) 42 | 43 | chc_variantChoices = [] 44 | self.chc_variant = wx.Choice( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, chc_variantChoices, 0 ) 45 | self.chc_variant.SetSelection( 0 ) 46 | sz_variant.Add( self.chc_variant, 1, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.LEFT, 5 ) 47 | 48 | self.bt_var_menu = MenuButton( self, wx.ID_ANY, u"︙", wx.DefaultPosition, wx.Size( 32,-1 ), wx.BU_EXACTFIT ) 49 | self.bt_var_menu.SetToolTip( u"Manage variant definitions" ) 50 | self.bt_var_menu.SetMinSize( wx.Size( 32,-1 ) ) 51 | self.bt_var_menu.SetMaxSize( wx.Size( 32,-1 ) ) 52 | 53 | sz_variant.Add( self.bt_var_menu, 0, wx.ALIGN_CENTER|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.LEFT, 5 ) 54 | 55 | 56 | sz_var_left.Add( sz_variant, 0, wx.BOTTOM|wx.EXPAND|wx.TOP, 5 ) 57 | 58 | sbox_bound = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"Bound Aspects" ), wx.VERTICAL ) 59 | 60 | self.scw_bound = wx.ScrolledWindow( sbox_bound.GetStaticBox(), wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.HSCROLL|wx.VSCROLL ) 61 | self.scw_bound.SetScrollRate( 8, 8 ) 62 | sz_bound = wx.BoxSizer( wx.VERTICAL ) 63 | 64 | self.pnl_bound = wx.Panel( self.scw_bound, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) 65 | fgsz_bound = wx.FlexGridSizer( 0, 3, 5, 5 ) 66 | fgsz_bound.AddGrowableCol( 0, 1 ) 67 | fgsz_bound.AddGrowableCol( 2, 1 ) 68 | fgsz_bound.SetFlexibleDirection( wx.BOTH ) 69 | fgsz_bound.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) 70 | 71 | 72 | self.pnl_bound.SetSizer( fgsz_bound ) 73 | self.pnl_bound.Layout() 74 | fgsz_bound.Fit( self.pnl_bound ) 75 | sz_bound.Add( self.pnl_bound, 1, wx.EXPAND |wx.ALL, 5 ) 76 | 77 | 78 | self.scw_bound.SetSizer( sz_bound ) 79 | self.scw_bound.Layout() 80 | sz_bound.Fit( self.scw_bound ) 81 | sbox_bound.Add( self.scw_bound, 1, wx.EXPAND |wx.ALL, 5 ) 82 | 83 | 84 | sz_var_left.Add( sbox_bound, 1, wx.EXPAND, 5 ) 85 | 86 | 87 | sz_var_hor.Add( sz_var_left, 1, wx.EXPAND, 6 ) 88 | 89 | 90 | sz_var_hor.Add( ( 12, 0), 0, 0, 5 ) 91 | 92 | sz_var_right = wx.BoxSizer( wx.VERTICAL ) 93 | 94 | sz_var_right.SetMinSize( wx.Size( 300,100 ) ) 95 | sbox_free = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"Free Aspects" ), wx.VERTICAL ) 96 | 97 | self.scw_free = wx.ScrolledWindow( sbox_free.GetStaticBox(), wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.HSCROLL|wx.VSCROLL ) 98 | self.scw_free.SetScrollRate( 8, 8 ) 99 | sz_free = wx.BoxSizer( wx.VERTICAL ) 100 | 101 | self.pnl_free = wx.Panel( self.scw_free, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) 102 | fgsz_free = wx.FlexGridSizer( 0, 3, 5, 5 ) 103 | fgsz_free.AddGrowableCol( 0, 1 ) 104 | fgsz_free.AddGrowableCol( 2, 1 ) 105 | fgsz_free.SetFlexibleDirection( wx.BOTH ) 106 | fgsz_free.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) 107 | 108 | 109 | self.pnl_free.SetSizer( fgsz_free ) 110 | self.pnl_free.Layout() 111 | fgsz_free.Fit( self.pnl_free ) 112 | sz_free.Add( self.pnl_free, 1, wx.ALL|wx.EXPAND, 5 ) 113 | 114 | 115 | self.scw_free.SetSizer( sz_free ) 116 | self.scw_free.Layout() 117 | sz_free.Fit( self.scw_free ) 118 | sbox_free.Add( self.scw_free, 1, wx.ALL|wx.EXPAND, 5 ) 119 | 120 | 121 | sz_var_right.Add( sbox_free, 1, wx.EXPAND, 5 ) 122 | 123 | 124 | sz_var_hor.Add( sz_var_right, 1, wx.EXPAND, 6 ) 125 | 126 | 127 | sz_main.Add( sz_var_hor, 12, wx.LEFT|wx.RIGHT|wx.TOP|wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL, 10 ) 128 | 129 | sz_changes = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"Changes to Be Applied" ), wx.VERTICAL ) 130 | 131 | sz_changes.SetMinSize( wx.Size( 700,100 ) ) 132 | lbx_changesChoices = [] 133 | self.lbx_changes = PcbItemListBox( sz_changes.GetStaticBox(), wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, lbx_changesChoices, wx.LB_HSCROLL|wx.LB_NEEDED_SB|wx.LB_SINGLE ) 134 | self.lbx_changes.SetMinSize( wx.Size( 700,100 ) ) 135 | 136 | sz_changes.Add( self.lbx_changes, 1, wx.ALL|wx.EXPAND, 5 ) 137 | 138 | 139 | sz_main.Add( sz_changes, 10, wx.ALL|wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL, 10 ) 140 | 141 | sz_bottom = wx.BoxSizer( wx.HORIZONTAL ) 142 | 143 | self.link_help = wx.adv.HyperlinkCtrl( self, wx.ID_ANY, u"Help ...", u"https://kivar.markh.de", wx.DefaultPosition, wx.DefaultSize, wx.adv.HL_DEFAULT_STYLE ) 144 | sz_bottom.Add( self.link_help, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 5 ) 145 | 146 | 147 | sz_bottom.Add( ( 0, 0), 1, wx.EXPAND, 5 ) 148 | 149 | sdbsz = wx.StdDialogButtonSizer() 150 | self.sdbszOK = wx.Button( self, wx.ID_OK ) 151 | sdbsz.AddButton( self.sdbszOK ) 152 | self.sdbszCancel = wx.Button( self, wx.ID_CANCEL ) 153 | sdbsz.AddButton( self.sdbszCancel ) 154 | sdbsz.Realize() 155 | 156 | sz_bottom.Add( sdbsz, 0, wx.ALIGN_CENTER_VERTICAL, 5 ) 157 | 158 | 159 | sz_main.Add( sz_bottom, 0, wx.BOTTOM|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_HORIZONTAL, 5 ) 160 | 161 | 162 | self.SetSizer( sz_main ) 163 | self.Layout() 164 | sz_main.Fit( self ) 165 | self.menu_var = wx.Menu() 166 | self.mi_create_defs = wx.MenuItem( self.menu_var, wx.ID_ANY, u"&Create Definition Table File...", wx.EmptyString, wx.ITEM_NORMAL ) 167 | self.menu_var.Append( self.mi_create_defs ) 168 | 169 | self.mi_add_def = wx.MenuItem( self.menu_var, wx.ID_ANY, u"&Add New Definition...", wx.EmptyString, wx.ITEM_NORMAL ) 170 | self.menu_var.Append( self.mi_add_def ) 171 | 172 | self.mi_edit_defs = wx.MenuItem( self.menu_var, wx.ID_ANY, u"&Edit Definition Table...", wx.EmptyString, wx.ITEM_NORMAL ) 173 | self.menu_var.Append( self.mi_edit_defs ) 174 | 175 | self.mi_del_def = wx.MenuItem( self.menu_var, wx.ID_ANY, u"&Delete Selected Definition...", wx.EmptyString, wx.ITEM_NORMAL ) 176 | self.menu_var.Append( self.mi_del_def ) 177 | 178 | self.menu_var.AppendSeparator() 179 | 180 | self.mi_reload = wx.MenuItem( self.menu_var, wx.ID_ANY, u"&Reload and Reset", wx.EmptyString, wx.ITEM_NORMAL ) 181 | self.menu_var.Append( self.mi_reload ) 182 | 183 | 184 | 185 | self.Centre( wx.BOTH ) 186 | 187 | # Connect Events 188 | self.chc_variant.Bind( wx.EVT_CHOICE, self.on_variant_change ) 189 | self.Bind( wx.EVT_MENU, self.on_mi_create_defs, id = self.mi_create_defs.GetId() ) 190 | self.Bind( wx.EVT_MENU, self.on_mi_add_def, id = self.mi_add_def.GetId() ) 191 | self.Bind( wx.EVT_MENU, self.on_mi_edit_defs, id = self.mi_edit_defs.GetId() ) 192 | self.Bind( wx.EVT_MENU, self.on_mi_del_def, id = self.mi_del_def.GetId() ) 193 | self.Bind( wx.EVT_MENU, self.on_mi_reload, id = self.mi_reload.GetId() ) 194 | 195 | def __del__( self ): 196 | pass 197 | 198 | 199 | # Virtual event handlers, override them in your derived class 200 | def on_variant_change( self, event ): 201 | event.Skip() 202 | 203 | def on_mi_create_defs( self, event ): 204 | event.Skip() 205 | 206 | def on_mi_add_def( self, event ): 207 | event.Skip() 208 | 209 | def on_mi_edit_defs( self, event ): 210 | event.Skip() 211 | 212 | def on_mi_del_def( self, event ): 213 | event.Skip() 214 | 215 | def on_mi_reload( self, event ): 216 | event.Skip() 217 | 218 | 219 | ########################################################################### 220 | ## Class MissingRulesDialog 221 | ########################################################################### 222 | 223 | class MissingRulesDialog ( wx.Dialog ): 224 | 225 | def __init__( self, parent ): 226 | wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"Missing Rule Definitions", pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_DIALOG_STYLE ) 227 | 228 | self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) 229 | 230 | sz_main = wx.BoxSizer( wx.VERTICAL ) 231 | 232 | self.txt_info = wx.StaticText( self, wx.ID_ANY, u"KiVar could not find any valid rule definitions.\n\nPlease consult the KiVar documentation to learn how to\nassign variation rules to symbols or footprints:", wx.DefaultPosition, wx.DefaultSize, 0 ) 233 | self.txt_info.SetLabelMarkup( u"KiVar could not find any valid rule definitions.\n\nPlease consult the KiVar documentation to learn how to\nassign variation rules to symbols or footprints:" ) 234 | self.txt_info.Wrap( -1 ) 235 | 236 | sz_main.Add( self.txt_info, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.EXPAND, 20 ) 237 | 238 | self.link_help = wx.adv.HyperlinkCtrl( self, wx.ID_ANY, u"...", wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.adv.HL_DEFAULT_STYLE ) 239 | sz_main.Add( self.link_help, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.EXPAND, 5 ) 240 | 241 | self.btn_close = wx.Button( self, wx.ID_OK, u"Close", wx.DefaultPosition, wx.DefaultSize, 0 ) 242 | sz_main.Add( self.btn_close, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 20 ) 243 | 244 | 245 | self.SetSizer( sz_main ) 246 | self.Layout() 247 | sz_main.Fit( self ) 248 | 249 | self.Centre( wx.BOTH ) 250 | 251 | def __del__( self ): 252 | pass 253 | 254 | 255 | ########################################################################### 256 | ## Class ErrorListDialog 257 | ########################################################################### 258 | 259 | class ErrorListDialog ( wx.Dialog ): 260 | 261 | def __init__( self, parent ): 262 | wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"Initialization Failure", pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_DIALOG_STYLE|wx.MAXIMIZE_BOX|wx.RESIZE_BORDER ) 263 | 264 | self.SetSizeHints( wx.Size( 400,200 ), wx.DefaultSize ) 265 | 266 | sz_main = wx.BoxSizer( wx.VERTICAL ) 267 | 268 | sz_errors = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"Errors" ), wx.VERTICAL ) 269 | 270 | lbx_errorsChoices = [] 271 | self.lbx_errors = PcbItemListBox( sz_errors.GetStaticBox(), wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, lbx_errorsChoices, wx.LB_HSCROLL|wx.LB_NEEDED_SB|wx.LB_SINGLE ) 272 | sz_errors.Add( self.lbx_errors, 1, wx.ALL|wx.EXPAND, 5 ) 273 | 274 | 275 | sz_main.Add( sz_errors, 1, wx.ALL|wx.EXPAND, 10 ) 276 | 277 | sz_bottom = wx.BoxSizer( wx.HORIZONTAL ) 278 | 279 | self.link_help = wx.adv.HyperlinkCtrl( self, wx.ID_ANY, u"Usage Guide", u"...", wx.DefaultPosition, wx.DefaultSize, wx.adv.HL_DEFAULT_STYLE ) 280 | sz_bottom.Add( self.link_help, 0, wx.ALIGN_CENTER_VERTICAL, 5 ) 281 | 282 | 283 | sz_bottom.Add( ( 0, 0), 1, wx.EXPAND, 5 ) 284 | 285 | self.btn_close = wx.Button( self, wx.ID_OK, u"Close", wx.DefaultPosition, wx.DefaultSize, 0 ) 286 | sz_bottom.Add( self.btn_close, 0, wx.ALIGN_CENTER_VERTICAL, 5 ) 287 | 288 | 289 | sz_main.Add( sz_bottom, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 10 ) 290 | 291 | 292 | sz_main.Add( ( 0, 5), 0, 0, 5 ) 293 | 294 | 295 | self.SetSizer( sz_main ) 296 | self.Layout() 297 | sz_main.Fit( self ) 298 | 299 | self.Centre( wx.BOTH ) 300 | 301 | def __del__( self ): 302 | pass 303 | 304 | 305 | ########################################################################### 306 | ## Class CreateTableDialog 307 | ########################################################################### 308 | 309 | class CreateTableDialog ( wx.Dialog ): 310 | 311 | def __init__( self, parent ): 312 | wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"Create Variant Definition Table", pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_DIALOG_STYLE ) 313 | 314 | self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) 315 | 316 | sz_main = wx.BoxSizer( wx.VERTICAL ) 317 | 318 | self.txt_intro = wx.StaticText( self, wx.ID_ANY, u"This will create a Variant Definition Table binding the following aspects:", wx.DefaultPosition, wx.DefaultSize, 0 ) 319 | self.txt_intro.SetLabelMarkup( u"This will create a Variant Definition Table binding the following aspects:" ) 320 | self.txt_intro.Wrap( 1 ) 321 | 322 | sz_main.Add( self.txt_intro, 0, wx.ALIGN_CENTER|wx.ALL|wx.EXPAND, 12 ) 323 | 324 | self.txt_aspects = wx.StaticText( self, wx.ID_ANY, u"...", wx.DefaultPosition, wx.DefaultSize, 0 ) 325 | self.txt_aspects.Wrap( -1 ) 326 | 327 | sz_main.Add( self.txt_aspects, 0, wx.ALIGN_CENTER|wx.ALL, 12 ) 328 | 329 | self.txt_explain_sel = wx.StaticText( self, wx.ID_ANY, u"Aspect binding: To bind aspects to the variant definitions, assign the desired specific choices\nto them in the main dialog. To keep aspects as free, select the unspecified choice for them.\n\nIf you are not yet satisfied with the above bindings, go back to the main dialog and make the\nappropriate selections now.\n\nTip: You can customize the display order of aspects and variants by modifying their order in\nthe variants table file (use the 'Edit Definition Table...' option).", wx.DefaultPosition, wx.DefaultSize, 0 ) 330 | self.txt_explain_sel.SetLabelMarkup( u"Aspect binding: To bind aspects to the variant definitions, assign the desired specific choices\nto them in the main dialog. To keep aspects as free, select the unspecified choice for them.\n\nIf you are not yet satisfied with the above bindings, go back to the main dialog and make the\nappropriate selections now.\n\nTip: You can customize the display order of aspects and variants by modifying their order in\nthe variants table file (use the 'Edit Definition Table...' option)." ) 331 | self.txt_explain_sel.Wrap( -1 ) 332 | 333 | sz_main.Add( self.txt_explain_sel, 0, wx.ALIGN_CENTER|wx.ALL|wx.EXPAND, 12 ) 334 | 335 | sz_varid = wx.BoxSizer( wx.HORIZONTAL ) 336 | 337 | self.lbl_varid = wx.StaticText( self, wx.ID_ANY, u"Initial variant identifier:", wx.DefaultPosition, wx.DefaultSize, 0 ) 338 | self.lbl_varid.Wrap( -1 ) 339 | 340 | sz_varid.Add( self.lbl_varid, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT|wx.ALL, 5 ) 341 | 342 | self.txc_varid = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER ) 343 | sz_varid.Add( self.txc_varid, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) 344 | 345 | 346 | sz_main.Add( sz_varid, 1, wx.ALIGN_CENTER_HORIZONTAL|wx.EXPAND|wx.ALL, 12 ) 347 | 348 | sdbsz = wx.StdDialogButtonSizer() 349 | self.sdbszOK = wx.Button( self, wx.ID_OK ) 350 | sdbsz.AddButton( self.sdbszOK ) 351 | self.sdbszCancel = wx.Button( self, wx.ID_CANCEL ) 352 | sdbsz.AddButton( self.sdbszCancel ) 353 | sdbsz.Realize() 354 | 355 | sz_main.Add( sdbsz, 1, wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL, 5 ) 356 | 357 | 358 | self.SetSizer( sz_main ) 359 | self.Layout() 360 | sz_main.Fit( self ) 361 | 362 | self.Centre( wx.BOTH ) 363 | 364 | # Connect Events 365 | self.txc_varid.Bind( wx.EVT_TEXT_ENTER, self.on_confirm ) 366 | self.sdbszOK.Bind( wx.EVT_BUTTON, self.on_confirm ) 367 | 368 | def __del__( self ): 369 | pass 370 | 371 | 372 | # Virtual event handlers, override them in your derived class 373 | def on_confirm( self, event ): 374 | event.Skip() 375 | 376 | 377 | 378 | ########################################################################### 379 | ## Class AddVariantDialog 380 | ########################################################################### 381 | 382 | class AddVariantDialog ( wx.Dialog ): 383 | 384 | def __init__( self, parent ): 385 | wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"Add Variant Definition", pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_DIALOG_STYLE ) 386 | 387 | self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) 388 | 389 | sz_main = wx.BoxSizer( wx.VERTICAL ) 390 | 391 | self.txt_intro = wx.StaticText( self, wx.ID_ANY, u"This will add a Variant Definition with the following assignments:", wx.DefaultPosition, wx.DefaultSize, 0 ) 392 | self.txt_intro.SetLabelMarkup( u"This will add a Variant Definition with the following assignments:" ) 393 | self.txt_intro.Wrap( 1 ) 394 | 395 | sz_main.Add( self.txt_intro, 0, wx.ALIGN_CENTER|wx.ALL|wx.EXPAND, 12 ) 396 | 397 | self.txt_aspects = wx.StaticText( self, wx.ID_ANY, u"...", wx.DefaultPosition, wx.DefaultSize, 0 ) 398 | self.txt_aspects.Wrap( -1 ) 399 | 400 | sz_main.Add( self.txt_aspects, 0, wx.ALIGN_CENTER|wx.ALL, 12 ) 401 | 402 | self.txt_explain_sel = wx.StaticText( self, wx.ID_ANY, u"If you are not yet satisfied with the above assignments, go back\nto the main dialog and make the appropriate selections now.", wx.DefaultPosition, wx.DefaultSize, 0 ) 403 | self.txt_explain_sel.SetLabelMarkup( u"If you are not yet satisfied with the above assignments, go back\nto the main dialog and make the appropriate selections now." ) 404 | self.txt_explain_sel.Wrap( -1 ) 405 | 406 | sz_main.Add( self.txt_explain_sel, 0, wx.ALIGN_CENTER|wx.ALL|wx.EXPAND, 12 ) 407 | 408 | sz_varid = wx.BoxSizer( wx.HORIZONTAL ) 409 | 410 | self.lbl_varid = wx.StaticText( self, wx.ID_ANY, u"New variant identifier:", wx.DefaultPosition, wx.DefaultSize, 0 ) 411 | self.lbl_varid.Wrap( -1 ) 412 | 413 | sz_varid.Add( self.lbl_varid, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT|wx.ALL, 5 ) 414 | 415 | self.txc_varid = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER ) 416 | sz_varid.Add( self.txc_varid, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) 417 | 418 | 419 | sz_main.Add( sz_varid, 1, wx.ALIGN_CENTER_HORIZONTAL|wx.EXPAND|wx.ALL, 12 ) 420 | 421 | sdbsz = wx.StdDialogButtonSizer() 422 | self.sdbszOK = wx.Button( self, wx.ID_OK ) 423 | sdbsz.AddButton( self.sdbszOK ) 424 | self.sdbszCancel = wx.Button( self, wx.ID_CANCEL ) 425 | sdbsz.AddButton( self.sdbszCancel ) 426 | sdbsz.Realize() 427 | 428 | sz_main.Add( sdbsz, 1, wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL, 5 ) 429 | 430 | 431 | self.SetSizer( sz_main ) 432 | self.Layout() 433 | sz_main.Fit( self ) 434 | 435 | self.Centre( wx.BOTH ) 436 | 437 | # Connect Events 438 | self.txc_varid.Bind( wx.EVT_TEXT_ENTER, self.on_confirm ) 439 | self.sdbszOK.Bind( wx.EVT_BUTTON, self.on_confirm ) 440 | 441 | def __del__( self ): 442 | pass 443 | 444 | 445 | # Virtual event handlers, override them in your derived class 446 | def on_confirm( self, event ): 447 | event.Skip() 448 | 449 | 450 | 451 | -------------------------------------------------------------------------------- /doc/kivar-icon.inkscape.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 58 | 67 | 68 | 70 | 75 | 80 | 82 | 86 | 87 | 89 | 93 | 94 | 96 | 100 | 101 | 104 | 108 | 109 | 112 | 116 | 117 | 120 | 124 | 125 | 128 | 132 | 133 | 136 | 140 | 141 | 144 | 148 | 149 | 159 | 164 | 174 | 183 | 192 | 201 | 210 | 219 | 229 | 239 | 248 | 249 | 255 | 260 | 265 | 271 | 272 | 278 | 284 | 290 | 296 | 302 | 309 | 310 | 348 | 354 | 360 | 361 | 367 | 373 | 374 | 375 | -------------------------------------------------------------------------------- /source/kivar_cli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | import argparse 6 | 7 | try: from kivar_engine import * 8 | except ModuleNotFoundError: from .kivar_engine import * 9 | try: from kivar_version import version 10 | except ModuleNotFoundError: from .kivar_version import version 11 | 12 | # TODO support variants 13 | # * assigning variants 14 | # * have a switch that forbids changing bound aspects 15 | # * add a simple "create table" command, which simply binds all aspects (user needs to edit csv manually) 16 | # * ... more ideas ... (CLI support may be implemented later than GUI, unsure about usage stats) 17 | 18 | # TODO for list, allow another structure: aspect -> component -> choice (in addition to current aspect -> choice -> component) 19 | # TODO make use of verbose options, where applicable 20 | # also check all TODO notes buried in the code below! 21 | # TODO have common help URL for both plugin and CLI? place in backend? 22 | 23 | def doc_vcs_ref(): 24 | return f'v{version()}' 25 | 26 | def doc_base_url(): 27 | return f'https://doc.kivar.markh.de/{doc_vcs_ref()}/README.md' 28 | 29 | no_color = False 30 | load_errors = None 31 | 32 | class Msg: 33 | RESET = '\033[0m' 34 | CODES = { 35 | 'error': '\033[0;1;37;41m', 36 | 'pass': '\033[0;1;32m', 37 | 'fail': '\033[0;1;31m', 38 | 'mod': '\033[0;34m', 39 | 'ref': '\033[0;3;33m', 40 | 'value': '\033[0;1m', 41 | 'field': '\033[0;3;34m', 42 | 'fvalue': '\033[0;1m', 43 | 'var': '\033[0;33m', 44 | 'avar': '\033[0;1;37;43m', 45 | 'bound': '\033[0;33m', 46 | 'aspect': '\033[0;35m', 47 | 'choice': '\033[0;36m', 48 | 'achoice': '\033[0;1;37;46m', 49 | 'yes': '\033[0;32m', 50 | 'no': '\033[0;31m' 51 | } 52 | 53 | def __init__(self, error=False): 54 | self.stream = sys.stderr if error else sys.stdout 55 | global no_color 56 | self.use_color = self.stream.isatty() and not no_color 57 | self.clear() 58 | 59 | def clear(self): 60 | self.data = '' 61 | self.reset() 62 | return self 63 | 64 | def text(self, text_string): 65 | self.data += text_string 66 | return self 67 | 68 | def color(self, color): 69 | if self.use_color and color in self.CODES: 70 | self.data += self.CODES[color] 71 | return self 72 | 73 | def reset(self): 74 | self.data += self.RESET 75 | return self 76 | 77 | def flush(self, end='\n'): 78 | self.reset() 79 | print(self.data, file=self.stream, end=end) 80 | self.clear() 81 | return self 82 | 83 | def content(self): 84 | self.reset() 85 | content = self.data 86 | self.clear() 87 | return content 88 | 89 | class ErrMsg(Msg): 90 | def __init__(self, error=True): 91 | super().__init__(error) 92 | 93 | def c(self): 94 | self.color('error') 95 | return self 96 | 97 | def sym_variant(): 98 | return '~' 99 | 100 | def load_board(in_file): 101 | if not os.path.isfile(in_file) or not os.access(in_file, os.R_OK): 102 | ErrMsg().c().text(f"Error: '{in_file}' is not a readable file.").flush() 103 | return None 104 | return pcbnew.LoadBoard(in_file) 105 | 106 | def save_board(out_file, board): 107 | return pcbnew.SaveBoard(out_file, board) 108 | 109 | def build_vardict_wrapper(fpdict, field_ids): 110 | vardict, errors = build_vardict(fpdict, field_ids) 111 | if len(errors) > 0: 112 | ErrMsg().c().text(f'Errors ({len(errors)}):').flush() 113 | for uuid, order, error in sorted(errors, key=lambda x: natural_sort_key(x[1])): 114 | ErrMsg().text(' ' + error).flush() 115 | return None 116 | if len(vardict) == 0: 117 | ErrMsg().c().text('Error: No rule definitions found.').flush() 118 | ErrMsg().text(f' Read {doc_base_url()}#usage for usage instructions.').flush() 119 | return None 120 | return vardict 121 | 122 | def load_varinfo_wrapper(in_file, vardict): 123 | varinfo = VariantInfo(in_file) 124 | errors = varinfo.read_csv(get_choice_dict(vardict)) 125 | if not varinfo.is_loaded(): show_variants = False # if not loaded, disable variants altogether 126 | if len(errors) > 0: 127 | ErrMsg().c().text(f'Errors when loading variant table:').flush() 128 | for error in errors: 129 | ErrMsg().text(' ' + error).flush() 130 | return None 131 | return varinfo 132 | 133 | def reorder_aspects(all_aspects, bound_aspects): 134 | free_aspects = [aspect for aspect in all_aspects if aspect not in bound_aspects] 135 | return bound_aspects + free_aspects 136 | 137 | def list_command(in_file=None, long=False, prop_codes=False, detailed=False, selected=False, use_variants=False, only_variants=False, cust_asp_order=False): 138 | b = load_board(in_file) 139 | if b is None: return False 140 | 141 | fpdict = build_fpdict(b) 142 | vardict = build_vardict_wrapper(fpdict, field_ids(b)) 143 | if vardict is None: return False 144 | 145 | if selected: 146 | sel = detect_current_choices(fpdict, vardict) 147 | 148 | if use_variants: 149 | varinfo = load_varinfo_wrapper(in_file, vardict) 150 | if varinfo is None: return False 151 | if not varinfo.is_loaded(): use_variants = False 152 | 153 | ndict = {} 154 | for uuid in vardict: 155 | aspect = vardict[uuid][Key.ASPECT] 156 | if not aspect in ndict: 157 | ndict[aspect] = {} 158 | for choice in vardict[uuid][Key.CMP]: 159 | if not choice in ndict[aspect]: 160 | ndict[aspect][choice] = {} 161 | if detailed: 162 | cmp_info = [] 163 | cmp_value = vardict[uuid][Key.CMP][choice][Key.VALUE] 164 | if cmp_value is not None: 165 | cmp_info.append(Msg().color('value').text(quote_str(cmp_value)).content()) 166 | cmp_props = vardict[uuid][Key.CMP][choice][Key.PROPS] 167 | for prop_code in fpdict[uuid][Key.PROPS]: 168 | if prop_code in cmp_props and cmp_props[prop_code] is not None: 169 | m = Msg() 170 | if prop_codes: 171 | if cmp_props[prop_code]: 172 | m.color('yes').text('+') 173 | else: 174 | m.color('no').text('-') 175 | cmp_info.append(m.text(prop_code).content()) 176 | else: 177 | m.text('<') 178 | if cmp_props[prop_code]: 179 | m.color('yes') 180 | state_text = 'Yes' 181 | else: 182 | m.color('no') 183 | state_text = 'No' 184 | cmp_info.append(m.text(prop_abbrev(prop_code) + ':' + state_text).reset().text('>').content()) 185 | ndict[aspect][choice][uuid] = ' '.join(cmp_info) 186 | 187 | all_aspects = sorted(ndict, key=natural_sort_key) 188 | if use_variants: 189 | if cust_asp_order: 190 | all_aspects = reorder_aspects(all_aspects, varinfo.aspects()) 191 | if selected: 192 | varsel = varinfo.match_variant(sel) 193 | if long: 194 | Msg().color('var').text(sym_variant()).flush() 195 | else: 196 | Msg().color('var').text(sym_variant()).reset().text(':').flush(end='') 197 | for variant in varinfo.variants(): 198 | p_var = quote_str(variant) 199 | if selected and variant == varsel: 200 | if long: 201 | Msg().text(' + ').color('avar').text(p_var).flush() 202 | else: 203 | Msg().text(' [').color('avar').text(p_var).reset().text(']').flush(end='') 204 | else: 205 | if long: 206 | Msg().text(' ').color('var').text(p_var).flush() 207 | else: 208 | Msg().text(' ').color('var').text(p_var).flush(end='') 209 | if detailed: 210 | choices = varinfo.choices()[variant] 211 | for index, aspect in enumerate(varinfo.aspects()): 212 | Msg().text(' ').color('aspect').text(quote_str(aspect)).reset().text(': ').color('choice').text(choices[index]).flush() 213 | 214 | Msg().flush() 215 | 216 | if not only_variants: 217 | for aspect in all_aspects: 218 | p_aspect = quote_str(aspect) 219 | if long: 220 | m = Msg().color('aspect').text(p_aspect) 221 | if use_variants and aspect in varinfo.aspects(): 222 | m.color('bound').text(sym_variant()) 223 | m.flush() 224 | else: 225 | m = Msg().color('aspect').text(p_aspect) 226 | if use_variants and aspect in varinfo.aspects(): 227 | m.color('bound').text(sym_variant()) 228 | m.reset().text(':').flush(end='') 229 | for choice in sorted(ndict[aspect], key=natural_sort_key): 230 | p_choice = quote_str(choice) 231 | if selected and sel[aspect] == choice: 232 | if long: 233 | Msg().text(' + ').color('achoice').text(p_choice).flush() 234 | else: 235 | Msg().text(' [').color('achoice').text(p_choice).reset().text(']').flush(end='') 236 | else: 237 | if long: 238 | Msg().text(' ').color('choice').text(p_choice).flush() 239 | else: 240 | Msg().text(' ').color('choice').text(p_choice).flush(end='') 241 | if detailed: 242 | for uuid in sorted(ndict[aspect][choice], key=lambda x: natural_sort_key(fpdict[x][Key.REF])): 243 | ref = fpdict[uuid][Key.REF] 244 | Msg().text(' ').color('ref').text(ref).reset().text(': ' + ndict[aspect][choice][uuid]).flush() 245 | for field in sorted(vardict[uuid][Key.FLD], key=natural_sort_key): 246 | f = quote_str(field) 247 | variant = quote_str(vardict[uuid][Key.FLD][field][choice][Key.VALUE]) 248 | Msg().text(' ').color('field').text(f).reset().text(': ').color('fvalue').text(variant).flush() 249 | # Future note: When properties for field scope expressions are allowed, print them here 250 | Msg().flush() 251 | 252 | return True 253 | 254 | def state_command(in_file=None, all=False, query_aspect=None, use_variants=False, only_variants=False, cust_asp_order=False): 255 | b = load_board(in_file) 256 | if b is None: return False 257 | 258 | fpdict = build_fpdict(b) 259 | vardict = build_vardict_wrapper(fpdict, field_ids(b)) 260 | if vardict is None: return False 261 | 262 | if use_variants: 263 | varinfo = load_varinfo_wrapper(in_file, vardict) 264 | if varinfo is None: return False 265 | if not varinfo.is_loaded(): use_variants = False 266 | 267 | sel = detect_current_choices(fpdict, vardict) 268 | if query_aspect is not None: 269 | choices = [] 270 | for qa in query_aspect: 271 | q = cook_raw_string(qa) 272 | if q in sel: 273 | choices.append(quote_str(sel[q])) 274 | else: 275 | ErrMsg().c().text(f"Error: No such aspect '{escape_str(q)}'.").flush() 276 | return False 277 | for c in choices: 278 | Msg().color('choice').text(c).flush() 279 | else: 280 | if use_variants: 281 | varsel = varinfo.match_variant(sel) 282 | p_v = '' if varsel is None else quote_str(varsel) 283 | Msg().color('var').text(sym_variant()).reset().text('=').color('var').text(p_v).flush() 284 | 285 | if not only_variants: 286 | all_aspects = sorted(sel, key=natural_sort_key) 287 | if use_variants and cust_asp_order: 288 | all_aspects = reorder_aspects(all_aspects, varinfo.aspects()) 289 | for aspect in all_aspects: 290 | choice = sel[aspect] 291 | if all or choice is not None: 292 | p_aspect = quote_str(aspect) 293 | p_c = '' if choice is None else quote_str(choice) 294 | Msg().color('aspect').text(p_aspect).reset().text('=').color('choice').text(p_c).flush() 295 | 296 | return True 297 | 298 | def check_command(in_file=None, variants=False, no_variants=False): 299 | b = load_board(in_file) 300 | if b is None: return False 301 | 302 | fpdict = build_fpdict(b) 303 | vardict = build_vardict_wrapper(fpdict, field_ids(b)) 304 | if vardict is None: return False 305 | 306 | sel = detect_current_choices(fpdict, vardict) 307 | 308 | varinfo = load_varinfo_wrapper(in_file, vardict) 309 | if varinfo is None: return False 310 | if variants and not varinfo.is_loaded(): 311 | ErrMsg().c().text("Error: No variant definitions found (omit option '--variants' to skip check).").flush() 312 | return False 313 | 314 | check_variant_match = varinfo.is_loaded() and not no_variants 315 | 316 | failed = [] 317 | for aspect in sorted(sel, key=natural_sort_key): 318 | choice = sel[aspect] 319 | if choice is None: 320 | failed.append(aspect) 321 | 322 | if len(failed) > 0: 323 | Msg().color('fail').text('Check failed.').reset().text(f' No matching choice found for {len(failed)} (of {len(sel)}) aspect(s):').flush() 324 | for aspect in failed: 325 | p_aspect = quote_str(aspect) 326 | Msg().text(' ').color('aspect').text(p_aspect).flush() 327 | return False 328 | 329 | if check_variant_match: 330 | var_match = varinfo.match_variant(sel) 331 | if var_match is None: 332 | Msg().color('fail').text('Check failed.').reset().text(f' No matching variant found for the current set of aspect choices.').flush() 333 | return False 334 | 335 | Msg().color('pass').text('Check passed.').reset().text(f" Matching {'variant and ' if check_variant_match else ''}choices found for complete set of {len(sel)} aspect(s).").flush() 336 | return True 337 | 338 | def set_command(in_file=None, out_file=None, force_save=False, variant=None, assign=None, bound=False, dry_run=False, verbose=False): 339 | if variant is None and assign is None: 340 | ErrMsg().c().text('Error: No variant or aspect assignments passed.').flush() 341 | return False 342 | 343 | b = load_board(in_file) 344 | if b is None: return False 345 | 346 | fpdict = build_fpdict(b) 347 | vardict = build_vardict_wrapper(fpdict, field_ids(b)) 348 | if vardict is None: return False 349 | 350 | sel = detect_current_choices(fpdict, vardict) 351 | 352 | if variant is not None: 353 | varinfo = load_varinfo_wrapper(in_file, vardict) 354 | if varinfo is None: return False 355 | if varinfo.is_loaded(): 356 | if variant not in varinfo.variants(): 357 | ErrMsg().c().text(f"Error: Undefined variant identifier '{escape_str(variant)}'.").flush() 358 | return False 359 | else: 360 | choices = varinfo.choices()[variant] 361 | for aspect_index, aspect in enumerate(varinfo.aspects()): 362 | sel[aspect] = choices[aspect_index] 363 | else: 364 | ErrMsg().c().text("Error: Cannot set variant without a valid variant definition table.").flush() 365 | return False 366 | 367 | if assign is not None: 368 | choice_dict = get_choice_dict(vardict) 369 | for asmt in assign: 370 | l = split_raw_str(asmt, '=', False) 371 | if len(l) == 2: 372 | aspect = cook_raw_string(l[0]) 373 | choice = cook_raw_string(l[1]) 374 | p_aspect = escape_str(aspect) 375 | p_choice = escape_str(choice) 376 | p_asmt = f'{p_aspect}={p_choice}' 377 | if aspect in sel: 378 | if not (variant is not None and aspect in varinfo.aspects() and not bound): 379 | if choice in choice_dict[aspect]: 380 | sel[aspect] = choice 381 | else: 382 | ErrMsg().c().text(f"Error: Assignment '{p_asmt}' failed: No such choice '{escape_str(p_choice)}' for aspect '{escape_str(p_aspect)}'.").flush() 383 | return False 384 | else: 385 | ErrMsg().c().text(f"Error: Overriding choices of bound aspect '{escape_str(aspect)}' is forbidden (use option '--bound' to allow).").flush() 386 | return False 387 | else: 388 | ErrMsg().c().text(f"Error: Assignment '{p_asmt}' failed: No such aspect '{escape_str(p_aspect)}'.").flush() 389 | return False 390 | else: 391 | ErrMsg().c().text(f"Error: Assignment '{asmt}' failed: Format error: Wrong number of '=' separators.").flush() 392 | return False 393 | 394 | changes = apply_selection(fpdict, vardict, sel, dry_run=False) 395 | if verbose: # or dry_run: 396 | if changes: 397 | Msg().text(f'Changes ({len(changes)}):').flush() 398 | for uuid, order, change in sorted(changes, key=lambda x: natural_sort_key(x[1])): 399 | Msg().text(' ').color('mod').text(change).flush() 400 | else: 401 | Msg().text('No changes.').flush() 402 | 403 | if not dry_run and (changes or force_save): 404 | store_fpdict(b, fpdict) 405 | if save_board(out_file, b): 406 | if verbose: 407 | Msg().text(f"Board saved to file '{out_file}'.").flush() 408 | else: 409 | ErrMsg().c().text(f"Error: Failed to save board to file '{out_file}'.").flush() 410 | return False 411 | 412 | return True 413 | 414 | def main(): 415 | global no_color 416 | parser = argparse.ArgumentParser(description=f"KiVar Command Line Interface ({version()})") 417 | parser.add_argument("-n", "--no-color", action="store_true", help="suppress output coloring (default for stdout redirection)") 418 | parser.add_argument( "--version", action="store_true", help="print version information and exit") 419 | subparsers = parser.add_subparsers(dest="command") 420 | 421 | list_parser = subparsers.add_parser("list", help="list all available aspects and choices") 422 | list_parser.add_argument("-V", "--variants", action="store_true", help="variant information only, skip aspect list") 423 | list_parser.add_argument("-N", "--no-variants", action="store_true", help="suppress variant information") 424 | list_parser.add_argument("-O", "--std-order", action="store_true", help="ignore aspect order from variant table") 425 | list_parser.add_argument("-s", "--selection", action="store_true", help="mark currently matching choices") 426 | list_parser.add_argument("-l", "--long", action="store_true", help="long output style") 427 | list_parser.add_argument("-d", "--detailed", action="store_true", help="show component assignments (implies --long)") 428 | list_parser.add_argument("-c", "--prop-codes", action="store_true", help="display property codes (implies --detailed)") 429 | list_parser.add_argument("pcb", help="input KiCad PCB file name") 430 | 431 | state_parser = subparsers.add_parser("state", help="show currently matching choice for each aspect") 432 | state_parser.add_argument("-V", "--variants", action="store_true", help="variant information only, skip aspect list") 433 | state_parser.add_argument("-N", "--no-variants", action="store_true", help="suppress variant information") 434 | state_parser.add_argument("-O", "--std-order", action="store_true", help="ignore aspect order from variant table") 435 | state_parser.add_argument("-Q", "--query", action="append", help="add aspect to query for matching choice", metavar="aspect") 436 | state_parser.add_argument("-a", "--all", action="store_true", help="list all aspects (default: list only aspects with matching choice)") 437 | state_parser.add_argument("pcb", help="input KiCad PCB file name") 438 | 439 | check_parser = subparsers.add_parser("check", help="check variants and/or aspects for matching choices, exit with error if check fails") 440 | check_parser.add_argument("-V", "--variants", action="store_true", help="fail if no variant definition table exists") 441 | check_parser.add_argument("-N", "--no-variants", action="store_true", help="only check aspects, not variants") 442 | check_parser.add_argument("pcb", help="input KiCad PCB file name") 443 | 444 | set_parser = subparsers.add_parser("set", help="assign choices to aspects") 445 | set_parser.add_argument("-V", "--variant", action="append", help="switch to specific variant", metavar="str") 446 | set_parser.add_argument("-A", "--assign", action="append", help="assign choice to aspect ('str' format: \"aspect=choice\")", metavar="str") 447 | set_parser.add_argument("-b", "--bound", action="store_true", help="allow overriding bound aspects (applied after variant)") 448 | set_parser.add_argument("-D", "--dry-run", action="store_true", help="only print assignments, do not really perform/save them") 449 | set_parser.add_argument("-o", "--output", action="append", help="override output file name and force saving", metavar="out_pcb") 450 | set_parser.add_argument("-v", "--verbose", action="store_true", help="verbose output") 451 | set_parser.add_argument("pcb", help="input (and default output) KiCad PCB file name") 452 | 453 | args = parser.parse_args() 454 | no_color = args.no_color 455 | exitcode = 0 456 | 457 | if load_errors is not None: 458 | for e in load_errors: ErrMsg().c().text(e).flush() 459 | exitcode = 4 460 | elif args.version: 461 | Msg().text(f"KiVar {version()} (using pcbnew {pcbnew.Version()})").flush() 462 | exitcode = 0 463 | else: 464 | # TODO do this check only prior to executing corresponding commands? 465 | compatibility_problem = pcbnew_compatibility_error() 466 | if compatibility_problem is not None: 467 | ErrMsg().c().text('Compatibility error:').flush() 468 | ErrMsg().text(compatibility_problem).flush() 469 | exitcode = 3 470 | else: 471 | cmd = args.command 472 | if cmd == "list": 473 | if args.prop_codes: args.detailed = True 474 | if args.detailed: args.long = True 475 | if args.variants and args.no_variants: 476 | ErrMsg().c().text("Error: Options '--variants' and '--no-variants' are mutually exclusive.").flush() 477 | exitcode = 5 478 | elif not list_command(in_file=args.pcb, long=args.long, prop_codes=args.prop_codes, detailed=args.detailed, selected=args.selection, use_variants=not args.no_variants, only_variants=args.variants, cust_asp_order=not args.std_order): exitcode = 1 479 | elif cmd == "state": 480 | if args.all and args.query_aspect: 481 | ErrMsg().c().text("Error: Options '--all' and '--query' are mutually exclusive.").flush() 482 | exitcode = 5 483 | elif not state_command(in_file=args.pcb, all=args.all, query_aspect=args.query, use_variants=not args.no_variants, only_variants=args.variants, cust_asp_order=not args.std_order): exitcode = 1 484 | elif cmd == "check": 485 | if not check_command(in_file=args.pcb, variants=args.variants, no_variants=args.no_variants): exitcode = 1 486 | elif cmd == "set": 487 | if args.output is not None and len(args.output) > 1: 488 | ErrMsg().c().text("Error: Option '--output' cannot be used multiple times.").flush() 489 | exitcode = 5 490 | elif args.variant is not None and len(args.variant) > 1: 491 | ErrMsg().c().text("Error: Option '--variant' cannot be used multiple times.").flush() 492 | exitcode = 5 493 | else: 494 | if args.output is not None and len(args.output) == 1: 495 | out_file = args.output[0] 496 | force_save = True 497 | else: 498 | out_file = args.pcb 499 | force_save = False 500 | variant = None if args.variant is None else args.variant[0] 501 | if not set_command(in_file=args.pcb, out_file=out_file, force_save=force_save, variant=variant, assign=args.assign, bound=args.bound, dry_run=args.dry_run, verbose=args.verbose): exitcode = 1 502 | else: 503 | Msg().text(f'This is the KiVar CLI, version {version()}.').flush() 504 | parser.print_usage() 505 | exitcode = 2 506 | 507 | return exitcode 508 | 509 | try: import pcbnew 510 | except ModuleNotFoundError as e: 511 | load_errors = [f"Error: The '{e.name}' Python module cannot be loaded.", "Please ensure that KiCad is installed and that your Python environment is configured to access KiCad's scripting modules."] 512 | 513 | if __name__ == "__main__": 514 | sys.exit(main()) 515 | -------------------------------------------------------------------------------- /demo/example_3.kicad_sch: -------------------------------------------------------------------------------- 1 | (kicad_sch 2 | (version 20231120) 3 | (generator "eeschema") 4 | (generator_version "8.0") 5 | (uuid "217d3891-8c7f-461d-9bac-b46f12d24a00") 6 | (paper "A5") 7 | (title_block 8 | (title "KiVar Demo") 9 | (date "2025-04-30") 10 | (rev "0.5.0") 11 | (company "Author: Mark Hämmerling ") 12 | (comment 1 "https://kivar.markh.de") 13 | (comment 4 "Undervoltage Trip Points") 14 | ) 15 | (lib_symbols 16 | (symbol "Device:C" 17 | (pin_numbers hide) 18 | (pin_names 19 | (offset 0.254) 20 | ) 21 | (exclude_from_sim no) 22 | (in_bom yes) 23 | (on_board yes) 24 | (property "Reference" "C" 25 | (at 0.635 2.54 0) 26 | (effects 27 | (font 28 | (size 1.27 1.27) 29 | ) 30 | (justify left) 31 | ) 32 | ) 33 | (property "Value" "C" 34 | (at 0.635 -2.54 0) 35 | (effects 36 | (font 37 | (size 1.27 1.27) 38 | ) 39 | (justify left) 40 | ) 41 | ) 42 | (property "Footprint" "" 43 | (at 0.9652 -3.81 0) 44 | (effects 45 | (font 46 | (size 1.27 1.27) 47 | ) 48 | (hide yes) 49 | ) 50 | ) 51 | (property "Datasheet" "~" 52 | (at 0 0 0) 53 | (effects 54 | (font 55 | (size 1.27 1.27) 56 | ) 57 | (hide yes) 58 | ) 59 | ) 60 | (property "Description" "Unpolarized capacitor" 61 | (at 0 0 0) 62 | (effects 63 | (font 64 | (size 1.27 1.27) 65 | ) 66 | (hide yes) 67 | ) 68 | ) 69 | (property "ki_keywords" "cap capacitor" 70 | (at 0 0 0) 71 | (effects 72 | (font 73 | (size 1.27 1.27) 74 | ) 75 | (hide yes) 76 | ) 77 | ) 78 | (property "ki_fp_filters" "C_*" 79 | (at 0 0 0) 80 | (effects 81 | (font 82 | (size 1.27 1.27) 83 | ) 84 | (hide yes) 85 | ) 86 | ) 87 | (symbol "C_0_1" 88 | (polyline 89 | (pts 90 | (xy -2.032 -0.762) (xy 2.032 -0.762) 91 | ) 92 | (stroke 93 | (width 0.508) 94 | (type default) 95 | ) 96 | (fill 97 | (type none) 98 | ) 99 | ) 100 | (polyline 101 | (pts 102 | (xy -2.032 0.762) (xy 2.032 0.762) 103 | ) 104 | (stroke 105 | (width 0.508) 106 | (type default) 107 | ) 108 | (fill 109 | (type none) 110 | ) 111 | ) 112 | ) 113 | (symbol "C_1_1" 114 | (pin passive line 115 | (at 0 3.81 270) 116 | (length 2.794) 117 | (name "~" 118 | (effects 119 | (font 120 | (size 1.27 1.27) 121 | ) 122 | ) 123 | ) 124 | (number "1" 125 | (effects 126 | (font 127 | (size 1.27 1.27) 128 | ) 129 | ) 130 | ) 131 | ) 132 | (pin passive line 133 | (at 0 -3.81 90) 134 | (length 2.794) 135 | (name "~" 136 | (effects 137 | (font 138 | (size 1.27 1.27) 139 | ) 140 | ) 141 | ) 142 | (number "2" 143 | (effects 144 | (font 145 | (size 1.27 1.27) 146 | ) 147 | ) 148 | ) 149 | ) 150 | ) 151 | ) 152 | (symbol "Device:R" 153 | (pin_numbers hide) 154 | (pin_names 155 | (offset 0) 156 | ) 157 | (exclude_from_sim no) 158 | (in_bom yes) 159 | (on_board yes) 160 | (property "Reference" "R" 161 | (at 2.032 0 90) 162 | (effects 163 | (font 164 | (size 1.27 1.27) 165 | ) 166 | ) 167 | ) 168 | (property "Value" "R" 169 | (at 0 0 90) 170 | (effects 171 | (font 172 | (size 1.27 1.27) 173 | ) 174 | ) 175 | ) 176 | (property "Footprint" "" 177 | (at -1.778 0 90) 178 | (effects 179 | (font 180 | (size 1.27 1.27) 181 | ) 182 | (hide yes) 183 | ) 184 | ) 185 | (property "Datasheet" "~" 186 | (at 0 0 0) 187 | (effects 188 | (font 189 | (size 1.27 1.27) 190 | ) 191 | (hide yes) 192 | ) 193 | ) 194 | (property "Description" "Resistor" 195 | (at 0 0 0) 196 | (effects 197 | (font 198 | (size 1.27 1.27) 199 | ) 200 | (hide yes) 201 | ) 202 | ) 203 | (property "ki_keywords" "R res resistor" 204 | (at 0 0 0) 205 | (effects 206 | (font 207 | (size 1.27 1.27) 208 | ) 209 | (hide yes) 210 | ) 211 | ) 212 | (property "ki_fp_filters" "R_*" 213 | (at 0 0 0) 214 | (effects 215 | (font 216 | (size 1.27 1.27) 217 | ) 218 | (hide yes) 219 | ) 220 | ) 221 | (symbol "R_0_1" 222 | (rectangle 223 | (start -1.016 -2.54) 224 | (end 1.016 2.54) 225 | (stroke 226 | (width 0.254) 227 | (type default) 228 | ) 229 | (fill 230 | (type none) 231 | ) 232 | ) 233 | ) 234 | (symbol "R_1_1" 235 | (pin passive line 236 | (at 0 3.81 270) 237 | (length 1.27) 238 | (name "~" 239 | (effects 240 | (font 241 | (size 1.27 1.27) 242 | ) 243 | ) 244 | ) 245 | (number "1" 246 | (effects 247 | (font 248 | (size 1.27 1.27) 249 | ) 250 | ) 251 | ) 252 | ) 253 | (pin passive line 254 | (at 0 -3.81 90) 255 | (length 1.27) 256 | (name "~" 257 | (effects 258 | (font 259 | (size 1.27 1.27) 260 | ) 261 | ) 262 | ) 263 | (number "2" 264 | (effects 265 | (font 266 | (size 1.27 1.27) 267 | ) 268 | ) 269 | ) 270 | ) 271 | ) 272 | ) 273 | (symbol "markh-de:MIC841HYC5" 274 | (exclude_from_sim no) 275 | (in_bom yes) 276 | (on_board yes) 277 | (property "Reference" "U" 278 | (at -7.62 7.62 0) 279 | (effects 280 | (font 281 | (size 1.27 1.27) 282 | ) 283 | (justify left) 284 | ) 285 | ) 286 | (property "Value" "MIC841HYC5" 287 | (at 1.27 7.62 0) 288 | (effects 289 | (font 290 | (size 1.27 1.27) 291 | ) 292 | (justify left) 293 | ) 294 | ) 295 | (property "Footprint" "Package_TO_SOT_SMD:SOT-353_SC-70-5" 296 | (at 2.54 -7.62 0) 297 | (effects 298 | (font 299 | (size 1.27 1.27) 300 | ) 301 | (justify left) 302 | (hide yes) 303 | ) 304 | ) 305 | (property "Datasheet" "https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005758A.pdf" 306 | (at 0 -21.59 0) 307 | (effects 308 | (font 309 | (size 1.27 1.27) 310 | ) 311 | (hide yes) 312 | ) 313 | ) 314 | (property "Description" "Comparator with 1.25% Reference and Adjustable Hysteresis, SC-70-5" 315 | (at 0 0 0) 316 | (effects 317 | (font 318 | (size 1.27 1.27) 319 | ) 320 | (hide yes) 321 | ) 322 | ) 323 | (property "ki_keywords" "Supervisor Reset" 324 | (at 0 0 0) 325 | (effects 326 | (font 327 | (size 1.27 1.27) 328 | ) 329 | (hide yes) 330 | ) 331 | ) 332 | (property "ki_fp_filters" "SOT*353*" 333 | (at 0 0 0) 334 | (effects 335 | (font 336 | (size 1.27 1.27) 337 | ) 338 | (hide yes) 339 | ) 340 | ) 341 | (symbol "MIC841HYC5_0_1" 342 | (rectangle 343 | (start -7.62 6.35) 344 | (end 7.62 -6.35) 345 | (stroke 346 | (width 0.254) 347 | (type default) 348 | ) 349 | (fill 350 | (type background) 351 | ) 352 | ) 353 | ) 354 | (symbol "MIC841HYC5_1_1" 355 | (pin input line 356 | (at -10.16 -3.81 0) 357 | (length 2.54) 358 | (name "HTH" 359 | (effects 360 | (font 361 | (size 1.27 1.27) 362 | ) 363 | ) 364 | ) 365 | (number "1" 366 | (effects 367 | (font 368 | (size 1.27 1.27) 369 | ) 370 | ) 371 | ) 372 | ) 373 | (pin power_in line 374 | (at 0 -8.89 90) 375 | (length 2.54) 376 | (name "GND" 377 | (effects 378 | (font 379 | (size 1.27 1.27) 380 | ) 381 | ) 382 | ) 383 | (number "2" 384 | (effects 385 | (font 386 | (size 1.27 1.27) 387 | ) 388 | ) 389 | ) 390 | ) 391 | (pin input line 392 | (at -10.16 3.81 0) 393 | (length 2.54) 394 | (name "LTH" 395 | (effects 396 | (font 397 | (size 1.27 1.27) 398 | ) 399 | ) 400 | ) 401 | (number "3" 402 | (effects 403 | (font 404 | (size 1.27 1.27) 405 | ) 406 | ) 407 | ) 408 | ) 409 | (pin output line 410 | (at 10.16 3.81 180) 411 | (length 2.54) 412 | (name "OUT" 413 | (effects 414 | (font 415 | (size 1.27 1.27) 416 | ) 417 | ) 418 | ) 419 | (number "4" 420 | (effects 421 | (font 422 | (size 1.27 1.27) 423 | ) 424 | ) 425 | ) 426 | ) 427 | (pin power_in line 428 | (at 0 8.89 270) 429 | (length 2.54) 430 | (name "VDD" 431 | (effects 432 | (font 433 | (size 1.27 1.27) 434 | ) 435 | ) 436 | ) 437 | (number "5" 438 | (effects 439 | (font 440 | (size 1.27 1.27) 441 | ) 442 | ) 443 | ) 444 | ) 445 | ) 446 | ) 447 | (symbol "power:+BATT" 448 | (power) 449 | (pin_names 450 | (offset 0) 451 | ) 452 | (exclude_from_sim no) 453 | (in_bom yes) 454 | (on_board yes) 455 | (property "Reference" "#PWR" 456 | (at 0 -3.81 0) 457 | (effects 458 | (font 459 | (size 1.27 1.27) 460 | ) 461 | (hide yes) 462 | ) 463 | ) 464 | (property "Value" "+BATT" 465 | (at 0 3.556 0) 466 | (effects 467 | (font 468 | (size 1.27 1.27) 469 | ) 470 | ) 471 | ) 472 | (property "Footprint" "" 473 | (at 0 0 0) 474 | (effects 475 | (font 476 | (size 1.27 1.27) 477 | ) 478 | (hide yes) 479 | ) 480 | ) 481 | (property "Datasheet" "" 482 | (at 0 0 0) 483 | (effects 484 | (font 485 | (size 1.27 1.27) 486 | ) 487 | (hide yes) 488 | ) 489 | ) 490 | (property "Description" "Power symbol creates a global label with name \"+BATT\"" 491 | (at 0 0 0) 492 | (effects 493 | (font 494 | (size 1.27 1.27) 495 | ) 496 | (hide yes) 497 | ) 498 | ) 499 | (property "ki_keywords" "global power battery" 500 | (at 0 0 0) 501 | (effects 502 | (font 503 | (size 1.27 1.27) 504 | ) 505 | (hide yes) 506 | ) 507 | ) 508 | (symbol "+BATT_0_1" 509 | (polyline 510 | (pts 511 | (xy -0.762 1.27) (xy 0 2.54) 512 | ) 513 | (stroke 514 | (width 0) 515 | (type default) 516 | ) 517 | (fill 518 | (type none) 519 | ) 520 | ) 521 | (polyline 522 | (pts 523 | (xy 0 0) (xy 0 2.54) 524 | ) 525 | (stroke 526 | (width 0) 527 | (type default) 528 | ) 529 | (fill 530 | (type none) 531 | ) 532 | ) 533 | (polyline 534 | (pts 535 | (xy 0 2.54) (xy 0.762 1.27) 536 | ) 537 | (stroke 538 | (width 0) 539 | (type default) 540 | ) 541 | (fill 542 | (type none) 543 | ) 544 | ) 545 | ) 546 | (symbol "+BATT_1_1" 547 | (pin power_in line 548 | (at 0 0 90) 549 | (length 0) hide 550 | (name "+BATT" 551 | (effects 552 | (font 553 | (size 1.27 1.27) 554 | ) 555 | ) 556 | ) 557 | (number "1" 558 | (effects 559 | (font 560 | (size 1.27 1.27) 561 | ) 562 | ) 563 | ) 564 | ) 565 | ) 566 | ) 567 | (symbol "power:GND" 568 | (power) 569 | (pin_names 570 | (offset 0) 571 | ) 572 | (exclude_from_sim no) 573 | (in_bom yes) 574 | (on_board yes) 575 | (property "Reference" "#PWR" 576 | (at 0 -6.35 0) 577 | (effects 578 | (font 579 | (size 1.27 1.27) 580 | ) 581 | (hide yes) 582 | ) 583 | ) 584 | (property "Value" "GND" 585 | (at 0 -3.81 0) 586 | (effects 587 | (font 588 | (size 1.27 1.27) 589 | ) 590 | ) 591 | ) 592 | (property "Footprint" "" 593 | (at 0 0 0) 594 | (effects 595 | (font 596 | (size 1.27 1.27) 597 | ) 598 | (hide yes) 599 | ) 600 | ) 601 | (property "Datasheet" "" 602 | (at 0 0 0) 603 | (effects 604 | (font 605 | (size 1.27 1.27) 606 | ) 607 | (hide yes) 608 | ) 609 | ) 610 | (property "Description" "Power symbol creates a global label with name \"GND\" , ground" 611 | (at 0 0 0) 612 | (effects 613 | (font 614 | (size 1.27 1.27) 615 | ) 616 | (hide yes) 617 | ) 618 | ) 619 | (property "ki_keywords" "global power" 620 | (at 0 0 0) 621 | (effects 622 | (font 623 | (size 1.27 1.27) 624 | ) 625 | (hide yes) 626 | ) 627 | ) 628 | (symbol "GND_0_1" 629 | (polyline 630 | (pts 631 | (xy 0 0) (xy 0 -1.27) (xy 1.27 -1.27) (xy 0 -2.54) (xy -1.27 -1.27) (xy 0 -1.27) 632 | ) 633 | (stroke 634 | (width 0) 635 | (type default) 636 | ) 637 | (fill 638 | (type none) 639 | ) 640 | ) 641 | ) 642 | (symbol "GND_1_1" 643 | (pin power_in line 644 | (at 0 0 270) 645 | (length 0) hide 646 | (name "GND" 647 | (effects 648 | (font 649 | (size 1.27 1.27) 650 | ) 651 | ) 652 | ) 653 | (number "1" 654 | (effects 655 | (font 656 | (size 1.27 1.27) 657 | ) 658 | ) 659 | ) 660 | ) 661 | ) 662 | ) 663 | ) 664 | (junction 665 | (at 123.825 46.99) 666 | (diameter 0) 667 | (color 0 0 0 0) 668 | (uuid "37270815-a413-4f9e-a9ad-9710377555d1") 669 | ) 670 | (junction 671 | (at 108.585 72.39) 672 | (diameter 0) 673 | (color 0 0 0 0) 674 | (uuid "8eadf351-3157-4be6-8a67-9631d91c3423") 675 | ) 676 | (junction 677 | (at 108.585 64.77) 678 | (diameter 0) 679 | (color 0 0 0 0) 680 | (uuid "bc2d2e3e-081c-454a-857f-553eb10b94e6") 681 | ) 682 | (wire 683 | (pts 684 | (xy 127.635 46.99) (xy 127.635 59.69) 685 | ) 686 | (stroke 687 | (width 0) 688 | (type default) 689 | ) 690 | (uuid "0b571bd6-c365-4c62-9af7-1dab80fcab94") 691 | ) 692 | (wire 693 | (pts 694 | (xy 123.825 46.99) (xy 127.635 46.99) 695 | ) 696 | (stroke 697 | (width 0) 698 | (type default) 699 | ) 700 | (uuid "20226eae-1736-4f48-914d-6ffbde093c1f") 701 | ) 702 | (wire 703 | (pts 704 | (xy 108.585 63.5) (xy 108.585 64.77) 705 | ) 706 | (stroke 707 | (width 0) 708 | (type default) 709 | ) 710 | (uuid "3441d1ec-b7d4-4091-ac63-86a6b00563cd") 711 | ) 712 | (wire 713 | (pts 714 | (xy 121.285 46.99) (xy 123.825 46.99) 715 | ) 716 | (stroke 717 | (width 0) 718 | (type default) 719 | ) 720 | (uuid "55801b71-6b23-4c44-95ea-430fe42adc35") 721 | ) 722 | (wire 723 | (pts 724 | (xy 108.585 54.61) (xy 108.585 55.88) 725 | ) 726 | (stroke 727 | (width 0) 728 | (type default) 729 | ) 730 | (uuid "655f4d28-6274-4148-a4f2-a7746e2d4418") 731 | ) 732 | (wire 733 | (pts 734 | (xy 108.585 72.39) (xy 117.475 72.39) 735 | ) 736 | (stroke 737 | (width 0) 738 | (type default) 739 | ) 740 | (uuid "9d1bc960-cfdf-452a-8fe6-f58aa8816210") 741 | ) 742 | (wire 743 | (pts 744 | (xy 108.585 73.66) (xy 108.585 72.39) 745 | ) 746 | (stroke 747 | (width 0) 748 | (type default) 749 | ) 750 | (uuid "b597590a-64d8-4804-9243-4c138e04a54f") 751 | ) 752 | (wire 753 | (pts 754 | (xy 108.585 64.77) (xy 117.475 64.77) 755 | ) 756 | (stroke 757 | (width 0) 758 | (type default) 759 | ) 760 | (uuid "c47ef0de-8f91-40c2-8072-b3d2818ddb3e") 761 | ) 762 | (wire 763 | (pts 764 | (xy 147.955 64.77) (xy 137.795 64.77) 765 | ) 766 | (stroke 767 | (width 0) 768 | (type default) 769 | ) 770 | (uuid "f20e13a4-d413-4cd5-a358-3d0a125e3250") 771 | ) 772 | (rectangle 773 | (start 59.69 38.1) 774 | (end 152.4 90.17) 775 | (stroke 776 | (width 0.254) 777 | (type default) 778 | (color 72 72 72 1) 779 | ) 780 | (fill 781 | (type none) 782 | ) 783 | (uuid a4399393-0544-4dcc-a512-81fadfd19879) 784 | ) 785 | (text "Figure" 786 | (exclude_from_sim no) 787 | (at 59.69 91.44 0) 788 | (effects 789 | (font 790 | (size 1.27 1.27) 791 | (italic yes) 792 | (color 72 72 72 1) 793 | ) 794 | (justify left top) 795 | ) 796 | (uuid "856563cf-5e8a-458d-b4f7-44be78042fa0") 797 | ) 798 | (label "EN_+3V3" 799 | (at 147.955 64.77 180) 800 | (effects 801 | (font 802 | (size 1.27 1.27) 803 | ) 804 | (justify right bottom) 805 | ) 806 | (uuid "5b90c8b2-09d6-4b8e-bbf1-b6630c582025") 807 | ) 808 | (label "LTH" 809 | (at 112.395 64.77 0) 810 | (effects 811 | (font 812 | (size 1.27 1.27) 813 | ) 814 | (justify left bottom) 815 | ) 816 | (uuid "736f2067-c51d-4ba7-a45c-a6b5fd4a6fce") 817 | ) 818 | (label "HTH" 819 | (at 112.395 72.39 0) 820 | (effects 821 | (font 822 | (size 1.27 1.27) 823 | ) 824 | (justify left bottom) 825 | ) 826 | (uuid "9c9f3292-e7fe-4a33-b43d-392999d57983") 827 | ) 828 | (symbol 829 | (lib_id "power:GND") 830 | (at 123.825 54.61 0) 831 | (unit 1) 832 | (exclude_from_sim no) 833 | (in_bom yes) 834 | (on_board yes) 835 | (dnp no) 836 | (fields_autoplaced yes) 837 | (uuid "154e06a6-dfe5-4634-8bfe-08521f4aa95e") 838 | (property "Reference" "#PWR011" 839 | (at 123.825 60.96 0) 840 | (effects 841 | (font 842 | (size 1.27 1.27) 843 | ) 844 | (hide yes) 845 | ) 846 | ) 847 | (property "Value" "GND" 848 | (at 123.825 59.69 0) 849 | (effects 850 | (font 851 | (size 1.27 1.27) 852 | ) 853 | ) 854 | ) 855 | (property "Footprint" "" 856 | (at 123.825 54.61 0) 857 | (effects 858 | (font 859 | (size 1.27 1.27) 860 | ) 861 | (hide yes) 862 | ) 863 | ) 864 | (property "Datasheet" "" 865 | (at 123.825 54.61 0) 866 | (effects 867 | (font 868 | (size 1.27 1.27) 869 | ) 870 | (hide yes) 871 | ) 872 | ) 873 | (property "Description" "" 874 | (at 123.825 54.61 0) 875 | (effects 876 | (font 877 | (size 1.27 1.27) 878 | ) 879 | (hide yes) 880 | ) 881 | ) 882 | (pin "1" 883 | (uuid "9da0876a-a854-4798-ac53-7f77d8c4a0bc") 884 | ) 885 | (instances 886 | (project "kivar-demo" 887 | (path "/cd745a5c-45fb-4106-85c8-c86c130b6ee7/98917921-089a-4f53-9391-423838589d96" 888 | (reference "#PWR011") 889 | (unit 1) 890 | ) 891 | ) 892 | ) 893 | ) 894 | (symbol 895 | (lib_id "power:+BATT") 896 | (at 121.285 46.99 0) 897 | (unit 1) 898 | (exclude_from_sim no) 899 | (in_bom yes) 900 | (on_board yes) 901 | (dnp no) 902 | (fields_autoplaced yes) 903 | (uuid "211a1472-7fa1-4347-b30f-450951f86d98") 904 | (property "Reference" "#PWR033" 905 | (at 121.285 50.8 0) 906 | (effects 907 | (font 908 | (size 1.27 1.27) 909 | ) 910 | (hide yes) 911 | ) 912 | ) 913 | (property "Value" "+BATT" 914 | (at 121.285 42.545 0) 915 | (effects 916 | (font 917 | (size 1.27 1.27) 918 | ) 919 | ) 920 | ) 921 | (property "Footprint" "" 922 | (at 121.285 46.99 0) 923 | (effects 924 | (font 925 | (size 1.27 1.27) 926 | ) 927 | (hide yes) 928 | ) 929 | ) 930 | (property "Datasheet" "" 931 | (at 121.285 46.99 0) 932 | (effects 933 | (font 934 | (size 1.27 1.27) 935 | ) 936 | (hide yes) 937 | ) 938 | ) 939 | (property "Description" "Power symbol creates a global label with name \"+BATT\"" 940 | (at 121.285 46.99 0) 941 | (effects 942 | (font 943 | (size 1.27 1.27) 944 | ) 945 | (hide yes) 946 | ) 947 | ) 948 | (pin "1" 949 | (uuid "a82d8bc0-b5f2-4747-aac9-615596822269") 950 | ) 951 | (instances 952 | (project "kivar-demo" 953 | (path "/cd745a5c-45fb-4106-85c8-c86c130b6ee7/98917921-089a-4f53-9391-423838589d96" 954 | (reference "#PWR033") 955 | (unit 1) 956 | ) 957 | ) 958 | ) 959 | ) 960 | (symbol 961 | (lib_id "Device:R") 962 | (at 108.585 68.58 0) 963 | (unit 1) 964 | (exclude_from_sim no) 965 | (in_bom yes) 966 | (on_board yes) 967 | (dnp no) 968 | (fields_autoplaced yes) 969 | (uuid "2ccc51d3-0eb9-4181-909b-6b48a6a96e2e") 970 | (property "Reference" "R14" 971 | (at 106.68 66.0399 0) 972 | (effects 973 | (font 974 | (size 1.27 1.27) 975 | ) 976 | (justify right) 977 | ) 978 | ) 979 | (property "Value" "100kΩ" 980 | (at 106.68 68.5799 0) 981 | (effects 982 | (font 983 | (size 1.27 1.27) 984 | ) 985 | (justify right) 986 | ) 987 | ) 988 | (property "Footprint" "Resistor_SMD:R_0402_1005Metric" 989 | (at 106.807 68.58 90) 990 | (effects 991 | (font 992 | (size 1.27 1.27) 993 | ) 994 | (hide yes) 995 | ) 996 | ) 997 | (property "Datasheet" "~" 998 | (at 108.585 68.58 0) 999 | (effects 1000 | (font 1001 | (size 1.27 1.27) 1002 | ) 1003 | (hide yes) 1004 | ) 1005 | ) 1006 | (property "Description" "" 1007 | (at 108.585 68.58 0) 1008 | (effects 1009 | (font 1010 | (size 1.27 1.27) 1011 | ) 1012 | (hide yes) 1013 | ) 1014 | ) 1015 | (property "Var.Aspect" "UVLO_LO/HI" 1016 | (at 108.585 68.58 0) 1017 | (effects 1018 | (font 1019 | (size 1.27 1.27) 1020 | ) 1021 | (hide yes) 1022 | ) 1023 | ) 1024 | (property "Var" "'2.41V/3.40V'(309kΩ) '3.15V/3.57V'(100kΩ)" 1025 | (at 106.68 71.1199 0) 1026 | (effects 1027 | (font 1028 | (size 1.27 1.27) 1029 | (italic yes) 1030 | ) 1031 | (justify right) 1032 | ) 1033 | ) 1034 | (pin "1" 1035 | (uuid "fdc06558-85a4-4a6b-b97c-56890d55d2b8") 1036 | ) 1037 | (pin "2" 1038 | (uuid "7e201883-41cb-45ad-9e4d-b9fc39c0bd09") 1039 | ) 1040 | (instances 1041 | (project "kivar-demo" 1042 | (path "/cd745a5c-45fb-4106-85c8-c86c130b6ee7/98917921-089a-4f53-9391-423838589d96" 1043 | (reference "R14") 1044 | (unit 1) 1045 | ) 1046 | ) 1047 | ) 1048 | ) 1049 | (symbol 1050 | (lib_id "markh-de:MIC841HYC5") 1051 | (at 127.635 68.58 0) 1052 | (unit 1) 1053 | (exclude_from_sim no) 1054 | (in_bom yes) 1055 | (on_board yes) 1056 | (dnp no) 1057 | (fields_autoplaced yes) 1058 | (uuid "31167cc4-bc00-4641-b31e-e5cb8e47b998") 1059 | (property "Reference" "U2" 1060 | (at 129.8291 57.15 0) 1061 | (effects 1062 | (font 1063 | (size 1.27 1.27) 1064 | ) 1065 | (justify left) 1066 | ) 1067 | ) 1068 | (property "Value" "MIC841HYC5" 1069 | (at 129.8291 59.69 0) 1070 | (effects 1071 | (font 1072 | (size 1.27 1.27) 1073 | ) 1074 | (justify left) 1075 | ) 1076 | ) 1077 | (property "Footprint" "Package_TO_SOT_SMD:SOT-353_SC-70-5" 1078 | (at 130.175 76.2 0) 1079 | (effects 1080 | (font 1081 | (size 1.27 1.27) 1082 | ) 1083 | (justify left) 1084 | (hide yes) 1085 | ) 1086 | ) 1087 | (property "Datasheet" "https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005758A.pdf" 1088 | (at 127.635 90.17 0) 1089 | (effects 1090 | (font 1091 | (size 1.27 1.27) 1092 | ) 1093 | (hide yes) 1094 | ) 1095 | ) 1096 | (property "Description" "" 1097 | (at 127.635 68.58 0) 1098 | (effects 1099 | (font 1100 | (size 1.27 1.27) 1101 | ) 1102 | (hide yes) 1103 | ) 1104 | ) 1105 | (pin "1" 1106 | (uuid "f71b1110-7194-4cf5-a7b1-511ad60f2f61") 1107 | ) 1108 | (pin "2" 1109 | (uuid "fec43831-82b6-4fe3-8c1f-6c7f9d849072") 1110 | ) 1111 | (pin "3" 1112 | (uuid "bc796006-bb81-4337-bc56-a56a283b3143") 1113 | ) 1114 | (pin "4" 1115 | (uuid "a63a3177-12d6-4cee-8fa7-e371359b399f") 1116 | ) 1117 | (pin "5" 1118 | (uuid "e3c0ecaf-2407-4d3c-9ed1-ef033bb9811e") 1119 | ) 1120 | (instances 1121 | (project "kivar-demo" 1122 | (path "/cd745a5c-45fb-4106-85c8-c86c130b6ee7/98917921-089a-4f53-9391-423838589d96" 1123 | (reference "U2") 1124 | (unit 1) 1125 | ) 1126 | ) 1127 | ) 1128 | ) 1129 | (symbol 1130 | (lib_id "power:+BATT") 1131 | (at 108.585 46.99 0) 1132 | (unit 1) 1133 | (exclude_from_sim no) 1134 | (in_bom yes) 1135 | (on_board yes) 1136 | (dnp no) 1137 | (fields_autoplaced yes) 1138 | (uuid "325d22d3-8868-4393-9df2-c0476c7f1f2a") 1139 | (property "Reference" "#PWR09" 1140 | (at 108.585 50.8 0) 1141 | (effects 1142 | (font 1143 | (size 1.27 1.27) 1144 | ) 1145 | (hide yes) 1146 | ) 1147 | ) 1148 | (property "Value" "+BATT" 1149 | (at 108.585 42.545 0) 1150 | (effects 1151 | (font 1152 | (size 1.27 1.27) 1153 | ) 1154 | ) 1155 | ) 1156 | (property "Footprint" "" 1157 | (at 108.585 46.99 0) 1158 | (effects 1159 | (font 1160 | (size 1.27 1.27) 1161 | ) 1162 | (hide yes) 1163 | ) 1164 | ) 1165 | (property "Datasheet" "" 1166 | (at 108.585 46.99 0) 1167 | (effects 1168 | (font 1169 | (size 1.27 1.27) 1170 | ) 1171 | (hide yes) 1172 | ) 1173 | ) 1174 | (property "Description" "Power symbol creates a global label with name \"+BATT\"" 1175 | (at 108.585 46.99 0) 1176 | (effects 1177 | (font 1178 | (size 1.27 1.27) 1179 | ) 1180 | (hide yes) 1181 | ) 1182 | ) 1183 | (pin "1" 1184 | (uuid "ff306039-f553-46ee-9da6-a162d0535682") 1185 | ) 1186 | (instances 1187 | (project "kivar-demo" 1188 | (path "/cd745a5c-45fb-4106-85c8-c86c130b6ee7/98917921-089a-4f53-9391-423838589d96" 1189 | (reference "#PWR09") 1190 | (unit 1) 1191 | ) 1192 | ) 1193 | ) 1194 | ) 1195 | (symbol 1196 | (lib_id "Device:R") 1197 | (at 108.585 50.8 0) 1198 | (mirror y) 1199 | (unit 1) 1200 | (exclude_from_sim no) 1201 | (in_bom yes) 1202 | (on_board yes) 1203 | (dnp no) 1204 | (uuid "54c95d51-ec8d-4420-b00a-f1ba680dfcf8") 1205 | (property "Reference" "R12" 1206 | (at 106.68 48.2599 0) 1207 | (effects 1208 | (font 1209 | (size 1.27 1.27) 1210 | ) 1211 | (justify left) 1212 | ) 1213 | ) 1214 | (property "Value" "309kΩ" 1215 | (at 106.68 50.7999 0) 1216 | (effects 1217 | (font 1218 | (size 1.27 1.27) 1219 | ) 1220 | (justify left) 1221 | ) 1222 | ) 1223 | (property "Footprint" "Resistor_SMD:R_0402_1005Metric" 1224 | (at 110.363 50.8 90) 1225 | (effects 1226 | (font 1227 | (size 1.27 1.27) 1228 | ) 1229 | (hide yes) 1230 | ) 1231 | ) 1232 | (property "Datasheet" "~" 1233 | (at 108.585 50.8 0) 1234 | (effects 1235 | (font 1236 | (size 1.27 1.27) 1237 | ) 1238 | (hide yes) 1239 | ) 1240 | ) 1241 | (property "Description" "" 1242 | (at 108.585 50.8 0) 1243 | (effects 1244 | (font 1245 | (size 1.27 1.27) 1246 | ) 1247 | (hide yes) 1248 | ) 1249 | ) 1250 | (property "Var.Aspect" "UVLO_LO/HI" 1251 | (at 62.23 41.91 0) 1252 | (show_name yes) 1253 | (do_not_autoplace yes) 1254 | (effects 1255 | (font 1256 | (size 1.27 1.27) 1257 | (italic yes) 1258 | ) 1259 | (justify right) 1260 | ) 1261 | ) 1262 | (property "Var" "'2.41V/3.40V'(0Ω) '3.15V/3.57V'(309kΩ)" 1263 | (at 106.68 53.3399 0) 1264 | (effects 1265 | (font 1266 | (size 1.27 1.27) 1267 | (italic yes) 1268 | ) 1269 | (justify left) 1270 | ) 1271 | ) 1272 | (property "VarID" "3" 1273 | (at 108.585 50.8 0) 1274 | (effects 1275 | (font 1276 | (size 1.27 1.27) 1277 | ) 1278 | (hide yes) 1279 | ) 1280 | ) 1281 | (property "VarID.Var" "'2.41V/3.40V'(2) '3.15V/3.57V'(3)" 1282 | (at 108.585 50.8 0) 1283 | (effects 1284 | (font 1285 | (size 1.27 1.27) 1286 | ) 1287 | (hide yes) 1288 | ) 1289 | ) 1290 | (pin "1" 1291 | (uuid "d1feb168-a658-4abe-92d2-48536dd13a72") 1292 | ) 1293 | (pin "2" 1294 | (uuid "2909c6ee-f858-4b3a-b2a1-e0e60509d1a7") 1295 | ) 1296 | (instances 1297 | (project "kivar-demo" 1298 | (path "/cd745a5c-45fb-4106-85c8-c86c130b6ee7/98917921-089a-4f53-9391-423838589d96" 1299 | (reference "R12") 1300 | (unit 1) 1301 | ) 1302 | ) 1303 | ) 1304 | ) 1305 | (symbol 1306 | (lib_id "power:GND") 1307 | (at 108.585 81.28 0) 1308 | (unit 1) 1309 | (exclude_from_sim no) 1310 | (in_bom yes) 1311 | (on_board yes) 1312 | (dnp no) 1313 | (fields_autoplaced yes) 1314 | (uuid "8019f6ea-75e8-4589-ba4a-0c4bba68651e") 1315 | (property "Reference" "#PWR013" 1316 | (at 108.585 87.63 0) 1317 | (effects 1318 | (font 1319 | (size 1.27 1.27) 1320 | ) 1321 | (hide yes) 1322 | ) 1323 | ) 1324 | (property "Value" "GND" 1325 | (at 108.585 86.36 0) 1326 | (effects 1327 | (font 1328 | (size 1.27 1.27) 1329 | ) 1330 | ) 1331 | ) 1332 | (property "Footprint" "" 1333 | (at 108.585 81.28 0) 1334 | (effects 1335 | (font 1336 | (size 1.27 1.27) 1337 | ) 1338 | (hide yes) 1339 | ) 1340 | ) 1341 | (property "Datasheet" "" 1342 | (at 108.585 81.28 0) 1343 | (effects 1344 | (font 1345 | (size 1.27 1.27) 1346 | ) 1347 | (hide yes) 1348 | ) 1349 | ) 1350 | (property "Description" "" 1351 | (at 108.585 81.28 0) 1352 | (effects 1353 | (font 1354 | (size 1.27 1.27) 1355 | ) 1356 | (hide yes) 1357 | ) 1358 | ) 1359 | (pin "1" 1360 | (uuid "f1d61637-82e4-4329-9efb-af8ea88f5da8") 1361 | ) 1362 | (instances 1363 | (project "kivar-demo" 1364 | (path "/cd745a5c-45fb-4106-85c8-c86c130b6ee7/98917921-089a-4f53-9391-423838589d96" 1365 | (reference "#PWR013") 1366 | (unit 1) 1367 | ) 1368 | ) 1369 | ) 1370 | ) 1371 | (symbol 1372 | (lib_id "Device:C") 1373 | (at 123.825 50.8 0) 1374 | (mirror y) 1375 | (unit 1) 1376 | (exclude_from_sim no) 1377 | (in_bom yes) 1378 | (on_board yes) 1379 | (dnp no) 1380 | (fields_autoplaced yes) 1381 | (uuid "93a73913-2e0b-430d-91ec-eb5d89767884") 1382 | (property "Reference" "C2" 1383 | (at 120.65 49.5299 0) 1384 | (effects 1385 | (font 1386 | (size 1.27 1.27) 1387 | ) 1388 | (justify left) 1389 | ) 1390 | ) 1391 | (property "Value" "0.1µF" 1392 | (at 120.65 52.0699 0) 1393 | (effects 1394 | (font 1395 | (size 1.27 1.27) 1396 | ) 1397 | (justify left) 1398 | ) 1399 | ) 1400 | (property "Footprint" "Capacitor_SMD:C_0402_1005Metric" 1401 | (at 122.8598 54.61 0) 1402 | (effects 1403 | (font 1404 | (size 1.27 1.27) 1405 | ) 1406 | (hide yes) 1407 | ) 1408 | ) 1409 | (property "Datasheet" "~" 1410 | (at 123.825 50.8 0) 1411 | (effects 1412 | (font 1413 | (size 1.27 1.27) 1414 | ) 1415 | (hide yes) 1416 | ) 1417 | ) 1418 | (property "Description" "" 1419 | (at 123.825 50.8 0) 1420 | (effects 1421 | (font 1422 | (size 1.27 1.27) 1423 | ) 1424 | (hide yes) 1425 | ) 1426 | ) 1427 | (pin "1" 1428 | (uuid "2d385849-6873-4439-b4b3-bda0046d92e5") 1429 | ) 1430 | (pin "2" 1431 | (uuid "877855c4-828e-4f6c-b633-7a6f58c9e521") 1432 | ) 1433 | (instances 1434 | (project "kivar-demo" 1435 | (path "/cd745a5c-45fb-4106-85c8-c86c130b6ee7/98917921-089a-4f53-9391-423838589d96" 1436 | (reference "C2") 1437 | (unit 1) 1438 | ) 1439 | ) 1440 | ) 1441 | ) 1442 | (symbol 1443 | (lib_id "Device:R") 1444 | (at 108.585 77.47 0) 1445 | (mirror y) 1446 | (unit 1) 1447 | (exclude_from_sim no) 1448 | (in_bom yes) 1449 | (on_board yes) 1450 | (dnp no) 1451 | (fields_autoplaced yes) 1452 | (uuid "a0b6fe2a-c5c9-4b16-b092-a05117637bc1") 1453 | (property "Reference" "R15" 1454 | (at 106.68 74.9299 0) 1455 | (effects 1456 | (font 1457 | (size 1.27 1.27) 1458 | ) 1459 | (justify left) 1460 | ) 1461 | ) 1462 | (property "Value" "750kΩ" 1463 | (at 106.68 77.4699 0) 1464 | (effects 1465 | (font 1466 | (size 1.27 1.27) 1467 | ) 1468 | (justify left) 1469 | ) 1470 | ) 1471 | (property "Footprint" "Resistor_SMD:R_0402_1005Metric" 1472 | (at 110.363 77.47 90) 1473 | (effects 1474 | (font 1475 | (size 1.27 1.27) 1476 | ) 1477 | (hide yes) 1478 | ) 1479 | ) 1480 | (property "Datasheet" "~" 1481 | (at 108.585 77.47 0) 1482 | (effects 1483 | (font 1484 | (size 1.27 1.27) 1485 | ) 1486 | (hide yes) 1487 | ) 1488 | ) 1489 | (property "Description" "" 1490 | (at 108.585 77.47 0) 1491 | (effects 1492 | (font 1493 | (size 1.27 1.27) 1494 | ) 1495 | (hide yes) 1496 | ) 1497 | ) 1498 | (property "Var.Aspect" "UVLO_LO/HI" 1499 | (at 108.585 77.47 0) 1500 | (effects 1501 | (font 1502 | (size 1.27 1.27) 1503 | ) 1504 | (hide yes) 1505 | ) 1506 | ) 1507 | (property "Var" "?(750kΩ)" 1508 | (at 106.68 80.0099 0) 1509 | (effects 1510 | (font 1511 | (size 1.27 1.27) 1512 | (italic yes) 1513 | ) 1514 | (justify left) 1515 | ) 1516 | ) 1517 | (pin "1" 1518 | (uuid "1113b2af-f3a8-426b-94c1-7b226c6dfd9e") 1519 | ) 1520 | (pin "2" 1521 | (uuid "54d864dc-85bf-46df-a4f9-5246e2fda2d1") 1522 | ) 1523 | (instances 1524 | (project "kivar-demo" 1525 | (path "/cd745a5c-45fb-4106-85c8-c86c130b6ee7/98917921-089a-4f53-9391-423838589d96" 1526 | (reference "R15") 1527 | (unit 1) 1528 | ) 1529 | ) 1530 | ) 1531 | ) 1532 | (symbol 1533 | (lib_id "Device:R") 1534 | (at 108.585 59.69 0) 1535 | (mirror y) 1536 | (unit 1) 1537 | (exclude_from_sim no) 1538 | (in_bom yes) 1539 | (on_board yes) 1540 | (dnp no) 1541 | (fields_autoplaced yes) 1542 | (uuid "b814fe6b-59a2-4356-befe-86b98ed0b7cd") 1543 | (property "Reference" "R13" 1544 | (at 106.68 57.1499 0) 1545 | (effects 1546 | (font 1547 | (size 1.27 1.27) 1548 | ) 1549 | (justify left) 1550 | ) 1551 | ) 1552 | (property "Value" "1MΩ" 1553 | (at 106.68 59.6899 0) 1554 | (effects 1555 | (font 1556 | (size 1.27 1.27) 1557 | ) 1558 | (justify left) 1559 | ) 1560 | ) 1561 | (property "Footprint" "Resistor_SMD:R_0402_1005Metric" 1562 | (at 110.363 59.69 90) 1563 | (effects 1564 | (font 1565 | (size 1.27 1.27) 1566 | ) 1567 | (hide yes) 1568 | ) 1569 | ) 1570 | (property "Datasheet" "~" 1571 | (at 108.585 59.69 0) 1572 | (effects 1573 | (font 1574 | (size 1.27 1.27) 1575 | ) 1576 | (hide yes) 1577 | ) 1578 | ) 1579 | (property "Description" "" 1580 | (at 108.585 59.69 0) 1581 | (effects 1582 | (font 1583 | (size 1.27 1.27) 1584 | ) 1585 | (hide yes) 1586 | ) 1587 | ) 1588 | (property "Var.Aspect" "UVLO_LO/HI" 1589 | (at 108.585 59.69 0) 1590 | (effects 1591 | (font 1592 | (size 1.27 1.27) 1593 | ) 1594 | (hide yes) 1595 | ) 1596 | ) 1597 | (property "Var" "?(1MΩ)" 1598 | (at 106.68 62.2299 0) 1599 | (effects 1600 | (font 1601 | (size 1.27 1.27) 1602 | (italic yes) 1603 | ) 1604 | (justify left) 1605 | ) 1606 | ) 1607 | (pin "1" 1608 | (uuid "64169b0c-cfce-4cba-a1cc-b87bfe58a8ca") 1609 | ) 1610 | (pin "2" 1611 | (uuid "67229dc2-e259-4e74-ab21-bc46aa35e2da") 1612 | ) 1613 | (instances 1614 | (project "kivar-demo" 1615 | (path "/cd745a5c-45fb-4106-85c8-c86c130b6ee7/98917921-089a-4f53-9391-423838589d96" 1616 | (reference "R13") 1617 | (unit 1) 1618 | ) 1619 | ) 1620 | ) 1621 | ) 1622 | (symbol 1623 | (lib_id "power:GND") 1624 | (at 127.635 77.47 0) 1625 | (unit 1) 1626 | (exclude_from_sim no) 1627 | (in_bom yes) 1628 | (on_board yes) 1629 | (dnp no) 1630 | (fields_autoplaced yes) 1631 | (uuid "f43b4bfa-8f1b-402b-8ab6-a6a5c418f4f3") 1632 | (property "Reference" "#PWR012" 1633 | (at 127.635 83.82 0) 1634 | (effects 1635 | (font 1636 | (size 1.27 1.27) 1637 | ) 1638 | (hide yes) 1639 | ) 1640 | ) 1641 | (property "Value" "GND" 1642 | (at 127.635 82.55 0) 1643 | (effects 1644 | (font 1645 | (size 1.27 1.27) 1646 | ) 1647 | ) 1648 | ) 1649 | (property "Footprint" "" 1650 | (at 127.635 77.47 0) 1651 | (effects 1652 | (font 1653 | (size 1.27 1.27) 1654 | ) 1655 | (hide yes) 1656 | ) 1657 | ) 1658 | (property "Datasheet" "" 1659 | (at 127.635 77.47 0) 1660 | (effects 1661 | (font 1662 | (size 1.27 1.27) 1663 | ) 1664 | (hide yes) 1665 | ) 1666 | ) 1667 | (property "Description" "" 1668 | (at 127.635 77.47 0) 1669 | (effects 1670 | (font 1671 | (size 1.27 1.27) 1672 | ) 1673 | (hide yes) 1674 | ) 1675 | ) 1676 | (pin "1" 1677 | (uuid "5f39049a-f4b7-4533-a26d-742ef6de2336") 1678 | ) 1679 | (instances 1680 | (project "kivar-demo" 1681 | (path "/cd745a5c-45fb-4106-85c8-c86c130b6ee7/98917921-089a-4f53-9391-423838589d96" 1682 | (reference "#PWR012") 1683 | (unit 1) 1684 | ) 1685 | ) 1686 | ) 1687 | ) 1688 | ) 1689 | --------------------------------------------------------------------------------