{"id":13399563,"url":"https://github.com/fyears/electron-python-example","last_synced_at":"2025-05-15T23:06:09.913Z","repository":{"id":34025190,"uuid":"37783549","full_name":"fyears/electron-python-example","owner":"fyears","description":"Electron as GUI of Python Applications","archived":false,"fork":false,"pushed_at":"2020-10-05T11:55:20.000Z","size":62,"stargazers_count":2063,"open_issues_count":14,"forks_count":233,"subscribers_count":81,"default_branch":"master","last_synced_at":"2025-04-08T10:21:58.337Z","etag":null,"topics":["desktop-application","electron","gui","python-application"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fyears.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-06-20T19:47:13.000Z","updated_at":"2025-04-01T16:57:34.000Z","dependencies_parsed_at":"2022-06-27T11:14:57.238Z","dependency_job_id":null,"html_url":"https://github.com/fyears/electron-python-example","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fyears%2Felectron-python-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fyears%2Felectron-python-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fyears%2Felectron-python-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fyears%2Felectron-python-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fyears","download_url":"https://codeload.github.com/fyears/electron-python-example/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254436944,"owners_count":22070946,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["desktop-application","electron","gui","python-application"],"created_at":"2024-07-30T19:00:39.516Z","updated_at":"2025-05-15T23:06:04.898Z","avatar_url":"https://github.com/fyears.png","language":"JavaScript","readme":"# Electron as GUI of Python Applications (Updated)\n\n## tl;dr\n\nThis post shows how to use Electron as the GUI component of Python applications. (Updated version of one of my previous posts.) The frontend and backend communicate with each other using `zerorpc`. The complete code is on [GitHub repo](https://github.com/fyears/electron-python-example).\n\n## important notice\n\n**Disclaimer on Dec 2019: This article was originally written in 2017, and I haven't updated or maintained this repo for a long time. Right now (Dec 2019), the code in this article may be outdated, and may or may not be working!!!**\n\nThe following are copied from my [original post](https://www.fyears.org/2017/02/electron-as-gui-of-python-apps-updated.html). They should be the same. **If there are inconsistencies, the `README.md` on the GitHub repo is more accurate.**\n\n## original post and debates\n\n### attention\n\nThe current post is a **updated** version of the [previous post](https://www.fyears.org/2015/06/electron-as-gui-of-python-apps.html) a few years before. Readers do **NOT** need to read the previous post if haven't.\n\n### debates\n\nI didn't expect that the previous post attracted so many visitors. Some other people even posted it on Hacker News and Reddit. The previous post also attracted some criticisms. Here I would like to share my replies to some debates.\n\n#### Do you know `Tkinter`, `GTK`, `QT` (`PySide` and `PyQT`), `wxPython`, `Kivy`, [`thrust`](https://github.com/breach/thrust), ...?\n\nYes, I know at least their existences and try a few of them. I still think `QT` is the best among them. BTW, [`pyotherside`](https://github.com/thp/pyotherside) is one of the actively maintaining bindings for Python. I am just offering another \"web technology oriented\" way here.\n\n#### ... And [`cefpython`](https://github.com/cztomczak/cefpython).\n\nIt's more or less be in \"lower level\" than where Electron is. For example, `PySide` is based on it.\n\n#### I can directly write things in JavaScript!\n\nCorrect. Unless some libraries such as `numpy` are not available in JS. Moreover, the original intention is using Electron / JavaScript / web technologies to **enhance Python Applications**.\n\n#### I can use [`QT WebEngine`](http://doc.qt.io/qt-5/qtwebengine-index.html).\n\nGo ahead and give it a try. But since you are using \"web engine\", why not also give Electron a try?\n\n#### You have two runtimes!\n\nYes. One for JavaScript and one for Python. Unfortunately, Python and JavaScript are dynamic languages, which usually need run-time support.\n\n## the architectures and the choice\n\nIn the previous post, I showed an example architecture: Python to build up a `localhost` server, then Electron is just a local web browser.\n\n```text\nstart\n |\n V\n+------------+\n|            | start\n|            +-------------\u003e +-------------------+\n|  electron  | sub process   |                   |\n|            |               | python web server |\n| (basically |     http      |                   |\n|  browser)  | \u003c-----------\u003e | (business logic)  |\n|            | communication |                   |\n|            |               | (all html/css/js) |\n|            |               |                   |\n+------------+               +-------------------+\n```\n\nThat is **just one not-so-efficient solution**.\n\nLet's reconsider the core needs here: we have a Python application, and a Node.js application (Electron). How to combine them and communicate with each other?\n\n**We actually need an interprocess communication (IPC) mechanism.** It is unavoidable unless Python and JavaScript have direct `FFI` for each other.\n\nHTTP is merely one of the popular ways to implement IPC, and it was merely the first thing came up to my mind when I was writing the previous post.\n\nWe have more choices.\n\nWe can (and should) use `socket`. Then, based on that, we want an abstract messaging layer that could be implemented with [`ZeroMq`](http://zeromq.org/) that is one of the best messaging libraries. Moreover, based on that, we need to define some schema upon raw data that could be implemented with [`zerorpc`](http://www.zerorpc.io/).\n\n(Luckily, `zerorpc` fits our needs here because it supports Python and Node.js. For more general languages support, check out [`gRPC`](http://www.grpc.io/).)\n\n**Thus, in this post, I will show another example using `zerorpc` for communication as follows, which should be more efficient than what I showed in my previous post.**\n\n```text\nstart\n |\n V\n+--------------------+\n|                    | start\n|  electron          +-------------\u003e +------------------+\n|                    | sub process   |                  |\n| (browser)          |               | python server    |\n|                    |               |                  |\n| (all html/css/js)  |               | (business logic) |\n|                    |   zerorpc     |                  |\n| (node.js runtime,  | \u003c-----------\u003e | (zeromq server)  |\n|  zeromq client)    | communication |                  |\n|                    |               |                  |\n+--------------------+               +------------------+\n```\n\n## preparation\n\nAttention: the example could be successfully run on my Windows 10 machine with Python 3.6, Electron 1.7, Node.js v6.\n\nWe need the python application, `python`, `pip`, `node`, `npm`, available in command line. For using `zerorpc`, we also need the C/C++ compilers (`cc` and `c++` in the command line, and/or MSVC on Windows).\n\nThe structure of this project is\n\n```text\n.\n|-- index.html\n|-- main.js\n|-- package.json\n|-- renderer.js\n|\n|-- pycalc\n|   |-- api.py\n|   |-- calc.py\n|   `-- requirements.txt\n|\n|-- LICENSE\n`-- README.md\n```\n\nAs shown above, the Python application is wrapped in a subfolder. In this example, Python application `pycalc/calc.py` provides a function: `calc(text)` that could take a text like `1 + 1 / 2` and return the result like `1.5` (assuming it be like `eval()`). The `pycalc/api.py` is what we are going to figure out.\n\nAnd the `index.html`, `main.js`, `package.json` and `renderer.js` are modified from [`electron-quick-start`](https://github.com/electron/electron-quick-start).\n\n### Python part\n\nFirst of all, since we already have the Python application running, the Python environment should be fine. I strongly recommend developing Python applications in `virtualenv`.\n\nTry install `zerorpc`, and `pyinstaller` (for packaging). On Linux / Ubuntu we may need to run `sudo apt-get install libzmq3-dev` **before** `pip install`.\n\n```bash\npip install zerorpc\npip install pyinstaller\n\n# for windows only\npip install pypiwin32 # for pyinstaller\n```\n\nIf properly configured, the above commands should have no problem. Otherwise, please check out the guides online.\n\n### Node.js / Electron part\n\nSecondly, try to configure the Node.js and Electron environment. I assume that `node` and `npm` can be invoked in the command line and are of latest versions.\n\nWe need to configure the `package.json`, especially the `main` entry:\n\n```json\n{\n  \"name\": \"pretty-calculator\",\n  \"main\": \"main.js\",\n  \"scripts\": {\n    \"start\": \"electron .\"\n  },\n  \"dependencies\": {\n    \"zerorpc\": \"git+https://github.com/0rpc/zerorpc-node.git\"\n  },\n  \"devDependencies\": {\n    \"electron\": \"^1.7.6\",\n    \"electron-packager\": \"^9.0.1\"\n  }\n}\n```\n\nClean the caches:\n\n```bash\n# On Linux / OS X\n# clean caches, very important!!!!!\nrm -rf ~/.node-gyp\nrm -rf ~/.electron-gyp\nrm -rf ./node_modules\n```\n\n```powershell\n# On Window PowerShell (not cmd.exe!!!)\n# clean caches, very important!!!!!\nRemove-Item \"$($env:USERPROFILE)\\.node-gyp\" -Force -Recurse -ErrorAction Ignore\nRemove-Item \"$($env:USERPROFILE)\\.electron-gyp\" -Force -Recurse -ErrorAction Ignore\nRemove-Item .\\node_modules -Force -Recurse -ErrorAction Ignore\n```\n\nThen run `npm`:\n\n```bash\n# 1.7.6 is the version of electron\n# It's very important to set the electron version correctly!!!\n# check out the version value in your package.json\nnpm install --runtime=electron --target=1.7.6\n\n# verify the electron binary and its version by opening it\n./node_modules/.bin/electron\n```\n\n~~The `npm install` will install `zerorpc-node` from [my fork](https://github.com/0rpc/zerorpc-node/pull/84) to skip building from sources.~~ Updated: the pull request of `zerorpc-node` was [merged](https://github.com/0rpc/zerorpc-node/pull/84) so everyone is encouraged to use the official repo instead.\n\n(Consider [adding `./.npmrc`](https://docs.npmjs.com/files/npmrc) in the project folder if necessary.)\n\nAll libraries should be fine now.\n\n#### optional: building from sources\n\nIf the above installation causes any errors **even while setting the electron version correctly**, we may have to build the packages from sources.\n\nIronically, to compile Node.js C/C++ native codes, we need to have `python2` configured, no matter what Python version we are using for our Python application. Check out the [official guide](https://github.com/nodejs/node-gyp).\n\nEspecially, if working on Windows, open PowerShell **as Administrator**, and run `npm install --global --production windows-build-tools` to install a separated Python 2.7 in `%USERPROFILE%\\.windows-build-tools\\python27` and other required VS libraries. We only need to do it at once.\n\nThen, **clean `~/.node-gyp` and `./node_modules` caches as described above at first.**\n\nSet the `npm` [for Electron](https://github.com/electron/electron/blob/master/docs/tutorial/using-native-node-modules.md), and install the required libraries.\n\nSet the environment variables for Linux (Ubuntu) / OS X / Windows:\n\n```bash\n# On Linux / OS X:\n\n# env\nexport npm_config_target=1.7.6 # electron version\nexport npm_config_runtime=electron\nexport npm_config_disturl=https://atom.io/download/electron\nexport npm_config_build_from_source=true\n\n# may not be necessary\n#export npm_config_arch=x64\n#export npm_config_target_arch=x64\n\nnpm config ls\n```\n\n```powershell\n# On Window PowerShell (not cmd.exe!!!)\n\n$env:npm_config_target=\"1.7.6\" # electron version\n$env:npm_config_runtime=\"electron\"\n$env:npm_config_disturl=\"https://atom.io/download/electron\"\n$env:npm_config_build_from_source=\"true\"\n\n# may not be necessary\n#$env:npm_config_arch=\"x64\"\n#$env:npm_config_target_arch=\"x64\"\n\nnpm config ls\n```\n\nThen install things:\n\n```bash\n# in the same shell as above!!!\n# because you want to make good use of the above environment variables\n\n# install everything based on the package.json\nnpm install\n\n# verify the electron binary and its version by opening it\n./node_modules/.bin/electron\n```\n\n(Consider [adding `./.npmrc`](https://docs.npmjs.com/files/npmrc) in the project folder if necessary.)\n\n## core functions\n\n### Python part\n\nWe want to build up a ZeroMQ server in Python end.\n\nPut `calc.py` into folder `pycalc/`. Then create another file `pycalc/api.py`. Check [`zerorpc-python`](https://github.com/0rpc/zerorpc-python) for reference.\n\n```python\nfrom __future__ import print_function\nfrom calc import calc as real_calc\nimport sys\nimport zerorpc\n\nclass CalcApi(object):\n    def calc(self, text):\n        \"\"\"based on the input text, return the int result\"\"\"\n        try:\n            return real_calc(text)\n        except Exception as e:\n            return 0.0\n    def echo(self, text):\n        \"\"\"echo any text\"\"\"\n        return text\n\ndef parse_port():\n    return 4242\n\ndef main():\n    addr = 'tcp://127.0.0.1:' + parse_port()\n    s = zerorpc.Server(CalcApi())\n    s.bind(addr)\n    print('start running on {}'.format(addr))\n    s.run()\n\nif __name__ == '__main__':\n    main()\n```\n\nTo test the correctness, run `python pycalc/api.py` in one terminal. Then **open another terminal**, run this command and see the result:\n\n```bash\nzerorpc tcp://localhost:4242 calc \"1 + 1\"\n## connecting to \"tcp://localhost:4242\"\n## 2.0\n```\n\nAfter debugging, **remember to terminate the Python function**.\n\nActually, this is yet another **server**, communicated over `zeromq` over TCP, rather than traditional web server over HTTP.\n\n### Node.js / Electron part\n\nBasic idea: In the main process, spawn the Python child process and create the window. In the render process, use Node.js runtime and `zerorpc` library to communicate with Python child process. All the HTML / JavaScript / CSS are managed by Electron, instead of by Python web server (The example in the previous post used Python web server to dynamically generate HTML codes).\n\nIn `main.js`, these are default codes to start from, with nothing special:\n\n```js\n// main.js\n\nconst electron = require('electron')\nconst app = electron.app\nconst BrowserWindow = electron.BrowserWindow\nconst path = require('path')\n\nlet mainWindow = null\nconst createWindow = () =\u003e {\n  mainWindow = new BrowserWindow({width: 800, height: 600})\n  mainWindow.loadURL(require('url').format({\n    pathname: path.join(__dirname, 'index.html'),\n    protocol: 'file:',\n    slashes: true\n  }))\n  mainWindow.webContents.openDevTools()\n  mainWindow.on('closed', () =\u003e {\n    mainWindow = null\n  })\n}\napp.on('ready', createWindow)\napp.on('window-all-closed', () =\u003e {\n  if (process.platform !== 'darwin') {\n    app.quit()\n  }\n})\napp.on('activate', () =\u003e {\n  if (mainWindow === null) {\n    createWindow()\n  }\n})\n```\n\nWe want to add some code to spawn Python child process:\n\n```js\n// add these to the end or middle of main.js\n\nlet pyProc = null\nlet pyPort = null\n\nconst selectPort = () =\u003e {\n  pyPort = 4242\n  return pyPort\n}\n\nconst createPyProc = () =\u003e {\n  let port = '' + selectPort()\n  let script = path.join(__dirname, 'pycalc', 'api.py')\n  pyProc = require('child_process').spawn('python', [script, port])\n  if (pyProc != null) {\n    console.log('child process success')\n  }\n}\n\nconst exitPyProc = () =\u003e {\n  pyProc.kill()\n  pyProc = null\n  pyPort = null\n}\n\napp.on('ready', createPyProc)\napp.on('will-quit', exitPyProc)\n```\n\nIn `index.html`, we have an `\u003cinput\u003e` for input, and `\u003cdiv\u003e` for output:\n\n```html\n\u003c!-- index.html --\u003e\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"UTF-8\"\u003e\n    \u003ctitle\u003eHello Calculator!\u003c/title\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003ch1\u003eHello Calculator!\u003c/h1\u003e\n    \u003cp\u003eInput something like \u003ccode\u003e1 + 1\u003c/code\u003e.\u003c/p\u003e\n    \u003cp\u003eThis calculator supports \u003ccode\u003e+-*/^()\u003c/code\u003e,\n    whitespaces, and integers and floating numbers.\u003c/p\u003e\n    \u003cinput id=\"formula\" value=\"1 + 2.0 * 3.1 / (4 ^ 5.6)\"\u003e\u003c/input\u003e\n    \u003cdiv id=\"result\"\u003e\u003c/div\u003e\n  \u003c/body\u003e\n  \u003cscript\u003e\n    require('./renderer.js')\n  \u003c/script\u003e\n\u003c/html\u003e\n```\n\nIn `renderer.js`, we have codes for initialization of `zerorpc` **client**, and the code for watching the changes in the input. Once the user types some formula into the text area, the JS send the text to Python backend and retrieve the computed result.\n\n```js\n// renderer.js\n\nconst zerorpc = require(\"zerorpc\")\nlet client = new zerorpc.Client()\nclient.connect(\"tcp://127.0.0.1:4242\")\n\nlet formula = document.querySelector('#formula')\nlet result = document.querySelector('#result')\nformula.addEventListener('input', () =\u003e {\n  client.invoke(\"calc\", formula.value, (error, res) =\u003e {\n    if(error) {\n      console.error(error)\n    } else {\n      result.textContent = res\n    }\n  })\n})\nformula.dispatchEvent(new Event('input'))\n```\n\n## running\n\nRun this to see the magic:\n\n```bash\n./node_modules/.bin/electron .\n```\n\nAwesome!\n\nIf something like dynamic linking errors shows up, try to clean the caches and install the libraries again.\n\n```bash\nrm -rf node_modules\nrm -rf ~/.node-gyp ~/.electron-gyp\n\nnpm install\n```\n\n## packaging\n\nSome people are asking for the packaging. This is easy: apply the knowledge of how to package Python applications and Electron applications.\n\n### Python part\n\nUser [PyInstaller](http://www.pyinstaller.org/).\n\nRun the following in the terminal:\n\n```bash\npyinstaller pycalc/api.py --distpath pycalcdist\n\nrm -rf build/\nrm -rf api.spec\n```\n\nIf everything goes well, the `pycalcdist/api/` folder should show up, as well as the executable inside that folder. This is the complete independent Python executable that could be moved to somewhere else.\n\n**Attention: the independent Python executable has to be generated!** Because the target machine we want to distribute to may not have correct Python shell and/or required Python libraries. It's almost impossible to just copy the Python source codes.\n\n### Node.js / Electron part\n\nThis is tricky because of the Python executable.\n\nIn the above example code, I write\n\n```js\n  // part of main.js\n  let script = path.join(__dirname, 'pycalc', 'api.py')\n  pyProc = require('child_process').spawn('python', [script, port])\n```\n\nHowever, once we package the Python code, **we should no longer `spawn` Python script**. Instead, **we should `execFile` the generated excutable**.\n\nElectron doesn't provide functions to check whether the app is under distributed or not (at least I don't find it). So I use a workaround here: check whether the Python executable has been generated or not.\n\nIn `main.js`, add the following functions:\n\n```js\n// main.js\n\nconst PY_DIST_FOLDER = 'pycalcdist'\nconst PY_FOLDER = 'pycalc'\nconst PY_MODULE = 'api' // without .py suffix\n\nconst guessPackaged = () =\u003e {\n  const fullPath = path.join(__dirname, PY_DIST_FOLDER)\n  return require('fs').existsSync(fullPath)\n}\n\nconst getScriptPath = () =\u003e {\n  if (!guessPackaged()) {\n    return path.join(__dirname, PY_FOLDER, PY_MODULE + '.py')\n  }\n  if (process.platform === 'win32') {\n    return path.join(__dirname, PY_DIST_FOLDER, PY_MODULE, PY_MODULE + '.exe')\n  }\n  return path.join(__dirname, PY_DIST_FOLDER, PY_MODULE, PY_MODULE)\n}\n```\n\nAnd change the function `createPyProc` to this:\n\n```js\n// main.js\n// the improved version\nconst createPyProc = () =\u003e {\n  let script = getScriptPath()\n  let port = '' + selectPort()\n\n  if (guessPackaged()) {\n    pyProc = require('child_process').execFile(script, [port])\n  } else {\n    pyProc = require('child_process').spawn('python', [script, port])\n  }\n\n  if (pyProc != null) {\n    //console.log(pyProc)\n    console.log('child process success on port ' + port)\n  }\n}\n```\n\nThe key point is, check whether the `*dist` folder has been generated or not. If generated, it means we are in \"production\" mode, `execFile` the executable directly; otherwise, `spawn` the script using a Python shell.\n\nIn the end, run [`electron-packager`](https://github.com/electron-userland/electron-packager) to generate the bundled application. We also want to exclude some folders (For example, `pycalc/` is no longer needed to be bundled), **using regex** (instead of glob, surprise!). The name, platform, and arch are inferred from `package.json`. For more options, check out the docs.\n\n```bash\n# we need to make sure we have bundled the latest Python code\n# before running the below command!\n# Or, actually, we could bundle the Python executable later,\n# and copy the output into the correct distributable Electron folder...\n\n./node_modules/.bin/electron-packager . --overwrite --ignore=\"pycalc$\" --ignore=\"\\.venv\" --ignore=\"old-post-backup\"\n## Packaging app for platform win32 x64 using electron v1.7.6\n## Wrote new app to ./pretty-calculator-win32-x64\n```\n\nI do not check `asar` format's availability. I guess it will slow down the startup speed.\n\nAfter that, we have the generated packaged Electron in current directory! For me, the result is `./pretty-calculator-win32-x64/`. On my machine, it's around 170 MB (Electron itself occupies more than 84.2 MB). I also tried to compress it, the generated `.7z` file is around 43.3 MB.\n\nCopy / Move the folder(s) to anywhere or other machines to check the result! :-)\n\n## further faq\n\n### full code?\n\nSee [GitHub `electron-python-example`](https://github.com/fyears/electron-python-example).\n\n### solutions to errors\n\n[issue #6](https://github.com/fyears/electron-python-example/issues/6): `... failed with KeyError`\n\n[issue #7](https://github.com/fyears/electron-python-example/issues/7): `Uncaught Error: Module version mismatch. Expected 50, got 48.`\n\nUninstall everything, **set up the npm environment variables correctly especially for the electron version**, remember to `activate` the virtualenv if using Python `virtualenv`.\n\n### further optimization?\n\nTrim some unnecessary files in Python executable by configuring `pyinstaller` further. Trim Electron (is it possible?). Use even faster IPC methods (though `ZeroMQ` is one of the fastest in most cases).\n\nWhat's more, use QT (huh??), rewrite necessary codes in Node.js / Go / C / C++ (huh??). You name it.\n\n### Can I use other programming languages besides Python?\n\nSure. The solution described here can also be applied to any other programming languages besides Python. Except that, if you want to use Electron as GUI of C/C++ applications, I strongly recommend using Node.js native C/C++ communication mechanism instead of using IPC. Moreover, if you have Java, C# application, using `Swing` or `WPF` are much more mature choices.\n\nBut, unfortunately, Electron is not for mobile applications and it makes little sense even if possible. Please use native GUI on those platforms.\n\n## conclusion and further thinkings\n\nIt's still a promising solution. For drawing interface, we want to use some markup language for declarative UI. HTML happens to be one of the best choices, and its companions JS and CSS happen to have one of the most optimized renderers: the web browser. That's why I am (so) interested in using web technologies for GUI when possible. A few years before the web browsers were not powerful enough, but the story is kind of different now.\n\nI hope this post is helpful to you.\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffyears%2Felectron-python-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffyears%2Felectron-python-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffyears%2Felectron-python-example/lists"}