20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/src/index.jsx:
--------------------------------------------------------------------------------
1 |
2 | import ReactDOM from 'react-dom'
3 | import reportWebVitals from './reportWebVitals';
4 |
5 | import Header from './components/Header/Header'
6 | import Editor from './components/Editor/Editor'
7 | import Ticker from './components/Ticker/Ticker'
8 |
9 | import './index.sass'
10 |
11 |
12 | const App = function () {
13 | return (
14 | <>
15 |
16 |
17 |
18 | >
19 | )
20 | }
21 |
22 | const view = App('pywebview')
23 |
24 | const element = document.getElementById('app')
25 | ReactDOM.render(view, element)
26 |
27 | export default App
28 |
29 |
30 |
31 |
32 |
33 | // If you want to start measuring performance in your app, pass a function
34 | // to log results (for example: reportWebVitals(console.log))
35 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
36 | reportWebVitals();
--------------------------------------------------------------------------------
/src/components/Ticker/Ticker.tsx:
--------------------------------------------------------------------------------
1 | /* global pywebview */
2 |
3 | import * as React from 'react'
4 |
5 | import './Ticker.sass'
6 | import logo from '../../assets/logo.png'
7 |
8 |
9 |
10 | export default function Ticker() {
11 | const [ticker, setTicker] = React.useState('')
12 |
13 | React.useEffect(() => {
14 | window.addEventListener('pywebviewready', function () {
15 |
16 |
17 | if (!(window as any).pywebview.state) {
18 | (window as any).pywebview.state = {}
19 | }
20 | // Expose setTicker in order to call it from Python
21 | (window as any).pywebview.state.setTicker = setTicker
22 |
23 | })
24 | }, [])
25 |
26 | return (
27 |
28 |
Welcome to pywebview
29 |
30 |
31 | You can freely communicate between Javascript with Python without an HTTP server. This value, for example, is being generated by Python code:
32 | {ticker}
33 |
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/index.spec:
--------------------------------------------------------------------------------
1 | # -*- mode: python ; coding: utf-8 -*-
2 |
3 |
4 | block_cipher = None
5 |
6 |
7 | a = Analysis(['src/index.py'],
8 | pathex=[],
9 | binaries=[],
10 | datas=[],
11 | hiddenimports=[],
12 | hookspath=[],
13 | hooksconfig={},
14 | runtime_hooks=[],
15 | excludes=[],
16 | win_no_prefer_redirects=False,
17 | win_private_assemblies=False,
18 | cipher=block_cipher,
19 | noarchive=False)
20 | pyz = PYZ(a.pure, a.zipped_data,
21 | cipher=block_cipher)
22 |
23 | exe = EXE(pyz,
24 | a.scripts,
25 | a.binaries,
26 | a.zipfiles,
27 | a.datas,
28 | [],
29 | name='index',
30 | debug=False,
31 | bootloader_ignore_signals=False,
32 | strip=False,
33 | upx=True,
34 | upx_exclude=[],
35 | runtime_tmpdir=None,
36 | console=True,
37 | disable_windowed_traceback=False,
38 | target_arch=None,
39 | codesign_identity=None,
40 | entitlements_file=None )
41 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2020, Roman
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
8 |
10 |
12 |
14 |
16 |
20 |
22 |
31 | React App
32 |
33 |
34 |
35 |
36 |
37 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/src/index.py:
--------------------------------------------------------------------------------
1 | import os
2 | import threading
3 | import webview
4 |
5 | from time import time
6 |
7 |
8 | class Api:
9 | def fullscreen(self):
10 | webview.windows[0].toggle_fullscreen()
11 |
12 | def save_content(self, content):
13 | filename = webview.windows[0].create_file_dialog(webview.SAVE_DIALOG)
14 | if not filename:
15 | return
16 |
17 | with open(filename, 'w') as f:
18 | f.write(content)
19 |
20 | def ls(self):
21 | return os.listdir('.')
22 |
23 |
24 | def get_entrypoint():
25 | def exists(path):
26 | return os.path.exists(os.path.join(os.path.dirname(__file__), path))
27 |
28 | if exists('../gui/index.html'): # unfrozen development
29 | return '../gui/index.html'
30 |
31 | if exists('../Resources/gui/index.html'): # frozen py2app
32 | return '../Resources/gui/index.html'
33 |
34 | if exists('./gui/index.html'):
35 | return './gui/index.html'
36 |
37 | raise Exception('No index.html found')
38 |
39 |
40 | def set_interval(interval):
41 | def decorator(function):
42 | def wrapper(*args, **kwargs):
43 | stopped = threading.Event()
44 |
45 | def loop(): # executed in another thread
46 | while not stopped.wait(interval): # until stopped
47 | function(*args, **kwargs)
48 |
49 | t = threading.Thread(target=loop)
50 | t.daemon = True # stop if the program exits
51 | t.start()
52 | return stopped
53 | return wrapper
54 | return decorator
55 |
56 |
57 |
58 | entry = get_entrypoint()
59 |
60 | @set_interval(1)
61 | def update_ticker():
62 | if len(webview.windows) > 0:
63 | webview.windows[0].evaluate_js('window.pywebview.state.setTicker("%d")' % time())
64 |
65 |
66 | if __name__ == '__main__':
67 | window = webview.create_window('pywebview-react boilerplate', entry, js_api=Api())
68 | webview.start(update_ticker, debug=True)
69 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pywebview-react-boilerplate
2 | This is a simple boilerplate to help you start with _pywebview_ and React. It sets up the development environment, install dependencies, as well as provides scripts for building an executable. Stack is based on Pywebview, React (create-react-app), SASS, Pyinstaller (Windows/Linux) and Py2app (macOS).
3 |
4 | ## Requirements
5 | - Python 3
6 | - Node
7 | - virtualenv
8 |
9 | ## Installation
10 |
11 | ``` bash
12 | yarn run init
13 | ```
14 |
15 | This will create a virtual environment, install pip and Node dependencies. Alternatively you can perform these steps manually.
16 |
17 | ``` bash
18 | yarn install
19 | pip install -r requirements.txt
20 | ```
21 |
22 | On Linux systems installation system makes educated guesses. If you run KDE, QT dependencies are installed, otherwise GTK is chosen. `apt` is used for installing GTK dependencies. In case you are running a non apt-based system, you will have to install GTK dependencies manually. See [installation](https://pywebview.flowrl.com/guide/installation.html) for details.
23 |
24 | ## Usage
25 |
26 | To launch the application.
27 |
28 | ``` bash
29 | yarn run start
30 | ```
31 |
32 | To build an executable. The output binary will be produced in the `dist` directory.
33 |
34 | ``` bash
35 | yarn run build
36 | ```
37 |
38 | To start a development server (only for testing frontend code).
39 |
40 | ``` bash
41 | yarn run dev
42 | ```
43 |
44 | To clean the developement environment, this will delete `gui`, `dist`, `build` directories.
45 |
46 | ``` bash
47 | yarn run clean
48 | ```
49 |
50 |
51 | To eject create-react-app and tweak the configuration as you may wish.
52 |
53 | ``` bash
54 | yarn run eject
55 | ```
56 |
57 | To test the frontend code if you have written tests.
58 |
59 | ``` bash
60 | yarn run frontend:test
61 | ```
62 |
63 |
64 | ## Bug reporting
65 |
66 | Please report _pywebview_ related bugs directly to [pywebview's repository](https://github.com/r0x0r/pywebview). This repository is only for the issues related to this boilerplate.
67 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-pywebview-boilerplate",
3 | "version": "0.1.0",
4 | "private": true,
5 | "homepage": "./",
6 | "dependencies": {
7 | "@testing-library/jest-dom": "^6.1.2",
8 | "@testing-library/react": "^14.0.0",
9 | "@testing-library/user-event": "^14.4.3",
10 | "@types/jest": "^29.5.4",
11 | "@types/node": "^20.5.7",
12 | "@types/react": "^18.2.21",
13 | "@types/react-dom": "^18.2.7",
14 | "react": "^18.2.0",
15 | "react-dom": "^18.2.0",
16 | "react-scripts": "5.0.1",
17 | "sass": "^1.66.1",
18 | "typescript": "^5.2.2",
19 | "web-vitals": "^3.4.0"
20 | },
21 | "scripts": {
22 | "dev": "react-scripts start",
23 | "frontend:dev": "BUILD_PATH='./gui' react-scripts build",
24 | "frontend:prod": "BUILD_PATH='./gui' react-scripts build",
25 | "frontend:test": "react-scripts test",
26 | "frontend:eject": "react-scripts eject",
27 | "build": "yarn run clean && yarn run frontend:prod && run-script-os",
28 | "build:macos": "./venv-pywebview/bin/python build-macos.py py2app",
29 | "build:windows": ".\\venv-pywebview\\Scripts\\pyinstaller build-windows.spec",
30 | "build:linux": "./venv-pywebview/bin/pyinstaller build-linux.spec --onefile",
31 | "clean": "run-script-os",
32 | "clean:default": "rm -rf gui 2>/dev/null; rm -rf build 2>/dev/null; rm -rf dist 2>/dev/null; ",
33 | "clean:windows": "if exist gui rd /S /Q gui & if exist build rd /S /Q build & if exist dist rd /S /Q dist",
34 | "init": "yarn install && run-script-os",
35 | "init:windows": "virtualenv -p python venv-pywebview && .\\venv-pywebview\\Scripts\\pip install -r requirements.txt",
36 | "init:linux": "virtualenv -p python3 venv-pywebview && if [[ -z \"${KDE_FULL_SESSION}\" ]]; then yarn run init:qt5; else yarn run init:gtk; fi",
37 | "init:default": "virtualenv -p python3 venv-pywebview && ./venv-pywebview/bin/pip install -r requirements.txt",
38 | "init:qt5": "./venv-pywebview/bin/pip install pyqt5 pyqtwebengine -r requirements.txt",
39 | "init:gtk": "sudo apt install libgirepository1.0-dev gcc libcairo2-dev pkg-config python3-dev gir1.2-gtk-3.0 && ./venv-pywebview/bin/pip install pycairo pygobject -r requirements.txt",
40 | "start": "yarn run frontend:dev && run-script-os",
41 | "start:windows": ".\\venv-pywebview\\Scripts\\python src\\index.py",
42 | "start:default": "./venv-pywebview/bin/python src/index.py"
43 | },
44 | "eslintConfig": {
45 | "extends": [
46 | "react-app",
47 | "react-app/jest"
48 | ]
49 | },
50 | "browserslist": {
51 | "production": [
52 | ">0.2%",
53 | "not dead",
54 | "not op_mini all"
55 | ],
56 | "development": [
57 | "last 1 chrome version",
58 | "last 1 firefox version",
59 | "last 1 safari version"
60 | ]
61 | },
62 | "devDependencies": {
63 | "run-script-os": "^1.1.6"
64 | }
65 | }
66 |
--------------------------------------------------------------------------------