mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-05 13:21:18 +00:00
Remove PySide dependency and deprecate desktop builds (#475)
* Remove PySide, gui option from code * Remove pyside 6 dependency from code * Remove workflows which build desktop applications * Update unit tests and update line in documentation * Remove additional references to pyinstaller, gui * Add uninstall steps to normal uninstall instructions
This commit is contained in:
@@ -1,75 +0,0 @@
|
||||
# Standard Packages
|
||||
import webbrowser
|
||||
import os
|
||||
import signal
|
||||
|
||||
# External Packages
|
||||
from PySide6 import QtGui, QtWidgets
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
# Internal Packages
|
||||
from khoj.utils import constants
|
||||
from PySide6.QtCore import QThread
|
||||
|
||||
|
||||
class ServerThread(QThread):
|
||||
def __init__(self, start_server_func, parent=None):
|
||||
super(ServerThread, self).__init__(parent)
|
||||
self.start_server_func = start_server_func
|
||||
|
||||
def __del__(self):
|
||||
self.wait()
|
||||
|
||||
def run(self):
|
||||
self.start_server_func()
|
||||
|
||||
def exit(self):
|
||||
os.kill(os.getpid(), signal.SIGTERM)
|
||||
super(ServerThread, self).exit()
|
||||
|
||||
|
||||
class MainWindow(QtWidgets.QMainWindow):
|
||||
"""Create Window to Navigate users to the web UI"""
|
||||
|
||||
def __init__(self, host: str, port: int):
|
||||
super(MainWindow, self).__init__()
|
||||
|
||||
# Initialize Configure Window
|
||||
self.setWindowTitle("Khoj")
|
||||
|
||||
# Set Window Icon
|
||||
icon_path = constants.web_directory / "assets/icons/favicon-128x128.png"
|
||||
self.setWindowIcon(QtGui.QIcon(f"{icon_path.absolute()}"))
|
||||
|
||||
# Initialize Configure Window Layout
|
||||
self.wlayout = QtWidgets.QVBoxLayout()
|
||||
|
||||
# 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()
|
||||
|
||||
def position_window(self):
|
||||
"Position the window at center of X axis and near top on Y axis"
|
||||
window_rectangle = self.geometry()
|
||||
screen_center = self.screen().availableGeometry().center()
|
||||
window_rectangle.moveCenter(screen_center)
|
||||
self.move(window_rectangle.topLeft().x(), 25)
|
||||
|
||||
def show_on_top(self):
|
||||
"Bring Window on Top"
|
||||
self.show()
|
||||
self.setWindowState(Qt.WindowState.WindowActive)
|
||||
self.activateWindow() # For Bringing to Top on Windows
|
||||
self.raise_() # For Bringing to Top from Minimized State on OSX
|
||||
@@ -1,43 +0,0 @@
|
||||
# Standard Packages
|
||||
import webbrowser
|
||||
|
||||
# External Packages
|
||||
from PySide6 import QtGui, QtWidgets
|
||||
|
||||
# Internal Packages
|
||||
from khoj.utils import constants, state
|
||||
from khoj.interface.desktop.main_window import MainWindow
|
||||
|
||||
|
||||
def create_system_tray(gui: QtWidgets.QApplication, main_window: MainWindow):
|
||||
"""Create System Tray with Menu. Menu contain options to
|
||||
1. Open Search Page on the Web Interface
|
||||
2. Open App Configuration Screen
|
||||
3. Quit Application
|
||||
"""
|
||||
|
||||
# Create the system tray with icon
|
||||
icon_path = constants.web_directory / "assets/icons/favicon-128x128.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(f"http://{state.host}:{state.port}/")),
|
||||
("Configure", lambda: webbrowser.open(f"http://{state.host}:{state.port}/config")),
|
||||
("App", main_window.show),
|
||||
("Quit", gui.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) # type: ignore[attr-defined]
|
||||
menu.addAction(menu_action)
|
||||
|
||||
# Add the menu to the system tray
|
||||
tray.setContextMenu(menu)
|
||||
|
||||
return tray
|
||||
@@ -1,6 +1,5 @@
|
||||
# Standard Packages
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
import locale
|
||||
|
||||
@@ -12,8 +11,6 @@ if sys.stderr is None:
|
||||
import logging
|
||||
import threading
|
||||
import warnings
|
||||
from platform import system
|
||||
import webbrowser
|
||||
from importlib.metadata import version
|
||||
|
||||
# Ignore non-actionable warnings
|
||||
@@ -70,81 +67,13 @@ def run():
|
||||
|
||||
logger.info("🌘 Starting Khoj")
|
||||
|
||||
if not args.gui:
|
||||
# Setup task scheduler
|
||||
poll_task_scheduler()
|
||||
# Setup task scheduler
|
||||
poll_task_scheduler()
|
||||
|
||||
# Start Server
|
||||
configure_routes(app)
|
||||
initialize_server(args.config, required=False)
|
||||
start_server(app, host=args.host, port=args.port, socket=args.socket)
|
||||
else:
|
||||
from PySide6 import QtWidgets
|
||||
from PySide6.QtCore import QTimer
|
||||
|
||||
from khoj.interface.desktop.main_window import MainWindow, ServerThread
|
||||
from khoj.interface.desktop.system_tray import create_system_tray
|
||||
|
||||
# Setup GUI
|
||||
gui = QtWidgets.QApplication([])
|
||||
main_window = MainWindow(args.host, args.port)
|
||||
|
||||
# System tray is only available on Windows, MacOS.
|
||||
# On Linux (Gnome) the System tray is not supported.
|
||||
# Since only the Main Window is available
|
||||
# Quitting it should quit the application
|
||||
if system() in ["Windows", "Darwin"]:
|
||||
gui.setQuitOnLastWindowClosed(False)
|
||||
tray = create_system_tray(gui, main_window)
|
||||
tray.show()
|
||||
|
||||
# Setup Server
|
||||
initialize_server(args.config, required=False)
|
||||
configure_routes(app)
|
||||
server = ServerThread(start_server_func=lambda: start_server(app, host=args.host, port=args.port), parent=gui)
|
||||
|
||||
url = f"http://{args.host}:{args.port}"
|
||||
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
|
||||
if args.config is None or system() not in ["Windows", "Darwin"]:
|
||||
main_window.show()
|
||||
|
||||
# Setup Signal Handlers
|
||||
signal.signal(signal.SIGINT, sigint_handler)
|
||||
# Invoke Python interpreter every 500ms to handle signals, run scheduled tasks
|
||||
timer = QTimer()
|
||||
timer.start(500)
|
||||
timer.timeout.connect(schedule.run_pending)
|
||||
|
||||
# Start Application
|
||||
server.start()
|
||||
gui.aboutToQuit.connect(server.exit)
|
||||
|
||||
# Close Splash Screen if still open
|
||||
if system() != "Darwin":
|
||||
try:
|
||||
import pyi_splash
|
||||
|
||||
# Update the text on the splash screen
|
||||
pyi_splash.update_text("Khoj setup complete")
|
||||
# Close Splash Screen
|
||||
pyi_splash.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
gui.exec()
|
||||
|
||||
|
||||
def sigint_handler(*args):
|
||||
from PySide6 import QtWidgets
|
||||
|
||||
QtWidgets.QApplication.quit()
|
||||
# Start Server
|
||||
configure_routes(app)
|
||||
initialize_server(args.config, required=False)
|
||||
start_server(app, host=args.host, port=args.port, socket=args.socket)
|
||||
|
||||
|
||||
def set_state(args):
|
||||
@@ -171,12 +100,3 @@ def poll_task_scheduler():
|
||||
timer_thread.daemon = True
|
||||
timer_thread.start()
|
||||
schedule.run_pending()
|
||||
|
||||
|
||||
def run_gui():
|
||||
sys.argv += ["--gui"]
|
||||
run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_gui()
|
||||
|
||||
@@ -17,7 +17,6 @@ def cli(args=None):
|
||||
parser.add_argument(
|
||||
"--config-file", "-c", default="~/.khoj/khoj.yml", type=pathlib.Path, help="YAML file to configure Khoj"
|
||||
)
|
||||
parser.add_argument("--gui", action="store_true", default=False, help="Show native desktop GUI. Default: false")
|
||||
parser.add_argument(
|
||||
"--regenerate",
|
||||
action="store_true",
|
||||
|
||||
@@ -106,11 +106,6 @@ def load_model(
|
||||
return model
|
||||
|
||||
|
||||
def is_pyinstaller_app():
|
||||
"Returns true if the app is running from Native GUI created by PyInstaller"
|
||||
return getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS")
|
||||
|
||||
|
||||
def get_class_by_name(name: str) -> object:
|
||||
"Returns the class object from name string"
|
||||
module_name, class_name = name.rsplit(".", 1)
|
||||
|
||||
Reference in New Issue
Block a user