├── .gitignore ├── README.md ├── __init__.py ├── component_loader.py ├── create_pcm_archive.sh ├── easyeda_lib_loader.png ├── easyeda_lib_loader.py ├── easyeda_lib_loader.svg ├── easyeda_lib_loader_64.png ├── easyeda_lib_loader_dialog.fbp ├── easyeda_lib_loader_dialog.py └── pcm ├── icon.png └── metadata.template.json /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | .vscode/ 3 | .out/archive/ 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JLCPCB/LCSC Library Loader 2 | 3 | This KiCad plugin allows you to search and download symbols/footprints with 3D models to a local .elibz library that can be read by KiCad. 4 | 5 | ![image](https://github.com/user-attachments/assets/37e16749-94ea-46e8-88c9-e85164eaf495) 6 | 7 | # System support 8 | 9 | - **KiCad**: version 8.0.7 or newer. 10 | - **Windows**: version 10 or newer with normal KiCad installation. 11 | - **Ubuntu**: install KiCad from PPA. To make the preview work, install `python3-wxgtk-webview4.0`. 12 | - **Flatpak**: works but preview is not available due to missing webkitgtk2. 13 | - **macOS**: not tested 14 | 15 | # Installation 16 | 17 | 1. Download the latest `jlc-kicad-lib-loader-*-pcm.zip` archive from [Releases page](https://github.com/dsa-t/jlc-kicad-lib-loader/releases). 18 | 19 | 2. Open PCM in KiCad, click "Install from File...", then choose the downloaded `-pcm` archive: 20 | 21 | ![image](https://github.com/user-attachments/assets/debae118-1292-498a-81f2-29fdc2cf455d) 22 | 23 | # Library setup 24 | 25 | Add the .elibz library to your Symbol/Footprint library tables: 26 | 27 | ![image](https://github.com/user-attachments/assets/45583737-6747-4aa8-975c-2a90a6f192d6) 28 | 29 | ## Symbol library table: 30 | 31 | ![image](https://github.com/user-attachments/assets/a3ff3856-5637-46da-8349-0b965986680f) 32 | 33 | ## Footprint library table: 34 | 35 | ![image](https://github.com/user-attachments/assets/8512a77f-95e5-4d4f-bba6-4a2b5660e218) 36 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | from .easyeda_lib_loader import EasyEDALibLoaderPlugin 2 | 3 | EasyEDALibLoaderPlugin().register() -------------------------------------------------------------------------------- /component_loader.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import os 3 | import json 4 | import traceback 5 | import requests 6 | import concurrent.futures 7 | import zipfile 8 | import urllib 9 | 10 | from logging import info, warning, debug, error, critical 11 | from typing import Callable 12 | 13 | from pcbnew import * 14 | 15 | 16 | MODELS_DIR = "EASYEDA_MODELS" 17 | 18 | # UUID strings can be in the format |. This function gets the part 19 | def getUuidFirstPart(uuid): 20 | if not uuid: 21 | return None 22 | return uuid.split("|")[0] 23 | 24 | class ComponentLoader(): 25 | def __init__(self, kiprjmod, target_path, target_name, progress: Callable[[int, int], None]): 26 | self.kiprjmod = kiprjmod 27 | self.target_path = target_path 28 | self.target_name = target_name 29 | self.progress = progress 30 | 31 | def downloadAll(self, components): 32 | self.progress(0, 100) 33 | 34 | try: 35 | libDeviceFile, fetched_3dmodels = self.downloadSymFp(components) 36 | self.downloadModels(libDeviceFile, fetched_3dmodels) 37 | self.progress(100, 100) 38 | except Exception as e: 39 | traceback.print_exc() 40 | error(f"Failed to download components: {traceback.format_exc()}") 41 | 42 | def downloadSymFp(self, components): 43 | info(f"Fetching info...") 44 | 45 | # Separate components into code-based and direct UUIDs 46 | code_components = [] 47 | direct_uuids = [] 48 | 49 | for comp in components: 50 | if comp.startswith("C"): 51 | code_components.append(comp) 52 | else: 53 | direct_uuids.append(comp) 54 | 55 | fetched_devices = {} 56 | 57 | # Fetch UUIDs from code-based components 58 | if code_components: 59 | resp = requests.post("https://pro.easyeda.com/api/v2/devices/searchByCodes", data={"codes[]": code_components}) 60 | resp.raise_for_status() 61 | found = resp.json() 62 | 63 | debug("searchByCodes: " + json.dumps(found, indent=4)) 64 | 65 | if not found.get("success") or not found.get("result"): 66 | raise Exception(f"Unable to fetch device info: {found}") 67 | 68 | # Append fetched UUIDs to direct_uuids 69 | for entry in found["result"]: 70 | direct_uuids.append(entry['uuid']) 71 | 72 | # Fetch device info by UUID 73 | def fetch_device_info(dev_uuid): 74 | dev_info = requests.get(f"https://pro.easyeda.com/api/devices/{dev_uuid}") 75 | dev_info.raise_for_status() 76 | 77 | debug("device info: " + json.dumps(dev_info.json(), indent=4)) 78 | 79 | device = dev_info.json()["result"] 80 | fetched_devices[device["uuid"]] = device 81 | 82 | with concurrent.futures.ThreadPoolExecutor() as executor: 83 | for dev_uuid in direct_uuids: 84 | executor.submit(fetch_device_info, dev_uuid) 85 | 86 | # Collect symbol/footprint/3D model UUIDs to fetch 87 | fetched_symbols = {} 88 | fetched_footprints = {} 89 | fetched_3dmodels = {} 90 | uuid_to_obj_map = {} 91 | 92 | all_uuids = set() 93 | for entry in fetched_devices.values(): 94 | if entry['attributes'].get('Symbol'): 95 | all_uuids.add(entry['attributes']['Symbol']) 96 | uuid_to_obj_map[entry['attributes']['Symbol']] = fetched_symbols 97 | 98 | if entry['attributes'].get('Footprint'): 99 | all_uuids.add(entry['attributes']['Footprint']) 100 | uuid_to_obj_map[entry['attributes']['Footprint']] = fetched_footprints 101 | 102 | if entry['attributes'].get('3D Model'): 103 | all_uuids.add(getUuidFirstPart(entry['attributes']['3D Model'])) 104 | uuid_to_obj_map[getUuidFirstPart(entry['attributes']['3D Model'])] = fetched_3dmodels 105 | 106 | # Fetch symbols/footprints/3D models 107 | def fetch_component(uuid): 108 | url = f"https://pro.easyeda.com/api/v2/components/{uuid}" 109 | r = requests.get(url) 110 | r.raise_for_status() 111 | return r.json()["result"] 112 | 113 | with concurrent.futures.ThreadPoolExecutor() as executor: 114 | futures = {executor.submit(fetch_component, uuid): uuid for uuid in all_uuids} 115 | for future in concurrent.futures.as_completed(futures): 116 | try: 117 | compData = future.result() 118 | debug(f"Fetched component {json.dumps(compData, indent=4)}") 119 | 120 | uuid_to_obj_map[compData["uuid"]][compData["uuid"]] = compData 121 | except Exception as e: 122 | error(f"Failed to fetch component for uuid {futures[future]}: {e}") 123 | 124 | # Set symbol/footprint type fields 125 | for device in fetched_devices.values(): 126 | if device['attributes'].get('Symbol'): 127 | fetched_symbols[device["attributes"]["Symbol"]]["type"] = device["symbol_type"] 128 | 129 | if device['attributes'].get('Footprint'): 130 | fetched_footprints[device["attributes"]["Footprint"]]["type"] = device["footprint_type"] 131 | 132 | # Extract dataStr 133 | footprint_data_str = {} 134 | symbol_data_str = {} 135 | 136 | # Separate dataStr for footprints 137 | for f_uuid, f_data in fetched_footprints.items(): 138 | ds = f_data.pop("dataStr", None) 139 | if ds: 140 | footprint_data_str[f_uuid] = ds 141 | 142 | # Separate dataStr for symbols 143 | for s_uuid, s_data in fetched_symbols.items(): 144 | ds = s_data.pop("dataStr", None) 145 | if ds: 146 | symbol_data_str[s_uuid] = ds 147 | 148 | libDeviceFile = { 149 | "devices": fetched_devices, 150 | "symbols": fetched_symbols, 151 | "footprints": fetched_footprints 152 | } 153 | 154 | os.makedirs(self.target_path, exist_ok=True) 155 | 156 | zip_filename = f"{self.target_path}/{self.target_name}.elibz" 157 | merged_data = copy.deepcopy(libDeviceFile) 158 | 159 | try: 160 | if os.path.exists(zip_filename): 161 | with zipfile.ZipFile(zip_filename, "r") as old_zip: 162 | for name in old_zip.namelist(): 163 | if name == "device.json": 164 | old_data = json.loads(old_zip.read("device.json").decode("utf-8")) 165 | for entry_type in ["devices", "symbols", "footprints"]: 166 | for key in old_data[entry_type]: 167 | if key not in merged_data[entry_type]: 168 | merged_data[entry_type][key] = old_data[entry_type][key] 169 | if name.endswith('.esym'): 170 | symbol_uuid = os.path.splitext(os.path.basename(name))[0] 171 | if symbol_uuid not in symbol_data_str: 172 | symbol_data_str[symbol_uuid] = old_zip.read(name).decode('utf-8') 173 | elif name.endswith('.efoo'): 174 | footprint_uuid = os.path.splitext(os.path.basename(name))[0] 175 | if footprint_uuid not in footprint_data_str: 176 | footprint_data_str[footprint_uuid] = old_zip.read(name).decode('utf-8') 177 | except Exception as e: 178 | warning(f"Failed to merge device.json data, overwriting: {e}") 179 | 180 | with zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_DEFLATED) as zf: 181 | zf.writestr("device.json", json.dumps(merged_data, indent=4)) 182 | for fp_uuid, ds in footprint_data_str.items(): 183 | zf.writestr(f"FOOTPRINT/{fp_uuid}.efoo", ds) 184 | for sym_uuid, ds in symbol_data_str.items(): 185 | zf.writestr(f"SYMBOL/{sym_uuid}.esym", ds) 186 | 187 | info( "*****************************" ) 188 | info(f"Downloaded {len(fetched_devices)} devices, {len(fetched_symbols)} symbols, {len(fetched_footprints)} footprints and added to library: {zip_filename}") 189 | return libDeviceFile, fetched_3dmodels 190 | 191 | def downloadModels(self, libDeviceFile, fetched_3dmodels): 192 | self.totalToDownload = 0 193 | self.downloadedCounter = 0 194 | self.statExisting = 0 195 | self.statDownloaded = 0 196 | self.statFailed = 0 197 | 198 | info( "*****************************" ) 199 | info(f"Loading 3D models...") 200 | self.progress(0, 100) 201 | 202 | uuidToTargetFileMap = {} 203 | uuidsToTransform = {} 204 | 205 | debug("fetched_3dmodels: " + json.dumps(fetched_3dmodels, indent=4)) 206 | debug("libDeviceFile: " + json.dumps(libDeviceFile, indent=4)) 207 | 208 | for device in libDeviceFile["devices"].values(): 209 | try: 210 | modelUuid = getUuidFirstPart(device["attributes"].get("3D Model")) 211 | 212 | if not modelUuid or modelUuid not in fetched_3dmodels: 213 | info("No model for device '%s', footprint '%s'" 214 | % (device.get("product_code", device.get("uuid")), 215 | device.get("footprint").get("display_title") if device.get("footprint") else "None")) 216 | continue 217 | 218 | modelTitle = device["attributes"]["3D Model Title"] 219 | modelTransform = device["attributes"].get("3D Model Transform", "") 220 | 221 | dataStr = fetched_3dmodels[modelUuid].get("dataStr") 222 | directUuid = json.loads(dataStr)["model"] 223 | 224 | uuidsToTransform[directUuid] = [float(x) for x in modelTransform.split(",")] 225 | 226 | easyEdaFilename = os.path.join(self.kiprjmod, MODELS_DIR, modelTitle + ".step") 227 | easyEdaFilename = os.path.normpath(easyEdaFilename) 228 | 229 | uuidToTargetFileMap[directUuid] = easyEdaFilename 230 | except KeyboardInterrupt: 231 | return 232 | except Exception as e: 233 | traceback.print_exc() 234 | info("Cannot get model for device '%s': %s" % (device.get("product_code"), traceback.format_exc())) 235 | continue 236 | 237 | with concurrent.futures.ThreadPoolExecutor(1) as texecutor: 238 | def fixupModel(fixTaskArgs): 239 | directUuid, kfilePath = fixTaskArgs 240 | 241 | file_name = os.path.splitext( os.path.basename( kfilePath ) ) [0] 242 | jfilePath = kfilePath + "_jlc" 243 | 244 | debug( "Loading STEP model %s" % (file_name) ) 245 | model: UTILS_STEP_MODEL = UTILS_STEP_MODEL.LoadSTEP(jfilePath) 246 | 247 | if not model: 248 | error( "Error loading model '%s'" % (file_name) ) 249 | return 250 | 251 | debug( "Converting STEP model '%s'" % (file_name) ) 252 | bbox: UTILS_BOX3D = model.GetBoundingBox() 253 | 254 | try: 255 | if directUuid in uuidsToTransform: 256 | # Convert mils to mm 257 | fitXmm = uuidsToTransform[directUuid][0] / 39.37 258 | fitYmm = uuidsToTransform[directUuid][1] / 39.37 259 | 260 | bsize: VECTOR3D = bbox.GetSize() 261 | scaleFactorX = fitXmm / bsize.x; 262 | scaleFactorY = fitYmm / bsize.y; 263 | scaleFactor = ( scaleFactorX + scaleFactorY ) / 2 264 | 265 | debug( "Dimensions %f %f factors %f %f avg %f model '%s'" % 266 | (fitXmm, fitYmm, scaleFactorX, scaleFactorY, scaleFactor, file_name) ) 267 | 268 | if abs( scaleFactorX - scaleFactorY ) > 0.1: 269 | warning( "Scale factors do not match: X %.3f; Y %.3f for model '%s'." % 270 | (scaleFactorX, scaleFactorY, file_name) ) 271 | warning( "**** The model '%s' might be misoriented! ****" % (file_name) ) 272 | elif abs( scaleFactor - 1.0 ) > 0.01: 273 | warning( "Scaling '%s' by %f" % (file_name, scaleFactor) ) 274 | model.Scale( scaleFactor ); 275 | else: 276 | debug( "No scaling for %s" % (file_name) ) 277 | 278 | except Exception as e: 279 | traceback.print_exc() 280 | error( "Error scaling model '%s': %s" % (file_name, str(e)) ) 281 | return 282 | 283 | newbbox = model.GetBoundingBox() 284 | center: VECTOR3D = newbbox.GetCenter() 285 | 286 | model.Translate( -center.x, -center.y, -newbbox.Min().z ) 287 | 288 | debug( "Saving STEP model %s" % (file_name) ) 289 | model.SaveSTEP( kfilePath ) 290 | 291 | with concurrent.futures.ThreadPoolExecutor(8) as dexecutor: 292 | def downloadStep(dnlTaskArgs): 293 | directUuid, kfilePath = dnlTaskArgs 294 | file_name = os.path.splitext( os.path.basename( kfilePath ) ) [0] 295 | 296 | try: 297 | if not os.path.exists(kfilePath): 298 | stepUrlFormat = "https://modules.easyeda.com/qAxj6KHrDKw4blvCG8QJPs7Y/{uuid}" 299 | jfilePath = kfilePath + "_jlc" 300 | url = stepUrlFormat.format(uuid=directUuid) 301 | 302 | debug("Downloading '%s'" % (file_name)) 303 | debug("'%s' from '%s'" % (file_name, url)) 304 | os.makedirs(os.path.dirname(kfilePath), exist_ok=True) 305 | urllib.request.urlretrieve(url, jfilePath) 306 | 307 | if os.path.isfile(jfilePath): 308 | debug("Downloaded '%s'." % (file_name)) 309 | self.statDownloaded += 1 310 | 311 | fixTaskArgs = [directUuid, kfilePath] 312 | texecutor.submit(fixupModel, fixTaskArgs) 313 | else: 314 | warning( "Path '%s' is not a file." % jfilePath ) 315 | else: 316 | info("Skipping '%s': STEP model file already exists." % (file_name)) 317 | self.statExisting += 1 318 | 319 | except Exception as e: 320 | warning("Failed to download model '%s': %s" % (file_name, str(e))) 321 | self.statFailed += 1 322 | 323 | self.downloadedCounter += 1 324 | self.progress(self.downloadedCounter, self.totalToDownload) 325 | 326 | self.totalToDownload = len(uuidToTargetFileMap) 327 | dexecutor.map(downloadStep, uuidToTargetFileMap.items()) 328 | 329 | info( "" ) 330 | info( "*****************************" ) 331 | info( " All done. " ) 332 | info( "*****************************" ) 333 | info( "" ) 334 | info( "Total model count: %d" % len(uuidToTargetFileMap) ) 335 | info( "STEP models downloaded: %d" % self.statDownloaded ) 336 | info( "Already existing models: %d" % self.statExisting ) 337 | info( "Failed downloads: %d" % self.statFailed ) 338 | self.progress(100, 100) -------------------------------------------------------------------------------- /create_pcm_archive.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | VERSION="1.0.4" 4 | 5 | echo "Clean up old files" 6 | rm -rf .out/archive 7 | rm -f .out/* 8 | 9 | 10 | echo "Create folder structure for ZIP" 11 | mkdir -p .out/archive/plugins 12 | mkdir -p .out/archive/resources 13 | 14 | echo "Copy files to destination" 15 | cp *.py .out/archive/plugins 16 | cp *.png .out/archive/plugins 17 | cp pcm/icon.png .out/archive/resources 18 | cp pcm/metadata.template.json .out/archive/metadata.json 19 | 20 | echo "Write version info to file" 21 | echo $VERSION > .out/archive/plugins/VERSION 22 | 23 | echo "Modify archive metadata.json" 24 | sed -i "s/VERSION_HERE/$VERSION/g" .out/archive/metadata.json 25 | sed -i "s/\"kicad_version_max\": \"9.0\",/\"kicad_version_max\": \"9.0\"/g" .out/archive/metadata.json 26 | sed -i "/SHA256_HERE/d" .out/archive/metadata.json 27 | sed -i "/DOWNLOAD_SIZE_HERE/d" .out/archive/metadata.json 28 | sed -i "/DOWNLOAD_URL_HERE/d" .out/archive/metadata.json 29 | sed -i "/INSTALL_SIZE_HERE/d" .out/archive/metadata.json 30 | 31 | echo "Zip PCM archive" 32 | cd .out/archive 33 | zip -r ../jlc-kicad-lib-loader-$VERSION-pcm.zip . 34 | cd ../.. 35 | 36 | echo "Gather data for repo rebuild" 37 | CI_ENV="${CI_ENV:-.out/env}" 38 | 39 | echo VERSION=$VERSION >> $CI_ENV 40 | echo DOWNLOAD_SHA256=$(shasum --algorithm 256 .out/jlc-kicad-lib-loader-$VERSION-pcm.zip | xargs | cut -d' ' -f1) >> $CI_ENV 41 | echo DOWNLOAD_SIZE=$(ls -l .out/jlc-kicad-lib-loader-$VERSION-pcm.zip | xargs | cut -d' ' -f5) >> $CI_ENV 42 | echo DOWNLOAD_URL="https://github.com/dsa-t/jlc-kicad-lib-loader/releases/download/$VERSION/jlc-kicad-lib-loader-$VERSION-pcm.zip" >> $CI_ENV 43 | echo INSTALL_SIZE=$(unzip -l .out/jlc-kicad-lib-loader-$VERSION-pcm.zip | tail -1 | xargs | cut -d' ' -f1) >> $CI_ENV 44 | -------------------------------------------------------------------------------- /easyeda_lib_loader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsa-t/jlc-kicad-lib-loader/c379260bb20031a6935565689739a65e98b4aa61/easyeda_lib_loader.png -------------------------------------------------------------------------------- /easyeda_lib_loader.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import math 4 | import sys 5 | import os 6 | import traceback 7 | import requests 8 | import logging 9 | import wx 10 | 11 | wx_html2_available = True 12 | try: 13 | import wx.html2 14 | except ImportError as e: 15 | wx_html2_available = False 16 | 17 | from threading import Lock, Thread 18 | from logging import info, warning, debug, error, critical 19 | from io import StringIO 20 | 21 | import wx.dataview 22 | 23 | from .component_loader import * 24 | from .easyeda_lib_loader_dialog import EasyEdaLibLoaderDialog 25 | 26 | from pcbnew import * 27 | import ctypes 28 | 29 | log_stream = StringIO() 30 | logging.basicConfig(stream=log_stream, level=logging.INFO) 31 | 32 | def interrupt_thread(thread): 33 | print("interrupt_thread") 34 | if not thread.is_alive(): 35 | return 36 | 37 | exc = ctypes.py_object(KeyboardInterrupt) 38 | res = ctypes.pythonapi.PyThreadState_SetAsyncExc( 39 | ctypes.c_long(thread.ident), exc) 40 | 41 | if res == 0: 42 | print("nonexistent thread id") 43 | return False 44 | elif res > 1: 45 | # """if it returns a number greater than one, you're in trouble, 46 | # and you should call it again with exc=NULL to revert the effect""" 47 | ctypes.pythonapi.PyThreadState_SetAsyncExc(thread.ident, None) 48 | print("PyThreadState_SetAsyncExc failed") 49 | 50 | return False 51 | 52 | print("interrupt_thread success") 53 | return True 54 | 55 | 56 | class WxTextCtrlHandler(logging.Handler): 57 | def __init__(self, ctrl: wx.TextCtrl): 58 | logging.Handler.__init__(self) 59 | self.ctrl = ctrl 60 | 61 | def emit(self, record): 62 | s = self.format(record) + '\n' 63 | wx.CallAfter(self.ctrl.AppendText, s) 64 | 65 | class EasyEDALibLoaderPlugin(ActionPlugin): 66 | downloadThread: Thread | None = None 67 | searchThread: Thread | None = None 68 | searchPage = 1 69 | components = [] 70 | 71 | def defaults(self): 72 | self.name = "EasyEDA (LCEDA) Library Loader" 73 | self.category = "3D data loader" 74 | self.description = "Load library parts from EasyEDA (LCEDA)" 75 | self.show_toolbar_button = True 76 | self.icon_file_name = os.path.join(os.path.dirname(__file__), 'easyeda_lib_loader.png') 77 | 78 | def Run(self): 79 | dlg = EasyEdaLibLoaderDialog(None) 80 | 81 | handler = WxTextCtrlHandler(dlg.m_log) 82 | logging.getLogger().handlers.clear(); 83 | logging.getLogger().addHandler(handler) 84 | FORMAT = "%(levelname)s: %(message)s" 85 | handler.setFormatter(logging.Formatter(FORMAT)) 86 | logging.getLogger().setLevel(level=logging.INFO) 87 | 88 | def progressHandler( current, total ): 89 | wx.CallAfter(dlg.m_progress.SetRange, total) 90 | wx.CallAfter(dlg.m_progress.SetValue, current) 91 | 92 | def onDebugCheckbox( event: wx.CommandEvent ): 93 | logging.getLogger().setLevel( logging.DEBUG if event.IsChecked() else logging.INFO ) 94 | 95 | def onDownload( event ): 96 | dlg.m_log.Clear() 97 | 98 | if not dlg.m_textCtrlParts.GetValue().strip(): 99 | for sel in dlg.m_searchResultsTree.GetSelections(): 100 | dlg.m_textCtrlParts.AppendText(dlg.m_searchResultsTree.GetItemText(sel) + "\n") 101 | 102 | components = dlg.m_textCtrlParts.GetValue().splitlines() 103 | 104 | if not components: 105 | error( "No parts to download." ) 106 | return 107 | 108 | kiprjmod = os.getenv("KIPRJMOD") or "" 109 | 110 | if not kiprjmod: 111 | error( "KIPRJMOD is not set properly." ) 112 | return 113 | 114 | lib_field = dlg.m_textCtrlOutLibName.GetValue() 115 | 116 | if os.path.isabs(lib_field): 117 | target_path = lib_field 118 | else: 119 | target_path = os.path.join(kiprjmod, lib_field) 120 | 121 | target_name = os.path.basename(target_path); 122 | 123 | def threadedFn(): 124 | loader = ComponentLoader(kiprjmod=kiprjmod, target_path=target_path, target_name=target_name, progress=progressHandler) 125 | loader.downloadAll(components) 126 | 127 | wx.CallAfter(dlg.m_actionBtn.Enable) 128 | 129 | dlg.m_actionBtn.Disable() 130 | self.downloadThread = Thread(target = threadedFn, daemon=True) 131 | self.downloadThread.start() 132 | 133 | def searchFn(facet, words, page): 134 | def setStatus( status ): 135 | wx.CallAfter(dlg.m_searchStatus.SetLabel, status) 136 | wx.CallAfter(dlg.m_statusPanel.Layout) 137 | 138 | def setPageText( pageText ): 139 | wx.CallAfter(dlg.m_searchPage.SetLabel, pageText) 140 | wx.CallAfter(dlg.m_statusPanel.Layout) 141 | 142 | def clearItems(): 143 | wx.CallAfter(dlg.m_searchResultsTree.DeleteAllItems) 144 | 145 | def appendItem( data ): 146 | treeItem = dlg.m_searchResultsTree.AppendItem( dlg.m_searchResultsTree.GetRootItem(), data[0] ) 147 | 148 | for i in range(1, len(data)): 149 | dlg.m_searchResultsTree.SetItemText(treeItem, i, data[i]); 150 | 151 | def addItem( item ): 152 | wx.CallAfter(appendItem, item) 153 | 154 | 155 | setStatus("Searching...") 156 | clearItems() 157 | 158 | wx.CallAfter(dlg.m_prevPageBtn.Disable) 159 | wx.CallAfter(dlg.m_nextPageBtn.Disable) 160 | 161 | try: 162 | pageSize = 50 163 | 164 | reqData={ 165 | "page": page, 166 | "pageSize": pageSize, 167 | "wd": words, 168 | "returnListStyle": "classifyarr" 169 | } 170 | 171 | if facet: 172 | reqData |= { 173 | "uid": facet, 174 | "path": facet, 175 | } 176 | 177 | resp = requests.post( "https://pro.easyeda.com/api/v2/devices/search", data=reqData ) 178 | resp.raise_for_status() 179 | found = resp.json() 180 | 181 | debug(json.dumps(found, indent=4)) 182 | 183 | if not found.get("success") or not found.get("result"): 184 | raise Exception(f"Unable to search: {found}") 185 | 186 | totalDevices = sum(found["result"]["facets"].values()) 187 | 188 | for facet in found["result"]["lists"].values(): 189 | for entry in facet: 190 | addItem([ 191 | entry.get("product_code", entry["uuid"]), 192 | entry["display_title"], 193 | entry["attributes"].get("Manufacturer", ""), 194 | entry["symbol"]["display_title"] if entry.get("symbol") else "", 195 | entry["footprint"]["display_title"] if entry.get("footprint") else "" 196 | ]) 197 | 198 | curPage = int(found['result']['page']) 199 | totalPages = math.ceil(totalDevices / pageSize) 200 | 201 | if(curPage > 1): 202 | wx.CallAfter(dlg.m_prevPageBtn.Enable) 203 | 204 | if(curPage < totalPages): 205 | wx.CallAfter(dlg.m_nextPageBtn.Enable) 206 | 207 | setStatus(f"{totalDevices} parts.") 208 | setPageText(f"Page {curPage}/{totalPages}") 209 | 210 | except KeyboardInterrupt: 211 | print("KeyboardInterrupt.") 212 | except Exception as e: 213 | traceback.print_exc() 214 | setStatus(f"Failed to search parts: {e}") 215 | 216 | finally: 217 | self.searchThread = None 218 | 219 | def loadSearchPage( facetId, words, page ): 220 | if self.searchThread: 221 | interrupt_thread(self.searchThread) 222 | self.searchThread.join() 223 | 224 | facet = [None, "lcsc", "user"][facetId] 225 | 226 | self.searchThread = Thread(target = searchFn, 227 | daemon=True, 228 | args=(facet, words, page)) 229 | self.searchThread.start() 230 | 231 | def onSearch( event ): 232 | self.searchPage = 1 233 | loadSearchPage(dlg.m_libSourceChoice.GetSelection(), dlg.m_textCtrlSearch.GetValue(), self.searchPage) 234 | 235 | def onNextPage( event ): 236 | self.searchPage += 1 237 | loadSearchPage(dlg.m_libSourceChoice.GetSelection(), dlg.m_textCtrlSearch.GetValue(), self.searchPage) 238 | 239 | def onPrevPage( event ): 240 | self.searchPage -= 1 241 | loadSearchPage(dlg.m_libSourceChoice.GetSelection(), dlg.m_textCtrlSearch.GetValue(), self.searchPage) 242 | 243 | def onSearchItemActivated( event ): 244 | if dlg.m_textCtrlParts.GetValue() and not dlg.m_textCtrlParts.GetValue().endswith("\n"): 245 | dlg.m_textCtrlParts.AppendText("\n") 246 | 247 | dlg.m_textCtrlParts.AppendText(dlg.m_searchResultsTree.GetItemText(event.GetItem()) + "\n") 248 | 249 | def onSearchItemSelected( event ): 250 | itemCode = dlg.m_searchResultsTree.GetItemText(event.GetItem()) 251 | 252 | if itemCode.startswith("C"): 253 | dlg.m_searchHyperlink1.SetLabelText( f"{itemCode} Preview" ) 254 | dlg.m_searchHyperlink1.SetURL( f"https://jlcpcb.com/user-center/lcsvg/svg.html?code={itemCode}" ) 255 | dlg.m_searchHyperlink1.Show() 256 | 257 | dlg.m_searchHyperlink2.SetLabelText( f"JLCPCB" ) 258 | dlg.m_searchHyperlink2.SetURL( f"https://jlcpcb.com/partdetail/{itemCode}" ) 259 | dlg.m_searchHyperlink2.Show() 260 | 261 | dlg.m_searchHyperlink3.SetLabelText( f"LCSC" ) 262 | dlg.m_searchHyperlink3.SetURL( f"https://www.lcsc.com/product-detail/{itemCode}.html" ) 263 | dlg.m_searchHyperlink3.Show() 264 | else: 265 | easyedaLink = None 266 | 267 | try: 268 | dev_info = requests.get(f"https://pro.easyeda.com/api/devices/{itemCode}") 269 | dev_info.raise_for_status() 270 | debug("device info: " + json.dumps(dev_info.json(), indent=4)) 271 | device = dev_info.json()["result"] 272 | attributes = device['attributes'] 273 | 274 | if attributes.get('Symbol') or attributes.get('Footprint'): 275 | # https://pro.easyeda.com/editor#tab=*!{sym_uuid}(device){dev_uuid}|!{fp_uuid}(device){dev_uuid} 276 | tabList = [] 277 | 278 | if attributes.get('Symbol'): 279 | tabList.append(f"!{attributes['Symbol']}(device){itemCode}") 280 | 281 | if attributes.get('Footprint'): 282 | tabList.append(f"!{attributes['Footprint']}(device){itemCode}") 283 | 284 | easyedaLink = f"https://pro.easyeda.com/editor#tab=*{'|'.join(tabList)}" 285 | except Exception as e: 286 | pass 287 | 288 | if easyedaLink: 289 | dlg.m_searchHyperlink1.SetLabelText( f"Open in EasyEDA Pro" ) 290 | dlg.m_searchHyperlink1.SetURL( easyedaLink ) 291 | dlg.m_searchHyperlink1.Show() 292 | else: 293 | dlg.m_searchHyperlink1.Hide() 294 | 295 | dlg.m_searchHyperlink2.Hide() 296 | dlg.m_searchHyperlink3.Hide() 297 | 298 | dlg.m_statusPanel.Layout() 299 | 300 | global wx_html2_available 301 | if wx_html2_available: 302 | self.webView.Hide() 303 | 304 | if itemCode.startswith("C"): 305 | self.webView.LoadURL( f"https://jlcpcb.com/user-center/lcsvg/svg.html?code={itemCode}" ) 306 | self.webView.SetZoomFactor(0.8) 307 | else: 308 | table_rows = ''.join( 309 | f""" 310 | {key} 311 | 312 | {value if not (isinstance(value, str) and value.startswith(('http://', 'https://'))) else f'{value}'} 313 | 314 | """ 315 | for key, value in attributes.items() 316 | ) 317 | 318 | style = """ 319 | body { 320 | font-family: sans-serif; 321 | } 322 | table { 323 | border:1px solid #CCC; 324 | border-collapse:collapse; 325 | } 326 | td { 327 | border:1px solid #CCC; 328 | padding: 2px; 329 | } 330 | """ 331 | 332 | html_content = f""" 333 | 334 | 335 | 338 | 339 | 340 |

Device UUID: {itemCode}

341 | 342 | {table_rows} 343 |
344 | 345 | 346 | """ 347 | self.webView.SetPage(html_content, "") 348 | self.webView.SetZoomFactor(1.0) 349 | 350 | def onWebviewLoaded( event ): 351 | self.webView.Show() 352 | 353 | def onWebviewNewWindow( event ): 354 | wx.LaunchDefaultBrowser( event.GetURL() ) 355 | 356 | def onClose( event ): 357 | if self.searchThread: 358 | interrupt_thread(self.searchThread) 359 | self.searchThread.join( 5 ) 360 | 361 | if self.downloadThread: 362 | interrupt_thread(self.downloadThread) 363 | self.downloadThread.join( 5 ) 364 | 365 | dlg.Destroy(); 366 | 367 | dlg.m_searchResultsTree.AppendColumn("Code/UUID", width=wx.COL_WIDTH_AUTOSIZE, flags=wx.COL_RESIZABLE | wx.COL_SORTABLE ) 368 | dlg.m_searchResultsTree.AppendColumn("Name", width=wx.COL_WIDTH_AUTOSIZE, flags=wx.COL_RESIZABLE | wx.COL_SORTABLE) 369 | dlg.m_searchResultsTree.AppendColumn("Manufacturer", width=wx.COL_WIDTH_AUTOSIZE, flags=wx.COL_RESIZABLE | wx.COL_SORTABLE) 370 | dlg.m_searchResultsTree.AppendColumn("Symbol", width=wx.COL_WIDTH_AUTOSIZE, flags=wx.COL_RESIZABLE | wx.COL_SORTABLE) 371 | dlg.m_searchResultsTree.AppendColumn("Footprint", width=wx.COL_WIDTH_AUTOSIZE, flags=wx.COL_RESIZABLE | wx.COL_SORTABLE) 372 | 373 | dlg.m_textCtrlOutLibName.SetValue("EasyEDA_Lib"); 374 | 375 | global wx_html2_available 376 | if wx_html2_available: 377 | try: 378 | self.webView = wx.html2.WebView.New(dlg.m_webViewPanel) 379 | self.webView.Bind(wx.html2.EVT_WEBVIEW_LOADED, onWebviewLoaded) 380 | self.webView.Bind(wx.html2.EVT_WEBVIEW_NEWWINDOW, onWebviewNewWindow) 381 | except NotImplementedError as err: 382 | self.webView = wx.StaticText(dlg.m_webViewPanel, style=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTRE_HORIZONTAL) 383 | self.webView.SetLabel("Preview is not supported in this wxPython environment.") 384 | dlg.m_webViewPanel.SetMinSize( wx.Size(20, 20) ) 385 | wx_html2_available = False 386 | else: 387 | self.webView = wx.StaticText(dlg.m_webViewPanel, style=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTRE_HORIZONTAL) 388 | self.webView.SetLabel("wx.html2 is not available. Install python3-wxgtk-webview4.0 (Debian/Ubuntu)") 389 | dlg.m_webViewPanel.SetMinSize( wx.Size(20, 20) ) 390 | 391 | dlg.m_webViewPanel.GetSizer().Add(self.webView, 1, wx.EXPAND) 392 | dlg.m_webViewPanel.Layout() 393 | 394 | dlg.SetEscapeId(wx.ID_CANCEL) 395 | dlg.Bind(wx.EVT_CLOSE, onClose) 396 | 397 | dlg.m_searchResultsTree.Bind(wx.dataview.EVT_TREELIST_ITEM_ACTIVATED, onSearchItemActivated) 398 | dlg.m_searchResultsTree.Bind(wx.dataview.EVT_TREELIST_SELECTION_CHANGED, onSearchItemSelected) 399 | dlg.m_actionBtn.Bind(wx.EVT_BUTTON, onDownload) 400 | dlg.m_searchBtn.Bind(wx.EVT_BUTTON, onSearch) 401 | dlg.m_prevPageBtn.Bind(wx.EVT_BUTTON, onPrevPage) 402 | dlg.m_nextPageBtn.Bind(wx.EVT_BUTTON, onNextPage) 403 | dlg.m_textCtrlSearch.Bind(wx.EVT_TEXT_ENTER, onSearch) 404 | dlg.m_libSourceChoice.Bind(wx.EVT_CHOICE, onSearch) 405 | dlg.m_debug.Bind(wx.EVT_CHECKBOX, onDebugCheckbox) 406 | 407 | dlg.m_textCtrlSearch.SetFocus() 408 | dlg.Show() 409 | -------------------------------------------------------------------------------- /easyeda_lib_loader.svg: -------------------------------------------------------------------------------- 1 | 2 | 19 | easyeda_3d_loader 21 | 45 | 56 | 57 | 59 | 60 | 62 | 64 | easyeda_3d_loader 65 | 66 | 68 | 70 | 72 | 74 | 75 | 76 | 77 | 79 | 81 | 90 | 95 | 96 | 97 | 99 | 104 | Lib 111 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /easyeda_lib_loader_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsa-t/jlc-kicad-lib-loader/c379260bb20031a6935565689739a65e98b4aa61/easyeda_lib_loader_64.png -------------------------------------------------------------------------------- /easyeda_lib_loader_dialog.fbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Python 6 | ; 7 | 1 8 | connect 9 | none 10 | 11 | 12 | 0 13 | 0 14 | res 15 | UTF-8 16 | easyeda_lib_loader_dialog 17 | 1000 18 | 0 19 | 1 20 | UI 21 | easyeda_lib_loader_dialog 22 | . 23 | 0 24 | source_name 25 | 1 26 | 0 27 | source_name 28 | 29 | 30 | 1 31 | 1 32 | 0 33 | 0 34 | 35 | 0 36 | wxAUI_MGR_DEFAULT 37 | 38 | wxBOTH 39 | 40 | 1 41 | 0 42 | 1 43 | impl_virtual 44 | 45 | 46 | 47 | 0 48 | wxID_ANY 49 | 50 | 51 | EasyEdaLibLoaderDialog 52 | 53 | -1,-1 54 | wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER 55 | ; ; forward_declare 56 | JLCPCB/LCSC Library Loader. Unofficial, use at your own risk. 57 | 58 | 0 59 | 60 | 61 | 62 | 63 | 64 | bSizer6 65 | wxHORIZONTAL 66 | none 67 | 68 | 5 69 | wxEXPAND 70 | 1 71 | 72 | 1 73 | 1 74 | 1 75 | 1 76 | 0 77 | 78 | 0 79 | 0 80 | 81 | 82 | 83 | 1 84 | 0 85 | 1 86 | 87 | 1 88 | 0 89 | Dock 90 | 0 91 | Left 92 | 0 93 | 1 94 | 95 | 1 96 | 97 | 0 98 | 0 99 | wxID_ANY 100 | 101 | 0 102 | 103 | 100 104 | 105 | 0 106 | 107 | 1 108 | m_splitter2 109 | 1 110 | 111 | 112 | protected 113 | 1 114 | 115 | Resizable 116 | 1 117 | 650 118 | -1 119 | 1 120 | 121 | wxSPLIT_VERTICAL 122 | wxSP_3D|wxSP_LIVE_UPDATE 123 | ; ; forward_declare 124 | 0 125 | 126 | 127 | 128 | 129 | 130 | 131 | 1 132 | 1 133 | 1 134 | 1 135 | 0 136 | 137 | 0 138 | 0 139 | 140 | 141 | 142 | 1 143 | 0 144 | 1 145 | 146 | 1 147 | 0 148 | Dock 149 | 0 150 | Left 151 | 0 152 | 1 153 | 154 | 1 155 | 156 | 0 157 | 0 158 | wxID_ANY 159 | 160 | 0 161 | 162 | 163 | 0 164 | 165 | 1 166 | m_leftPanel 167 | 1 168 | 169 | 170 | protected 171 | 1 172 | 173 | Resizable 174 | 1 175 | 176 | ; ; forward_declare 177 | 0 178 | 179 | 180 | 181 | wxTAB_TRAVERSAL 182 | 183 | 184 | bSizer4 185 | wxVERTICAL 186 | none 187 | 188 | 5 189 | wxEXPAND 190 | 0 191 | 192 | 193 | bSizer5 194 | wxHORIZONTAL 195 | none 196 | 197 | 5 198 | wxALIGN_CENTER_VERTICAL|wxALL 199 | 0 200 | 201 | 1 202 | 1 203 | 1 204 | 1 205 | 0 206 | 207 | 0 208 | 0 209 | 210 | 211 | 212 | 1 213 | 0 214 | "All Sources" "JLC System" "JLC Public" 215 | 1 216 | 217 | 1 218 | 0 219 | Dock 220 | 0 221 | Left 222 | 0 223 | 1 224 | 225 | 1 226 | 227 | 0 228 | 0 229 | wxID_ANY 230 | 231 | 0 232 | 233 | 234 | 0 235 | 236 | 1 237 | m_libSourceChoice 238 | 1 239 | 240 | 241 | protected 242 | 1 243 | 244 | Resizable 245 | 0 246 | 1 247 | 248 | 249 | ; ; forward_declare 250 | 0 251 | 252 | 253 | wxFILTER_NONE 254 | wxDefaultValidator 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 5 263 | wxALIGN_CENTER_VERTICAL|wxALL 264 | 1 265 | 266 | 1 267 | 1 268 | 1 269 | 1 270 | 0 271 | 272 | 0 273 | 0 274 | 275 | 276 | 277 | 1 278 | 0 279 | 1 280 | 281 | 1 282 | 0 283 | Dock 284 | 0 285 | Left 286 | 0 287 | 1 288 | 289 | 1 290 | 291 | 0 292 | 0 293 | wxID_ANY 294 | 295 | 0 296 | 297 | 0 298 | 299 | 0 300 | 301 | 1 302 | m_textCtrlSearch 303 | 1 304 | 305 | 306 | protected 307 | 1 308 | 309 | Resizable 310 | 1 311 | 312 | wxTE_PROCESS_ENTER 313 | ; ; forward_declare 314 | 0 315 | 316 | 317 | wxFILTER_NONE 318 | wxDefaultValidator 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 5 328 | wxALIGN_CENTER_VERTICAL|wxALL 329 | 0 330 | 331 | 1 332 | 1 333 | 1 334 | 1 335 | 0 336 | 337 | 0 338 | 0 339 | 0 340 | 341 | 342 | 343 | 344 | 1 345 | 0 346 | 1 347 | 348 | 1 349 | 350 | 0 351 | 0 352 | 353 | Dock 354 | 0 355 | Left 356 | 0 357 | 1 358 | 359 | 1 360 | 361 | 362 | 0 363 | 0 364 | wxID_ANY 365 | Find 366 | 367 | 0 368 | 369 | 0 370 | 371 | 372 | 0 373 | 374 | 1 375 | m_searchBtn 376 | 1 377 | 378 | 379 | protected 380 | 1 381 | 382 | 383 | 384 | Resizable 385 | 1 386 | 387 | 388 | ; ; forward_declare 389 | 0 390 | 391 | 392 | wxFILTER_NONE 393 | wxDefaultValidator 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 5 404 | wxEXPAND 405 | 1 406 | 407 | 1 408 | 1 409 | 1 410 | 1 411 | 0 412 | 413 | 0 414 | 0 415 | 416 | 417 | 418 | 1 419 | 0 420 | 1 421 | 422 | 1 423 | 0 424 | Dock 425 | 0 426 | Left 427 | 0 428 | 1 429 | 430 | 1 431 | 432 | 0 433 | 0 434 | wxID_ANY 435 | 436 | 0 437 | 438 | 100 439 | 440 | 0 441 | 442 | 1 443 | m_splitter5 444 | 1 445 | 446 | 447 | protected 448 | 1 449 | 450 | Resizable 451 | 0 452 | 0 453 | -1 454 | 1 455 | 650,680 456 | wxSPLIT_HORIZONTAL 457 | wxSP_3D|wxSP_LIVE_UPDATE 458 | ; ; forward_declare 459 | 0 460 | 461 | 462 | 463 | 464 | 465 | 466 | 1 467 | 1 468 | 1 469 | 1 470 | 0 471 | 472 | 0 473 | 0 474 | 475 | 476 | 477 | 1 478 | 0 479 | 1 480 | 481 | 1 482 | 0 483 | Dock 484 | 0 485 | Left 486 | 0 487 | 1 488 | 489 | 1 490 | 491 | 0 492 | 0 493 | wxID_ANY 494 | 495 | 0 496 | 497 | 498 | 0 499 | 500 | 1 501 | m_statusPanel 502 | 1 503 | 504 | 505 | protected 506 | 1 507 | 508 | Resizable 509 | 1 510 | 511 | ; ; forward_declare 512 | 0 513 | 514 | 515 | 516 | wxTAB_TRAVERSAL 517 | 518 | 519 | bSizer18 520 | wxVERTICAL 521 | none 522 | 523 | 5 524 | wxEXPAND | wxALL 525 | 1 526 | 527 | 1 528 | 1 529 | 1 530 | 1 531 | 0 532 | 533 | 0 534 | 0 535 | 536 | 537 | 538 | 1 539 | 0 540 | 1 541 | 542 | 1 543 | 0 544 | Dock 545 | 0 546 | Left 547 | 0 548 | 1 549 | 550 | 1 551 | 552 | 0 553 | 0 554 | wxID_ANY 555 | 556 | 0 557 | 558 | 559 | 0 560 | 400,300 561 | 1 562 | m_searchResultsTree 563 | 1 564 | 565 | 566 | protected 567 | 1 568 | 569 | Resizable 570 | 1 571 | 572 | wxTL_MULTIPLE 573 | ; ; forward_declare 574 | 0 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 5 583 | wxEXPAND 584 | 0 585 | 586 | 587 | bStatusSizer 588 | wxHORIZONTAL 589 | protected 590 | 591 | 5 592 | wxALIGN_CENTER_VERTICAL|wxALL 593 | 0 594 | 595 | 1 596 | 1 597 | 1 598 | 1 599 | 0 600 | 601 | 0 602 | 0 603 | 604 | 605 | 606 | 1 607 | 0 608 | 1 609 | 610 | 1 611 | 0 612 | Dock 613 | 0 614 | Left 615 | 0 616 | 1 617 | 618 | 1 619 | 620 | 0 621 | 0 622 | wxID_ANY 623 | 624 | 0 625 | 626 | 0 627 | 628 | 629 | 0 630 | 631 | 1 632 | m_searchStatus 633 | 1 634 | 635 | 636 | protected 637 | 1 638 | 639 | Resizable 640 | 1 641 | 642 | 643 | ; ; forward_declare 644 | 0 645 | 646 | 647 | 648 | 649 | -1 650 | 651 | 652 | 653 | 2 654 | wxALIGN_CENTER_VERTICAL|wxTOP 655 | 0 656 | 657 | 1 658 | 1 659 | 1 660 | 1 661 | 0 662 | 663 | 0 664 | 0 665 | 666 | 667 | 668 | 1 669 | 0 670 | 1 671 | 672 | 1 673 | 0 674 | Dock 675 | 0 676 | Left 677 | 0 678 | 1 679 | 680 | 1 681 | 682 | 0 683 | 0 684 | wxID_ANY 685 | 686 | 0 687 | 688 | 0 689 | 690 | 691 | 0 692 | 693 | 1 694 | m_searchStatus2 695 | 1 696 | 697 | 698 | protected 699 | 1 700 | 701 | Resizable 702 | 1 703 | 704 | 705 | ; ; forward_declare 706 | 0 707 | 708 | 709 | 710 | 711 | -1 712 | 713 | 714 | 715 | 5 716 | wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT|wxALL 717 | 0 718 | 719 | 1 720 | 1 721 | 1 722 | 1 723 | 0 724 | 725 | 0 726 | 0 727 | 728 | 729 | 730 | 1 731 | 0 732 | 1 733 | 734 | 1 735 | 0 736 | Dock 737 | 0 738 | Left 739 | 0 740 | 1 741 | 742 | 1 743 | 744 | 0 745 | 0 746 | 747 | wxID_ANY 748 | 749 | 750 | 0 751 | 752 | 753 | 0 754 | 755 | 1 756 | m_searchHyperlink1 757 | 758 | 1 759 | 760 | 761 | protected 762 | 1 763 | 764 | Resizable 765 | 1 766 | 767 | wxHL_ALIGN_LEFT|wxHL_CONTEXTMENU 768 | ; ; forward_declare 769 | 0 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 5 780 | wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT|wxALL 781 | 0 782 | 783 | 1 784 | 1 785 | 1 786 | 1 787 | 0 788 | 789 | 0 790 | 0 791 | 792 | 793 | 794 | 1 795 | 0 796 | 1 797 | 798 | 1 799 | 0 800 | Dock 801 | 0 802 | Left 803 | 0 804 | 1 805 | 806 | 1 807 | 808 | 0 809 | 0 810 | 811 | wxID_ANY 812 | 813 | 814 | 0 815 | 816 | 817 | 0 818 | 819 | 1 820 | m_searchHyperlink2 821 | 822 | 1 823 | 824 | 825 | protected 826 | 1 827 | 828 | Resizable 829 | 1 830 | 831 | wxHL_ALIGN_LEFT|wxHL_CONTEXTMENU 832 | ; ; forward_declare 833 | 0 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 5 844 | wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT|wxALL 845 | 0 846 | 847 | 1 848 | 1 849 | 1 850 | 1 851 | 0 852 | 853 | 0 854 | 0 855 | 856 | 857 | 858 | 1 859 | 0 860 | 1 861 | 862 | 1 863 | 0 864 | Dock 865 | 0 866 | Left 867 | 0 868 | 1 869 | 870 | 1 871 | 872 | 0 873 | 0 874 | 875 | wxID_ANY 876 | 877 | 878 | 0 879 | 880 | 881 | 0 882 | 883 | 1 884 | m_searchHyperlink3 885 | 886 | 1 887 | 888 | 889 | protected 890 | 1 891 | 892 | Resizable 893 | 1 894 | 895 | wxHL_ALIGN_LEFT|wxHL_CONTEXTMENU 896 | ; ; forward_declare 897 | 0 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 5 908 | wxEXPAND 909 | 1 910 | 911 | 0 912 | protected 913 | 0 914 | 915 | 916 | 917 | 5 918 | wxALIGN_CENTER_VERTICAL|wxALL 919 | 0 920 | 921 | 1 922 | 1 923 | 1 924 | 1 925 | 0 926 | 927 | 0 928 | 0 929 | 930 | 931 | 932 | 1 933 | 0 934 | 1 935 | 936 | 1 937 | 0 938 | Dock 939 | 0 940 | Left 941 | 0 942 | 1 943 | 944 | 1 945 | 946 | 0 947 | 0 948 | wxID_ANY 949 | 950 | 0 951 | 952 | 0 953 | 954 | 955 | 0 956 | 957 | 1 958 | m_searchPage 959 | 1 960 | 961 | 962 | protected 963 | 1 964 | 965 | Resizable 966 | 1 967 | 968 | 969 | ; ; forward_declare 970 | 0 971 | 972 | 973 | 974 | 975 | -1 976 | 977 | 978 | 979 | 5 980 | wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxTOP 981 | 0 982 | 983 | 1 984 | 1 985 | 1 986 | 1 987 | 0 988 | 989 | 0 990 | 0 991 | 0 992 | 993 | 994 | 995 | 996 | 1 997 | 0 998 | 1 999 | 1000 | 1 1001 | 1002 | 0 1003 | 0 1004 | 1005 | Dock 1006 | 0 1007 | Left 1008 | 0 1009 | 0 1010 | 1011 | 1 1012 | 1013 | 1014 | 0 1015 | 0 1016 | wxID_ANY 1017 | < 1018 | 1019 | 0 1020 | 1021 | 0 1022 | 1023 | 1024 | 0 1025 | 1026 | 1 1027 | m_prevPageBtn 1028 | 1 1029 | 1030 | 1031 | protected 1032 | 1 1033 | 1034 | 1035 | 1036 | Resizable 1037 | 1 1038 | 1039 | wxBU_EXACTFIT 1040 | ; ; forward_declare 1041 | 0 1042 | 1043 | 1044 | wxFILTER_NONE 1045 | wxDefaultValidator 1046 | 1047 | 1048 | 1049 | 1050 | 1051 | 1052 | 1053 | 5 1054 | wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxTOP 1055 | 0 1056 | 1057 | 1 1058 | 1 1059 | 1 1060 | 1 1061 | 0 1062 | 1063 | 0 1064 | 0 1065 | 0 1066 | 1067 | 1068 | 1069 | 1070 | 1 1071 | 0 1072 | 1 1073 | 1074 | 1 1075 | 1076 | 0 1077 | 0 1078 | 1079 | Dock 1080 | 0 1081 | Left 1082 | 0 1083 | 0 1084 | 1085 | 1 1086 | 1087 | 1088 | 0 1089 | 0 1090 | wxID_ANY 1091 | > 1092 | 1093 | 0 1094 | 1095 | 0 1096 | 1097 | 1098 | 0 1099 | 1100 | 1 1101 | m_nextPageBtn 1102 | 1 1103 | 1104 | 1105 | protected 1106 | 1 1107 | 1108 | 1109 | 1110 | Resizable 1111 | 1 1112 | 1113 | wxBU_EXACTFIT 1114 | ; ; forward_declare 1115 | 0 1116 | 1117 | 1118 | wxFILTER_NONE 1119 | wxDefaultValidator 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1130 | 1131 | 1132 | 1133 | 1 1134 | 1 1135 | 1 1136 | 1 1137 | 0 1138 | 1139 | 0 1140 | 0 1141 | 1142 | 1143 | 1144 | 1 1145 | 0 1146 | 1 1147 | 1148 | 1 1149 | 0 1150 | Dock 1151 | 0 1152 | Left 1153 | 0 1154 | 1 1155 | 1156 | 1 1157 | 1158 | 0 1159 | 0 1160 | wxID_ANY 1161 | 1162 | 0 1163 | 1164 | 1165 | 0 1166 | 650,380 1167 | 1 1168 | m_webViewPanel 1169 | 1 1170 | 1171 | 1172 | protected 1173 | 1 1174 | 1175 | Resizable 1176 | 1 1177 | 1178 | ; ; forward_declare 1179 | 0 1180 | 1181 | 1182 | 1183 | wxTAB_TRAVERSAL 1184 | 1185 | -1,-1 1186 | bSizer19 1187 | wxVERTICAL 1188 | none 1189 | 1190 | 1191 | 1192 | 1193 | 1194 | 1195 | 1196 | 1197 | 1198 | 1199 | 1 1200 | 1 1201 | 1 1202 | 1 1203 | 0 1204 | 1205 | 0 1206 | 0 1207 | 1208 | 1209 | 1210 | 1 1211 | 0 1212 | 1 1213 | 1214 | 1 1215 | 0 1216 | Dock 1217 | 0 1218 | Left 1219 | 0 1220 | 1 1221 | 1222 | 1 1223 | 1224 | 0 1225 | 0 1226 | wxID_ANY 1227 | 1228 | 0 1229 | 1230 | 1231 | 0 1232 | 1233 | 1 1234 | m_panel1 1235 | 1 1236 | 1237 | 1238 | protected 1239 | 1 1240 | 1241 | Resizable 1242 | 1 1243 | 1244 | ; ; forward_declare 1245 | 0 1246 | 1247 | 1248 | 1249 | wxTAB_TRAVERSAL 1250 | 1251 | 1252 | bSizer13 1253 | wxVERTICAL 1254 | none 1255 | 1256 | 5 1257 | wxEXPAND 1258 | 1 1259 | 1260 | 1 1261 | 1 1262 | 1 1263 | 1 1264 | 0 1265 | 1266 | 0 1267 | 0 1268 | 1269 | 1270 | 1271 | 1 1272 | 0 1273 | 1 1274 | 1275 | 1 1276 | 0 1277 | Dock 1278 | 0 1279 | Left 1280 | 0 1281 | 1 1282 | 1283 | 1 1284 | 1285 | 0 1286 | 0 1287 | wxID_ANY 1288 | 1289 | 0 1290 | 1291 | 100 1292 | 1293 | 0 1294 | 1295 | 1 1296 | m_splitter3 1297 | 1 1298 | 1299 | 1300 | protected 1301 | 1 1302 | 1303 | Resizable 1304 | 0.5 1305 | 0 1306 | -1 1307 | 1 1308 | 1309 | wxSPLIT_HORIZONTAL 1310 | wxSP_3D|wxSP_LIVE_UPDATE 1311 | ; ; forward_declare 1312 | 0 1313 | 1314 | 1315 | 1316 | 1317 | 1318 | 1319 | 1 1320 | 1 1321 | 1 1322 | 1 1323 | 0 1324 | 1325 | 0 1326 | 0 1327 | 1328 | 1329 | 1330 | 1 1331 | 0 1332 | 1 1333 | 1334 | 1 1335 | 0 1336 | Dock 1337 | 0 1338 | Left 1339 | 0 1340 | 1 1341 | 1342 | 1 1343 | 1344 | 0 1345 | 0 1346 | wxID_ANY 1347 | 1348 | 0 1349 | 1350 | 1351 | 0 1352 | 1353 | 1 1354 | m_panel5 1355 | 1 1356 | 1357 | 1358 | protected 1359 | 1 1360 | 1361 | Resizable 1362 | 1 1363 | 1364 | ; ; forward_declare 1365 | 0 1366 | 1367 | 1368 | 1369 | wxTAB_TRAVERSAL 1370 | 1371 | 1372 | bSizer1 1373 | wxVERTICAL 1374 | none 1375 | 1376 | 5 1377 | wxALL 1378 | 0 1379 | 1380 | 1 1381 | 1 1382 | 1 1383 | 1 1384 | 0 1385 | 1386 | 0 1387 | 0 1388 | 1389 | 1390 | 1391 | 1 1392 | 0 1393 | 1 1394 | 1395 | 1 1396 | 0 1397 | Dock 1398 | 0 1399 | Left 1400 | 0 1401 | 1 1402 | 1403 | 1 1404 | 1405 | 0 1406 | 0 1407 | wxID_ANY 1408 | Enter JLCPCB/LCSC codes or UUIDs to download: 1409 | 0 1410 | 1411 | 0 1412 | 1413 | 1414 | 0 1415 | 1416 | 1 1417 | m_staticText1 1418 | 1 1419 | 1420 | 1421 | protected 1422 | 1 1423 | 1424 | Resizable 1425 | 1 1426 | 1427 | 1428 | ; ; forward_declare 1429 | 0 1430 | 1431 | 1432 | 1433 | 1434 | -1 1435 | 1436 | 1437 | 1438 | 5 1439 | wxALL|wxEXPAND 1440 | 1 1441 | 1442 | 1 1443 | 1 1444 | 1 1445 | 1 1446 | 0 1447 | 1448 | 0 1449 | 0 1450 | 1451 | 1452 | 1453 | 1 1454 | 0 1455 | 1 1456 | 1457 | 1 1458 | 0 1459 | Dock 1460 | 0 1461 | Left 1462 | 0 1463 | 1 1464 | 1465 | 1 1466 | 1467 | 0 1468 | 0 1469 | wxID_ANY 1470 | 1471 | 0 1472 | 1473 | 0 1474 | 1475 | 0 1476 | 1477 | 1 1478 | m_textCtrlParts 1479 | 1 1480 | 1481 | 1482 | protected 1483 | 1 1484 | 1485 | Resizable 1486 | 1 1487 | 1488 | wxTE_MULTILINE 1489 | ; ; forward_declare 1490 | 0 1491 | 1492 | 1493 | wxFILTER_NONE 1494 | wxDefaultValidator 1495 | 1496 | 1497 | 1498 | 1499 | 1500 | 1501 | 1502 | 1503 | 5 1504 | wxEXPAND 1505 | 0 1506 | 1507 | 1508 | bSizer3 1509 | wxHORIZONTAL 1510 | none 1511 | 1512 | 5 1513 | wxALIGN_CENTER_VERTICAL|wxALL 1514 | 0 1515 | 1516 | 1 1517 | 1 1518 | 1 1519 | 1 1520 | 0 1521 | 1522 | 0 1523 | 0 1524 | 1525 | 1526 | 1527 | 1 1528 | 0 1529 | 1 1530 | 1531 | 1 1532 | 0 1533 | Dock 1534 | 0 1535 | Left 1536 | 0 1537 | 1 1538 | 1539 | 1 1540 | 1541 | 0 1542 | 0 1543 | wxID_ANY 1544 | Library path: 1545 | 0 1546 | 1547 | 0 1548 | 1549 | 1550 | 0 1551 | 1552 | 1 1553 | m_staticText2 1554 | 1 1555 | 1556 | 1557 | protected 1558 | 1 1559 | 1560 | Resizable 1561 | 1 1562 | 1563 | 1564 | ; ; forward_declare 1565 | 0 1566 | 1567 | 1568 | 1569 | 1570 | -1 1571 | 1572 | 1573 | 1574 | 5 1575 | wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND 1576 | 1 1577 | 1578 | 1 1579 | 1 1580 | 1 1581 | 1 1582 | 0 1583 | 1584 | 0 1585 | 0 1586 | 1587 | 1588 | 1589 | 1 1590 | 0 1591 | 1 1592 | 1593 | 1 1594 | 0 1595 | Dock 1596 | 0 1597 | Left 1598 | 0 1599 | 1 1600 | 1601 | 1 1602 | 1603 | 0 1604 | 0 1605 | wxID_ANY 1606 | 1607 | 0 1608 | 1609 | 0 1610 | 1611 | 0 1612 | 1613 | 1 1614 | m_textCtrlOutLibName 1615 | 1 1616 | 1617 | 1618 | protected 1619 | 1 1620 | 1621 | Resizable 1622 | 1 1623 | 1624 | 1625 | ; ; forward_declare 1626 | 0 1627 | 1628 | 1629 | wxFILTER_NONE 1630 | wxDefaultValidator 1631 | 1632 | 1633 | 1634 | 1635 | 1636 | 1637 | 1638 | 1639 | 1640 | 1641 | 5 1642 | wxALIGN_CENTER_HORIZONTAL|wxALL 1643 | 0 1644 | 1645 | 1 1646 | 1 1647 | 1 1648 | 1 1649 | 0 1650 | 1651 | 0 1652 | 0 1653 | 0 1654 | 1655 | 1656 | 1657 | 1658 | 1 1659 | 0 1660 | 1 1661 | 1662 | 1 1663 | 1664 | 0 1665 | 0 1666 | 1667 | Dock 1668 | 0 1669 | Left 1670 | 0 1671 | 1 1672 | 1673 | 1 1674 | 1675 | 1676 | 0 1677 | 0 1678 | wxID_ANY 1679 | Download parts 1680 | 1681 | 0 1682 | 1683 | 0 1684 | 1685 | 1686 | 0 1687 | 1688 | 1 1689 | m_actionBtn 1690 | 1 1691 | 1692 | 1693 | protected 1694 | 1 1695 | 1696 | 1697 | 1698 | Resizable 1699 | 1 1700 | 1701 | 1702 | ; ; forward_declare 1703 | 0 1704 | 1705 | 1706 | wxFILTER_NONE 1707 | wxDefaultValidator 1708 | 1709 | 1710 | 1711 | 1712 | 1713 | 1714 | 1715 | 1716 | 1717 | 1718 | 1719 | 1 1720 | 1 1721 | 1 1722 | 1 1723 | 0 1724 | 1725 | 0 1726 | 0 1727 | 1728 | 1729 | 1730 | 1 1731 | 0 1732 | 1 1733 | 1734 | 1 1735 | 0 1736 | Dock 1737 | 0 1738 | Left 1739 | 0 1740 | 1 1741 | 1742 | 1 1743 | 1744 | 0 1745 | 0 1746 | wxID_ANY 1747 | 1748 | 0 1749 | 1750 | 1751 | 0 1752 | 1753 | 1 1754 | m_panel6 1755 | 1 1756 | 1757 | 1758 | protected 1759 | 1 1760 | 1761 | Resizable 1762 | 1 1763 | 1764 | ; ; forward_declare 1765 | 0 1766 | 1767 | 1768 | 1769 | wxTAB_TRAVERSAL 1770 | 1771 | 1772 | bSizer14 1773 | wxVERTICAL 1774 | none 1775 | 1776 | 5 1777 | wxALL|wxEXPAND 1778 | 0 1779 | 1780 | 1 1781 | 1 1782 | 1 1783 | 1 1784 | 0 1785 | 1786 | 0 1787 | 0 1788 | 1789 | 1790 | 1791 | 1 1792 | 0 1793 | 1 1794 | 1795 | 1 1796 | 0 1797 | Dock 1798 | 0 1799 | Left 1800 | 0 1801 | 1 1802 | 1803 | 1 1804 | 1805 | 0 1806 | 0 1807 | wxID_ANY 1808 | 1809 | 0 1810 | 1811 | 1812 | 0 1813 | 1814 | 1 1815 | m_progress 1816 | 1 1817 | 1818 | 1819 | protected 1820 | 1 1821 | 1822 | 100 1823 | Resizable 1824 | 1 1825 | 1826 | wxGA_HORIZONTAL 1827 | ; ; forward_declare 1828 | 0 1829 | 1830 | 1831 | wxFILTER_NONE 1832 | wxDefaultValidator 1833 | 1834 | 0 1835 | 1836 | 1837 | 1838 | 1839 | 1840 | 1841 | 5 1842 | wxALL|wxEXPAND 1843 | 1 1844 | 1845 | 1 1846 | 1 1847 | 1 1848 | 1 1849 | 0 1850 | 1851 | 0 1852 | 0 1853 | 1854 | 1855 | 1856 | 1 1857 | 0 1858 | 1 1859 | 1860 | 1 1861 | 0 1862 | Dock 1863 | 0 1864 | Left 1865 | 0 1866 | 1 1867 | 1868 | 1 1869 | 1870 | 0 1871 | 0 1872 | wxID_ANY 1873 | 1874 | 0 1875 | 1876 | 0 1877 | 1878 | 0 1879 | 400,40 1880 | 1 1881 | m_log 1882 | 1 1883 | 1884 | 1885 | protected 1886 | 1 1887 | 1888 | Resizable 1889 | 1 1890 | 1891 | wxTE_BESTWRAP|wxTE_MULTILINE|wxTE_READONLY 1892 | ; ; forward_declare 1893 | 0 1894 | 1895 | 1896 | wxFILTER_NONE 1897 | wxDefaultValidator 1898 | 1899 | 1900 | 1901 | 1902 | 1903 | 1904 | 1905 | 1906 | 5 1907 | wxEXPAND 1908 | 0 1909 | 1910 | 1911 | bSizer2 1912 | wxHORIZONTAL 1913 | none 1914 | 1915 | 5 1916 | wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT|wxALL 1917 | 0 1918 | 1919 | 1 1920 | 1 1921 | 1 1922 | 1 1923 | 0 1924 | 1925 | 0 1926 | 0 1927 | 1928 | 1929 | 1930 | 1 1931 | 0 1932 | 0 1933 | 1 1934 | 1935 | 1 1936 | 0 1937 | Dock 1938 | 0 1939 | Left 1940 | 0 1941 | 1 1942 | 1943 | 1 1944 | 1945 | 0 1946 | 0 1947 | wxID_ANY 1948 | Debug 1949 | 1950 | 0 1951 | 1952 | 1953 | 0 1954 | 1955 | 1 1956 | m_debug 1957 | 1 1958 | 1959 | 1960 | protected 1961 | 1 1962 | 1963 | Resizable 1964 | 1 1965 | 1966 | 1967 | ; ; forward_declare 1968 | 0 1969 | 1970 | 1971 | wxFILTER_NONE 1972 | wxDefaultValidator 1973 | 1974 | 1975 | 1976 | 1977 | 1978 | 1979 | 1980 | 5 1981 | wxEXPAND 1982 | 1 1983 | 1984 | 0 1985 | protected 1986 | 0 1987 | 1988 | 1989 | 1990 | 5 1991 | wxALIGN_CENTER_VERTICAL|wxALL 1992 | 0 1993 | 1994 | 1 1995 | 1 1996 | 1 1997 | 1 1998 | 0 1999 | 2000 | 0 2001 | 0 2002 | 0 2003 | 2004 | 2005 | 2006 | 2007 | 1 2008 | 0 2009 | 1 2010 | 2011 | 1 2012 | 2013 | 0 2014 | 0 2015 | 2016 | Dock 2017 | 0 2018 | Left 2019 | 0 2020 | 1 2021 | 2022 | 1 2023 | 2024 | 2025 | 0 2026 | 0 2027 | wxID_CANCEL 2028 | Close dialog 2029 | 2030 | 0 2031 | 2032 | 0 2033 | 2034 | 2035 | 0 2036 | 2037 | 1 2038 | m_closeButton 2039 | 1 2040 | 2041 | 2042 | protected 2043 | 1 2044 | 2045 | 2046 | 2047 | Resizable 2048 | 1 2049 | 2050 | 2051 | ; ; forward_declare 2052 | 0 2053 | 2054 | 2055 | wxFILTER_NONE 2056 | wxDefaultValidator 2057 | 2058 | 2059 | 2060 | 2061 | 2062 | 2063 | 2064 | 2065 | 2066 | 2067 | 2068 | 2069 | 2070 | 2071 | 2072 | 2073 | 2074 | 2075 | 2076 | 2077 | 2078 | 2079 | -------------------------------------------------------------------------------- /easyeda_lib_loader_dialog.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ########################################################################### 4 | ## Python code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6) 5 | ## http://www.wxformbuilder.org/ 6 | ## 7 | ## PLEASE DO *NOT* EDIT THIS FILE! 8 | ########################################################################### 9 | 10 | import wx 11 | import wx.xrc 12 | import wx.dataview 13 | import wx.adv 14 | 15 | ########################################################################### 16 | ## Class EasyEdaLibLoaderDialog 17 | ########################################################################### 18 | 19 | class EasyEdaLibLoaderDialog ( wx.Dialog ): 20 | 21 | def __init__( self, parent ): 22 | wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"JLCPCB/LCSC Library Loader. Unofficial, use at your own risk.", pos = wx.DefaultPosition, size = wx.Size( -1,-1 ), style = wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER ) 23 | 24 | self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) 25 | 26 | bSizer6 = wx.BoxSizer( wx.HORIZONTAL ) 27 | 28 | self.m_splitter2 = wx.SplitterWindow( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.SP_3D|wx.SP_LIVE_UPDATE ) 29 | self.m_splitter2.SetSashGravity( 1 ) 30 | self.m_splitter2.Bind( wx.EVT_IDLE, self.m_splitter2OnIdle ) 31 | self.m_splitter2.SetMinimumPaneSize( 100 ) 32 | 33 | self.m_leftPanel = wx.Panel( self.m_splitter2, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) 34 | bSizer4 = wx.BoxSizer( wx.VERTICAL ) 35 | 36 | bSizer5 = wx.BoxSizer( wx.HORIZONTAL ) 37 | 38 | m_libSourceChoiceChoices = [ u"All Sources", u"JLC System", u"JLC Public" ] 39 | self.m_libSourceChoice = wx.Choice( self.m_leftPanel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, m_libSourceChoiceChoices, 0 ) 40 | self.m_libSourceChoice.SetSelection( 0 ) 41 | bSizer5.Add( self.m_libSourceChoice, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) 42 | 43 | self.m_textCtrlSearch = wx.TextCtrl( self.m_leftPanel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER ) 44 | bSizer5.Add( self.m_textCtrlSearch, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) 45 | 46 | self.m_searchBtn = wx.Button( self.m_leftPanel, wx.ID_ANY, u"Find", wx.DefaultPosition, wx.DefaultSize, 0 ) 47 | bSizer5.Add( self.m_searchBtn, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) 48 | 49 | 50 | bSizer4.Add( bSizer5, 0, wx.EXPAND, 5 ) 51 | 52 | self.m_splitter5 = wx.SplitterWindow( self.m_leftPanel, wx.ID_ANY, wx.DefaultPosition, wx.Size( 650,680 ), wx.SP_3D|wx.SP_LIVE_UPDATE ) 53 | self.m_splitter5.SetSashGravity( 0 ) 54 | self.m_splitter5.Bind( wx.EVT_IDLE, self.m_splitter5OnIdle ) 55 | self.m_splitter5.SetMinimumPaneSize( 100 ) 56 | 57 | self.m_statusPanel = wx.Panel( self.m_splitter5, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) 58 | bSizer18 = wx.BoxSizer( wx.VERTICAL ) 59 | 60 | self.m_searchResultsTree = wx.dataview.TreeListCtrl( self.m_statusPanel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.dataview.TL_MULTIPLE ) 61 | self.m_searchResultsTree.SetMinSize( wx.Size( 400,300 ) ) 62 | 63 | 64 | bSizer18.Add( self.m_searchResultsTree, 1, wx.EXPAND |wx.ALL, 5 ) 65 | 66 | bStatusSizer = wx.BoxSizer( wx.HORIZONTAL ) 67 | 68 | self.m_searchStatus = wx.StaticText( self.m_statusPanel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) 69 | self.m_searchStatus.Wrap( -1 ) 70 | 71 | bStatusSizer.Add( self.m_searchStatus, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) 72 | 73 | self.m_searchStatus2 = wx.StaticText( self.m_statusPanel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) 74 | self.m_searchStatus2.Wrap( -1 ) 75 | 76 | bStatusSizer.Add( self.m_searchStatus2, 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, 2 ) 77 | 78 | self.m_searchHyperlink1 = wx.adv.HyperlinkCtrl( self.m_statusPanel, wx.ID_ANY, wx.EmptyString, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.adv.HL_ALIGN_LEFT|wx.adv.HL_CONTEXTMENU ) 79 | bStatusSizer.Add( self.m_searchHyperlink1, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT|wx.ALL, 5 ) 80 | 81 | self.m_searchHyperlink2 = wx.adv.HyperlinkCtrl( self.m_statusPanel, wx.ID_ANY, wx.EmptyString, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.adv.HL_ALIGN_LEFT|wx.adv.HL_CONTEXTMENU ) 82 | bStatusSizer.Add( self.m_searchHyperlink2, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT|wx.ALL, 5 ) 83 | 84 | self.m_searchHyperlink3 = wx.adv.HyperlinkCtrl( self.m_statusPanel, wx.ID_ANY, wx.EmptyString, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.adv.HL_ALIGN_LEFT|wx.adv.HL_CONTEXTMENU ) 85 | bStatusSizer.Add( self.m_searchHyperlink3, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT|wx.ALL, 5 ) 86 | 87 | 88 | bStatusSizer.Add( ( 0, 0), 1, wx.EXPAND, 5 ) 89 | 90 | self.m_searchPage = wx.StaticText( self.m_statusPanel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) 91 | self.m_searchPage.Wrap( -1 ) 92 | 93 | bStatusSizer.Add( self.m_searchPage, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) 94 | 95 | self.m_prevPageBtn = wx.Button( self.m_statusPanel, wx.ID_ANY, u" < ", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) 96 | self.m_prevPageBtn.Enable( False ) 97 | 98 | bStatusSizer.Add( self.m_prevPageBtn, 0, wx.ALIGN_CENTER_VERTICAL|wx.BOTTOM|wx.TOP, 5 ) 99 | 100 | self.m_nextPageBtn = wx.Button( self.m_statusPanel, wx.ID_ANY, u" > ", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) 101 | self.m_nextPageBtn.Enable( False ) 102 | 103 | bStatusSizer.Add( self.m_nextPageBtn, 0, wx.ALIGN_CENTER_VERTICAL|wx.BOTTOM|wx.RIGHT|wx.TOP, 5 ) 104 | 105 | 106 | bSizer18.Add( bStatusSizer, 0, wx.EXPAND, 5 ) 107 | 108 | 109 | self.m_statusPanel.SetSizer( bSizer18 ) 110 | self.m_statusPanel.Layout() 111 | bSizer18.Fit( self.m_statusPanel ) 112 | self.m_webViewPanel = wx.Panel( self.m_splitter5, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) 113 | self.m_webViewPanel.SetMinSize( wx.Size( 650,380 ) ) 114 | 115 | bSizer19 = wx.BoxSizer( wx.VERTICAL ) 116 | 117 | 118 | self.m_webViewPanel.SetSizer( bSizer19 ) 119 | self.m_webViewPanel.Layout() 120 | bSizer19.Fit( self.m_webViewPanel ) 121 | self.m_splitter5.SplitHorizontally( self.m_statusPanel, self.m_webViewPanel, 0 ) 122 | bSizer4.Add( self.m_splitter5, 1, wx.EXPAND, 5 ) 123 | 124 | 125 | self.m_leftPanel.SetSizer( bSizer4 ) 126 | self.m_leftPanel.Layout() 127 | bSizer4.Fit( self.m_leftPanel ) 128 | self.m_panel1 = wx.Panel( self.m_splitter2, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) 129 | bSizer13 = wx.BoxSizer( wx.VERTICAL ) 130 | 131 | self.m_splitter3 = wx.SplitterWindow( self.m_panel1, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.SP_3D|wx.SP_LIVE_UPDATE ) 132 | self.m_splitter3.SetSashGravity( 0.5 ) 133 | self.m_splitter3.Bind( wx.EVT_IDLE, self.m_splitter3OnIdle ) 134 | self.m_splitter3.SetMinimumPaneSize( 100 ) 135 | 136 | self.m_panel5 = wx.Panel( self.m_splitter3, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) 137 | bSizer1 = wx.BoxSizer( wx.VERTICAL ) 138 | 139 | self.m_staticText1 = wx.StaticText( self.m_panel5, wx.ID_ANY, u"Enter JLCPCB/LCSC codes or UUIDs to download:", wx.DefaultPosition, wx.DefaultSize, 0 ) 140 | self.m_staticText1.Wrap( -1 ) 141 | 142 | bSizer1.Add( self.m_staticText1, 0, wx.ALL, 5 ) 143 | 144 | self.m_textCtrlParts = wx.TextCtrl( self.m_panel5, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_MULTILINE ) 145 | bSizer1.Add( self.m_textCtrlParts, 1, wx.ALL|wx.EXPAND, 5 ) 146 | 147 | bSizer3 = wx.BoxSizer( wx.HORIZONTAL ) 148 | 149 | self.m_staticText2 = wx.StaticText( self.m_panel5, wx.ID_ANY, u"Library path:", wx.DefaultPosition, wx.DefaultSize, 0 ) 150 | self.m_staticText2.Wrap( -1 ) 151 | 152 | bSizer3.Add( self.m_staticText2, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) 153 | 154 | self.m_textCtrlOutLibName = wx.TextCtrl( self.m_panel5, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) 155 | bSizer3.Add( self.m_textCtrlOutLibName, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, 5 ) 156 | 157 | 158 | bSizer1.Add( bSizer3, 0, wx.EXPAND, 5 ) 159 | 160 | self.m_actionBtn = wx.Button( self.m_panel5, wx.ID_ANY, u"Download parts", wx.DefaultPosition, wx.DefaultSize, 0 ) 161 | bSizer1.Add( self.m_actionBtn, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 5 ) 162 | 163 | 164 | self.m_panel5.SetSizer( bSizer1 ) 165 | self.m_panel5.Layout() 166 | bSizer1.Fit( self.m_panel5 ) 167 | self.m_panel6 = wx.Panel( self.m_splitter3, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) 168 | bSizer14 = wx.BoxSizer( wx.VERTICAL ) 169 | 170 | self.m_progress = wx.Gauge( self.m_panel6, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL ) 171 | self.m_progress.SetValue( 0 ) 172 | bSizer14.Add( self.m_progress, 0, wx.ALL|wx.EXPAND, 5 ) 173 | 174 | self.m_log = wx.TextCtrl( self.m_panel6, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_BESTWRAP|wx.TE_MULTILINE|wx.TE_READONLY ) 175 | self.m_log.SetMinSize( wx.Size( 400,40 ) ) 176 | 177 | bSizer14.Add( self.m_log, 1, wx.ALL|wx.EXPAND, 5 ) 178 | 179 | bSizer2 = wx.BoxSizer( wx.HORIZONTAL ) 180 | 181 | self.m_debug = wx.CheckBox( self.m_panel6, wx.ID_ANY, u"Debug", wx.DefaultPosition, wx.DefaultSize, 0 ) 182 | bSizer2.Add( self.m_debug, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT|wx.ALL, 5 ) 183 | 184 | 185 | bSizer2.Add( ( 0, 0), 1, wx.EXPAND, 5 ) 186 | 187 | self.m_closeButton = wx.Button( self.m_panel6, wx.ID_CANCEL, u"Close dialog", wx.DefaultPosition, wx.DefaultSize, 0 ) 188 | bSizer2.Add( self.m_closeButton, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) 189 | 190 | 191 | bSizer14.Add( bSizer2, 0, wx.EXPAND, 5 ) 192 | 193 | 194 | self.m_panel6.SetSizer( bSizer14 ) 195 | self.m_panel6.Layout() 196 | bSizer14.Fit( self.m_panel6 ) 197 | self.m_splitter3.SplitHorizontally( self.m_panel5, self.m_panel6, 0 ) 198 | bSizer13.Add( self.m_splitter3, 1, wx.EXPAND, 5 ) 199 | 200 | 201 | self.m_panel1.SetSizer( bSizer13 ) 202 | self.m_panel1.Layout() 203 | bSizer13.Fit( self.m_panel1 ) 204 | self.m_splitter2.SplitVertically( self.m_leftPanel, self.m_panel1, 650 ) 205 | bSizer6.Add( self.m_splitter2, 1, wx.EXPAND, 5 ) 206 | 207 | 208 | self.SetSizer( bSizer6 ) 209 | self.Layout() 210 | bSizer6.Fit( self ) 211 | 212 | self.Centre( wx.BOTH ) 213 | 214 | def __del__( self ): 215 | pass 216 | 217 | def m_splitter2OnIdle( self, event ): 218 | self.m_splitter2.SetSashPosition( 650 ) 219 | self.m_splitter2.Unbind( wx.EVT_IDLE ) 220 | 221 | def m_splitter5OnIdle( self, event ): 222 | self.m_splitter5.SetSashPosition( 0 ) 223 | self.m_splitter5.Unbind( wx.EVT_IDLE ) 224 | 225 | def m_splitter3OnIdle( self, event ): 226 | self.m_splitter3.SetSashPosition( 0 ) 227 | self.m_splitter3.Unbind( wx.EVT_IDLE ) 228 | 229 | 230 | -------------------------------------------------------------------------------- /pcm/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsa-t/jlc-kicad-lib-loader/c379260bb20031a6935565689739a65e98b4aa61/pcm/icon.png -------------------------------------------------------------------------------- /pcm/metadata.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://go.kicad.org/pcm/schemas/v1", 3 | "name": "JLCPCB/LCSC Library Loader", 4 | "description": "Browse and download parts from JLCPCB/LCSC.", 5 | "description_full": "Find symbols/footprints with 3D models on JLCPCB/LCSC and use them in your KiCad project. Uses native KiCad importer.", 6 | "identifier": "com.github.dsa-t.jlc-kicad-lib-loader", 7 | "type": "plugin", 8 | "author": { 9 | "name": "dsa-t", 10 | "contact": { 11 | } 12 | }, 13 | "license": "MIT", 14 | "resources": { 15 | "homepage": "https://github.com/dsa-t/jlc-kicad-lib-loader" 16 | }, 17 | "tags": [ 18 | "pcbnew", 19 | "models", 20 | "easyeda", 21 | "jlc" 22 | ], 23 | "versions": [ 24 | { 25 | "version": "VERSION_HERE", 26 | "status": "stable", 27 | "kicad_version": "8.0.7", 28 | "kicad_version_max": "9.0", 29 | "download_sha256": "SHA256_HERE", 30 | "download_size": DOWNLOAD_SIZE_HERE, 31 | "download_url": "DOWNLOAD_URL_HERE", 32 | "install_size": INSTALL_SIZE_HERE 33 | } 34 | ] 35 | } 36 | 37 | --------------------------------------------------------------------------------