Compare commits

..

1 Commits

Author SHA1 Message Date
Raoul Snyman f488106a4c Update WebAppify to the latest tech
ci/woodpecker/push/woodpecker Pipeline was successful Details
ci/woodpecker/pr/woodpecker Pipeline was successful Details
- Migrate to Hatch
- Migrate to Woodpecker CI
- Migrate to only Python 3
2022-06-10 11:42:03 -07:00
4 changed files with 79 additions and 131 deletions

2
.flake8 Normal file
View File

@ -0,0 +1,2 @@
[flake8]
max-line-length = 120

View File

@ -1,11 +1,11 @@
pipeline:
tests:
image: python:3
commands:
- pip install -U pip wheel pytest PyQt5 PyQtWebEngine
- pytest
when:
branch: master
# tests:
# image: python:3
# commands:
# - pip install -U pip wheel pytest PyQt5 PyQtWebEngine
# - pytest
# when:
# branch: master
build:
image: python:3
commands:

View File

@ -1,6 +1,7 @@
[build-system]
requires = [
"hatchling>=1.3.1",
"hatch-vcs"
]
build-backend = "hatchling.build"
@ -25,12 +26,10 @@ classifiers = [
"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",
"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",
@ -47,7 +46,7 @@ dynamic = [
Homepage = "https://git.snyman.info/raoul/webappify"
[tool.hatch.version]
path = "webappify/version.py"
source = "vcs"
[tool.hatch.build]
include = [
@ -55,4 +54,4 @@ include = [
]
[tool.hatch.build.targets.sdist]
[tool.hatch.build.targets.bdist]
[tool.hatch.build.targets.wheel]

View File

@ -24,208 +24,160 @@ This will create a window with the website, using the icon provided.
import sys
import platform
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets
IS_PY2 = sys.version_info[0] == 2
SETTINGS = [
QtWebEngineWidgets.QWebEngineSettings.PluginsEnabled,
QtWebEngineWidgets.QWebEngineSettings.JavascriptCanAccessClipboard,
QtWebEngineWidgets.QWebEngineSettings.LocalContentCanAccessRemoteUrls
]
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)
# WebView = QtWebEngineWidgets.QWebEngineView
class WebWindow(QtWidgets.QWidget):
"""
A window with a single web view and nothing else
"""
def __init__(self, app, title, url, icon, canMinimizeToTray=False):
def __init__(self, app, title, url, icon, can_minimize_to_tray=False, canMinimizeToTray=False):
"""
Create the window
"""
super(WebWindow, self).__init__(None)
self.hasShownWarning = False
self._has_shown_warning = False
self.app = app
self.icon = QtGui.QIcon(icon)
self.canMinimizeToTray = canMinimizeToTray
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 = 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)
self.webview.titleChanged.connect(self.on_title_changed)
def _showWarning(self):
def _show_warning(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
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 _updateTrayMenu(self):
def _update_tray_menu(self):
"""
Update the enabled/disabled status of the items in the tray icon menu
"""
if not self.canMinimizeToTray:
if not self.can_minimize_to_tray:
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())
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 _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):
def _restore_window(self):
"""
Restore the window and activate it
"""
self.showNormal()
self.activateWindow()
self._raiseWindow()
self.raise_()
def _maximizeWindow(self):
def _maximize_window(self):
"""
Restore the window and activate it
"""
self.showMaximized()
self.activateWindow()
self._raiseWindow()
self.raise_()
def _getTrayMenu(self):
def _get_tray_menu(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)
self.restore_action = QtWidgets.QAction('&Restore', self)
self.restore_action.triggered.connect(self._restoreWindow)
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._maximizeWindow)
self.quit_action = QtWidgets.QAction('&Quit', self)
self.quit_action.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
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 setupTrayIcon(self):
def setup_tray_icon(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()
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.canMinimizeToTray:
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._showWarning()
self._show_warning()
self.hide()
event.ignore()
# Update the menu to match
self._updateTrayMenu()
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._updateTrayMenu()
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._updateTrayMenu()
self._update_tray_menu()
def changeEvent(self, event):
"""
Catch the minimize event and close the form
"""
if self.canMinimizeToTray:
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 onTitleChanged(self, title):
def on_title_changed(self, title):
"""
React to title changes
"""
if title:
self.setWindowTitle(title)
if self.canMinimizeToTray:
self.trayIcon.setToolTip(title)
if self.can_minimize_to_tray:
self.tray_icon.setToolTip(title)
def onTrayIconActivated(self, reason):
def on_tray_icon_activated(self, reason):
"""
React to the tray icon being activated
"""
@ -246,12 +198,12 @@ class WebApp(QtWidgets.QApplication):
"""
super(WebApp, self).__init__(sys.argv)
self.window = None
self.trayIcon = None
self.tray_icon = None
self.title = title
self.url = url
self.icon = icon
self.canMinimizeToTray = QtWidgets.QSystemTrayIcon.isSystemTrayAvailable() and canMinimizeToTray
if self.canMinimizeToTray:
self.can_minimize_to_tray = QtWidgets.QSystemTrayIcon.isSystemTrayAvailable() and can_minimize_to_tray
if self.can_minimize_to_tray:
self.setQuitOnLastWindowClosed(False)
self.setWindowIcon(QtGui.QIcon(self.icon))
self.setApplicationName(title)
@ -261,13 +213,8 @@ class WebApp(QtWidgets.QApplication):
"""
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 = 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()
# Get the "exec" method depending on Python 2 or 3
if IS_PY2:
runner = getattr(self, 'exec_')
else:
runner = getattr(self, 'exec')
return runner()
return self.exec()