#### Jade Application Kit # * https://codesardine.github.io/Jade-Application-Kit # * Vitor Lopes Copyright (c) 2016 - 2020 # * https://vitorlopes.me import sys import os from JAK.Utils import Instance, bindings, getScreenGeometry from JAK.KeyBindings import KeyPress if bindings() == "PyQt5": from PyQt5.QtCore import Qt, QSize, QUrl from PyQt5.QtGui import QIcon, QPixmap, QImage from PyQt5.QtWidgets import QMainWindow, QWidget, QMessageBox, QDesktopWidget, QSystemTrayIcon,\ QAction, QToolBar, QMenu, QMenuBar, QFileDialog, QLabel else: from PySide2.QtCore import Qt, QSize, QUrl from PySide2.QtGui import QIcon, QPixmap, QImage from PySide2.QtWidgets import QMainWindow, QWidget, QMessageBox, QDesktopWidget, QSystemTrayIcon,\ QAction, QToolBar, QMenu, QMenuBar, QFileDialog, QLabel class SystemTrayIcon(QSystemTrayIcon): def __init__(self, icon, app, config): self.config = config self.icon = icon super(SystemTrayIcon, self).__init__(icon, parent=app) self.setContextMenu(self.tray_menu()) self.show() def tray_menu(self): """ Create menu for the tray icon """ self.menu = QMenu() for item in self.config['window']["SystemTrayIcon"]: try: self.action = QAction(f"{item['title']}", self) self.action.triggered.connect(item['action']) if item['icon']: self.action.setIcon(QIcon(QPixmap(item['icon']))) self.menu.addAction(self.action) except KeyError: pass return self.menu class JWindow(QMainWindow): """ #### Imports: from JAK.Widgets import JWindow """ def __init__(self, config): super().__init__() self.config = config if config["window"]["backgroundImage"]: # Transparency must be set to True self.label = QLabel(self) self.setObjectName("JAKWindow") self.setBackgroundImage(config["window"]["backgroundImage"]) self.video_corner = False self.center = getScreenGeometry().center() self.setWindowTitle(config['window']["title"]) self.setWindowFlags(config['window']["setFlags"]) self.setWAttribute(Qt.WA_DeleteOnClose) for attr in config['window']["setAttribute"]: self.setWAttribute(attr) if config['window']["state"]: self.setWindowState(config['window']["state"]) if config['window']["icon"] and os.path.isfile(config['window']["icon"]): self.icon = QIcon(config['window']["icon"]) else: print(f"icon not found: {config['window']['icon']}") print("loading default icon:") self.icon = QIcon.fromTheme("applications-internet") view = Instance.retrieve("view") if view: self.view = view self.setCentralWidget(self.view) self.view.iconChanged.connect(self._icon_changed) if config['webview']["online"]: self.view.page().titleChanged.connect(self.status_message) if config['window']["transparent"]: # Set Background Transparency self.setWAttribute(Qt.WA_TranslucentBackground) self.setAutoFillBackground(True) if config['webview']["online"]: # Do not display toolbar or system tray offline if config['window']["toolbar"]: self.toolbar = JToolbar(self, config['window']["toolbar"], self.icon, config['window']["title"]) self.addToolBar(self.toolbar) self.setMenuBar(Menu(self, config['window']["menus"])) else: if config['window']["showHelpMenu"]: self.setMenuBar(Menu(self, config['window']["menus"])) self.view.page().titleChanged.connect(self.status_message) if config['window']["SystemTrayIcon"]: self.system_tray = SystemTrayIcon(self.icon, self, config) if config["debug"]: self.showInspector() self._set_icons() def setBackgroundImage(self, image): screen = getScreenGeometry() pixmap = QPixmap(QImage(image)).scaled(screen.width(), screen.height(), Qt.KeepAspectRatioByExpanding) self.label.setPixmap(pixmap) self.label.setGeometry(0, 0, screen.width(), self.label.sizeHint().height()) def showInspector(self): from JAK.DevTools import WebView, InspectorDock self.inspector_dock = InspectorDock(self) self.inspector_view = WebView(parent=self) self.inspector_view.set_inspected_view(self.view) self.inspector_dock.setWidget(self.inspector_view) self.addDockWidget(Qt.TopDockWidgetArea, self.inspector_dock) def hideInspector(self): self.inspector_dock.hide() def setWAttribute(self, attr): self.setAttribute(attr, True) def keyPressEvent(self, event): KeyPress(event, self.config) def _set_icons(self): self.setWindowIcon(self.icon) if self.config['window']["SystemTrayIcon"]: self.system_tray.setIcon(self.icon) def _icon_changed(self): if not self.view.icon().isNull(): self.icon = self.view.icon() self._set_icons() def status_message(self): # Show status message self.statusbar = self.statusBar() self.statusbar.showMessage(self.view.page().title(), 10000) def hide_show_bar(self): if self.isFullScreen() or self.video_corner: self.statusbar.hide() if self.config['window']["toolbar"]: self.toolbar.hide() else: self.statusbar.show() if self.config['window']["toolbar"]: self.toolbar.show() def default_size(self, size: str): # Set to 70% screen size screen = getScreenGeometry() if size == "width": return screen.width() * 2 / 3 elif size == "height": return screen.height() * 2 / 3 def set_window_to_defaults(self): self.window_original_position.moveCenter(self.center) self.move(self.window_original_position.topLeft()) self.resize(self.default_size("width"), self.default_size("height")) self.hide_show_bar() self.setWindowFlags(Qt.Window) self.show() def set_window_to_corner(self): self.move(self.window_original_position.bottomRight()) # Set to 30% screen size screen = getScreenGeometry() self.resize(screen.width() * 0.7 / 2, screen.height() * 0.7 / 2) self.hide_show_bar() self.setWindowFlags(Qt.SplashScreen | Qt.WindowStaysOnTopHint) self.show() def corner_window(self): if self.video_corner: self.video_corner = False self.set_window_to_defaults() else: self.video_corner = True if self.isFullScreen(): self.showNormal() self.set_window_to_corner() class JCancelConfirmDialog(QWidget): """ #### Imports: from JAK.Widgets import JCancelConfirmDialog """ def __init__(self, parent, title, msg, on_confirm): """ * :param parent: Parent window * :param window_title:str * :param msg:str * :param on_confirm: Function to execute use parenthesis () """ super().__init__(parent) reply = QMessageBox.question(self, title, msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) self.setAttribute(Qt.WA_DeleteOnClose, True) self.setWindowTitle(title) view = Instance.retrieve("view") if view: self.setWindowIcon(view.icon()) if reply == QMessageBox.Yes: on_confirm() else: self.destroy() self.show() class JToolbar(QToolBar): """ #### Imports: from JAK.Widgets import JToolbar """ def __init__(self, parent, toolbar, icon, title): """ * :param parent: Parent window * :param toolbar:dict * :param icon:str * :param title:str """ super(JToolbar, self).__init__(parent) self.icon = icon self.setMovable(False) self.setContextMenuPolicy(Qt.PreventContextMenu) self.setIconSize(QSize(32, 32)) self.about_title = "About" if toolbar: # If a dict is passed generate buttons from dict for btn in toolbar: try: if btn["icon"]: item = QAction(QIcon(btn["icon"]), btn["name"], self) except KeyError: item = QAction(btn["name"], self) item.triggered.connect(self._on_click(btn["url"])) self.addAction(item) def _on_click(self, url: str, title=""): view = Instance.retrieve("view") if url.startswith("https"): return lambda: view.setUrl(QUrl(url)) else: msg = url return lambda: InfoDialog(self, title, msg) class Menu(QMenuBar): def __init__(self, parent, menus): super(Menu, self).__init__(parent) if menus: for menu in menus: if type(menu) is dict: title = self.addMenu(menu["title"]) for entry in menu["entries"]: submenu = QAction(entry[0], self) title.addAction(submenu) print(entry[1]) submenu.triggered.connect(self._on_click(entry[1])) help_menu = {"title": "Keyboard Shortcuts", "text": """ <body style='margin-right:46px;'><b> F11 Toggle Full Screen <br> F10 Toggle Corner Window <br> CTRL + Zoom In <br> CTRL - Zoom Out </body> """},\ {"title": "About JAK", "text": """ <body style='margin-right:46px;'> <small> This online application is copyright and ownership of their respective authors. <br><br> This wrapper offers the ability to run web applications, as a self contained native desktop application. Enjoy. </small> <br> <center> <b><small> Powered by: </b></small> <a href='https://github.com/codesardine/Jade-Application-Kit'>Jade Application Kit</a><center> <small> Native Hybrid Apps on Linux. <br> <b> Using QT WebEngine <b> </small> <br> <small> <br> This application comes with no warranty. License: <b>GPL</b> <br> Author:<b>Vitor Lopes<b> </small> </center> </body> """} help = self.addMenu("Help") for entry in help_menu: submenu = QAction(entry["title"], self) submenu.triggered.connect(self._on_click(entry["text"], entry["title"])) help.addAction(submenu) def _on_click(self, url: str, title=""): if url.startswith("https"): view = Instance.retrieve("view") return lambda: view.setUrl(QUrl(url)) elif url.endswith("()"): from JAK import IPC return lambda: IPC.Communication.send(url) else: msg = url return lambda: InfoDialog(self, title, msg) class InfoDialog(QWidget): """ #### Imports: from JAK.Widgets import InfoDialog """ def __init__(self, parent, title, msg): """ * :param parent: Parent window * :param msg:str * :param on_confirm: Function to execute omit parenthesis () """ super().__init__() self.setAttribute(Qt.WA_DeleteOnClose, True) self.setWindowTitle(title) QMessageBox.information(parent, title, msg, QMessageBox.Ok) self.show() class FileChooserDialog(QWidget): def __init__(self, parent=None, file_type="", title="Choose a File"): super().__init__(parent) self.file_type = file_type self.title = title self.show() def choose_file(self): dialog = QFileDialog() options = dialog.Options() file_name = dialog.getOpenFileName( self, self.title, os.environ['HOME'], f"{self.file_type.upper()} Files ({self.file_type})", options=options) if file_name[0]: return file_name[0]