diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..3959079 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,17 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/python +{ + "name": "Python 3", + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "ghcr.io/devcontainers/features/python:1": {}, + "ghcr.io/stuartleeks/dev-container-features/shell-history:0": {}, + "ghcr.io/neilenns/devcontainer-features/oh-my-zsh": { + "disableAutoUpdate": false, + "disableUpdatePrompt": false, + "stripWorkspacesFromPrompt": true, + "theme": "agnoster" + } + }, + "postCreateCommand": ".devcontainer/install-kicad.sh" +} diff --git a/.devcontainer/install-kicad.sh b/.devcontainer/install-kicad.sh new file mode 100755 index 0000000..b7859f5 --- /dev/null +++ b/.devcontainer/install-kicad.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -e + +echo "Installing prerequisites..." +sudo apt update > /dev/null 2>&1 && sudo apt install -y software-properties-common > /dev/null 2>&1 + +echo "Installing KiCad (takes a few minutes)..." +sudo add-apt-repository -y ppa:kicad/kicad-9.0-releases > /dev/null 2>&1 +sudo apt update > /dev/null 2>&1 +sudo apt install -y --no-install-recommends kicad kicad-symbols kicad-footprints > /dev/null 2>&1 + +# Link KiCad plugin to workspace path +echo "Linking KiCad plugin..." +mkdir -p "$HOME/.local/share/kicad/9.0/3rdparty/plugins" +ln -s /workspaces/Fabrication-Toolkit/plugins "$HOME/.local/share/kicad/9.0/3rdparty/plugins/com_github_bennymeg_JLC-Plugin-for-KiCad" +mkdir -p "$HOME/.local/share/kicad/9.0/3rdparty/resources" +ln -s /workspaces/Fabrication-Toolkit/resources "$HOME/.local/share/kicad/9.0/3rdparty/resources/com_github_bennymeg_JLC-Plugin-for-KiCad" + +# Add plugin to PYTHONPATH +echo "Adding plugin to PYTHONPATH..." +echo 'export PYTHONPATH="$PYTHONPATH:$HOME/.local/share/kicad/9.0/3rdparty/plugins"' >> "$HOME/.zshrc" \ No newline at end of file diff --git a/.gitignore b/.gitignore index a8600b9..b007e26 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .venv dist/ .DS_Store -*.pyc \ No newline at end of file +*.pyc +Test/ \ No newline at end of file diff --git a/README.md b/README.md index e86bd5d..58fa2ac 100644 --- a/README.md +++ b/README.md @@ -190,7 +190,7 @@ python3 -m plugins.cli -h usage: Fabrication Toolkit [-h] --path PATH [--additionalLayers LAYERS] [--user1VCut] [--user2AltVCut] [--autoTranslate] [--autoFill] [--excludeDNP] [--allActiveLayers] [--archiveName NAME] - [--openBrowser] + [--openBrowser] [--noBackup] Generates JLCPCB production files from a KiCAD board file @@ -209,6 +209,7 @@ options: --archiveName NAME, -aN NAME Name of the generated archives --openBrowser, -b Open web browser with directory file overview after generation + --noBackup, -nB Do not create a backup of the project before generation ``` diff --git a/plugins/cli.py b/plugins/cli.py index 72cf43b..8aff4df 100644 --- a/plugins/cli.py +++ b/plugins/cli.py @@ -19,6 +19,7 @@ if __name__ == '__main__': parser.add_argument("--archiveName", "-aN", type=str, help="Name of the generated archives", metavar="NAME") parser.add_argument("--openBrowser", "-b", action="store_true", help="Open webbrowser with directory file overview after generation") parser.add_argument("--nonInteractive", "-nI" ,action="store_true", help="Run in non-Interactive mode. Usefull in CI/CD enviroment.") + parser.add_argument("--noBackup", "-nB", action="store_true", help="Do not create backup files") args = parser.parse_args() options = dict() @@ -30,7 +31,8 @@ if __name__ == '__main__': options[ALL_ACTIVE_LAYERS_OPT] = args.allActiveLayers options[ARCHIVE_NAME] = args.archiveName options[EXTRA_LAYERS] = args.additionalLayers - + options[NO_BACKUP] = args.no_backup + openBrowser = args.openBrowser nonInteractive = args.nonInteractive diff --git a/plugins/options.py b/plugins/options.py index 73a09a9..a7d1f9a 100644 --- a/plugins/options.py +++ b/plugins/options.py @@ -7,4 +7,5 @@ ALTERNATIVE_EDGE_CUT_OPT = "ALTERNATIVE_EDGE_CUT" ALL_ACTIVE_LAYERS_OPT = "ALL_ACTIVE_LAYERS" OPEN_BROWSER_OPT = "OPEN BROWSER" ARCHIVE_NAME = "ARCHIVE_NAME" -EXTRA_LAYERS = "EXTRA_LAYERS" \ No newline at end of file +EXTRA_LAYERS = "EXTRA_LAYERS" +NO_BACKUP = "NO_BACKUP" diff --git a/plugins/plugin.py b/plugins/plugin.py index 13ddac1..2edf508 100644 --- a/plugins/plugin.py +++ b/plugins/plugin.py @@ -1,10 +1,10 @@ import os import wx -import pcbnew # type: ignore +import pcbnew # type: ignore from .thread import ProcessThread from .events import StatusEvent -from .options import AUTO_FILL_OPT, AUTO_TRANSLATE_OPT, EXCLUDE_DNP_OPT, EXTEND_EDGE_CUT_OPT, ALTERNATIVE_EDGE_CUT_OPT, EXTRA_LAYERS, ALL_ACTIVE_LAYERS_OPT, ARCHIVE_NAME, OPEN_BROWSER_OPT +from .options import AUTO_FILL_OPT, AUTO_TRANSLATE_OPT, EXCLUDE_DNP_OPT, EXTEND_EDGE_CUT_OPT, ALTERNATIVE_EDGE_CUT_OPT, EXTRA_LAYERS, ALL_ACTIVE_LAYERS_OPT, ARCHIVE_NAME, OPEN_BROWSER_OPT, NO_BACKUP from .utils import load_user_options, save_user_options, get_layer_names @@ -19,7 +19,7 @@ class KiCadToJLCForm(wx.Frame): pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_DIALOG_STYLE) - + # self.app = wx.PySimpleApp() icon = wx.Icon(os.path.join(os.path.dirname(__file__), 'icon.png')) self.SetIcon(icon) @@ -36,7 +36,8 @@ class KiCadToJLCForm(wx.Frame): AUTO_TRANSLATE_OPT: True, AUTO_FILL_OPT: True, EXCLUDE_DNP_OPT: False, - OPEN_BROWSER_OPT: True + OPEN_BROWSER_OPT: True, + NO_BACKUP: False, }) self.mOptionsLabel = wx.StaticText(self, label='Options:') @@ -66,6 +67,8 @@ class KiCadToJLCForm(wx.Frame): self.mExcludeDnpCheckbox.SetValue(userOptions[EXCLUDE_DNP_OPT]) self.mOpenBrowserCheckbox = wx.CheckBox(self, label='Open browser after generation') self.mOpenBrowserCheckbox.SetValue(userOptions[OPEN_BROWSER_OPT]) + self.mNoBackupCheckbox = wx.CheckBox(self, label='Do not create backup files') + self.mNoBackupCheckbox.SetValue(userOptions[NO_BACKUP]) self.mGaugeStatus = wx.Gauge( self, wx.ID_ANY, 100, wx.DefaultPosition, wx.Size(600, 20), wx.GA_HORIZONTAL) @@ -88,6 +91,7 @@ class KiCadToJLCForm(wx.Frame): boxSizer.Add(self.mAutomaticFillCheckbox, 0, wx.ALL, 5) boxSizer.Add(self.mExcludeDnpCheckbox, 0, wx.ALL, 5) boxSizer.Add(self.mOpenBrowserCheckbox, 0, wx.ALL, 5) + boxSizer.Add(self.mNoBackupCheckbox, 0, wx.ALL, 5) boxSizer.Add(self.mGaugeStatus, 0, wx.ALL, 5) boxSizer.Add(self.mGenerateButton, 0, wx.ALL, 5) @@ -107,7 +111,6 @@ class KiCadToJLCForm(wx.Frame): else: event.Skip() - def onGenerateButtonClick(self, event): options = dict() options[ARCHIVE_NAME] = self.mArchiveNameControl.GetValue() @@ -119,6 +122,7 @@ class KiCadToJLCForm(wx.Frame): options[AUTO_FILL_OPT] = self.mAutomaticFillCheckbox.GetValue() options[EXCLUDE_DNP_OPT] = self.mExcludeDnpCheckbox.GetValue() options[OPEN_BROWSER_OPT] = self.mOpenBrowserCheckbox.GetValue() + options[NO_BACKUP] = self.mNoBackupCheckbox.GetValue() save_user_options(options) @@ -131,6 +135,7 @@ class KiCadToJLCForm(wx.Frame): self.mAutomaticTranslationCheckbox.Hide() self.mAutomaticFillCheckbox.Hide() self.mExcludeDnpCheckbox.Hide() + self.mOpenBrowserCheckbox.Hide() self.mGenerateButton.Hide() self.mGaugeStatus.Show() diff --git a/plugins/thread.py b/plugins/thread.py index 9d0d133..3a61cf1 100644 --- a/plugins/thread.py +++ b/plugins/thread.py @@ -166,10 +166,11 @@ class ProcessThread(Thread): os.rename(os.path.join(temp_dir, placementFileName), os.path.join(temp_dir, ProcessManager.normalize_filename("_".join((baseName.strip() + '_positions.csv').split())))) os.rename(os.path.join(temp_dir, bomFileName), os.path.join(temp_dir, ProcessManager.normalize_filename("_".join((baseName.strip() + '_bom.csv').split())))) - timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H-%M-%S') - backup_name = ProcessManager.normalize_filename("_".join(("{} {}".format(baseName, timestamp).strip()).split())) - shutil.make_archive(os.path.join(output_path, 'backups', backup_name), 'zip', temp_dir) - + # Make a backup as long as the NO_BACKUP flag isn't set. + if not self.options[NO_BACKUP]: + timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H-%M-%S') + backup_name = ProcessManager.normalize_filename("_".join(("{} {}".format(baseName, timestamp).strip()).split())) + shutil.make_archive(os.path.join(output_path, 'backups', backup_name), 'zip', temp_dir) # copy to & open output dir try: