commit 65a87d4f85a6eb505a74d5228c6509cf0cc64f1e Author: Peter Date: Thu Nov 9 04:51:26 2023 +0800 initial commit - working diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..68bc17f --- /dev/null +++ b/.gitignore @@ -0,0 +1,160 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..f0d67e3 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +## Icons + +# Rich presence for LTspice + +## Install and run + +Install dependencies: + +```bash +python -m venv .venv +source .venv/bin/activate +python -m pip install -r requirements.txt +``` + +Run with: + +```bash +python .\ltspice-discord.py +``` + +## Icons + +Icons adapted from: + +https://commons.wikimedia.org/wiki/File:BJT_NPN_symbol-fr.svg + +https://en.wikipedia.org/wiki/File:Oscillation_amortie.svg?&useskin=vector + +https://commons.wikimedia.org/wiki/File:AND_ANSI.svg diff --git a/icons/graph.png b/icons/graph.png new file mode 100644 index 0000000..513360c Binary files /dev/null and b/icons/graph.png differ diff --git a/icons/schematic.png b/icons/schematic.png new file mode 100644 index 0000000..cbfaf88 Binary files /dev/null and b/icons/schematic.png differ diff --git a/icons/symbol.png b/icons/symbol.png new file mode 100644 index 0000000..fd15535 Binary files /dev/null and b/icons/symbol.png differ diff --git a/ltspice-discord.py b/ltspice-discord.py new file mode 100644 index 0000000..7c07db2 --- /dev/null +++ b/ltspice-discord.py @@ -0,0 +1,136 @@ +import os +from typing import List +import win32gui +import time +import psutil +import win32process +import pypresence + +rpc = pypresence.Presence("1171889899679531129") +data = None + +rpc.connect() + + +def find_active_processes(name: str) -> List[int]: + processes = [] + for pid in psutil.pids(): + try: + if psutil.Process(pid) and psutil.Process(pid).name() == name: + processes.append(pid) + except psutil.NoSuchProcess: + pass + return processes + + +LTSPICE_NAME = "LTSPICE.exe" + +last_state = {} +last_program = {"pid": None, "path": None} + + +def handle_ltspice(title: str) -> None: + global last_state + filename = title.replace("LTspice XVII - ", "") + file, ext = os.path.splitext(filename) + state = {} + if ext in (".asc",): # SCHEMATICS + state = { + "large_image": "schematic", + "large_text": "Schematic", + "details": "Editing a schematic", + "state": filename, + } + elif ext in ( + ".cir", + ".net", + ".sp", + ): # NETLISTS + state = { + "large_image": "symbol", # TODO: Add images for these + "large_text": "Symbol", + "details": "Editing a netlist", + "state": filename, + } + elif ext in ( + ".raw", + ".fra", + ): # WAVEFORMS + state = { + "large_image": "graph", + "large_text": "Waveform", + "details": "Viewing a graph", + "state": filename, + } + elif ext in ( + ".bjt", + ".cap", + ".dio", + ".ind", + ".jft", + ".mos", + ".res", + ): # DISCRETES + state = { + "large_image": "symbol", # TODO: Add images for each one of these + "large_text": "Symbol", + "details": "Editing a discrete component", + "state": filename, + } + elif ext in (".asy",): # SYMBOLS + state = { + "large_image": "symbol", + "large_text": "Symbol", + "details": "Editing a symbol", + "state": filename, + } + else: + rpc.clear() + state = { + "large_image": "logo", + "large_text": "LTspice", + "details": "Idling", + } + + if state != last_state: + print("UPDATED!") + print(filename, file, ext) + rpc.update( + **state, + start=int(time.time()), + small_image="logo", + small_text="LTspice", + ) + last_state = state + return True + return False + + +def main() -> None: + global last_program + WINDOW_HANDLERS = { + LTSPICE_NAME: handle_ltspice, + } + while True: + time.sleep(3) + foreground_hwnd = win32gui.GetForegroundWindow() + pid = win32process.GetWindowThreadProcessId(foreground_hwnd) + if pid[-1] >= 0: + active_process = psutil.Process(pid[-1]) + if active_process.name() in WINDOW_HANDLERS.keys(): + handler = WINDOW_HANDLERS[active_process.name()] + if handler(win32gui.GetWindowText(foreground_hwnd)): + last_program = { + "pid": active_process.pid, + "path": active_process.name(), + } + if last_program["pid"] is not None and last_program[ + "pid" + ] not in find_active_processes(last_program["path"]): + last_program = {"pid": None, "path": None} + print("Clear RPC - program closed") + rpc.clear() + + +if __name__ == "__main__": + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ff9e86d Binary files /dev/null and b/requirements.txt differ