From 083fefdd07bf139fd45d6b26dfe73ab2ecc52385 Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Fri, 5 Aug 2022 23:49:48 +0300 Subject: [PATCH] Create Native Menu Bar with PyQt to open Search, Config webpages - Run FastAPI server in a separate thread. - This allows starting both the server and gui in parallel - Create System Tray for Khoj - Contains menu items that open search or config pages in browser - Rearrange code to have only the code required to start Backend and GUI in the run() method - Move the backend setup code into a separate method --- setup.py | 1 + src/main.py | 79 +++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index eca9ce4d..8646e6cb 100644 --- a/setup.py +++ b/setup.py @@ -39,6 +39,7 @@ setup( "pillow >= 9.0.1", "aiofiles == 0.8.0", "dateparser == 1.1.1", + "pyqt6 == 6.3.1", ], include_package_data=True, entry_points={"console_scripts": ["khoj = src.main:run"]}, diff --git a/src/main.py b/src/main.py index f2bc7db9..d93856d6 100644 --- a/src/main.py +++ b/src/main.py @@ -4,6 +4,7 @@ import time from typing import Optional from pathlib import Path from functools import lru_cache +import webbrowser # External Packages import uvicorn @@ -12,6 +13,7 @@ from fastapi import FastAPI, Request from fastapi.responses import HTMLResponse, FileResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates +from PyQt6 import QtCore, QtGui, QtWidgets # Internal Packages from src.search_type import image_search, text_search @@ -285,7 +287,7 @@ def shutdown_event(): print('INFO:\tConversation logs saved to disk.') -def run(): +def setup_server(): # Load config from CLI args = cli(sys.argv[1:]) @@ -312,11 +314,78 @@ def run(): global processor_config processor_config = initialize_processor(args.config) + return args.host, args.port, args.socket + + +def run(): + # Setup Application Server + host, port, socket = setup_server() + + # Setup GUI + gui = QtWidgets.QApplication([]) + gui.setQuitOnLastWindowClosed(False) + tray = create_system_tray() + # Start Application Server - if args.socket: - uvicorn.run(app, proxy_headers=True, uds=args.socket) - else: - uvicorn.run(app, host=args.host, port=args.port) + server = ServerThread(app, host, port, socket) + server.start() + gui.aboutToQuit.connect(server.terminate) + + # Start the GUI + tray.show() + gui.exec() + + +class ServerThread(QtCore.QThread): + def __init__(self, app, host=None, port=None, socket=None): + super(ServerThread, self).__init__() + self.app = app + self.host = host + self.port = port + self.socket = socket + + def __del__(self): + self.wait() + + def run(self): + if self.socket: + uvicorn.run(app, proxy_headers=True, uds=self.socket) + else: + uvicorn.run(app, host=self.host, port=self.port) + + +def create_system_tray(): + """Create System Tray with Menu + Menu Actions should contain + 1. option to open search page at localhost:8000/ + 2. option to open config page at localhost:8000/config + 3. to quit + """ + + # Create the system tray with icon + icon_path = web_directory / 'assets/icons/favicon-144x144.png' + icon = QtGui.QIcon(f'{icon_path.absolute()}') + tray = QtWidgets.QSystemTrayIcon(icon) + tray.setVisible(True) + + # Create the menu and menu actions + menu = QtWidgets.QMenu() + menu_actions = [ + ('Search', lambda: webbrowser.open('http://localhost:8000/')), + ('Configure', lambda: webbrowser.open('http://localhost:8000/config')), + ('Quit', quit), + ] + + # Add the menu actions to the menu + for action_text, action_function in menu_actions: + menu_action = QtGui.QAction(action_text, menu) + menu_action.triggered.connect(action_function) + menu.addAction(menu_action) + + # Add the menu to the system tray + tray.setContextMenu(menu) + + return tray if __name__ == '__main__':