Sådan omdannes en webapp til en desktopapp ved hjælp af Chromium og PyInstaller

Pakning og distribution af din app lyder i princippet simpelt. Det er bare software. Men i praksis er det ret udfordrende.

Jeg har arbejdet på et Python-modul kaldet Sofi, der genererer brugergrænseflader. Det kan levere en desktop-følelse, mens du bruger standard-sides webteknologier. For fleksibilitet designede jeg det til at arbejde gennem to distributionsmetoder: in-browser og eksekverbar.

Kører i browseren, den fungerer meget som en normal webside. Du kan indlæse den ved at åbne en fil eller starte den fra din shell. Jeg byggede også en eksekverbar fil, der kører som en pakket app, uafhængig og uden eksterne krav.

Over tid, da jeg hackede kode i Atom - min valgte redaktør i disse dage - huskede jeg, at Atom faktisk er en browser. Det bruger Node.js som en back-end og Electron-rammen til dets brugergrænseflade. Dette inspirerede mig til at begynde at pikke på Electrons interner i håb om at finde eksempler og bedste praksis for, hvordan de løste desktopemballage.

Det tog ikke lang tid for mig at opdage, at det hele er bygget oven på gratis og åbent indkøbte teknologier: Chromium-browseren og Chromium Embedded Framework. Dette indeholdt nemme at integrere eksempeltilpasninger, der var i stand til at opfylde mine krav.

Med alt dette i hånden kom jeg på arbejde.

Chromium Embedded Framework

Chrom er basiskoden, der føder Googles Chrome-browser. Det samler alle de elementer, der gengiver en grænseflade, behandler brugerinput og script dets funktioner.

Chromium Embedded Framework (CEF) er en gruppe C-funktioner, der kan kontrollere den browser. Det giver også scripts, der hjælper med at forenkle processen med at opbygge og kompilere den.

Visual Studio Code, Slack, Mattermost, Curse, Postman og Kitematic er alle eksempler på desktop-apps, der bruger Electron. Disse systemer kvalificerer sig alle som websteder, der udnytter browseren nedenunder med CEF.

Hvis du tænker på, at Python også kan binde til C og drage fordel af disse funktioner, så har du ret. Se ikke længere end pycef-projektet for at kalde CEF-indpakningsfunktionerne direkte. Det kommer dog med Chromium-binær som en ekstra afhængighed. Så hvis du er bekymret for at styre komplicerede supporterklæringer, så tænk inden du springer.

I min særlige situation styrer Sofi-projektet alle interaktioner via en websocket, hvilket giver en ensartet grænseflade på tværs af forskellige typer platforme (web, desktop, mobil osv.). Dette betyder, at jeg ikke behøver manuelt at kommandere eller køre browseren. Jeg ønsker kun at interagere med den DOM, som browseren viser gennem standardwebteknologier.

Mit mål er at tilpasse UI-elementerne, der får en browser til at ligne en browser. Jeg er nødt til at fjerne menuer, værktøjslinjer og statuslinjer. Dermed får jeg det til at se ud som om vi er i fuldskærmstilstand - men inde i et applikationsvindue.

I betragtning af mine enkle krav følte jeg, at pycef - eller andre bindinger på lavere niveau - var for meget. I stedet benyttede jeg mig af en forudbygget prøve fra CEF-projektet: cefsimple . Denne browser skjuler alle de visuelle elementer, jeg ønsker, så hvis jeg bruger dens CLI til at åbne en webside, har brugeren ingen idé om, at de faktisk er inde i en browser. Det ligner et almindeligt vindue fra enhver applikation.

At bygge cefsimple var ikke for kompliceret, når jeg gennemgik dokumentationen. Men det tager enormt meget tid, hvis du også bygger Chrom sammen med det. For at undgå dette leverer selve projektet forudbyggede binære filer, som du kan tilpasse og kompilere til cefsimple. Jeg fandt det bedst at drage fordel af disse.

Trinene er som følger:

  1. Se hurtigt på, hvordan du bygger med CEF fra binære filer.
  2. Grib en af ​​de binære distributioner fra repoen. Sørg for at læse værktøjstipene, før du vælger en, da ikke alle pakker indeholder de samme filer. Jeg ledte specifikt efter en med cefsimple.
  3. Se gennem CMakeLists.txtfilen, og sørg for at installere de nødvendige buildværktøjer. Dette er platformsspecifikt.
  4. Udfør build. Dette forklares i samme fil som det foregående trin og er også platformsspecifikt, men det har tendens til at følge processen med: lav og cd til build-katalog, kør cmake til dine kompileringsværktøjer og arkitektur, mens du peger på den overordnede bibliotek. Da jeg brugte OSX Ninja-værktøjerne på en 64-bit platform, så kommandoen udcmake -G "Ninja" -DPROJECT_ARCH="x86_64" ..
  5. Byggemappen indeholder nu outputfilerne. Strukturen kan være lidt forvirrende, men den er beskrevet i det væsentlige README. Som reference resulterede det forrige trin i en app-pakke under build/tests/cefsimple/Release/cefsimple.app.
  6. Glem ikke, at du bliver nødt til at gøre dette for at oprette de binære filer, du har brug for til hver platform og OS-arkitektur, som din understøtter.

Nu hvor du har en eksekverbar, skal du køre den fra kommandolinjen med --urlindstillet til den webside, du vil åbne. Dette betyder, at inkorporering af det i et Python-script let gøres gennem subprocessmodulet.

Selvom det ikke er nødvendigt, kan du se CEF-dokumentationen, hvis du er interesseret i at sammensætte Chromium. Det vil pege dig i den rigtige retning. Men vær advaret, det tager meget tid at downloade, bygge og kompilere. God gammeldags forarbejdning hestekræfter vil helt sikkert hjælpe med at få hurtigere resultater.

Emballage

Nu hvor vi kan levere en desktopoplevelse, skal vi overveje, hvordan vi distribuerer det til vores brugere. Traditionel Python-pakkedistribution opnås gennem Python Package Index (PyPI). Det kræver dog, at vores brugere installerer Python-tolk og en eller anden form for emballeringsværktøj som easy_installeller pip.

Selvom dette ikke er særlig svært, bør du overveje det bredere udvalg af brugere. Administration af en installationsproces med separate manuelle trin bliver ret kompliceret. Især hos ikke-tekniske publikum - hvoraf nogle ikke ved, at Python er andet end en stor slange. Mens andre måske i det mindste kender lufthastighedshastigheden for en europæisk ulæsset svale.

Hvis de kender sproget, har de fleste allerede deres egen version installeret. Det er her, pakkeafhængigheder, forskellige operativsystemer, browsere, du aldrig har hørt om (eller troede var døde nu), kom i spil sammen med brugernes forskellige færdigheder i at oprette virtuelle miljøer. Dette har tendens til at oversættes til en stor mængde tid brugt på at understøtte uoverensstemmende software.

For at undgå et så stort rod er der værktøjer, der kan integrere alle dine afhængigheder i OS-specifikke eksekverbare filer. Efter nøje overvejelse er den, jeg valgte til mine bestræbelser, PyInstaller. Det ser ud til at give den mest fleksibilitet i understøttede platforme og formater.

Et kort uddrag fra deres GitHub-arkiv opsummerer tingene pænt:

PyInstaller læser et Python-script skrevet af dig. Den analyserer din kode for at opdage hvert andet modul og bibliotek, som dit script har brug for for at udføre. Derefter samler den kopier af alle disse filer - inklusive den aktive Python-tolk! - og placerer dem med dit script i en enkelt mappe eller eventuelt i en enkelt eksekverbar fil.

Værktøjet leveret på sit løfte. Jeg påpegede det til Python-fil til min prøve ansøgning og det bundter det i en mappe nemt nok med: pyinstaller sample.py. Når jeg vil have en eksekverbar i stedet, skal du blot tilføje --onefileparameteren.

Det bliver lidt vanskeligere, når du skal tilføje ikke-Python-data til din pakke. Dette er tilfældet med html- og js-filerne, der danner grundlaget for Sofi, og cefsimple- browseren, der præsenterer applikationsgrænsefladen fra tidligere. PyInstaller-hjælpeprogrammet giver dig mulighed for --add-dataat gøre netop dette, hvilket muliggør en kortlægning af stien i dit bundt, hvor datafilen (eller biblioteket) vil være. Det tog mig dog et stykke tid at finde ud af, hvordan jeg korrekt fik adgang til disse mapper fra min kode. Heldigvis pegede dokumentationen mig i den rigtige retning.

Når det viser sig, kan du ikke stole på __file__og lignende mekanismer til at bestemme stier , når du kører en PyInstaller-samlet applikation . I stedet gemmer PyInstaller bootloader den absolutte sti til bundtet i sys._MEIPASSog tilføjer en frozenattribut for at fortælle dig, at du kører inde i et bundt. Hvis sys.frozener Trueså indlæs dine filer baseret på sys._MEIPASS, ellers brug normale stifunktioner til at bestemme, hvor tingene er.

Jeg var i stand til med succes at oprette både en OSX-samlet app og en eksekverbar Linux-binær med det samme Python-script. Jeg bekræftede, at jeg kan gøre det samme med en Windows-eksekverbar, men har ikke haft tid til at sammensætte en Windows-version af cefsimple- browseren til at teste bundtstien endnu.

Det endelige produkt

For et eksempel på den browserbaserede brugergrænseflade, der er pakket med systemet beskrevet her, skal du se på min præsentation på PyCaribbean 2017.

Demoen, der er relevant for CEF og emballage, er af et billedgalleri, og den vises omkring 18:15.

For yderligere læsning om, hvordan jeg lavede Sofi, skal du kigge på A Python Ate My GUI-serien.

Hvis du kunne lide artiklen og vil læse mere om Python og softwarepraksis, skal du besøge tryexceptpass.org. Bliv informeret om deres seneste indhold ved at abonnere på adresselisten.