glue-ui 0.6.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- glue_ui-0.6.6/LICENSE +22 -0
- glue_ui-0.6.6/MANIFEST.in +1 -0
- glue_ui-0.6.6/PKG-INFO +450 -0
- glue_ui-0.6.6/README.md +405 -0
- glue_ui-0.6.6/glue/__init__.py +1048 -0
- glue_ui-0.6.6/glue/__main__.py +54 -0
- glue_ui-0.6.6/glue/browsers.py +156 -0
- glue_ui-0.6.6/glue/browsers_launcher.py +82 -0
- glue_ui-0.6.6/glue/chrome.py +37 -0
- glue_ui-0.6.6/glue/edge.py +31 -0
- glue_ui-0.6.6/glue/glue.js +520 -0
- glue_ui-0.6.6/glue/py.typed +0 -0
- glue_ui-0.6.6/glue/settings.py +134 -0
- glue_ui-0.6.6/glue/types.py +51 -0
- glue_ui-0.6.6/glue/webview.py +451 -0
- glue_ui-0.6.6/glue_ui.egg-info/PKG-INFO +450 -0
- glue_ui-0.6.6/glue_ui.egg-info/SOURCES.txt +21 -0
- glue_ui-0.6.6/glue_ui.egg-info/dependency_links.txt +1 -0
- glue_ui-0.6.6/glue_ui.egg-info/requires.txt +12 -0
- glue_ui-0.6.6/glue_ui.egg-info/top_level.txt +1 -0
- glue_ui-0.6.6/pyproject.toml +23 -0
- glue_ui-0.6.6/setup.cfg +4 -0
- glue_ui-0.6.6/setup.py +47 -0
glue_ui-0.6.6/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Chris Knott
|
|
4
|
+
Copyright (c) 2026 alepodj
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
include README.md
|
glue_ui-0.6.6/PKG-INFO
ADDED
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: glue-ui
|
|
3
|
+
Version: 0.6.6
|
|
4
|
+
Summary: For little HTML GUI applications, with easy Python/JS interop
|
|
5
|
+
Home-page: https://github.com/alepodj/Glue
|
|
6
|
+
Author: alepodj
|
|
7
|
+
Keywords: gui,html,javascript,desktop,pywebview,chrome,edge
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Natural Language :: English
|
|
10
|
+
Classifier: Operating System :: MacOS
|
|
11
|
+
Classifier: Operating System :: POSIX
|
|
12
|
+
Classifier: Operating System :: Microsoft :: Windows :: Windows 10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
20
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: bottle<1.0.0
|
|
25
|
+
Requires-Dist: bottle-websocket<1.0.0
|
|
26
|
+
Requires-Dist: gevent
|
|
27
|
+
Requires-Dist: gevent-websocket<1.0.0
|
|
28
|
+
Requires-Dist: pyparsing<4.0.0,>=3.0.0
|
|
29
|
+
Requires-Dist: pywebview>=6.0
|
|
30
|
+
Provides-Extra: jinja2
|
|
31
|
+
Requires-Dist: jinja2>=2.10; extra == "jinja2"
|
|
32
|
+
Provides-Extra: build
|
|
33
|
+
Requires-Dist: pyinstaller; extra == "build"
|
|
34
|
+
Dynamic: author
|
|
35
|
+
Dynamic: classifier
|
|
36
|
+
Dynamic: description
|
|
37
|
+
Dynamic: description-content-type
|
|
38
|
+
Dynamic: home-page
|
|
39
|
+
Dynamic: keywords
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
Dynamic: provides-extra
|
|
42
|
+
Dynamic: requires-dist
|
|
43
|
+
Dynamic: requires-python
|
|
44
|
+
Dynamic: summary
|
|
45
|
+
|
|
46
|
+
# Glue
|
|
47
|
+
<sub>Glue is a fork of [Eel](https://github.com/python-eel/Eel) by Chris Knott and contributors.</sub>
|
|
48
|
+
|
|
49
|
+
A little and opinionated Python library for making **desktop apps with HTML, CSS, and JavaScript** — plus full access to Python. It hosts a local window, then lets Python and JavaScript call each other. No new UI framework.
|
|
50
|
+
|
|
51
|
+

|
|
52
|
+

|
|
53
|
+

|
|
54
|
+

|
|
55
|
+

|
|
56
|
+
|
|
57
|
+
## Contents
|
|
58
|
+
|
|
59
|
+
- [Install](#install)
|
|
60
|
+
- [Quick start](#quick-start)
|
|
61
|
+
- [Demo](#demo)
|
|
62
|
+
- [Call both ways](#call-both-ways)
|
|
63
|
+
- [Hello, World!](#hello-world)
|
|
64
|
+
- [Return values](#return-values)
|
|
65
|
+
- [Settings](#settings)
|
|
66
|
+
- [App options](#app-options)
|
|
67
|
+
- [Hosts](#hosts)
|
|
68
|
+
- [Async Python](#async-python)
|
|
69
|
+
- [Package with PyInstaller](#package-with-pyinstaller)
|
|
70
|
+
- [Examples](#examples)
|
|
71
|
+
- [Project layout](#project-layout)
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Install
|
|
76
|
+
|
|
77
|
+
```shell
|
|
78
|
+
pip install glue-ui
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
That installs the **PyPI** project `glue-ui`. You still `import glue` in Python (the import name is unchanged).
|
|
82
|
+
|
|
83
|
+
From a clone of this repo:
|
|
84
|
+
|
|
85
|
+
```shell
|
|
86
|
+
pip install .
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Editable / development (code changes apply without reinstalling):
|
|
90
|
+
|
|
91
|
+
```shell
|
|
92
|
+
pip install -e .
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Directly from GitHub (no local clone required):
|
|
96
|
+
|
|
97
|
+
```shell
|
|
98
|
+
pip install "git+https://github.com/alepodj/Glue.git@main"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Optional extras:
|
|
102
|
+
|
|
103
|
+
```shell
|
|
104
|
+
pip install "glue-ui[jinja2]" # Jinja2 templates
|
|
105
|
+
pip install "glue-ui[build]" # PyInstaller for packaging
|
|
106
|
+
# From a clone: pip install ".[jinja2]" / pip install ".[build]"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Quick start
|
|
112
|
+
|
|
113
|
+
Put your frontend in a folder named `ui/` and `index.html` (the defaults), then:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
import glue
|
|
117
|
+
|
|
118
|
+
glue.init() # Override ui/ with any path: glue.init('frontend')
|
|
119
|
+
glue.start() # Override index.html: glue.start('main.html')
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
By default Glue opens a **PyWebView** native window. If that isn’t available, it falls back to **Chrome/Chromium** in app mode (`--app`), then **Edge** on Windows only.
|
|
123
|
+
|
|
124
|
+
Include the bridge on every html page:
|
|
125
|
+
|
|
126
|
+
```html
|
|
127
|
+
<script type="text/javascript" src="/glue.js"></script>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
***Put your backend logic in one Python file or split it across any folders you like — Glue does not require a fixed backend layout. You own the imports and paths; your start point is the Python script you run (often at the project root).***
|
|
131
|
+
|
|
132
|
+
### Security note
|
|
133
|
+
|
|
134
|
+
By default Glue binds to **localhost** and only accepts Glue WebSocket clients from the loopback interface. Every `@glue.expose`d Python function is callable by any page that can open that socket — treat exposed functions as your trust boundary. Do not put secrets under the frontend folder (`ui/`). Use `all_interfaces=True` only on trusted networks (it allows remote clients to call exposed functions).
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Demo
|
|
139
|
+
|
|
140
|
+
The [`examples/00 - presentation`](examples/00%20-%20presentation) app is a full walkthrough of the idea. Run it with:
|
|
141
|
+
|
|
142
|
+
```shell
|
|
143
|
+
cd "examples/00 - presentation"
|
|
144
|
+
python presentation.py
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Call both ways
|
|
150
|
+
|
|
151
|
+
### Python → available in JavaScript
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
#python
|
|
155
|
+
@glue.expose
|
|
156
|
+
def add(a, b):
|
|
157
|
+
return a + b
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
```javascript
|
|
161
|
+
//javascript
|
|
162
|
+
let sum = await glue.add(1, 2)();
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### JavaScript → available in Python
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
//javascript
|
|
169
|
+
glue.expose(js_random);
|
|
170
|
+
function js_random() {
|
|
171
|
+
return Math.random();
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
#python
|
|
177
|
+
n = glue.js_random()() # wait for the value
|
|
178
|
+
glue.js_random()(print) # or use a callback
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Complex values travel as JSON over a WebSocket.
|
|
182
|
+
|
|
183
|
+
If a JS bundler renames functions, expose them with an explicit name:
|
|
184
|
+
|
|
185
|
+
```javascript
|
|
186
|
+
glue.expose(someFunction, "my_javascript_function");
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Hello, World!
|
|
192
|
+
|
|
193
|
+
Full example: [`examples/01 - hello_world`](examples/01%20-%20hello_world)
|
|
194
|
+
|
|
195
|
+
**`ui/hello.html`**
|
|
196
|
+
|
|
197
|
+
```html
|
|
198
|
+
<!DOCTYPE html>
|
|
199
|
+
<html>
|
|
200
|
+
<head>
|
|
201
|
+
<title>Hello, World!</title>
|
|
202
|
+
<script type="text/javascript" src="/glue.js"></script>
|
|
203
|
+
<script type="text/javascript">
|
|
204
|
+
glue.expose(say_hello_js);
|
|
205
|
+
function say_hello_js(x) {
|
|
206
|
+
console.log("Hello from " + x);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
say_hello_js("Javascript World!");
|
|
210
|
+
glue.say_hello_py("Javascript World!");
|
|
211
|
+
</script>
|
|
212
|
+
</head>
|
|
213
|
+
<body>Hello, World!</body>
|
|
214
|
+
</html>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**`hello.py`**
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
import glue
|
|
221
|
+
|
|
222
|
+
glue.init()
|
|
223
|
+
|
|
224
|
+
@glue.expose
|
|
225
|
+
def say_hello_py(x):
|
|
226
|
+
print('Hello from %s' % x)
|
|
227
|
+
|
|
228
|
+
say_hello_py('Python World!')
|
|
229
|
+
glue.say_hello_js('Python World!')
|
|
230
|
+
|
|
231
|
+
glue.start('hello.html')
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Run `python hello.py`. Python→JS calls made before the page WebSocket connects are queued and flushed when the bridge is ready (the HTTP server is already up before the window opens).
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Return values
|
|
239
|
+
|
|
240
|
+
Python and the UI talk over a WebSocket. Glue gives you two ways to get a result back.
|
|
241
|
+
|
|
242
|
+
**Callback**
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
glue.js_random()(lambda n: print('Got', n))
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**Synchronous wait** (after `glue.start()`, or with `block=False`)
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
n = glue.js_random()()
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
In JavaScript, use `async` / `await`:
|
|
255
|
+
|
|
256
|
+
```javascript
|
|
257
|
+
let n = await glue.py_random()();
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Python waits up to 10 seconds for a JS result by default (`js_result_timeout` on `glue.init`).
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Settings
|
|
265
|
+
|
|
266
|
+
Optional durable JSON for themes, defaults, and other app state that should survive restarts. **Python only** — expose your own wrappers with `@glue.expose` if the UI needs it.
|
|
267
|
+
|
|
268
|
+
The default location is opt-in via `app_name`. Without it, Glue does not invent a path; you can still load/save any file with an explicit path.
|
|
269
|
+
|
|
270
|
+
```python
|
|
271
|
+
import glue
|
|
272
|
+
|
|
273
|
+
glue.init(app_name='myapp') # unlocks ~/.myapp/myapp.json (all OS)
|
|
274
|
+
|
|
275
|
+
data = glue.settings() # {} if the file does not exist yet — nothing created on disk
|
|
276
|
+
data['theme'] = 'dark'
|
|
277
|
+
glue.save_settings(data) # first save creates ~/.myapp/ and writes myapp.json
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
```python
|
|
281
|
+
# Custom file — works with or without app_name
|
|
282
|
+
data = glue.settings('/path/to/prefs.json')
|
|
283
|
+
glue.save_settings(data, '/path/to/prefs.json')
|
|
284
|
+
# or after settings(path), save_settings(data) reuses that path
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
- Structure of the JSON object is yours.
|
|
288
|
+
- `glue.settings_path()` returns the default path when `app_name` is set.
|
|
289
|
+
- Passing `app_name` alone does not create a folder or an empty file.
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## App options
|
|
294
|
+
|
|
295
|
+
Pass keyword arguments to `glue.start()`:
|
|
296
|
+
|
|
297
|
+
| Option | Default | Source | Hosts | Notes |
|
|
298
|
+
|--------|---------|--------|-------|--------|
|
|
299
|
+
| *(pages)* | `'index.html'` | Glue | all | Which HTML file(s) to open first (positional args), they must come before keyword args |
|
|
300
|
+
| `mode` | `'auto'` | Glue | all | How to show the UI — see [Hosts](#hosts) |
|
|
301
|
+
| `host` | `'localhost'` | Glue | all | Hostname the local HTTP server binds to |
|
|
302
|
+
| `port` | `8000` | Glue | all | Server port (`0` = pick a free port) |
|
|
303
|
+
| `block` | `True` | Glue | all | Keep `start()` running until the app exits. `False` returns so you can run your own loop (skips PyWebView in `auto`; PyWebView needs `True`) |
|
|
304
|
+
| `jinja_templates` | `None` | Glue | all | Subfolder of the UI root for Jinja2 templates |
|
|
305
|
+
| `close_callback` | `None` | Glue | all | Called when a window/WebSocket closes (cleanup / last-window logic) |
|
|
306
|
+
| `all_interfaces` | `False` | Glue | all | Listen on `0.0.0.0` and allow remote WebSocket clients (trusted networks only) |
|
|
307
|
+
| `disable_cache` | `True` | Glue | all | Send `no-store` so the browser doesn’t cache UI assets during development |
|
|
308
|
+
| `default_path` | `'index.html'` | Glue | all | Page served for the root URL `/` (homepage) |
|
|
309
|
+
| `app` | new Bottle | Glue | all | Optional custom [Bottle](https://bottlepy.org/) app (e.g. add auth/session middleware). Omit to let Glue create one |
|
|
310
|
+
| `shutdown_delay` | `1.0` | Glue | all | Seconds to wait after the last window closes before the Python process exits |
|
|
311
|
+
| `geometry` | `{}` | Glue | webview / chrome / edge | Per-page size/position when opening multiple files. Keys = page paths; values = `{'size': (w, h), 'position': (x, y)}` (either key optional). Overrides global `size` / `position` for that page |
|
|
312
|
+
| `app_mode` | `True` | Glue | chrome/edge | Open Chromium in `--app` (desktop-like, less browser chrome) |
|
|
313
|
+
| `cmdline_args` | `['--disable-http-cache']` | Glue | chrome/edge/custom | **Chrome/Edge:** extra flags appended to the browser command. **`custom`:** the full `Popen` argv (executable + args) |
|
|
314
|
+
| `frameless` | `True` | PyWebView | webview | Hide the OS title bar; Glue draws its in-page title bar (`False` = stock OS chrome) |
|
|
315
|
+
| `easy_drag` | `False` | PyWebView | webview | Allow dragging a frameless window from any empty area of the page |
|
|
316
|
+
| `shadow` | `True` on Windows | PyWebView | webview | Draw a drop shadow around the window |
|
|
317
|
+
| `debug` | `False` | PyWebView | webview | Enable DevTools / shortcuts (F12, F5, context menu) for debugging |
|
|
318
|
+
| `confirm_close` | `False` | PyWebView | webview | Ask “close this window?” before quitting |
|
|
319
|
+
| `fullscreen` | `False` | PyWebView | webview | Start covering the whole screen |
|
|
320
|
+
| `minimized` | `False` | PyWebView | webview | Start in the taskbar/dock (hidden until restored) |
|
|
321
|
+
| `maximized` | `False` | PyWebView | webview | Start maximized to the work area |
|
|
322
|
+
| `on_top` | `False` | PyWebView | webview | Keep the window above other windows |
|
|
323
|
+
| `min_size` | `(200, 100)` | PyWebView | webview | Smallest `(width, height)` the user can resize to |
|
|
324
|
+
| `icon` | `ui/favicon.ico` if present | PyWebView | webview / chrome / edge | **PyWebView:** app icon in the taskbar/dock (`.ico` / `.icns`). **Chrome/Edge:** caption icon comes from the page favicon — Glue injects `<link rel="icon" href="/favicon.ico?v=…">` into served HTML when `ui/favicon.ico` exists |
|
|
325
|
+
| `gui` | auto | PyWebView | webview | Force a specific web engine backend (`'edgechromium'`, `'qt'`, `'gtk'`, …) |
|
|
326
|
+
| `menu` | `[]` | PyWebView | webview | Native menu bar (`Menu` / `MenuAction` from PyWebView); empty = no menus |
|
|
327
|
+
| `webview_options` | `{}` | PyWebView | webview | Escape hatch for other [PyWebView](https://pywebview.flowrl.com/api/) kwargs; first-class kwargs win on conflict |
|
|
328
|
+
| `size` | `None` → **1280×720** | both | webview / chrome / edge | Initial content size as `(width, height)` pixels |
|
|
329
|
+
| `position` | `None` | both | webview / chrome / edge | Initial screen position as `(left, top)`; omit to leave placement to the host (usually centered) |
|
|
330
|
+
| `title` | `'Glue'` | both | webview | Text in the native/in-page title bar (Chrome/Edge use the page `<title>`) |
|
|
331
|
+
| `resizable` | `True` | both | webview | Allow the user to resize; on Windows frameless, Glue adds edge grips |
|
|
332
|
+
|
|
333
|
+
**Source Annotations:**
|
|
334
|
+
- `Glue` = Glue’s own API
|
|
335
|
+
- `PyWebView` = A PyWebView window option that Glue exposes on glue.start() for convenience
|
|
336
|
+
- `both` = Glue has always offered this as a first-class glue.start() option, and when the host is PyWebView, Glue also maps it into PyWebView’s kwargs
|
|
337
|
+
|
|
338
|
+
**Hosts Annotations:**
|
|
339
|
+
- Which window host honors the option (`all`, `webview`, `chrome/edge`, …).
|
|
340
|
+
|
|
341
|
+
**Defaults**:
|
|
342
|
+
- What you get when you omit the argument (Glue’s opinionated window setup). Pass an explicit value to override; that also wins over the same key in `webview_options`. Glue sets `SHOW_DEFAULT_MENUS=False` so stock Edit menus stay off unless you build your own. Advanced window control: `glue.get_webview_windows()`.
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## Hosts
|
|
347
|
+
|
|
348
|
+
**How Glue opens your UI** on any OS.
|
|
349
|
+
|
|
350
|
+
Default order for `mode='auto'`:
|
|
351
|
+
|
|
352
|
+
1. **PyWebView** — native window (Windows / macOS / Linux); full control via PyWebView APIs / `glue.get_webview_windows()`
|
|
353
|
+
2. **Chrome/Chromium** — app mode (`--app`) on all platforms
|
|
354
|
+
3. **Edge** — Windows only, if Chrome is missing
|
|
355
|
+
|
|
356
|
+
`size` / `position` / `geometry` apply on PyWebView as window kwargs, and on Chrome/Edge via `/glue.js` (`resizeTo` / `moveTo` on page load). When `ui/favicon.ico` is present, Glue injects a favicon `<link>` into served HTML so Chrome/Edge `--app` windows can show it in the caption.
|
|
357
|
+
|
|
358
|
+
PyWebView window options are first-class on [`glue.start()`](#app-options) (plus `webview_options` escape hatch).
|
|
359
|
+
|
|
360
|
+
| `mode` | Behavior |
|
|
361
|
+
|--------|----------|
|
|
362
|
+
| `'auto'` | PyWebView → Chrome → Edge (Windows); see above |
|
|
363
|
+
| `'webview'` | PyWebView only |
|
|
364
|
+
| `'chrome'` / `'edge'` | Force that Chromium browser |
|
|
365
|
+
| `None` | Server only (tests, custom frontends) |
|
|
366
|
+
| `'custom'` | Raw `Popen(cmdline_args)` — the full process argv (executable + args) |
|
|
367
|
+
|
|
368
|
+
Try [`01 - hello_world`](examples/01%20-%20hello_world) for auto launch, or [`02 - hello_world_chrome`](examples/02%20-%20hello_world_chrome) to force Chrome.
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## Async Python
|
|
373
|
+
|
|
374
|
+
Glue uses Bottle + Gevent. Prefer `glue.sleep()` and `glue.spawn()` over `time.sleep()`.
|
|
375
|
+
|
|
376
|
+
If you need Gevent monkey-patching, do it **before** importing Glue:
|
|
377
|
+
|
|
378
|
+
```python
|
|
379
|
+
from gevent import monkey
|
|
380
|
+
monkey.patch_all()
|
|
381
|
+
import glue
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
```python
|
|
385
|
+
import glue
|
|
386
|
+
glue.init()
|
|
387
|
+
|
|
388
|
+
def ticker():
|
|
389
|
+
while True:
|
|
390
|
+
print("tick")
|
|
391
|
+
glue.sleep(1.0)
|
|
392
|
+
|
|
393
|
+
glue.spawn(ticker)
|
|
394
|
+
glue.start(block=False)
|
|
395
|
+
|
|
396
|
+
while True:
|
|
397
|
+
print("main")
|
|
398
|
+
glue.sleep(1.0)
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
With `block=False`, `mode='auto'` skips PyWebView (its GUI loop needs the main thread) and uses Chrome/Edge instead.
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## Package with PyInstaller
|
|
406
|
+
|
|
407
|
+
1. Use a clean virtualenv with only what you need
|
|
408
|
+
2. `pip install "glue-ui[build]"` (or `pip install ".[build]"` from a clone)
|
|
409
|
+
3. From your app folder: `python -m glue your_script.py ui`
|
|
410
|
+
4. Check `dist/`, then ship with `--onefile --noconsole` when ready
|
|
411
|
+
|
|
412
|
+
Extra PyInstaller flags pass through, for example:
|
|
413
|
+
|
|
414
|
+
```shell
|
|
415
|
+
python -m glue file_access.py ui --exclude numpy --onefile --noconsole
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
See the [PyInstaller docs](https://pyinstaller.readthedocs.io/) for more.
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## Examples
|
|
423
|
+
|
|
424
|
+
| Example | What it shows |
|
|
425
|
+
|---------|----------------|
|
|
426
|
+
| [`00 - presentation`](examples/00%20-%20presentation) | Full product story (screenshots above) |
|
|
427
|
+
| [`01 - hello_world`](examples/01%20-%20hello_world) | Minimal two-way hello |
|
|
428
|
+
| [`02 - hello_world_chrome`](examples/02%20-%20hello_world_chrome) | Force Chrome |
|
|
429
|
+
| [`03 - callbacks`](examples/03%20-%20callbacks) | Async callbacks |
|
|
430
|
+
| [`04 - sync_callbacks`](examples/04%20-%20sync_callbacks) | Blocking returns |
|
|
431
|
+
| [`05 - file_access`](examples/05%20-%20file_access) | Python file access from the UI |
|
|
432
|
+
| [`06 - input`](examples/06%20-%20input) | Live input bridge |
|
|
433
|
+
| [`07 - jinja_templates`](examples/07%20-%20jinja_templates) | Jinja2 pages |
|
|
434
|
+
| [`08 - createreactapp`](examples/08%20-%20createreactapp) | React + TypeScript |
|
|
435
|
+
| [`09 - disable_cache`](examples/09%20-%20disable_cache) | Cache control |
|
|
436
|
+
| [`10 - custom_app_routes`](examples/10%20-%20custom_app_routes) | Custom Bottle routes |
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
## Project layout
|
|
441
|
+
|
|
442
|
+
```
|
|
443
|
+
my_app.py
|
|
444
|
+
ui/
|
|
445
|
+
index.html
|
|
446
|
+
css/
|
|
447
|
+
js/
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
Frontend lives under one `ui/` root (by default). Backend is normal Python. Glue glues them together.
|