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.
@@ -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
+ ![WebApp](docs/logo.png)
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
+ [![Unit tests](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/unittests.yml/badge.svg?branch=master)](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/unittests.yml)
30
+ [![codecov](https://codecov.io/gh/MarcinOrlowski/website-as-app/branch/master/graph/badge.svg?token=3THKJKSQ1G)](https://codecov.io/gh/MarcinOrlowski/website-as-app)
31
+ [![Code lint](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/linter.yml/badge.svg?branch=master)](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/linter.yml)
32
+ [![MD Lint](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/markdown.yml/badge.svg?branch=master)](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 &copy;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
+ ![WebApp](docs/logo.png)
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
+ [![Unit tests](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/unittests.yml/badge.svg?branch=master)](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/unittests.yml)
9
+ [![codecov](https://codecov.io/gh/MarcinOrlowski/website-as-app/branch/master/graph/badge.svg?token=3THKJKSQ1G)](https://codecov.io/gh/MarcinOrlowski/website-as-app)
10
+ [![Code lint](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/linter.yml/badge.svg?branch=master)](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/linter.yml)
11
+ [![MD Lint](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/markdown.yml/badge.svg?branch=master)](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 &copy;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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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
+ ![WebApp](docs/logo.png)
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
+ [![Unit tests](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/unittests.yml/badge.svg?branch=master)](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/unittests.yml)
30
+ [![codecov](https://codecov.io/gh/MarcinOrlowski/website-as-app/branch/master/graph/badge.svg?token=3THKJKSQ1G)](https://codecov.io/gh/MarcinOrlowski/website-as-app)
31
+ [![Code lint](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/linter.yml/badge.svg?branch=master)](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/linter.yml)
32
+ [![MD Lint](https://github.com/MarcinOrlowski/website-as-app/actions/workflows/markdown.yml/badge.svg?branch=master)](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 &copy;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,3 @@
1
+ [console_scripts]
2
+ runasapp = websiteapp.webapp:WebApp.run
3
+ webapp = websiteapp.webapp:WebApp.run
@@ -0,0 +1,3 @@
1
+ argparse>=1.4.0
2
+ PySide6
3
+ PyQtWebEngine
@@ -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'&copy;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
+ ]
@@ -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()