initial commit
This commit is contained in:
299
init_user.py
Normal file
299
init_user.py
Normal file
@@ -0,0 +1,299 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
init_user.py
|
||||
============
|
||||
Initialize a new user's KiCad environment with UM customizations.
|
||||
|
||||
This script:
|
||||
1. Creates ODBC System DSN for SQLite database (Windows only)
|
||||
2. Copies theme files from UM_KICAD/lib/themes to the user's KiCad config directory
|
||||
3. Updates KiCad preferences to use the UM theme
|
||||
4. Sets other UM-specific preferences
|
||||
|
||||
Usage:
|
||||
python3 init_user.py
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# KiCad v9 config location
|
||||
# ---------------------------------------------------------------------------
|
||||
def kicad9_config_dir() -> Path:
|
||||
home = Path.home()
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
appdata = os.environ.get("APPDATA")
|
||||
if not appdata:
|
||||
raise RuntimeError("APPDATA not set")
|
||||
return Path(appdata) / "kicad" / "9.0"
|
||||
|
||||
if sys.platform == "darwin":
|
||||
return home / "Library" / "Preferences" / "kicad" / "9.0"
|
||||
|
||||
xdg = os.environ.get("XDG_CONFIG_HOME")
|
||||
if xdg:
|
||||
return Path(xdg) / "kicad" / "9.0"
|
||||
|
||||
return home / ".config" / "kicad" / "9.0"
|
||||
|
||||
|
||||
def get_themes_dir() -> Path:
|
||||
"""Get the user's KiCad themes directory"""
|
||||
return kicad9_config_dir() / "colors"
|
||||
|
||||
|
||||
def get_kicad_common_json() -> Path:
|
||||
"""Get the path to kicad_common.json (main preferences file)"""
|
||||
return kicad9_config_dir() / "kicad_common.json"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ODBC DSN Creation (Windows only)
|
||||
# ---------------------------------------------------------------------------
|
||||
def create_odbc_dsn(um_root: Path) -> bool:
|
||||
"""
|
||||
Create a System DSN for SQLite ODBC connection on Windows.
|
||||
|
||||
Args:
|
||||
um_root: Path to UM_KICAD root directory
|
||||
|
||||
Returns:
|
||||
True if DSN was created or already exists, False on error
|
||||
"""
|
||||
if not sys.platform.startswith("win"):
|
||||
print(" ODBC DSN creation is only supported on Windows")
|
||||
return True # Not an error, just not applicable
|
||||
|
||||
try:
|
||||
import winreg
|
||||
except ImportError:
|
||||
print(" Warning: winreg module not available, skipping ODBC DSN creation")
|
||||
return False
|
||||
|
||||
# Database path
|
||||
db_path = um_root / "database" / "parts.sqlite"
|
||||
if not db_path.exists():
|
||||
print(f" Warning: Database not found at {db_path}")
|
||||
return False
|
||||
|
||||
dsn_name = "UM_2KiCad_Parts2"
|
||||
driver_name = "SQLite3 ODBC Driver"
|
||||
|
||||
try:
|
||||
# Check if DSN already exists
|
||||
try:
|
||||
key = winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
r"SOFTWARE\ODBC\ODBC.INI\UM_KiCad_Parts",
|
||||
0,
|
||||
winreg.KEY_READ
|
||||
)
|
||||
winreg.CloseKey(key)
|
||||
print(f" ODBC DSN '{dsn_name}' already exists")
|
||||
return True
|
||||
except FileNotFoundError:
|
||||
pass # DSN doesn't exist, we'll create it
|
||||
|
||||
# Create the DSN
|
||||
# First, add to ODBC Data Sources list
|
||||
key = winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
r"SOFTWARE\ODBC\ODBC.INI\ODBC Data Sources",
|
||||
0,
|
||||
winreg.KEY_WRITE
|
||||
)
|
||||
winreg.SetValueEx(key, dsn_name, 0, winreg.REG_SZ, driver_name)
|
||||
winreg.CloseKey(key)
|
||||
|
||||
# Create the DSN configuration key
|
||||
key = winreg.CreateKey(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
rf"SOFTWARE\ODBC\ODBC.INI\{dsn_name}"
|
||||
)
|
||||
|
||||
# Set DSN configuration values
|
||||
winreg.SetValueEx(key, "Driver", 0, winreg.REG_SZ, driver_name)
|
||||
winreg.SetValueEx(key, "Database", 0, winreg.REG_SZ, str(db_path))
|
||||
winreg.SetValueEx(key, "Description", 0, winreg.REG_SZ, "UM KiCad Parts Database")
|
||||
|
||||
winreg.CloseKey(key)
|
||||
|
||||
print(f" Created ODBC System DSN '{dsn_name}'")
|
||||
print(f" Database: {db_path}")
|
||||
return True
|
||||
|
||||
except PermissionError:
|
||||
print(" ERROR: Administrator privileges required to create System DSN")
|
||||
print(" Please run this script as Administrator, or create the DSN manually:")
|
||||
print(f" DSN Name: {dsn_name}")
|
||||
print(f" Driver: {driver_name}")
|
||||
print(f" Database: {db_path}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f" Warning: Failed to create ODBC DSN: {e}")
|
||||
print(f" You may need to create it manually:")
|
||||
print(f" DSN Name: {dsn_name}")
|
||||
print(f" Driver: {driver_name}")
|
||||
print(f" Database: {db_path}")
|
||||
return False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Theme installation
|
||||
# ---------------------------------------------------------------------------
|
||||
def install_themes(um_root: Path) -> list[str]:
|
||||
"""
|
||||
Copy all theme files from UM_KICAD/lib/themes to user's KiCad config.
|
||||
|
||||
Returns:
|
||||
List of installed theme names
|
||||
"""
|
||||
source_themes_dir = um_root / "lib" / "themes"
|
||||
if not source_themes_dir.exists():
|
||||
print(f"Warning: Themes directory not found at {source_themes_dir}")
|
||||
return []
|
||||
|
||||
dest_themes_dir = get_themes_dir()
|
||||
dest_themes_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
installed = []
|
||||
theme_files = list(source_themes_dir.glob("*.json"))
|
||||
|
||||
if not theme_files:
|
||||
print("Warning: No theme files found")
|
||||
return []
|
||||
|
||||
for theme_file in theme_files:
|
||||
dest_file = dest_themes_dir / theme_file.name
|
||||
shutil.copy2(theme_file, dest_file)
|
||||
theme_name = theme_file.stem
|
||||
installed.append(theme_name)
|
||||
print(f" Installed theme: {theme_name}")
|
||||
|
||||
return installed
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Preferences configuration
|
||||
# ---------------------------------------------------------------------------
|
||||
def set_theme_preference(theme_name: str):
|
||||
"""
|
||||
Update kicad_common.json and user.json to use the specified theme.
|
||||
|
||||
Args:
|
||||
theme_name: Name of the theme (without .json extension)
|
||||
"""
|
||||
# 1. Copy the theme to user.json (this is the active theme file)
|
||||
themes_dir = get_themes_dir()
|
||||
source_theme = themes_dir / f"{theme_name}.json"
|
||||
user_theme = themes_dir / "user.json"
|
||||
|
||||
if source_theme.exists():
|
||||
shutil.copy2(source_theme, user_theme)
|
||||
print(f" Activated theme '{theme_name}' by copying to user.json")
|
||||
else:
|
||||
print(f" Warning: Theme file not found: {source_theme}")
|
||||
|
||||
# 2. Update kicad_common.json to reference the theme
|
||||
config_file = get_kicad_common_json()
|
||||
|
||||
# Load existing config or create new one
|
||||
if config_file.exists():
|
||||
with open(config_file, 'r', encoding='utf-8') as f:
|
||||
config = json.load(f)
|
||||
print(f" Loaded existing preferences from {config_file}")
|
||||
else:
|
||||
config = {}
|
||||
print(f" Creating new preferences file at {config_file}")
|
||||
|
||||
# Ensure the appearance section exists
|
||||
if "appearance" not in config:
|
||||
config["appearance"] = {}
|
||||
|
||||
# Set the theme for all applications
|
||||
config["appearance"]["color_theme"] = theme_name
|
||||
|
||||
# Also set it for specific editors
|
||||
for editor in ["pcb_editor", "schematic_editor", "gerbview", "3d_viewer"]:
|
||||
if editor not in config:
|
||||
config[editor] = {}
|
||||
if "appearance" not in config[editor]:
|
||||
config[editor]["appearance"] = {}
|
||||
config[editor]["appearance"]["color_theme"] = theme_name
|
||||
|
||||
# Backup existing config
|
||||
if config_file.exists():
|
||||
backup_file = config_file.with_suffix('.json.bak')
|
||||
shutil.copy2(config_file, backup_file)
|
||||
print(f" Backed up existing config to {backup_file.name}")
|
||||
|
||||
# Write updated config
|
||||
config_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(config, f, indent=2)
|
||||
|
||||
print(f" Set theme to '{theme_name}' in KiCad preferences")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main
|
||||
# ---------------------------------------------------------------------------
|
||||
def main() -> int:
|
||||
um_root_env = os.environ.get("UM_KICAD")
|
||||
if not um_root_env:
|
||||
print("ERROR: UM_KICAD environment variable not set")
|
||||
print("Please set UM_KICAD to the root of your UM KiCad repository")
|
||||
return 1
|
||||
|
||||
um_root = Path(um_root_env).resolve()
|
||||
if not um_root.exists():
|
||||
print(f"ERROR: UM_KICAD path does not exist: {um_root}")
|
||||
return 1
|
||||
|
||||
print("=" * 60)
|
||||
print("UM KiCad User Initialization")
|
||||
print("=" * 60)
|
||||
print(f"\nUM_KICAD: {um_root}")
|
||||
print(f"KiCad config: {kicad9_config_dir()}")
|
||||
print()
|
||||
|
||||
# Create ODBC DSN (Windows only)
|
||||
if sys.platform.startswith("win"):
|
||||
print("Creating ODBC System DSN...")
|
||||
create_odbc_dsn(um_root)
|
||||
print()
|
||||
|
||||
# Install themes
|
||||
print("Installing themes...")
|
||||
installed_themes = install_themes(um_root)
|
||||
|
||||
if not installed_themes:
|
||||
print("\nNo themes were installed")
|
||||
return 1
|
||||
|
||||
print(f"\nInstalled {len(installed_themes)} theme(s)")
|
||||
|
||||
# Set the first theme as default (typically "UM")
|
||||
default_theme = installed_themes[0]
|
||||
print(f"\nSetting default theme...")
|
||||
set_theme_preference(default_theme)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Initialization complete!")
|
||||
print("=" * 60)
|
||||
print("\nNext steps:")
|
||||
print("1. Restart KiCad to see the new theme")
|
||||
print("2. You can change themes in Preferences > Colors")
|
||||
print(f"3. Available themes: {', '.join(installed_themes)}")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user