Merge pull request #1136 from ispyisail/feature/qet-mcp-install

Install the MCP server with QElectroTech
This commit is contained in:
ispyisail
2026-09-29 18:52:36 +13:00
committed by GitHub
9 changed files with 159 additions and 15 deletions
+4 -1
View File
@@ -227,6 +227,9 @@ jobs:
cp -r "$GITHUB_WORKSPACE/titleblocks" "$FILES/titleblocks" || true
cp -r "$GITHUB_WORKSPACE/examples" "$FILES/examples" || true
cp -r "$GITHUB_WORKSPACE/fonts" "$FILES/fonts" || true
# The MCP server finds bin/QElectroTech.exe and elements/ from mcp/.
mkdir -p "$FILES/mcp"
cp "$GITHUB_WORKSPACE/misc/qet-mcp/qet_mcp.py" "$GITHUB_WORKSPACE/misc/qet-mcp/README.md" "$FILES/mcp/"
# --- Translations ---
# Since PR #751 the .qm files are no longer tracked in git: lrelease
@@ -305,7 +308,7 @@ jobs:
&& echo " OK : $f" \
|| echo " MISSING: $f"
done
for d in ico elements lang titleblocks fonts examples bin; do
for d in ico elements lang titleblocks fonts examples bin mcp; do
[ -d "$FILES/$d" ] \
&& echo " OK : $d/" \
|| echo " MISSING: $d/"
+4
View File
@@ -358,6 +358,10 @@ if (NOT MINGW)
install(DIRECTORY elements DESTINATION share/qelectrotech)
install(DIRECTORY examples DESTINATION share/qelectrotech)
install(DIRECTORY titleblocks DESTINATION share/qelectrotech)
# The MCP server (misc/qet-mcp) finds this QElectroTech and its elements
# from here: <prefix>/share/qelectrotech/mcp -> <prefix>/bin.
install(PROGRAMS misc/qet-mcp/qet_mcp.py DESTINATION share/qelectrotech/mcp)
install(FILES misc/qet-mcp/README.md DESTINATION share/qelectrotech/mcp)
install(FILES LICENSE ELEMENTS.LICENSE CREDIT README ChangeLog DESTINATION share/doc/qelectrotech)
if(UNIX AND NOT APPLE)
install(FILES misc/org.qelectrotech.qelectrotech.desktop DESTINATION share/applications)
+8
View File
@@ -274,6 +274,13 @@ Section "$(Fonts)" SEC05
File /nonfatal /r "./files/fonts"
SectionEnd
; The MCP server lets an AI assistant open, check and edit drawings. It does
; nothing until a user sets up an assistant to start it.
Section "$(Mcp)" SEC06
SetOutPath "$INSTDIR\mcp"
File /nonfatal "./files/mcp/*"
SectionEnd
;--------------------------------
; Component descriptions
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
@@ -282,6 +289,7 @@ SectionEnd
!insertmacro MUI_DESCRIPTION_TEXT ${SEC03} $(var3)
!insertmacro MUI_DESCRIPTION_TEXT ${SEC04} $(var4)
!insertmacro MUI_DESCRIPTION_TEXT ${SEC05} $(var5)
!insertmacro MUI_DESCRIPTION_TEXT ${SEC06} $(var6)
!insertmacro MUI_FUNCTION_DESCRIPTION_END
;--------------------------------
+32
View File
@@ -19,6 +19,8 @@
LangString var3 ${LANG_ENGLISH} "Examples of cartridges"
LangString var4 ${LANG_ENGLISH} "Examples of diagrams"
LangString var5 ${LANG_ENGLISH} "Fonts"
LangString Mcp ${LANG_ENGLISH} "AI assistant (MCP)"
LangString var6 ${LANG_ENGLISH} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_ENGLISH} "Uninstallation of the previous version failed.$\nPlease uninstall ${SOFT_NAME} manually before continuing."
@@ -43,6 +45,8 @@
LangString var3 ${LANG_KOREAN} "표제란 예제"
LangString var4 ${LANG_KOREAN} "도면 예제"
LangString var5 ${LANG_KOREAN} "글꼴"
LangString Mcp ${LANG_KOREAN} "AI assistant (MCP)"
LangString var6 ${LANG_KOREAN} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_KOREAN} "이전 버전을 제거하지 못했습니다.$\n계속하기 전에 ${SOFT_NAME}을(를) 수동으로 제거해 주세요."
@@ -67,6 +71,8 @@
LangString var3 ${LANG_POLISH} "Przykłady tabliczek rysunkowych"
LangString var4 ${LANG_POLISH} "Przykłady schematów"
LangString var5 ${LANG_POLISH} "Czcionki"
LangString Mcp ${LANG_POLISH} "AI assistant (MCP)"
LangString var6 ${LANG_POLISH} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_POLISH} "Odinstalowanie poprzedniej wersji nie powiodło się.$\nPrzed kontynuowaniem odinstaluj ręcznie program ${SOFT_NAME}."
@@ -91,6 +97,8 @@
LangString var3 ${LANG_GREEK} "Παραδείγματα πινακίδων"
LangString var4 ${LANG_GREEK} "Παραδείγματα διαγραμμάτων"
LangString var5 ${LANG_GREEK} "Γραμματοσειρές"
LangString Mcp ${LANG_GREEK} "AI assistant (MCP)"
LangString var6 ${LANG_GREEK} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_GREEK} "Η απεγκατάσταση της προηγούμενης έκδοσης απέτυχε.$\nΠαρακαλώ απεγκαταστήστε χειροκίνητα το ${SOFT_NAME} πριν συνεχίσετε."
@@ -115,6 +123,8 @@
LangString var3 ${LANG_CZECH} "Příklady popisových polí"
LangString var4 ${LANG_CZECH} "Příklady schémat"
LangString var5 ${LANG_CZECH} "Písma"
LangString Mcp ${LANG_CZECH} "AI assistant (MCP)"
LangString var6 ${LANG_CZECH} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_CZECH} "Odinstalování předchozí verze se nezdařilo.$\nPřed pokračováním prosím odinstalujte ${SOFT_NAME} ručně."
@@ -139,6 +149,8 @@
LangString var3 ${LANG_SPANISH} "Ejemplos de cartelas"
LangString var4 ${LANG_SPANISH} "Ejemplos de esquemas"
LangString var5 ${LANG_SPANISH} "Fuentes"
LangString Mcp ${LANG_SPANISH} "AI assistant (MCP)"
LangString var6 ${LANG_SPANISH} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_SPANISH} "La desinstalación de la versión anterior ha fallado.$\nPor favor, desinstale ${SOFT_NAME} manualmente antes de continuar."
@@ -163,6 +175,8 @@
LangString var3 ${LANG_GERMAN} "Schriftfeld-Beispiele"
LangString var4 ${LANG_GERMAN} "Schaltplan-Beispiele"
LangString var5 ${LANG_GERMAN} "Schriften"
LangString Mcp ${LANG_GERMAN} "AI assistant (MCP)"
LangString var6 ${LANG_GERMAN} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_GERMAN} "Die Deinstallation der vorherigen Version ist fehlgeschlagen.$\nBitte deinstallieren Sie ${SOFT_NAME} manuell, bevor Sie fortfahren."
@@ -187,6 +201,8 @@
LangString var3 ${LANG_RUSSIAN} "Примеры штампов"
LangString var4 ${LANG_RUSSIAN} "Примеры схем"
LangString var5 ${LANG_RUSSIAN} "Шрифты"
LangString Mcp ${LANG_RUSSIAN} "AI assistant (MCP)"
LangString var6 ${LANG_RUSSIAN} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_RUSSIAN} "Удаление предыдущей версии завершилось с ошибкой.$\nПожалуйста, удалите ${SOFT_NAME} вручную перед продолжением."
@@ -211,6 +227,8 @@
LangString var3 ${LANG_ARABIC} "أمثلة على كتل العنوان"
LangString var4 ${LANG_ARABIC} "أمثلة على المخططات"
LangString var5 ${LANG_ARABIC} "الخطوط"
LangString Mcp ${LANG_ARABIC} "AI assistant (MCP)"
LangString var6 ${LANG_ARABIC} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_ARABIC} "فشل إلغاء تثبيت الإصدار السابق. يرجى إلغاء تثبيت ${SOFT_NAME} يدويًا قبل المتابعة."
@@ -235,6 +253,8 @@
LangString var3 ${LANG_CATALAN} "Exemples de cartutxos"
LangString var4 ${LANG_CATALAN} "Exemples d'esquemes"
LangString var5 ${LANG_CATALAN} "Tipus de lletra"
LangString Mcp ${LANG_CATALAN} "AI assistant (MCP)"
LangString var6 ${LANG_CATALAN} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_CATALAN} "La desinstal·lació de la versió anterior ha fallat.$\nSi us plau, desinstal·leu ${SOFT_NAME} manualment abans de continuar."
@@ -259,6 +279,8 @@
LangString var3 ${LANG_ITALIAN} "Cartigli di esempio"
LangString var4 ${LANG_ITALIAN} "Schemi di esempio"
LangString var5 ${LANG_ITALIAN} "Caratteri"
LangString Mcp ${LANG_ITALIAN} "AI assistant (MCP)"
LangString var6 ${LANG_ITALIAN} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_ITALIAN} "La disinstallazione della versione precedente non è riuscita.$\nSi prega di disinstallare ${SOFT_NAME} manualmente prima di continuare."
@@ -283,6 +305,8 @@
LangString var3 ${LANG_PORTUGUESE} "Exemplos de legendas"
LangString var4 ${LANG_PORTUGUESE} "Exemplos de esquemas"
LangString var5 ${LANG_PORTUGUESE} "Fontes"
LangString Mcp ${LANG_PORTUGUESE} "AI assistant (MCP)"
LangString var6 ${LANG_PORTUGUESE} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_PORTUGUESE} "A desinstalação da versão anterior falhou.$\nPor favor, desinstale ${SOFT_NAME} manualmente antes de continuar."
@@ -307,6 +331,8 @@
LangString var3 ${LANG_ROMANIAN} "Exemple de cartușe"
LangString var4 ${LANG_ROMANIAN} "Exemple de scheme"
LangString var5 ${LANG_ROMANIAN} "Fonturi"
LangString Mcp ${LANG_ROMANIAN} "AI assistant (MCP)"
LangString var6 ${LANG_ROMANIAN} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_ROMANIAN} "Dezinstalarea versiunii anterioare a eșuat.$\nVă rugăm să dezinstalați ${SOFT_NAME} manual înainte de a continua."
@@ -331,6 +357,8 @@
LangString var3 ${LANG_CROATIAN} "Primjeri zaglavlja"
LangString var4 ${LANG_CROATIAN} "Primjeri shema"
LangString var5 ${LANG_CROATIAN} "Fontovi"
LangString Mcp ${LANG_CROATIAN} "AI assistant (MCP)"
LangString var6 ${LANG_CROATIAN} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_CROATIAN} "Deinstalacija prethodne verzije nije uspjela.$\nMolimo deinstalirajte ${SOFT_NAME} ručno prije nastavka."
@@ -355,6 +383,8 @@
LangString var3 ${LANG_DUTCH} "Voorbeelden van titelblokken"
LangString var4 ${LANG_DUTCH} "Voorbeelden van schema's"
LangString var5 ${LANG_DUTCH} "Lettertypen"
LangString Mcp ${LANG_DUTCH} "AI assistant (MCP)"
LangString var6 ${LANG_DUTCH} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_DUTCH} "Het verwijderen van de vorige versie is mislukt.$\nVerwijder ${SOFT_NAME} handmatig voordat u verdergaat."
@@ -379,6 +409,8 @@
LangString var3 ${LANG_DANISH} "Titel blokke eksempler"
LangString var4 ${LANG_DANISH} "Diagram eksempler"
LangString var5 ${LANG_DANISH} "Skrifttyper"
LangString Mcp ${LANG_DANISH} "AI assistant (MCP)"
LangString var6 ${LANG_DANISH} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_DANISH} "Afinstallation af den tidligere version mislykkedes.$\nAfinstaller venligst ${SOFT_NAME} manuelt, inden du fortsætter."
+2
View File
@@ -19,4 +19,6 @@
LangString var3 ${LANG_FRENCH} "Exemples de cartouches"
LangString var4 ${LANG_FRENCH} "Exemples de schémas"
LangString var5 ${LANG_FRENCH} "Polices"
LangString Mcp ${LANG_FRENCH} "Assistant IA (MCP)"
LangString var6 ${LANG_FRENCH} "Permet à un assistant IA d'ouvrir, vérifier et modifier vos schémas (nécessite Python)"
LangString uninstFailed ${LANG_FRENCH} "La désinstallation de la version précédente a échoué.$\nVeuillez désinstaller ${SOFT_NAME} manuellement avant de continuer."
+24
View File
@@ -25,6 +25,8 @@
LangString var3 ${LANG_HUNGARIAN} "Rajzfejléc-példák"
LangString var4 ${LANG_HUNGARIAN} "Kapcsolási rajz példák"
LangString var5 ${LANG_HUNGARIAN} "Betűtípusok"
LangString Mcp ${LANG_HUNGARIAN} "AI assistant (MCP)"
LangString var6 ${LANG_HUNGARIAN} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_HUNGARIAN} "Az előző verzió eltávolítása nem sikerült.$\nKérjük, távolítsa el manuálisan a ${SOFT_NAME} programot, mielőtt folytatná."
@@ -52,6 +54,8 @@
LangString var3 ${LANG_JAPANESE} "表題欄の例"
LangString var4 ${LANG_JAPANESE} "回路図の例"
LangString var5 ${LANG_JAPANESE} "フォント"
LangString Mcp ${LANG_JAPANESE} "AI assistant (MCP)"
LangString var6 ${LANG_JAPANESE} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_JAPANESE} "前のバージョンのアンインストールに失敗しました。$\n続行する前に、${SOFT_NAME} を手動でアンインストールしてください。"
@@ -79,6 +83,8 @@
LangString var3 ${LANG_MONGOLIAN} "Гарчгийн блокын жишээнүүд"
LangString var4 ${LANG_MONGOLIAN} "Схемийн жишээнүүд"
LangString var5 ${LANG_MONGOLIAN} "Фонт"
LangString Mcp ${LANG_MONGOLIAN} "AI assistant (MCP)"
LangString var6 ${LANG_MONGOLIAN} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_MONGOLIAN} "Өмнөх хувилбарыг устгаж чадсангүй.$\nҮргэлжлүүлэхийн өмнө ${SOFT_NAME}-г гараар устгана уу."
@@ -106,6 +112,8 @@
LangString var3 ${LANG_NORWEGIAN} "Eksempler på titelblokker"
LangString var4 ${LANG_NORWEGIAN} "Eksempler på skjemaer"
LangString var5 ${LANG_NORWEGIAN} "Skrifttyper"
LangString Mcp ${LANG_NORWEGIAN} "AI assistant (MCP)"
LangString var6 ${LANG_NORWEGIAN} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_NORWEGIAN} "Avinstallasjon av forrige versjon mislyktes.$\nVennligst avinstaller ${SOFT_NAME} manuelt før du fortsetter."
@@ -143,6 +151,8 @@
LangString var3 ${LANG_PORTUGUESEBR} "Exemplos de legendas"
LangString var4 ${LANG_PORTUGUESEBR} "Exemplos de esquemas"
LangString var5 ${LANG_PORTUGUESEBR} "Fontes"
LangString Mcp ${LANG_PORTUGUESEBR} "AI assistant (MCP)"
LangString var6 ${LANG_PORTUGUESEBR} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_PORTUGUESEBR} "A desinstalação da versão anterior falhou.$\nPor favor, desinstale ${SOFT_NAME} manualmente antes de continuar."
@@ -170,6 +180,8 @@
LangString var3 ${LANG_SERBIAN} "Примери заглавља"
LangString var4 ${LANG_SERBIAN} "Примери шема"
LangString var5 ${LANG_SERBIAN} "Фонтови"
LangString Mcp ${LANG_SERBIAN} "AI assistant (MCP)"
LangString var6 ${LANG_SERBIAN} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_SERBIAN} "Деинсталација претходне верзије није успела.$\nМолимо деинсталирајте ${SOFT_NAME} ручно пре наставка."
@@ -197,6 +209,8 @@
LangString var3 ${LANG_SLOVAK} "Príklady popisových polí"
LangString var4 ${LANG_SLOVAK} "Príklady schém"
LangString var5 ${LANG_SLOVAK} "Písma"
LangString Mcp ${LANG_SLOVAK} "AI assistant (MCP)"
LangString var6 ${LANG_SLOVAK} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_SLOVAK} "Odinštalovanie predchádzajúcej verzie zlyhalo.$\nPred pokračovaním odinštalujte ${SOFT_NAME} ručne."
@@ -224,6 +238,8 @@
LangString var3 ${LANG_SLOVENIAN} "Primeri glav risb"
LangString var4 ${LANG_SLOVENIAN} "Primeri shem"
LangString var5 ${LANG_SLOVENIAN} "Pisave"
LangString Mcp ${LANG_SLOVENIAN} "AI assistant (MCP)"
LangString var6 ${LANG_SLOVENIAN} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_SLOVENIAN} "Odstranitev prejšnje različice ni uspela.$\nPred nadaljevanjem ročno odstranite ${SOFT_NAME}."
@@ -251,6 +267,8 @@
LangString var3 ${LANG_SWEDISH} "Exempel på ritningshuvuden"
LangString var4 ${LANG_SWEDISH} "Exempel på scheman"
LangString var5 ${LANG_SWEDISH} "Teckensnitt"
LangString Mcp ${LANG_SWEDISH} "AI assistant (MCP)"
LangString var6 ${LANG_SWEDISH} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_SWEDISH} "Avinstallationen av den föregående versionen misslyckades.$\nAvinstallera ${SOFT_NAME} manuellt innan du fortsätter."
@@ -278,6 +296,8 @@
LangString var3 ${LANG_TURKISH} "Başlık bloğu örnekleri"
LangString var4 ${LANG_TURKISH} "Şema örnekleri"
LangString var5 ${LANG_TURKISH} "Yazı Tipleri"
LangString Mcp ${LANG_TURKISH} "AI assistant (MCP)"
LangString var6 ${LANG_TURKISH} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_TURKISH} "Önceki sürümün kaldırılması başarısız oldu.$\nDevam etmeden önce lütfen ${SOFT_NAME}'i manuel olarak kaldırın."
@@ -305,6 +325,8 @@
LangString var3 ${LANG_UKRAINIAN} "Приклади штампів"
LangString var4 ${LANG_UKRAINIAN} "Приклади схем"
LangString var5 ${LANG_UKRAINIAN} "Шрифти"
LangString Mcp ${LANG_UKRAINIAN} "AI assistant (MCP)"
LangString var6 ${LANG_UKRAINIAN} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_UKRAINIAN} "Видалення попередньої версії завершилося помилкою.$\nБудь ласка, видаліть ${SOFT_NAME} вручну перед продовженням."
@@ -332,4 +354,6 @@
LangString var3 ${LANG_SIMPCHINESE} "标题栏示例"
LangString var4 ${LANG_SIMPCHINESE} "图纸示例"
LangString var5 ${LANG_SIMPCHINESE} "字体"
LangString Mcp ${LANG_SIMPCHINESE} "AI assistant (MCP)"
LangString var6 ${LANG_SIMPCHINESE} "Lets an AI assistant open, check and edit your drawings (needs Python)"
LangString uninstFailed ${LANG_SIMPCHINESE} "卸载旧版本失败。$\n请在继续之前手动卸载 ${SOFT_NAME}。"
+22
View File
@@ -82,6 +82,28 @@ Register it with an MCP client, for example:
QElectroTech (as `<prefix>/share/qelectrotech/mcp/qet_mcp.py`, which also
finds the installed element collection).
## Installed with QElectroTech
QElectroTech's packages install the server next to the program, and from
there it finds that QElectroTech and its element collection by itself:
| Package | Server | Finds |
|---|---|---|
| `make install`, Linux distributions | `<prefix>/share/qelectrotech/mcp/qet_mcp.py` | `<prefix>/bin/qelectrotech`, `<prefix>/share/qelectrotech/elements` |
| Windows installer, MSI, portable folder | `<folder>\mcp\qet_mcp.py` (the "AI assistant (MCP)" component) | `<folder>\bin\QElectroTech.exe`, `<folder>\elements` |
So a client configuration needs only the path to the server and the
workspace, for example on Windows:
```json
"args": ["C:\\Program Files\\QElectroTech\\mcp\\qet_mcp.py"]
```
Snap and flatpak install it too. Their QElectroTech is built to run inside
the package's sandbox, and starting it from the server has not been tested:
the tools that read a file work, exports and edits may not. If they fail,
point `QET_BINARY` at a QElectroTech installed another way.
## Using it from the Claude app
A web chat in a browser cannot start a program on your computer, so it
+26 -14
View File
@@ -3240,12 +3240,24 @@ def _env_paths(name: str) -> list:
return out
def _installed_prefix() -> Path | None:
"""The install prefix when this script is <prefix>/share/qelectrotech/mcp/."""
def _installation() -> tuple | None:
"""(program directory, element collection) of the QElectroTech this
script was installed with, or None when it runs from anywhere else.
Two layouts, both put there by QElectroTech's own packaging:
<prefix>/share/qelectrotech/mcp/ -> <prefix>/bin, <prefix>/share/qelectrotech/elements
(make install: Linux, snap, flatpak, macOS)
<root>/mcp/ -> <root>/bin, <root>/elements
(the Windows installers and portable folder)
"""
here = Path(__file__).resolve().parent
if here.name == "mcp" and here.parent.name == "qelectrotech" \
and here.parent.parent.name == "share":
return here.parent.parent.parent
if here.name != "mcp":
return None
if here.parent.name == "qelectrotech" and here.parent.parent.name == "share":
prefix = here.parent.parent.parent
return prefix / "bin", here.parent / "elements"
if (here.parent / "bin").is_dir():
return here.parent / "bin", here.parent / "elements"
return None
@@ -3259,10 +3271,12 @@ def resolve_binary() -> Path | None:
env = os.environ.get("QET_BINARY", "").strip()
if env:
return Path(env).expanduser().resolve()
prefix = _installed_prefix()
if prefix is not None:
for name in ("qelectrotech", "qelectrotech.exe"):
cand = prefix / "bin" / name
install = _installation()
if install is not None:
# The Windows build names it QElectroTech.exe; only a case-sensitive
# file system tells the spellings apart.
for name in ("qelectrotech", "qelectrotech.exe", "QElectroTech.exe"):
cand = install[0] / name
if cand.is_file():
return cand.resolve()
found = shutil.which("qelectrotech")
@@ -3271,11 +3285,9 @@ def resolve_binary() -> Path | None:
def default_elements_dir() -> Path | None:
"""The element collection of the install this script ships in, if any."""
prefix = _installed_prefix()
if prefix is not None:
coll = prefix / "share" / "qelectrotech" / "elements"
if coll.is_dir():
return coll.resolve()
install = _installation()
if install is not None and install[1].is_dir():
return install[1].resolve()
return None
+37
View File
@@ -2562,6 +2562,43 @@ class BinaryPolicy(unittest.TestCase):
self.assertEqual(args["elements_dir"],
str((prefix / "share" / "qelectrotech" / "elements").resolve()))
def installed_at(self, root: Path, mcp: Path):
"""Import a copy of the server placed at mcp/qet_mcp.py."""
import importlib.util
mcp.mkdir(parents=True)
shutil.copy2(HERE / "qet_mcp.py", mcp / "qet_mcp.py")
spec = importlib.util.spec_from_file_location(f"qet_mcp_{root.name}", mcp / "qet_mcp.py")
inst = importlib.util.module_from_spec(spec)
spec.loader.exec_module(inst)
return inst
def test_the_windows_layout_is_found_too(self):
"""The Windows installers and the portable folder put the server in
<root>/mcp, the program in <root>/bin as QElectroTech.exe, and the
collection in <root>/elements."""
root = Path(self.tmp.name) / "QElectroTech"
(root / "elements").mkdir(parents=True)
exe = fake_qet(root / "bin", "QElectroTech.exe")
inst = self.installed_at(root, root / "mcp")
del os.environ["QET_BINARY"]
args = {"project": str(self.root / "ok.qet")}
inst.enforce_path_policy("qet_query", args)
self.assertEqual(args["binary"], str(exe.resolve()))
self.assertEqual(args["elements_dir"], str((root / "elements").resolve()))
def test_a_copy_saved_anywhere_is_not_an_install(self):
"""A downloaded copy sitting beside some bin/ folder must not go
looking for QElectroTech there."""
home = Path(self.tmp.name) / "home"
fake_qet(home / "bin")
inst = self.installed_at(home, home / "Downloads")
self.assertIsNone(inst._installation())
def test_a_folder_named_mcp_is_not_an_install(self):
"""Without a bin/ beside it, an mcp/ folder is just a folder."""
inst = self.installed_at(Path(self.tmp.name) / "x", Path(self.tmp.name) / "x" / "mcp")
self.assertIsNone(inst._installation())
class LaunchExecutable(unittest.TestCase):
"""Windows cannot run a lone copy of QElectroTech (F065): its DLLs sit