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
This commit is contained in:
Debanjum Singh Solanky
2022-08-05 23:49:48 +03:00
parent 3a4e5de7fa
commit 083fefdd07
2 changed files with 75 additions and 5 deletions

View File

@@ -39,6 +39,7 @@ setup(
"pillow >= 9.0.1", "pillow >= 9.0.1",
"aiofiles == 0.8.0", "aiofiles == 0.8.0",
"dateparser == 1.1.1", "dateparser == 1.1.1",
"pyqt6 == 6.3.1",
], ],
include_package_data=True, include_package_data=True,
entry_points={"console_scripts": ["khoj = src.main:run"]}, entry_points={"console_scripts": ["khoj = src.main:run"]},

View File

@@ -4,6 +4,7 @@ import time
from typing import Optional from typing import Optional
from pathlib import Path from pathlib import Path
from functools import lru_cache from functools import lru_cache
import webbrowser
# External Packages # External Packages
import uvicorn import uvicorn
@@ -12,6 +13,7 @@ from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse, FileResponse from fastapi.responses import HTMLResponse, FileResponse
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
from PyQt6 import QtCore, QtGui, QtWidgets
# Internal Packages # Internal Packages
from src.search_type import image_search, text_search from src.search_type import image_search, text_search
@@ -285,7 +287,7 @@ def shutdown_event():
print('INFO:\tConversation logs saved to disk.') print('INFO:\tConversation logs saved to disk.')
def run(): def setup_server():
# Load config from CLI # Load config from CLI
args = cli(sys.argv[1:]) args = cli(sys.argv[1:])
@@ -312,11 +314,78 @@ def run():
global processor_config global processor_config
processor_config = initialize_processor(args.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 # Start Application Server
if args.socket: server = ServerThread(app, host, port, socket)
uvicorn.run(app, proxy_headers=True, uds=args.socket) server.start()
else: gui.aboutToQuit.connect(server.terminate)
uvicorn.run(app, host=args.host, port=args.port)
# 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__': if __name__ == '__main__':