Open Web interface within Desktop app in GUI mode (#429)

Previously the GUI mode (with khoj --gui or using the desktop app) would open the web interface in the users default web browser. Now the web interface is just rendered within the app itself using PyQT's Webview. This gives it a more proper app like feel
This commit is contained in:
Debanjum
2023-08-07 17:48:30 -07:00
committed by GitHub
4 changed files with 33 additions and 43 deletions

View File

@@ -24,7 +24,7 @@ For more detailed Windows installation and troubleshooting, see [Windows Install
### 2. Start ### 2. Start
Run the following command from your terminal to start the Khoj backend and open Khoj in your browser. Run the following command in your terminal to start the Khoj backend and open the Khoj native GUI
```shell ```shell
khoj --gui khoj --gui

View File

@@ -1,13 +1,11 @@
# Standard Packages
import webbrowser
# External Packages # External Packages
from PySide6 import QtGui, QtWidgets from PySide6 import QtGui
from PySide6.QtCore import Qt from PySide6.QtCore import Qt, QThread, QUrl
from PySide6.QtWebEngineWidgets import QWebEngineView
from PySide6.QtWebEngineCore import QWebEnginePage
# Internal Packages # Internal Packages
from khoj.utils import constants from khoj.utils import constants
from PySide6.QtCore import QThread
class ServerThread(QThread): class ServerThread(QThread):
@@ -22,11 +20,12 @@ class ServerThread(QThread):
self.start_server_func() self.start_server_func()
class MainWindow(QtWidgets.QMainWindow): class MainWindow(QWebEngineView):
"""Create Window to Navigate users to the web UI""" """Create Window to Navigate users to the web UI"""
def __init__(self, host: str, port: int): def __init__(self, url: str):
super(MainWindow, self).__init__() super(MainWindow, self).__init__()
self.base_url = url
# Initialize Configure Window # Initialize Configure Window
self.setWindowTitle("Khoj") self.setWindowTitle("Khoj")
@@ -35,25 +34,23 @@ class MainWindow(QtWidgets.QMainWindow):
icon_path = constants.web_directory / "assets/icons/favicon-128x128.png" icon_path = constants.web_directory / "assets/icons/favicon-128x128.png"
self.setWindowIcon(QtGui.QIcon(f"{icon_path.absolute()}")) self.setWindowIcon(QtGui.QIcon(f"{icon_path.absolute()}"))
# Initialize Configure Window Layout # Open Khoj Web App Root
self.wlayout = QtWidgets.QVBoxLayout() self.webpage = QWebEnginePage()
self.setPage(self.webpage)
self.webpage.load(QUrl(self.base_url))
# Add a Label that says "Khoj Configuration" to the Window
self.wlayout.addWidget(QtWidgets.QLabel("Welcome to Khoj"))
# Add a Button to open the Web UI at http://host:port/config
self.open_web_ui_button = QtWidgets.QPushButton("Open Web UI")
self.open_web_ui_button.clicked.connect(lambda: webbrowser.open(f"http://{host}:{port}/config"))
self.wlayout.addWidget(self.open_web_ui_button)
# Set the central widget of the Window. Widget will expand
# to take up all the space in the window by default.
self.config_window = QtWidgets.QWidget()
self.config_window.setLayout(self.wlayout)
self.setCentralWidget(self.config_window)
self.position_window() self.position_window()
def show_page(self, page: str = "", maximized=False):
def load_page():
self.webpage.load(QUrl(f"{self.base_url}/{page}"))
if maximized:
self.showMaximized()
else:
self.show()
return load_page
def position_window(self): def position_window(self):
"Position the window at center of X axis and near top on Y axis" "Position the window at center of X axis and near top on Y axis"
window_rectangle = self.geometry() window_rectangle = self.geometry()

View File

@@ -1,11 +1,8 @@
# Standard Packages
import webbrowser
# External Packages # External Packages
from PySide6 import QtGui, QtWidgets from PySide6 import QtGui, QtWidgets
# Internal Packages # Internal Packages
from khoj.utils import constants, state from khoj.utils import constants
from khoj.interface.desktop.main_window import MainWindow from khoj.interface.desktop.main_window import MainWindow
@@ -25,9 +22,9 @@ def create_system_tray(gui: QtWidgets.QApplication, main_window: MainWindow):
# Create the menu and menu actions # Create the menu and menu actions
menu = QtWidgets.QMenu() menu = QtWidgets.QMenu()
menu_actions = [ menu_actions = [
("Search", lambda: webbrowser.open(f"http://{state.host}:{state.port}/")), ("Search", main_window.show_page()),
("Configure", lambda: webbrowser.open(f"http://{state.host}:{state.port}/config")), ("Chat", main_window.show_page("chat")),
("App", main_window.show), ("Configure", main_window.show_page("config")),
("Quit", gui.quit), ("Quit", gui.quit),
] ]

View File

@@ -13,7 +13,6 @@ import logging
import threading import threading
import warnings import warnings
from platform import system from platform import system
import webbrowser
# Ignore non-actionable warnings # Ignore non-actionable warnings
warnings.filterwarnings("ignore", message=r"snapshot_download.py has been made private", category=FutureWarning) warnings.filterwarnings("ignore", message=r"snapshot_download.py has been made private", category=FutureWarning)
@@ -85,8 +84,9 @@ def run():
from khoj.interface.desktop.system_tray import create_system_tray from khoj.interface.desktop.system_tray import create_system_tray
# Setup GUI # Setup GUI
url = f"http://{args.host}:{args.port}"
gui = QtWidgets.QApplication([]) gui = QtWidgets.QApplication([])
main_window = MainWindow(args.host, args.port) main_window = MainWindow(url)
# System tray is only available on Windows, MacOS. # System tray is only available on Windows, MacOS.
# On Linux (Gnome) the System tray is not supported. # On Linux (Gnome) the System tray is not supported.
@@ -102,17 +102,13 @@ def run():
configure_routes(app) configure_routes(app)
server = ServerThread(start_server_func=lambda: start_server(app, host=args.host, port=args.port)) server = ServerThread(start_server_func=lambda: start_server(app, host=args.host, port=args.port))
url = f"http://{args.host}:{args.port}"
logger.info(f"🌗 Khoj is running at {url}") logger.info(f"🌗 Khoj is running at {url}")
try:
startup_url = url if args.config else f"{url}/config"
webbrowser.open(startup_url)
except:
logger.warning(f"🚧 Unable to open browser. Please open {url} manually to configure or use Khoj.")
# Show Main Window on First Run Experience or if on Linux # Show config window on first run and main window otherwise
if args.config is None or system() not in ["Windows", "Darwin"]: startup_window = (
main_window.show() main_window.show_page(maximized=True) if args.config else main_window.show_page("config", maximized=True)
)
startup_window()
# Setup Signal Handlers # Setup Signal Handlers
signal.signal(signal.SIGINT, sigint_handler) signal.signal(signal.SIGINT, sigint_handler)