#!/usr/bin/env python import sys from PySide6 import QtCore, QtWidgets, QtGui, QtWebEngineWidgets, QtWebEngineCore import gemini class GeminiPage(QtWebEngineCore.QWebEnginePage): def acceptNavigationRequest(self, url, navtype, mainframe): """ Block non-gemini page navigation and send the url to the os instead """ if url.scheme() == 'gemini': return True else: QtGui.QDesktopServices().openUrl(url) return False class GeminiSchemeHandler(QtWebEngineCore.QWebEngineUrlSchemeHandler): def requestStarted(self, request): print(request.requestUrl().toString()) gem = gemini.get(request.requestUrl().toString()) print(gem['status'], gem['meta']) buf = QtCore.QBuffer(parent=request) buf.open(QtCore.QIODevice.WriteOnly) buf.write(gemini.gem2html(gem['body']).encode('utf8')) buf.seek(0) buf.close() request.reply(b'text/html', buf) class GUrlBar(QtWidgets.QLineEdit): def __init__(self): QtWidgets.QLineEdit.__init__(self) def setUrl(self, url): return self.setText(url.toDisplayString()) class GBrowser(QtWidgets.QMainWindow): def __init__(self, initial_url=None, profile=None): if initial_url is None: initial_url = 'gemini://gemini.circumlunar.space/' if profile is None: profile = QtWebEngineCore.QWebEngineProfile.defaultProfile() QtWidgets.QMainWindow.__init__(self) # Navigation Toolbar back = QtWidgets.QPushButton("back") forward = QtWidgets.QPushButton("forward") address = GUrlBar() toolbar = QtWidgets.QToolBar() toolbar.setObjectName('navigationToolbar') toolbar.addWidget(back) toolbar.addWidget(forward) toolbar.addWidget(address) self.addToolBar(toolbar) # Main Viewport browser = QtWebEngineWidgets.QWebEngineView() page = GeminiPage(profile, browser) browser.setPage(page) self.setCentralWidget(browser) # Status Bar request_status = QtWidgets.QLabel() self.statusBar().addWidget(request_status) # Connect signals back.clicked.connect(browser.back) forward.clicked.connect(browser.forward) browser.urlChanged.connect(address.setUrl) address.returnPressed.connect(browser.load) browser.page().linkHovered.connect(self.set_status_url) browser.load(QtCore.QUrl(initial_url)) settings = QtCore.QSettings("xcolour.net", "GeminiBrowser") self.restoreGeometry(settings.value("geometry")) self.restoreState(settings.value("windowState")) self.setWindowTitle('Gemini Browser') self.show() def set_status_url(self, url): if url: self.statusBar().showMessage(url) else: self.statusBar().clearMessage() def closeEvent(self, event): settings = QtCore.QSettings("xcolour.net", "GeminiBrowser") settings.setValue("geometry", self.saveGeometry()) settings.setValue("windowState", self.saveState()) super().closeEvent(event) scheme = QtWebEngineCore.QWebEngineUrlScheme(b'gemini') scheme.setDefaultPort(1965) scheme.setFlags(QtWebEngineCore.QWebEngineUrlScheme.SecureScheme) QtWebEngineCore.QWebEngineUrlScheme.registerScheme(scheme) app = QtWidgets.QApplication(sys.argv) gem_handler = GeminiSchemeHandler() profile = QtWebEngineCore.QWebEngineProfile() profile.removeAllUrlSchemeHandlers() profile.installUrlSchemeHandler(b'gemini', gem_handler) ex = GBrowser(sys.argv[1] if len(sys.argv) > 1 else None, profile) sys.exit(app.exec())