Update WebAppify to the latest tech #3
10
.drone.yml
10
.drone.yml
@ -1,10 +0,0 @@
|
||||
kind: pipeline
|
||||
name: default
|
||||
|
||||
steps:
|
||||
- name: test
|
||||
image: python:3
|
||||
commands:
|
||||
- pip install -e .
|
||||
- pip install pytest
|
||||
- pytest
|
8
.editorconfig
Normal file
8
.editorconfig
Normal file
@ -0,0 +1,8 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
max_line_length = 120
|
||||
|
||||
[*.py]
|
||||
indent_style = space
|
||||
indent_size = 4
|
@ -1,5 +1,2 @@
|
||||
[wheel]
|
||||
universal = 1
|
||||
|
||||
[flake8]
|
||||
max-line-length = 120
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,3 +1,6 @@
|
||||
dist
|
||||
*.egg-info
|
||||
dist
|
||||
build
|
||||
.venv
|
||||
__pycache__
|
||||
*.py[co]
|
||||
|
23
.woodpecker.yml
Normal file
23
.woodpecker.yml
Normal file
@ -0,0 +1,23 @@
|
||||
pipeline:
|
||||
# tests:
|
||||
# image: python:3
|
||||
# commands:
|
||||
# - pip install -U pip wheel pytest PyQt5 PyQtWebEngine
|
||||
# - pytest
|
||||
# when:
|
||||
# branch: master
|
||||
build:
|
||||
image: python:3
|
||||
commands:
|
||||
- pip install -U pip wheel hatch hatch-vcs
|
||||
- hatch build
|
||||
when:
|
||||
branch: master
|
||||
release:
|
||||
image: python:3
|
||||
commands:
|
||||
- pip install -U pip wheel hatch hatch-vcs
|
||||
- hatch build
|
||||
- hatch publish
|
||||
when:
|
||||
event: tag
|
57
pyproject.toml
Normal file
57
pyproject.toml
Normal file
@ -0,0 +1,57 @@
|
||||
[build-system]
|
||||
requires = [
|
||||
"hatchling>=1.3.1",
|
||||
"hatch-vcs"
|
||||
]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "WebAppify"
|
||||
description = "Create desktop apps of your favourite websites"
|
||||
readme = "README.rst"
|
||||
license = "MIT"
|
||||
authors = [
|
||||
{ name = "Raoul Snyman", email = "raoul@snyman.info" },
|
||||
]
|
||||
keywords = [
|
||||
"Qt",
|
||||
"website",
|
||||
]
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Environment :: MacOS X",
|
||||
"Environment :: Win32 (MS Windows)",
|
||||
"Environment :: X11 Applications",
|
||||
"Environment :: X11 Applications :: Qt",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Topic :: Desktop Environment",
|
||||
"Topic :: Internet :: WWW/HTTP :: Browsers",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
]
|
||||
dependencies = [
|
||||
"PyQt5",
|
||||
"PyQtWebEngine",
|
||||
]
|
||||
dynamic = [
|
||||
"version",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://git.snyman.info/raoul/webappify"
|
||||
|
||||
[tool.hatch.version]
|
||||
source = "vcs"
|
||||
|
||||
[tool.hatch.build]
|
||||
include = [
|
||||
"/webappify",
|
||||
]
|
||||
|
||||
[tool.hatch.build.targets.sdist]
|
||||
[tool.hatch.build.targets.wheel]
|
45
setup.py
45
setup.py
@ -1,45 +0,0 @@
|
||||
"""
|
||||
The webappify package
|
||||
"""
|
||||
import os
|
||||
from codecs import open
|
||||
from setuptools import setup
|
||||
|
||||
HERE = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
with open(os.path.join(HERE, 'README.rst'), encoding='utf8') as f:
|
||||
LONG_DESCRIPTION = f.read()
|
||||
|
||||
|
||||
setup(
|
||||
name='WebAppify',
|
||||
version='0.3',
|
||||
description='Create desktop apps of your favourite websites',
|
||||
long_description=LONG_DESCRIPTION,
|
||||
url='https://launchpad.net/webappify',
|
||||
author='Raoul Snyman',
|
||||
author_email='raoul@snyman.info',
|
||||
license='MIT',
|
||||
classifiers=[
|
||||
'Development Status :: 4 - Beta',
|
||||
'Environment :: MacOS X',
|
||||
'Environment :: Win32 (MS Windows)',
|
||||
'Environment :: X11 Applications',
|
||||
'Environment :: X11 Applications :: Qt',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Topic :: Desktop Environment',
|
||||
'Topic :: Internet :: WWW/HTTP :: Browsers',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules'
|
||||
],
|
||||
keywords='Qt website',
|
||||
py_modules=['webappify'],
|
||||
install_requires=['PyQt5', 'PyQtWebEngine']
|
||||
)
|
273
webappify.py
273
webappify.py
@ -1,273 +0,0 @@
|
||||
"""
|
||||
WebAppify
|
||||
=========
|
||||
|
||||
WebAppify is a simple module to easily create your own desktop apps of websites. WebAppify uses PyQt5 and QtWebKit or
|
||||
QtWebEngine for displaying the web page, and works on Python 2.7 and Python 3.4 and up.
|
||||
|
||||
To create your own desktop web app, import and set up the WebApp class.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from webappify import WebApp
|
||||
|
||||
app = WebApp('OpenStreetMap', 'https://www.openstreetmap.org', 'osm.png')
|
||||
app.run()
|
||||
|
||||
This will create a window with the website, using the icon provided.
|
||||
|
||||
.. note::
|
||||
|
||||
If your site needs Flash Player, you'll need the appropriate Flash Player plugin installed system-wide. For QtWebKit
|
||||
you will need the NPAPI plugin, and for QtWebEngine you will need the PPAPI plugin.
|
||||
"""
|
||||
import sys
|
||||
import platform
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
IS_PY2 = sys.version_info[0] == 2
|
||||
|
||||
try:
|
||||
from PyQt5 import QtWebEngineWidgets
|
||||
HAS_WEBENGINE = True
|
||||
except ImportError:
|
||||
HAS_WEBENGINE = False
|
||||
|
||||
try:
|
||||
from PyQt5 import QtWebKit, QtWebKitWidgets
|
||||
HAS_WEBKIT = True
|
||||
except ImportError:
|
||||
HAS_WEBKIT = False
|
||||
|
||||
if HAS_WEBENGINE:
|
||||
SETTINGS = [
|
||||
QtWebEngineWidgets.QWebEngineSettings.PluginsEnabled,
|
||||
QtWebEngineWidgets.QWebEngineSettings.JavascriptCanAccessClipboard,
|
||||
QtWebEngineWidgets.QWebEngineSettings.LocalContentCanAccessRemoteUrls
|
||||
]
|
||||
WebView = QtWebEngineWidgets.QWebEngineView
|
||||
elif HAS_WEBKIT:
|
||||
SETTINGS = [
|
||||
QtWebKit.QWebSettings.PluginsEnabled,
|
||||
QtWebKit.QWebSettings.JavascriptCanOpenWindows,
|
||||
QtWebKit.QWebSettings.JavascriptCanCloseWindows,
|
||||
QtWebKit.QWebSettings.JavascriptCanAccessClipboard,
|
||||
QtWebKit.QWebSettings.OfflineStorageDatabaseEnabled,
|
||||
QtWebKit.QWebSettings.OfflineWebApplicationCacheEnabled,
|
||||
QtWebKit.QWebSettings.LocalStorageEnabled,
|
||||
QtWebKit.QWebSettings.LocalContentCanAccessRemoteUrls
|
||||
]
|
||||
WebView = QtWebKitWidgets.QWebView
|
||||
|
||||
class WebPage(QtWebKitWidgets.QWebPage):
|
||||
"""Custom class for overriding the user agent to make WebKit look like Chrome"""
|
||||
def userAgentForUrl(self, url):
|
||||
return 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) ' \
|
||||
'Chrome/28.0.1500.52 Safari/537.36'
|
||||
else:
|
||||
print('Cannot detect either QtWebEngine or QtWebKit!')
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class WebWindow(QtWidgets.QWidget):
|
||||
"""
|
||||
A window with a single web view and nothing else
|
||||
"""
|
||||
def __init__(self, app, title, url, icon, canMinimizeToTray=False):
|
||||
"""
|
||||
Create the window
|
||||
"""
|
||||
super(WebWindow, self).__init__(None)
|
||||
self.hasShownWarning = False
|
||||
self.app = app
|
||||
self.icon = QtGui.QIcon(icon)
|
||||
self.canMinimizeToTray = canMinimizeToTray
|
||||
self.setWindowTitle(title)
|
||||
self.setWindowIcon(self.icon)
|
||||
self.setContentsMargins(0, 0, 0, 0)
|
||||
self.layout = QtWidgets.QVBoxLayout(self)
|
||||
self.layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.webview = WebView(self)
|
||||
if not HAS_WEBENGINE and HAS_WEBKIT:
|
||||
self.webview.setPage(WebPage(self.webview))
|
||||
for setting in SETTINGS:
|
||||
self.webview.settings().setAttribute(setting, True)
|
||||
self.webview.setUrl(QtCore.QUrl(url))
|
||||
self.layout.addWidget(self.webview)
|
||||
self.webview.titleChanged.connect(self.onTitleChanged)
|
||||
|
||||
def _showWarning(self):
|
||||
"""
|
||||
Show a balloon message to inform the user that the app is minimized
|
||||
"""
|
||||
if not self.hasShownWarning:
|
||||
self.trayIcon.showMessage(self.windowTitle(), 'This program will continue running in the system tray. '
|
||||
'To close the program, choose <b>Quit</b> in the context menu of the system '
|
||||
'tray icon.', QtWidgets.QSystemTrayIcon.Information, 5000)
|
||||
self.hasShownWarning = True
|
||||
|
||||
def _updateTrayMenu(self):
|
||||
"""
|
||||
Update the enabled/disabled status of the items in the tray icon menu
|
||||
"""
|
||||
if not self.canMinimizeToTray:
|
||||
return
|
||||
self.restoreAction.setEnabled(not self.isVisible())
|
||||
self.minimizeAction.setEnabled(self.isVisible() and not self.isMinimized())
|
||||
self.maximizeAction.setEnabled(self.isVisible() and not self.isMaximized())
|
||||
|
||||
def _raiseWindow(self):
|
||||
"""
|
||||
Raise the Window, depending on the version of Python
|
||||
"""
|
||||
# Get the "raise" method depending on Python 2 or 3
|
||||
if IS_PY2:
|
||||
raiser = getattr(self, 'raise_')
|
||||
else:
|
||||
raiser = getattr(self, 'raise')
|
||||
raiser()
|
||||
|
||||
def _restoreWindow(self):
|
||||
"""
|
||||
Restore the window and activate it
|
||||
"""
|
||||
self.showNormal()
|
||||
self.activateWindow()
|
||||
self._raiseWindow()
|
||||
|
||||
def _maximizeWindow(self):
|
||||
"""
|
||||
Restore the window and activate it
|
||||
"""
|
||||
self.showMaximized()
|
||||
self.activateWindow()
|
||||
self._raiseWindow()
|
||||
|
||||
def _getTrayMenu(self):
|
||||
"""
|
||||
Create and return the menu for the tray icon
|
||||
"""
|
||||
# Create the actions for the menu
|
||||
self.restoreAction = QtWidgets.QAction('&Restore', self)
|
||||
self.restoreAction.triggered.connect(self._restoreWindow)
|
||||
self.minimizeAction = QtWidgets.QAction('Mi&nimize', self)
|
||||
self.minimizeAction.triggered.connect(self.close)
|
||||
self.maximizeAction = QtWidgets.QAction('Ma&ximize', self)
|
||||
self.maximizeAction.triggered.connect(self._maximizeWindow)
|
||||
self.quitAction = QtWidgets.QAction('&Quit', self)
|
||||
self.quitAction.triggered.connect(self.app.quit)
|
||||
# Create the menu and add the actions
|
||||
trayIconMenu = QtWidgets.QMenu(self)
|
||||
trayIconMenu.addAction(self.restoreAction)
|
||||
trayIconMenu.addAction(self.minimizeAction)
|
||||
trayIconMenu.addAction(self.maximizeAction)
|
||||
trayIconMenu.addSeparator()
|
||||
trayIconMenu.addAction(self.quitAction)
|
||||
return trayIconMenu
|
||||
|
||||
def setupTrayIcon(self):
|
||||
"""
|
||||
Set up the tray icon
|
||||
"""
|
||||
self.trayIcon = QtWidgets.QSystemTrayIcon(self.icon, self)
|
||||
self.trayIcon.setContextMenu(self._getTrayMenu())
|
||||
self.trayIcon.activated.connect(self.onTrayIconActivated)
|
||||
self.trayIcon.show()
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""
|
||||
Override the close event to minimize to the tray
|
||||
"""
|
||||
# If we don't want to minimize to the tray, just close the window as per usual
|
||||
if not self.canMinimizeToTray:
|
||||
super(WebWindow, self).closeEvent(event)
|
||||
return
|
||||
# If we want to minimize to the tray, then just hide the window
|
||||
if platform.platform().lower() == 'darwin' and (not event.spontaneous() or not self.isVisible()):
|
||||
return
|
||||
else:
|
||||
self._showWarning()
|
||||
self.hide()
|
||||
event.ignore()
|
||||
# Update the menu to match
|
||||
self._updateTrayMenu()
|
||||
|
||||
def showEvent(self, event):
|
||||
"""
|
||||
Override the show event to catch max/min/etc events and update the tray icon menu accordingly
|
||||
"""
|
||||
super(WebWindow, self).showEvent(event)
|
||||
self._updateTrayMenu()
|
||||
|
||||
def hideEvent(self, event):
|
||||
"""
|
||||
Override the hide event to catch max/min/etc events and update the tray icon menu accordingly
|
||||
"""
|
||||
super(WebWindow, self).hideEvent(event)
|
||||
self._updateTrayMenu()
|
||||
|
||||
def changeEvent(self, event):
|
||||
"""
|
||||
Catch the minimize event and close the form
|
||||
"""
|
||||
if self.canMinimizeToTray:
|
||||
if event.type() == QtCore.QEvent.WindowStateChange and self.windowState() & QtCore.Qt.WindowMinimized:
|
||||
self.close()
|
||||
super(WebWindow, self).changeEvent(event)
|
||||
|
||||
def onTitleChanged(self, title):
|
||||
"""
|
||||
React to title changes
|
||||
"""
|
||||
if title:
|
||||
self.setWindowTitle(title)
|
||||
if self.canMinimizeToTray:
|
||||
self.trayIcon.setToolTip(title)
|
||||
|
||||
def onTrayIconActivated(self, reason):
|
||||
"""
|
||||
React to the tray icon being activated
|
||||
"""
|
||||
if reason == QtWidgets.QSystemTrayIcon.Trigger:
|
||||
if self.isVisible():
|
||||
self.close()
|
||||
else:
|
||||
self.showNormal()
|
||||
|
||||
|
||||
class WebApp(QtWidgets.QApplication):
|
||||
"""
|
||||
A generic application to open a web page in a desktop app
|
||||
"""
|
||||
def __init__(self, title, url, icon, canMinimizeToTray=False):
|
||||
"""
|
||||
Create an application which loads a URL into a window
|
||||
"""
|
||||
super(WebApp, self).__init__(sys.argv)
|
||||
self.window = None
|
||||
self.trayIcon = None
|
||||
self.title = title
|
||||
self.url = url
|
||||
self.icon = icon
|
||||
self.canMinimizeToTray = QtWidgets.QSystemTrayIcon.isSystemTrayAvailable() and canMinimizeToTray
|
||||
if self.canMinimizeToTray:
|
||||
self.setQuitOnLastWindowClosed(False)
|
||||
self.setWindowIcon(QtGui.QIcon(self.icon))
|
||||
self.setApplicationName(title)
|
||||
self.setApplicationDisplayName(title)
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
Set up the window and the tray icon, and run the app
|
||||
"""
|
||||
self.window = WebWindow(self, self.title, self.url, self.icon, self.canMinimizeToTray)
|
||||
if self.canMinimizeToTray:
|
||||
self.window.setupTrayIcon()
|
||||
self.window.showMaximized()
|
||||
# Get the "exec" method depending on Python 2 or 3
|
||||
if IS_PY2:
|
||||
runner = getattr(self, 'exec_')
|
||||
else:
|
||||
runner = getattr(self, 'exec')
|
||||
return runner()
|
240
webappify/__init__.py
Normal file
240
webappify/__init__.py
Normal file
@ -0,0 +1,240 @@
|
||||
"""
|
||||
WebAppify
|
||||
=========
|
||||
|
||||
WebAppify is a simple module to easily create your own desktop apps of websites. WebAppify uses PyQt5 and QtWebKit or
|
||||
QtWebEngine for displaying the web page, and works on Python 2.7 and Python 3.4 and up.
|
||||
|
||||
To create your own desktop web app, import and set up the WebApp class.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from webappify import WebApp
|
||||
|
||||
app = WebApp('OpenStreetMap', 'https://www.openstreetmap.org', 'osm.png')
|
||||
app.run()
|
||||
|
||||
This will create a window with the website, using the icon provided.
|
||||
|
||||
.. note::
|
||||
|
||||
If your site needs Flash Player, you'll need the appropriate Flash Player plugin installed system-wide. For QtWebKit
|
||||
you will need the NPAPI plugin, and for QtWebEngine you will need the PPAPI plugin.
|
||||
"""
|
||||
import logging
|
||||
import sys
|
||||
import platform
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets
|
||||
|
||||
SETTINGS = [
|
||||
QtWebEngineWidgets.QWebEngineSettings.PluginsEnabled,
|
||||
QtWebEngineWidgets.QWebEngineSettings.JavascriptCanAccessClipboard,
|
||||
QtWebEngineWidgets.QWebEngineSettings.LocalContentCanAccessRemoteUrls
|
||||
]
|
||||
LOG_LEVELS = {
|
||||
QtWebEngineWidgets.QWebEnginePage.InfoMessageLevel: logging.INFO,
|
||||
QtWebEngineWidgets.QWebEnginePage.WarningMessageLevel: logging.WARNING,
|
||||
QtWebEngineWidgets.QWebEnginePage.ErrorMessageLevel: logging.ERROR
|
||||
}
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WebPage(QtWebEngineWidgets.QWebEnginePage):
|
||||
"""
|
||||
A custom QWebEnginePage which logs JS console messages to the Python logging system
|
||||
"""
|
||||
def javaScriptConsoleMessage(self, level, message, line_number, source_id):
|
||||
"""
|
||||
Custom logger to log console messages to the Python logging system
|
||||
"""
|
||||
log.log(LOG_LEVELS[level], f'{source_id}:{line_number} {message}')
|
||||
print(message)
|
||||
|
||||
|
||||
class WebWindow(QtWidgets.QWidget):
|
||||
"""
|
||||
A window with a single web view and nothing else
|
||||
"""
|
||||
def __init__(self, app, title, url, icon, can_minimize_to_tray=False, canMinimizeToTray=False):
|
||||
"""
|
||||
Create the window
|
||||
"""
|
||||
super(WebWindow, self).__init__(None)
|
||||
self._has_shown_warning = False
|
||||
self.app = app
|
||||
self.icon = QtGui.QIcon(icon)
|
||||
self.can_minimize_to_tray = can_minimize_to_tray or canMinimizeToTray
|
||||
self.setWindowTitle(title)
|
||||
self.setWindowIcon(self.icon)
|
||||
self.setContentsMargins(0, 0, 0, 0)
|
||||
self.layout = QtWidgets.QVBoxLayout(self)
|
||||
self.layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.webview = QtWebEngineWidgets.QWebEngineView(self)
|
||||
self.webview.setPage(WebPage(self.webview))
|
||||
for setting in SETTINGS:
|
||||
self.webview.settings().setAttribute(setting, True)
|
||||
self.webview.setUrl(QtCore.QUrl(url))
|
||||
self.layout.addWidget(self.webview)
|
||||
self.webview.titleChanged.connect(self.on_title_changed)
|
||||
|
||||
def _show_warning(self):
|
||||
"""
|
||||
Show a balloon message to inform the user that the app is minimized
|
||||
"""
|
||||
if not self._has_shown_warning:
|
||||
self.tray_icon.showMessage(self.windowTitle(), 'This program will continue running in the system tray. '
|
||||
'To close the program, choose <b>Quit</b> in the context menu of the system '
|
||||
'tray icon.', QtWidgets.QSystemTrayIcon.Information, 5000)
|
||||
self._has_shown_warning = True
|
||||
|
||||
def _update_tray_menu(self):
|
||||
"""
|
||||
Update the enabled/disabled status of the items in the tray icon menu
|
||||
"""
|
||||
if not self.can_minimize_to_tray:
|
||||
return
|
||||
self.restore_action.setEnabled(not self.isVisible())
|
||||
self.minimize_action.setEnabled(self.isVisible() and not self.isMinimized())
|
||||
self.maximize_action.setEnabled(self.isVisible() and not self.isMaximized())
|
||||
|
||||
def _restore_window(self):
|
||||
"""
|
||||
Restore the window and activate it
|
||||
"""
|
||||
self.showNormal()
|
||||
self.activateWindow()
|
||||
self.raise_()
|
||||
|
||||
def _maximize_window(self):
|
||||
"""
|
||||
Restore the window and activate it
|
||||
"""
|
||||
self.showMaximized()
|
||||
self.activateWindow()
|
||||
self.raise_()
|
||||
|
||||
def _get_tray_menu(self):
|
||||
"""
|
||||
Create and return the menu for the tray icon
|
||||
"""
|
||||
# Create the actions for the menu
|
||||
self.restore_action = QtWidgets.QAction('&Restore', self)
|
||||
self.restore_action.triggered.connect(self._restore_window)
|
||||
self.minimize_action = QtWidgets.QAction('Mi&nimize', self)
|
||||
self.minimize_action.triggered.connect(self.close)
|
||||
self.maximize_action = QtWidgets.QAction('Ma&ximize', self)
|
||||
self.maximize_action.triggered.connect(self._maximize_window)
|
||||
self.quit_action = QtWidgets.QAction('&Quit', self)
|
||||
self.quit_action.triggered.connect(self.app.quit)
|
||||
# Create the menu and add the actions
|
||||
tray_icon_menu = QtWidgets.QMenu(self)
|
||||
tray_icon_menu.addAction(self.restore_action)
|
||||
tray_icon_menu.addAction(self.minimize_action)
|
||||
tray_icon_menu.addAction(self.maximize_action)
|
||||
tray_icon_menu.addSeparator()
|
||||
tray_icon_menu.addAction(self.quit_action)
|
||||
return tray_icon_menu
|
||||
|
||||
def setup_tray_icon(self):
|
||||
"""
|
||||
Set up the tray icon
|
||||
"""
|
||||
self.tray_icon = QtWidgets.QSystemTrayIcon(self.icon, self)
|
||||
self.tray_icon.setContextMenu(self._get_tray_menu())
|
||||
self.tray_icon.activated.connect(self.on_tray_icon_activated)
|
||||
self.tray_icon.show()
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""
|
||||
Override the close event to minimize to the tray
|
||||
"""
|
||||
# If we don't want to minimize to the tray, just close the window as per usual
|
||||
if not self.can_minimize_to_tray:
|
||||
super(WebWindow, self).closeEvent(event)
|
||||
return
|
||||
# If we want to minimize to the tray, then just hide the window
|
||||
if platform.platform().lower() == 'darwin' and (not event.spontaneous() or not self.isVisible()):
|
||||
return
|
||||
else:
|
||||
self._show_warning()
|
||||
self.hide()
|
||||
event.ignore()
|
||||
# Update the menu to match
|
||||
self._update_tray_menu()
|
||||
|
||||
def showEvent(self, event):
|
||||
"""
|
||||
Override the show event to catch max/min/etc events and update the tray icon menu accordingly
|
||||
"""
|
||||
super(WebWindow, self).showEvent(event)
|
||||
self._update_tray_menu()
|
||||
|
||||
def hideEvent(self, event):
|
||||
"""
|
||||
Override the hide event to catch max/min/etc events and update the tray icon menu accordingly
|
||||
"""
|
||||
super(WebWindow, self).hideEvent(event)
|
||||
self._update_tray_menu()
|
||||
|
||||
def changeEvent(self, event):
|
||||
"""
|
||||
Catch the minimize event and close the form
|
||||
"""
|
||||
if self.can_minimize_to_tray:
|
||||
if event.type() == QtCore.QEvent.WindowStateChange and self.windowState() & QtCore.Qt.WindowMinimized:
|
||||
self.close()
|
||||
super(WebWindow, self).changeEvent(event)
|
||||
|
||||
def on_title_changed(self, title):
|
||||
"""
|
||||
React to title changes
|
||||
"""
|
||||
if title:
|
||||
self.setWindowTitle(title)
|
||||
if self.can_minimize_to_tray:
|
||||
self.tray_icon.setToolTip(title)
|
||||
|
||||
def on_tray_icon_activated(self, reason):
|
||||
"""
|
||||
React to the tray icon being activated
|
||||
"""
|
||||
if reason == QtWidgets.QSystemTrayIcon.Trigger:
|
||||
if self.isVisible():
|
||||
self.close()
|
||||
else:
|
||||
self.showNormal()
|
||||
|
||||
|
||||
class WebApp(QtWidgets.QApplication):
|
||||
"""
|
||||
A generic application to open a web page in a desktop app
|
||||
"""
|
||||
def __init__(self, title, url, icon, can_minimize_to_tray=False, canMinimizeToTray=False):
|
||||
"""
|
||||
Create an application which loads a URL into a window
|
||||
"""
|
||||
super(WebApp, self).__init__(sys.argv)
|
||||
self.window = None
|
||||
self.tray_icon = None
|
||||
self.title = title
|
||||
self.url = url
|
||||
self.icon = icon
|
||||
self.can_minimize_to_tray = QtWidgets.QSystemTrayIcon.isSystemTrayAvailable() and \
|
||||
(can_minimize_to_tray or canMinimizeToTray)
|
||||
if self.can_minimize_to_tray:
|
||||
self.setQuitOnLastWindowClosed(False)
|
||||
self.setWindowIcon(QtGui.QIcon(self.icon))
|
||||
self.setApplicationName(title)
|
||||
self.setApplicationDisplayName(title)
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
Set up the window and the tray icon, and run the app
|
||||
"""
|
||||
self.window = WebWindow(self, self.title, self.url, self.icon, self.can_minimize_to_tray)
|
||||
if self.can_minimize_to_tray:
|
||||
self.window.setup_tray_icon()
|
||||
self.window.showMaximized()
|
||||
return self.exec()
|
Loading…
Reference in New Issue
Block a user