website-as-app 1.0.0__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.
- website-as-app-1.0.0/LICENSE.md +15 -0
- website-as-app-1.0.0/PKG-INFO +100 -0
- website-as-app-1.0.0/README.md +79 -0
- website-as-app-1.0.0/setup.cfg +4 -0
- website-as-app-1.0.0/setup.py +80 -0
- website-as-app-1.0.0/website_as_app.egg-info/PKG-INFO +100 -0
- website-as-app-1.0.0/website_as_app.egg-info/SOURCES.txt +16 -0
- website-as-app-1.0.0/website_as_app.egg-info/dependency_links.txt +1 -0
- website-as-app-1.0.0/website_as_app.egg-info/entry_points.txt +3 -0
- website-as-app-1.0.0/website_as_app.egg-info/requires.txt +3 -0
- website-as-app-1.0.0/website_as_app.egg-info/top_level.txt +1 -0
- website-as-app-1.0.0/websiteapp/__init__.py +13 -0
- website-as-app-1.0.0/websiteapp/about.py +89 -0
- website-as-app-1.0.0/websiteapp/const.py +30 -0
- website-as-app-1.0.0/websiteapp/icons/default.png +0 -0
- website-as-app-1.0.0/websiteapp/icons/logo.png +0 -0
- website-as-app-1.0.0/websiteapp/utils.py +106 -0
- website-as-app-1.0.0/websiteapp/webapp.py +164 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Marcin Orlowski <mail (#) marcinorlowski (.) com>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
|
|
6
|
+
files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
|
|
7
|
+
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
|
8
|
+
Software is furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
11
|
+
|
|
12
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
13
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
14
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
15
|
+
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: website-as-app
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: ['Website As App v1.0.0 * Copyright 2023-2024 by Marcin Orlowski.', 'Opens any web site as standalone desktop app.', 'https://github.com/MarcinOrlowski/website-as-app/']
|
|
5
|
+
Home-page: https://github.com/MarcinOrlowski/website-as-app/
|
|
6
|
+
Author: Marcin Orlowski
|
|
7
|
+
Author-email: mail@marcinOrlowski.com
|
|
8
|
+
License: MIT License
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/MarcinOrlowski/website-as-app//issues/
|
|
10
|
+
Project-URL: Documentation, https://github.com/MarcinOrlowski/website-as-app/
|
|
11
|
+
Project-URL: Source Code, https://github.com/MarcinOrlowski/website-as-app/
|
|
12
|
+
Keywords: webapp desktop app
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE.md
|
|
18
|
+
Requires-Dist: argparse>=1.4.0
|
|
19
|
+
Requires-Dist: PySide6
|
|
20
|
+
Requires-Dist: PyQtWebEngine
|
|
21
|
+
|
|
22
|
+

|
|
23
|
+
|
|
24
|
+
# WebApp
|
|
25
|
+
|
|
26
|
+
Run any website as standalone desktop application
|
|
27
|
+
|
|
28
|
+
[master](https://github.com/MarcinOrlowski/website-as-app/tree/master) branch:
|
|
29
|
+
[](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/unittests.yml)
|
|
30
|
+
[](https://codecov.io/gh/MarcinOrlowski/website-as-app)
|
|
31
|
+
[](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/linter.yml)
|
|
32
|
+
[](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/markdown.yml)
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
Small Python script opening any web page in dedicated window, using embedded QT WebEngine. There are
|
|
36
|
+
no visible browser's UI etc, so the that can be useful to turn any website into standalone desktop
|
|
37
|
+
application. This is useful if you, as me, would like to have a website run as standalone app,
|
|
38
|
+
independently of your main browser which can be beneficial as it gives you separate entry in
|
|
39
|
+
window manager or task switcher etc.
|
|
40
|
+
|
|
41
|
+
> **IMPORTANT:** This tool is **NOT** turning websites into OFFLINE apps! It's about separating
|
|
42
|
+
> each of your key websites i.e. from each other, or gazzilions of your browser's tabs. But you
|
|
43
|
+
> still MUST be connected to the Internet for the apps (websites) to work as previously.
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
Use `pip` to install the script:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
$ pip install website-as-app
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Once app is running, please use `--help` to see all available options, as i.e. custom icon,
|
|
54
|
+
window title etc.
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
$ webapp "https://github.com"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Available options:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
webapp -h
|
|
66
|
+
usage: webapp [--profile PROFILE] [--name NAME] [--icon ICON] [--zoom ZOOM] [--no-tray] url
|
|
67
|
+
|
|
68
|
+
Open any website in standalone window (like it's an app)
|
|
69
|
+
|
|
70
|
+
positional arguments:
|
|
71
|
+
url The URL to open
|
|
72
|
+
|
|
73
|
+
options:
|
|
74
|
+
--profile PROFILE Profile name (for cookies isolation etc). Default: "default"
|
|
75
|
+
--name NAME, -n NAME Application name (shown as window title)
|
|
76
|
+
--geometry GEOMETRY Initial window geometry (in format "WIDTHxHEIGHT+X+Y")
|
|
77
|
+
--icon ICON, -i ICON Full path to image file to be used as app icon
|
|
78
|
+
--zoom ZOOM, -z ZOOM Initial WebBrowserView zoom factor. Default: 1.0
|
|
79
|
+
--no-tray Disables system tray support (closing window terminates app)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The most important option is `--profile` which allows you to isolate cookies and app settings
|
|
83
|
+
per instance. Any instance using the same profile will have access to the same cookies and
|
|
84
|
+
settings. This is useful if you want to run multiple instances of the same app, but with
|
|
85
|
+
different accounts. By default `default` profile is used and it's recommended to use different
|
|
86
|
+
profile per each app instance.
|
|
87
|
+
|
|
88
|
+
## Current limitations
|
|
89
|
+
|
|
90
|
+
* Due to security based limitations of embedded `QWebBrowerView` you will not be able
|
|
91
|
+
to save any file to your local storage nor filesystem.
|
|
92
|
+
* Website's Javascript code cannot write to system clipboard so you migth need to manually
|
|
93
|
+
select given portion of the site and copy using function from context menu as any buttons
|
|
94
|
+
on the page that is using JS to write to the host's clipboard will not currently work.
|
|
95
|
+
|
|
96
|
+
## License ##
|
|
97
|
+
|
|
98
|
+
* Written and copyrighted ©2023-2024 by Marcin Orlowski <mail (#) marcinorlowski (.) com>
|
|
99
|
+
* ResponseBuilder is open-sourced software licensed under
|
|
100
|
+
the [MIT license](http://opensource.org/licenses/MIT)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
# WebApp
|
|
4
|
+
|
|
5
|
+
Run any website as standalone desktop application
|
|
6
|
+
|
|
7
|
+
[master](https://github.com/MarcinOrlowski/website-as-app/tree/master) branch:
|
|
8
|
+
[](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/unittests.yml)
|
|
9
|
+
[](https://codecov.io/gh/MarcinOrlowski/website-as-app)
|
|
10
|
+
[](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/linter.yml)
|
|
11
|
+
[](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/markdown.yml)
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
Small Python script opening any web page in dedicated window, using embedded QT WebEngine. There are
|
|
15
|
+
no visible browser's UI etc, so the that can be useful to turn any website into standalone desktop
|
|
16
|
+
application. This is useful if you, as me, would like to have a website run as standalone app,
|
|
17
|
+
independently of your main browser which can be beneficial as it gives you separate entry in
|
|
18
|
+
window manager or task switcher etc.
|
|
19
|
+
|
|
20
|
+
> **IMPORTANT:** This tool is **NOT** turning websites into OFFLINE apps! It's about separating
|
|
21
|
+
> each of your key websites i.e. from each other, or gazzilions of your browser's tabs. But you
|
|
22
|
+
> still MUST be connected to the Internet for the apps (websites) to work as previously.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
Use `pip` to install the script:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
$ pip install website-as-app
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Once app is running, please use `--help` to see all available options, as i.e. custom icon,
|
|
33
|
+
window title etc.
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
$ webapp "https://github.com"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Available options:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
webapp -h
|
|
45
|
+
usage: webapp [--profile PROFILE] [--name NAME] [--icon ICON] [--zoom ZOOM] [--no-tray] url
|
|
46
|
+
|
|
47
|
+
Open any website in standalone window (like it's an app)
|
|
48
|
+
|
|
49
|
+
positional arguments:
|
|
50
|
+
url The URL to open
|
|
51
|
+
|
|
52
|
+
options:
|
|
53
|
+
--profile PROFILE Profile name (for cookies isolation etc). Default: "default"
|
|
54
|
+
--name NAME, -n NAME Application name (shown as window title)
|
|
55
|
+
--geometry GEOMETRY Initial window geometry (in format "WIDTHxHEIGHT+X+Y")
|
|
56
|
+
--icon ICON, -i ICON Full path to image file to be used as app icon
|
|
57
|
+
--zoom ZOOM, -z ZOOM Initial WebBrowserView zoom factor. Default: 1.0
|
|
58
|
+
--no-tray Disables system tray support (closing window terminates app)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The most important option is `--profile` which allows you to isolate cookies and app settings
|
|
62
|
+
per instance. Any instance using the same profile will have access to the same cookies and
|
|
63
|
+
settings. This is useful if you want to run multiple instances of the same app, but with
|
|
64
|
+
different accounts. By default `default` profile is used and it's recommended to use different
|
|
65
|
+
profile per each app instance.
|
|
66
|
+
|
|
67
|
+
## Current limitations
|
|
68
|
+
|
|
69
|
+
* Due to security based limitations of embedded `QWebBrowerView` you will not be able
|
|
70
|
+
to save any file to your local storage nor filesystem.
|
|
71
|
+
* Website's Javascript code cannot write to system clipboard so you migth need to manually
|
|
72
|
+
select given portion of the site and copy using function from context menu as any buttons
|
|
73
|
+
on the page that is using JS to write to the host's clipboard will not currently work.
|
|
74
|
+
|
|
75
|
+
## License ##
|
|
76
|
+
|
|
77
|
+
* Written and copyrighted ©2023-2024 by Marcin Orlowski <mail (#) marcinorlowski (.) com>
|
|
78
|
+
* ResponseBuilder is open-sourced software licensed under
|
|
79
|
+
the [MIT license](http://opensource.org/licenses/MIT)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
##################################################################################
|
|
5
|
+
#
|
|
6
|
+
# Website As App
|
|
7
|
+
# Run any website as standalone desktop application
|
|
8
|
+
#
|
|
9
|
+
# @author Marcin Orlowski <mail (#) marcinOrlowski (.) com>
|
|
10
|
+
# @copyright 2023-2024 Marcin Orlowski
|
|
11
|
+
# @license https://www.opensource.org/licenses/mit-license.php MIT
|
|
12
|
+
# @link https://github.com/MarcinOrlowski/website-as-app
|
|
13
|
+
#
|
|
14
|
+
#####
|
|
15
|
+
#
|
|
16
|
+
# https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html
|
|
17
|
+
#
|
|
18
|
+
# python -m venv venv
|
|
19
|
+
# source venv/bin/activate
|
|
20
|
+
# pip install -r requirements-dev.txt
|
|
21
|
+
# python -m build
|
|
22
|
+
# # Reinstall the app (do not do "install --upgrade" as cached bytecode can not be updated)
|
|
23
|
+
# pip uninstall --yes dist/website_as_app-1.0.0-py3-none-any.whl
|
|
24
|
+
# # intentionally no --upgrade for install to endforce conflict if not uninstalled fully first.
|
|
25
|
+
# pip install dist/website_as_app-1.0.0-py3-none-any.whl
|
|
26
|
+
# twine upload dist/*
|
|
27
|
+
#
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from setuptools import setup, find_packages
|
|
31
|
+
|
|
32
|
+
from websiteapp.const import Const
|
|
33
|
+
|
|
34
|
+
with open('README.md', 'r') as fh:
|
|
35
|
+
readme = fh.read()
|
|
36
|
+
|
|
37
|
+
setup(
|
|
38
|
+
name=Const.APP_PROJECT_NAME,
|
|
39
|
+
version=Const.APP_VERSION,
|
|
40
|
+
packages=find_packages(),
|
|
41
|
+
|
|
42
|
+
install_requires=[
|
|
43
|
+
'argparse>=1.4.0',
|
|
44
|
+
'PySide6',
|
|
45
|
+
'PyQtWebEngine',
|
|
46
|
+
],
|
|
47
|
+
entry_points={
|
|
48
|
+
'console_scripts': [
|
|
49
|
+
'webapp = websiteapp.webapp:WebApp.run',
|
|
50
|
+
'runasapp = websiteapp.webapp:WebApp.run',
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
package_data={
|
|
55
|
+
'websiteapp': [
|
|
56
|
+
'icons/default.png',
|
|
57
|
+
'icons/logo.png',
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
author='Marcin Orlowski',
|
|
62
|
+
author_email='mail@marcinOrlowski.com',
|
|
63
|
+
description=Const.APP_DESCRIPTION,
|
|
64
|
+
long_description=readme,
|
|
65
|
+
long_description_content_type='text/markdown',
|
|
66
|
+
url=Const.APP_URL,
|
|
67
|
+
keywords='webapp desktop app',
|
|
68
|
+
project_urls={
|
|
69
|
+
'Bug Tracker': f'{Const.APP_URL}/issues/',
|
|
70
|
+
'Documentation': Const.APP_URL,
|
|
71
|
+
'Source Code': Const.APP_URL,
|
|
72
|
+
},
|
|
73
|
+
# https://choosealicense.com/
|
|
74
|
+
license='MIT License',
|
|
75
|
+
classifiers=[
|
|
76
|
+
'Programming Language :: Python :: 3',
|
|
77
|
+
'License :: OSI Approved :: MIT License',
|
|
78
|
+
'Operating System :: OS Independent',
|
|
79
|
+
],
|
|
80
|
+
)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: website-as-app
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: ['Website As App v1.0.0 * Copyright 2023-2024 by Marcin Orlowski.', 'Opens any web site as standalone desktop app.', 'https://github.com/MarcinOrlowski/website-as-app/']
|
|
5
|
+
Home-page: https://github.com/MarcinOrlowski/website-as-app/
|
|
6
|
+
Author: Marcin Orlowski
|
|
7
|
+
Author-email: mail@marcinOrlowski.com
|
|
8
|
+
License: MIT License
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/MarcinOrlowski/website-as-app//issues/
|
|
10
|
+
Project-URL: Documentation, https://github.com/MarcinOrlowski/website-as-app/
|
|
11
|
+
Project-URL: Source Code, https://github.com/MarcinOrlowski/website-as-app/
|
|
12
|
+
Keywords: webapp desktop app
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE.md
|
|
18
|
+
Requires-Dist: argparse>=1.4.0
|
|
19
|
+
Requires-Dist: PySide6
|
|
20
|
+
Requires-Dist: PyQtWebEngine
|
|
21
|
+
|
|
22
|
+

|
|
23
|
+
|
|
24
|
+
# WebApp
|
|
25
|
+
|
|
26
|
+
Run any website as standalone desktop application
|
|
27
|
+
|
|
28
|
+
[master](https://github.com/MarcinOrlowski/website-as-app/tree/master) branch:
|
|
29
|
+
[](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/unittests.yml)
|
|
30
|
+
[](https://codecov.io/gh/MarcinOrlowski/website-as-app)
|
|
31
|
+
[](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/linter.yml)
|
|
32
|
+
[](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/markdown.yml)
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
Small Python script opening any web page in dedicated window, using embedded QT WebEngine. There are
|
|
36
|
+
no visible browser's UI etc, so the that can be useful to turn any website into standalone desktop
|
|
37
|
+
application. This is useful if you, as me, would like to have a website run as standalone app,
|
|
38
|
+
independently of your main browser which can be beneficial as it gives you separate entry in
|
|
39
|
+
window manager or task switcher etc.
|
|
40
|
+
|
|
41
|
+
> **IMPORTANT:** This tool is **NOT** turning websites into OFFLINE apps! It's about separating
|
|
42
|
+
> each of your key websites i.e. from each other, or gazzilions of your browser's tabs. But you
|
|
43
|
+
> still MUST be connected to the Internet for the apps (websites) to work as previously.
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
Use `pip` to install the script:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
$ pip install website-as-app
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Once app is running, please use `--help` to see all available options, as i.e. custom icon,
|
|
54
|
+
window title etc.
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
$ webapp "https://github.com"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Available options:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
webapp -h
|
|
66
|
+
usage: webapp [--profile PROFILE] [--name NAME] [--icon ICON] [--zoom ZOOM] [--no-tray] url
|
|
67
|
+
|
|
68
|
+
Open any website in standalone window (like it's an app)
|
|
69
|
+
|
|
70
|
+
positional arguments:
|
|
71
|
+
url The URL to open
|
|
72
|
+
|
|
73
|
+
options:
|
|
74
|
+
--profile PROFILE Profile name (for cookies isolation etc). Default: "default"
|
|
75
|
+
--name NAME, -n NAME Application name (shown as window title)
|
|
76
|
+
--geometry GEOMETRY Initial window geometry (in format "WIDTHxHEIGHT+X+Y")
|
|
77
|
+
--icon ICON, -i ICON Full path to image file to be used as app icon
|
|
78
|
+
--zoom ZOOM, -z ZOOM Initial WebBrowserView zoom factor. Default: 1.0
|
|
79
|
+
--no-tray Disables system tray support (closing window terminates app)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The most important option is `--profile` which allows you to isolate cookies and app settings
|
|
83
|
+
per instance. Any instance using the same profile will have access to the same cookies and
|
|
84
|
+
settings. This is useful if you want to run multiple instances of the same app, but with
|
|
85
|
+
different accounts. By default `default` profile is used and it's recommended to use different
|
|
86
|
+
profile per each app instance.
|
|
87
|
+
|
|
88
|
+
## Current limitations
|
|
89
|
+
|
|
90
|
+
* Due to security based limitations of embedded `QWebBrowerView` you will not be able
|
|
91
|
+
to save any file to your local storage nor filesystem.
|
|
92
|
+
* Website's Javascript code cannot write to system clipboard so you migth need to manually
|
|
93
|
+
select given portion of the site and copy using function from context menu as any buttons
|
|
94
|
+
on the page that is using JS to write to the host's clipboard will not currently work.
|
|
95
|
+
|
|
96
|
+
## License ##
|
|
97
|
+
|
|
98
|
+
* Written and copyrighted ©2023-2024 by Marcin Orlowski <mail (#) marcinorlowski (.) com>
|
|
99
|
+
* ResponseBuilder is open-sourced software licensed under
|
|
100
|
+
the [MIT license](http://opensource.org/licenses/MIT)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
LICENSE.md
|
|
2
|
+
README.md
|
|
3
|
+
setup.py
|
|
4
|
+
website_as_app.egg-info/PKG-INFO
|
|
5
|
+
website_as_app.egg-info/SOURCES.txt
|
|
6
|
+
website_as_app.egg-info/dependency_links.txt
|
|
7
|
+
website_as_app.egg-info/entry_points.txt
|
|
8
|
+
website_as_app.egg-info/requires.txt
|
|
9
|
+
website_as_app.egg-info/top_level.txt
|
|
10
|
+
websiteapp/__init__.py
|
|
11
|
+
websiteapp/about.py
|
|
12
|
+
websiteapp/const.py
|
|
13
|
+
websiteapp/utils.py
|
|
14
|
+
websiteapp/webapp.py
|
|
15
|
+
websiteapp/icons/default.png
|
|
16
|
+
websiteapp/icons/logo.png
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
websiteapp
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""
|
|
2
|
+
##################################################################################
|
|
3
|
+
#
|
|
4
|
+
# Website As App
|
|
5
|
+
# Run any website as standalone desktop application
|
|
6
|
+
#
|
|
7
|
+
# @author Marcin Orlowski <mail (#) marcinOrlowski (.) com>
|
|
8
|
+
# @copyright 2023-2024 Marcin Orlowski
|
|
9
|
+
# @license https://www.opensource.org/licenses/mit-license.php MIT
|
|
10
|
+
# @link https://github.com/MarcinOrlowski/website-as-app
|
|
11
|
+
#
|
|
12
|
+
#####
|
|
13
|
+
"""
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""
|
|
2
|
+
##################################################################################
|
|
3
|
+
#
|
|
4
|
+
# Website As App
|
|
5
|
+
# Run any website as standalone desktop application
|
|
6
|
+
#
|
|
7
|
+
# @author Marcin Orlowski <mail (#) marcinOrlowski (.) com>
|
|
8
|
+
# @copyright 2023-2024 Marcin Orlowski
|
|
9
|
+
# @license https://www.opensource.org/licenses/mit-license.php MIT
|
|
10
|
+
# @link https://github.com/MarcinOrlowski/website-as-app
|
|
11
|
+
#
|
|
12
|
+
##################################################################################
|
|
13
|
+
"""
|
|
14
|
+
import webbrowser
|
|
15
|
+
import importlib.resources as pkg_resources
|
|
16
|
+
|
|
17
|
+
from PySide6.QtCore import Qt
|
|
18
|
+
from PySide6.QtGui import QPixmap
|
|
19
|
+
from PySide6.QtWidgets import QApplication, QVBoxLayout, QLabel, QDialog, QPushButton, QHBoxLayout
|
|
20
|
+
|
|
21
|
+
from websiteapp.const import Const
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class About(QDialog):
|
|
25
|
+
def __init__(self):
|
|
26
|
+
super().__init__()
|
|
27
|
+
|
|
28
|
+
self.app = QApplication.instance()
|
|
29
|
+
|
|
30
|
+
# Set the layout for the dialog
|
|
31
|
+
layout = QVBoxLayout(self)
|
|
32
|
+
layout.setContentsMargins(0, 0, 0, 6)
|
|
33
|
+
layout.setSpacing(0)
|
|
34
|
+
self.setLayout(layout)
|
|
35
|
+
self.setWindowTitle(f"About {Const.APP_NAME}")
|
|
36
|
+
|
|
37
|
+
# Create a label for the logo image
|
|
38
|
+
logo_label = QLabel(self)
|
|
39
|
+
with pkg_resources.path('websiteapp.icons', 'logo.png') as icon_path:
|
|
40
|
+
icon_file = str(icon_path)
|
|
41
|
+
logo_label.setPixmap(QPixmap(icon_file))
|
|
42
|
+
layout.addWidget(logo_label)
|
|
43
|
+
|
|
44
|
+
# Create a label for the credits text
|
|
45
|
+
label_layout = QVBoxLayout()
|
|
46
|
+
|
|
47
|
+
label = QLabel(f'<b>{Const.APP_NAME}</b> v{Const.APP_VERSION}', self)
|
|
48
|
+
label.setTextFormat(Qt.RichText)
|
|
49
|
+
label.setAlignment(Qt.AlignCenter)
|
|
50
|
+
font = label.font()
|
|
51
|
+
font.setPointSize(font.pointSize() * 1.7)
|
|
52
|
+
label.setFont(font)
|
|
53
|
+
label_layout.addWidget(label)
|
|
54
|
+
|
|
55
|
+
label = QLabel(f'©2023-{Const.APP_YEAR} Marcin Orlowski', self)
|
|
56
|
+
label.setTextFormat(Qt.RichText)
|
|
57
|
+
label.setAlignment(Qt.AlignCenter)
|
|
58
|
+
font = label.font()
|
|
59
|
+
font.setPointSize(font.pointSize() * 1.2)
|
|
60
|
+
label.setFont(font)
|
|
61
|
+
label_layout.addWidget(label)
|
|
62
|
+
|
|
63
|
+
label = QLabel(Const.APP_URL, self)
|
|
64
|
+
# label.setTextFormat(Qt.RichText)
|
|
65
|
+
label.setAlignment(Qt.AlignCenter)
|
|
66
|
+
label_layout.addWidget(label)
|
|
67
|
+
|
|
68
|
+
layout.addLayout(label_layout)
|
|
69
|
+
|
|
70
|
+
# # Create a horizontal layout for the buttons
|
|
71
|
+
bt_website = QPushButton("Open project website", self)
|
|
72
|
+
bt_website.clicked.connect(self.on_ok_clicked)
|
|
73
|
+
bt_close = QPushButton("Close", self)
|
|
74
|
+
bt_close.clicked.connect(self.on_cancel_clicked)
|
|
75
|
+
bt_close.setFocus()
|
|
76
|
+
|
|
77
|
+
button_layout = QHBoxLayout()
|
|
78
|
+
button_layout.setContentsMargins(6, 6, 6, 6)
|
|
79
|
+
button_layout.setSpacing(6)
|
|
80
|
+
button_layout.addWidget(bt_website)
|
|
81
|
+
button_layout.addWidget(bt_close)
|
|
82
|
+
layout.addLayout(button_layout)
|
|
83
|
+
|
|
84
|
+
def on_ok_clicked(self):
|
|
85
|
+
webbrowser.open(Const.APP_URL)
|
|
86
|
+
self.reject()
|
|
87
|
+
|
|
88
|
+
def on_cancel_clicked(self):
|
|
89
|
+
self.reject()
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""
|
|
2
|
+
##################################################################################
|
|
3
|
+
#
|
|
4
|
+
# Website As App
|
|
5
|
+
# Run any website as standalone desktop application
|
|
6
|
+
#
|
|
7
|
+
# @author Marcin Orlowski <mail (#) marcinOrlowski (.) com>
|
|
8
|
+
# @copyright 2023-2024 Marcin Orlowski
|
|
9
|
+
# @license https://www.opensource.org/licenses/mit-license.php MIT
|
|
10
|
+
# @link https://github.com/MarcinOrlowski/website-as-app
|
|
11
|
+
#
|
|
12
|
+
#####
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from typing import List
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Const(object):
|
|
19
|
+
APP_NAME: str = 'Website As App'
|
|
20
|
+
APP_PROJECT_NAME: str = 'website-as-app'
|
|
21
|
+
APP_VERSION: str = '1.0.0'
|
|
22
|
+
APP_URL: str = 'https://github.com/MarcinOrlowski/website-as-app/'
|
|
23
|
+
APP_DESCRIPTION: str = 'Opens any web site as standalone desktop app.'
|
|
24
|
+
APP_YEAR: int = 2024
|
|
25
|
+
|
|
26
|
+
APP_DESCRIPTION: List[str] = [
|
|
27
|
+
f'{APP_NAME} v{APP_VERSION} * Copyright 2023-{APP_YEAR} by Marcin Orlowski.',
|
|
28
|
+
APP_DESCRIPTION,
|
|
29
|
+
APP_URL,
|
|
30
|
+
]
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""
|
|
2
|
+
##################################################################################
|
|
3
|
+
#
|
|
4
|
+
# Website As App
|
|
5
|
+
# Run any website as standalone desktop application
|
|
6
|
+
#
|
|
7
|
+
# @author Marcin Orlowski <mail (#) marcinOrlowski (.) com>
|
|
8
|
+
# @copyright 2023-2024 Marcin Orlowski
|
|
9
|
+
# @license https://www.opensource.org/licenses/mit-license.php MIT
|
|
10
|
+
# @link https://github.com/MarcinOrlowski/website-as-app
|
|
11
|
+
#
|
|
12
|
+
##################################################################################
|
|
13
|
+
"""
|
|
14
|
+
import importlib.resources as pkg_resources
|
|
15
|
+
import os, re
|
|
16
|
+
from typing import Optional
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
from PySide6.QtGui import QIcon
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Utils(object):
|
|
23
|
+
@staticmethod
|
|
24
|
+
def get_icon(icon: Optional[str] = None) -> QIcon:
|
|
25
|
+
"""
|
|
26
|
+
Attempts to construct QIcon object from given icon file path. If icon file is not given
|
|
27
|
+
or does not exist, default icon is used (so it always return valid QIcon object).
|
|
28
|
+
|
|
29
|
+
:param icon: Path to icon file (in format supported by QT, i.e. PNG)
|
|
30
|
+
:return: QIcon object
|
|
31
|
+
"""
|
|
32
|
+
icon_file = None
|
|
33
|
+
|
|
34
|
+
if icon and os.path.exists(icon):
|
|
35
|
+
icon_file = icon
|
|
36
|
+
|
|
37
|
+
if icon_file is None:
|
|
38
|
+
with pkg_resources.path('websiteapp.icons', 'default.png') as icon_path:
|
|
39
|
+
icon_file = str(icon_path)
|
|
40
|
+
|
|
41
|
+
return QIcon(icon_file)
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def parse_geometry(geometry_string: str) -> (int, int, int, int):
|
|
45
|
+
"""
|
|
46
|
+
Parse the geometry string and return the width, height, x, and y values.
|
|
47
|
+
|
|
48
|
+
:param geometry_string: A string representing the geometry in the format WIDTHxHEIGHT+X+Y.
|
|
49
|
+
|
|
50
|
+
:return: A tuple containing the x, y, width and height values.
|
|
51
|
+
:raises ValueError: If the geometry string format is incorrect.
|
|
52
|
+
"""
|
|
53
|
+
match = re.search(r'^(\d+)x(\d+)\+(\d+)\+(\d+)$', geometry_string)
|
|
54
|
+
if not match:
|
|
55
|
+
raise ValueError(f"Invalid geometry. Expected WIDTHxHEIGHT+X+Y, got '{geometry_string}")
|
|
56
|
+
|
|
57
|
+
width = int(match.group(1))
|
|
58
|
+
if width < 1:
|
|
59
|
+
raise ValueError(f"Invalid geometry. Width must be greater than 0, got '{width}'")
|
|
60
|
+
height = int(match.group(2))
|
|
61
|
+
if height < 1:
|
|
62
|
+
raise ValueError(f"Invalid geometry. Height must be greater than 0, got '{height}'")
|
|
63
|
+
x = int(match.group(3))
|
|
64
|
+
if x < 0:
|
|
65
|
+
raise ValueError(f"Invalid geometry. X must be greater than or equal to 0, got '{x}'")
|
|
66
|
+
y = int(match.group(4))
|
|
67
|
+
if y < 0:
|
|
68
|
+
raise ValueError(f"Invalid geometry. Y must be greater than or equal to 0, got '{y}'")
|
|
69
|
+
|
|
70
|
+
return x, y, width, height
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def handle_args():
|
|
74
|
+
"""
|
|
75
|
+
Create an argument parser to handle command line arguments for opening a website in a
|
|
76
|
+
standalone window.
|
|
77
|
+
|
|
78
|
+
:return: The parsed command line arguments.
|
|
79
|
+
"""
|
|
80
|
+
parser = argparse.ArgumentParser(
|
|
81
|
+
description="Open any website in standalone window (like it's an app)")
|
|
82
|
+
parser.add_argument('url', type=str, help='The URL to open')
|
|
83
|
+
|
|
84
|
+
parser.add_argument('--profile', '-p', type=str, default='default',
|
|
85
|
+
help='Profile name (for cookies isolation etc). Default: "%(default)s"')
|
|
86
|
+
|
|
87
|
+
# Can't use "title" as it is swallowed by QT and used for window title which cannot be later
|
|
88
|
+
# changed. So we use "name" instead.
|
|
89
|
+
parser.add_argument('--name', '-n', type=str, default=None,
|
|
90
|
+
help='Application name (shown as window title)')
|
|
91
|
+
|
|
92
|
+
parser.add_argument('--icon', '-i', type=str, default=None,
|
|
93
|
+
help='Full path to PNG image file to be used as app icon')
|
|
94
|
+
parser.add_argument('--geometry', '-g', type=str, default='450x600+0+0',
|
|
95
|
+
help='Initial window geometry ("WIDTHxHEIGHT+X+Y"). Default: "%(default)s"')
|
|
96
|
+
parser.add_argument('--zoom', '-z', type=float, default="1.0",
|
|
97
|
+
help='WebView zoom factor. Default: %(default)s')
|
|
98
|
+
parser.add_argument('--no-tray', action='store_true',
|
|
99
|
+
help='Disables docking app in system tray')
|
|
100
|
+
# parser.add_argument('--minimized', '-m', action='store_true',
|
|
101
|
+
# help='Starts app minimized', target='minimized')
|
|
102
|
+
|
|
103
|
+
parser.add_argument('--debug', '-d', action='store_true',
|
|
104
|
+
help='Makes app print more debug messages during execution')
|
|
105
|
+
|
|
106
|
+
return parser.parse_args()
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""
|
|
2
|
+
##################################################################################
|
|
3
|
+
#
|
|
4
|
+
# Website As App
|
|
5
|
+
# Run any website as standalone desktop application
|
|
6
|
+
#
|
|
7
|
+
# @author Marcin Orlowski <mail (#) marcinOrlowski (.) com>
|
|
8
|
+
# @copyright 2023-2024 Marcin Orlowski
|
|
9
|
+
# @license https://www.opensource.org/licenses/mit-license.php MIT
|
|
10
|
+
# @link https://github.com/MarcinOrlowski/website-as-app
|
|
11
|
+
#
|
|
12
|
+
##################################################################################
|
|
13
|
+
"""
|
|
14
|
+
import sys
|
|
15
|
+
from typing import Optional
|
|
16
|
+
|
|
17
|
+
from PySide6.QtCore import QUrl
|
|
18
|
+
from PySide6.QtGui import QIcon, QAction
|
|
19
|
+
from PySide6.QtWebEngineCore import QWebEngineProfile, QWebEnginePage, QWebEngineSettings
|
|
20
|
+
from PySide6.QtWebEngineWidgets import QWebEngineView
|
|
21
|
+
from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QSystemTrayIcon, \
|
|
22
|
+
QMenu
|
|
23
|
+
|
|
24
|
+
from websiteapp.about import About
|
|
25
|
+
from websiteapp.const import Const
|
|
26
|
+
from websiteapp.utils import Utils
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class WebApp(QMainWindow):
|
|
30
|
+
about_dialog: Optional[About] = None
|
|
31
|
+
|
|
32
|
+
def __init__(self):
|
|
33
|
+
super().__init__()
|
|
34
|
+
|
|
35
|
+
self.app = QApplication.instance()
|
|
36
|
+
self.args = Utils.handle_args()
|
|
37
|
+
|
|
38
|
+
# Set window geometry
|
|
39
|
+
x, y, width, height = Utils.parse_geometry(self.args.geometry)
|
|
40
|
+
self.setGeometry(x, y, width, height)
|
|
41
|
+
self.dbug(f'Geometry: {width}x{height}+{x}+{y}')
|
|
42
|
+
|
|
43
|
+
app_icon = Utils.get_icon(self.args.icon)
|
|
44
|
+
self.setWindowIcon(app_icon)
|
|
45
|
+
if self.args.no_tray:
|
|
46
|
+
self.app.setQuitOnLastWindowClosed(True) # Ensure we quit when last window is closed
|
|
47
|
+
else:
|
|
48
|
+
self.setup_tray_icon(app_icon)
|
|
49
|
+
|
|
50
|
+
window_title = self.args.name if self.args.name else self.args.url
|
|
51
|
+
window_title += f' ({self.args.profile})'
|
|
52
|
+
self.setWindowTitle(f'{window_title} · {Const.APP_NAME}')
|
|
53
|
+
|
|
54
|
+
# Create a persistent profile (cookie jar etc.)
|
|
55
|
+
self.dbug(f'Profile: {self.args.profile}')
|
|
56
|
+
self.profile = QWebEngineProfile(self.args.profile, self)
|
|
57
|
+
self.page = QWebEnginePage(self.profile, self)
|
|
58
|
+
|
|
59
|
+
self.browser = QWebEngineView(self)
|
|
60
|
+
web_settings = self.browser.settings()
|
|
61
|
+
web_settings.setAttribute(
|
|
62
|
+
QWebEngineSettings.WebAttribute.JavascriptCanAccessClipboard, True)
|
|
63
|
+
|
|
64
|
+
self.browser.setPage(self.page)
|
|
65
|
+
self.browser.setZoomFactor(self.args.zoom)
|
|
66
|
+
|
|
67
|
+
self.dbug(f'URL: {self.args.url}')
|
|
68
|
+
self.browser.setUrl(QUrl(self.args.url))
|
|
69
|
+
|
|
70
|
+
# Window layout
|
|
71
|
+
layout = QVBoxLayout()
|
|
72
|
+
layout.setContentsMargins(0, 0, 0, 0)
|
|
73
|
+
layout.setSpacing(0)
|
|
74
|
+
layout.addWidget(self.browser)
|
|
75
|
+
|
|
76
|
+
central_widget = QWidget()
|
|
77
|
+
central_widget.setLayout(layout)
|
|
78
|
+
self.setCentralWidget(central_widget)
|
|
79
|
+
|
|
80
|
+
def closeEvent(self, event) -> None:
|
|
81
|
+
"""
|
|
82
|
+
Override closeEvent to hide the window instead of closing it and hides the window instead.
|
|
83
|
+
|
|
84
|
+
:param event:
|
|
85
|
+
"""
|
|
86
|
+
if not self.args.no_tray:
|
|
87
|
+
self.hide()
|
|
88
|
+
event.ignore()
|
|
89
|
+
else:
|
|
90
|
+
super().closeEvent(event)
|
|
91
|
+
|
|
92
|
+
def setup_tray_icon(self, icon) -> None:
|
|
93
|
+
"""
|
|
94
|
+
This method is used to set up the system tray icon for the application.
|
|
95
|
+
|
|
96
|
+
:param icon: QPixmap object representing the icon that will be used in the system tray.
|
|
97
|
+
"""
|
|
98
|
+
tray_menu = QMenu()
|
|
99
|
+
|
|
100
|
+
tray_icon = QSystemTrayIcon(icon, self.app)
|
|
101
|
+
tray_icon.setContextMenu(tray_menu)
|
|
102
|
+
tray_icon.activated.connect(self.on_tray_icon_activated)
|
|
103
|
+
|
|
104
|
+
about_label = f'About {Const.APP_NAME}'
|
|
105
|
+
about_action = QAction(about_label, self.app)
|
|
106
|
+
about_action.triggered.connect(self.open_about_dialog)
|
|
107
|
+
tray_menu.addAction(about_action)
|
|
108
|
+
|
|
109
|
+
quit_label = f'Quit {self.args.name}' if self.args.name else 'Quit'
|
|
110
|
+
quit_action = QAction(quit_label, self.app)
|
|
111
|
+
quit_action.triggered.connect(self.quit_app)
|
|
112
|
+
tray_menu.addAction(quit_action)
|
|
113
|
+
|
|
114
|
+
tray_icon.show()
|
|
115
|
+
|
|
116
|
+
def quit_app(self) -> None:
|
|
117
|
+
"""
|
|
118
|
+
Closes the application.
|
|
119
|
+
"""
|
|
120
|
+
self.app.quit()
|
|
121
|
+
|
|
122
|
+
def open_about_dialog(self, checked) -> None:
|
|
123
|
+
if not self.about_dialog:
|
|
124
|
+
self.about_dialog = About()
|
|
125
|
+
|
|
126
|
+
if not self.about_dialog.isVisible():
|
|
127
|
+
self.about_dialog.show()
|
|
128
|
+
self.about_dialog.activateWindow()
|
|
129
|
+
|
|
130
|
+
def on_tray_icon_activated(self, reason) -> None:
|
|
131
|
+
"""
|
|
132
|
+
Callback invoked when application icon in system tray is clicked.
|
|
133
|
+
"""
|
|
134
|
+
if reason == QSystemTrayIcon.ActivationReason.Trigger:
|
|
135
|
+
# Check for left-click (Trigger)
|
|
136
|
+
self.toggle_window()
|
|
137
|
+
|
|
138
|
+
def toggle_window(self) -> None:
|
|
139
|
+
"""
|
|
140
|
+
Toggles the visibility of the window.
|
|
141
|
+
"""
|
|
142
|
+
self.hide() if self.isVisible() else self.show()
|
|
143
|
+
|
|
144
|
+
# ############################################################################################ #
|
|
145
|
+
|
|
146
|
+
def dbug(self, msg: str) -> None:
|
|
147
|
+
if self.args.debug:
|
|
148
|
+
print(msg, file=sys.stderr)
|
|
149
|
+
|
|
150
|
+
# ############################################################################################ #
|
|
151
|
+
|
|
152
|
+
@staticmethod
|
|
153
|
+
def run() -> None:
|
|
154
|
+
"""
|
|
155
|
+
Application entry point. When renamed, ensure setup.py's reference is updated as well.
|
|
156
|
+
"""
|
|
157
|
+
app = QApplication(sys.argv)
|
|
158
|
+
app.setOrganizationName("MarcinOrlowski")
|
|
159
|
+
app.setApplicationName("Website As App")
|
|
160
|
+
|
|
161
|
+
window = WebApp()
|
|
162
|
+
window.show()
|
|
163
|
+
|
|
164
|
+
app.exec()
|