Figured I’d share this here as there are very few practicable examples online other then the one i stumbled on the other day which enlightened me to how it works.
I now am able to some super badass stuff with my app and send commands to the ssh session programmatically from Python through the run javscript
full example code for reference.
#!/usr/bin/env python3
# encoding: utf-8
import os
import sys
from PyQt5 import uic, QtWidgets, QtCore, QtGui, QtWebEngineWidgets
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QApplication, QMessageBox, QDialog, QSplashScreen, \
QDateTimeEdit, QActionGroup, QAbstractItemView, QDockWidget, QPlainTextEdit, QTableWidgetItem
from PyQt5 import QtSql
from PyQt5.QtCore import (QCoreApplication, QRect, QSize, Qt, QUrl, QDateTime, QThread, pyqtSignal as Signal, QEvent,
QFile, QMetaObject, QRegExp, QSortFilterProxyModel, QSettings, QTimer, QTextStream,
QStandardPaths, QObject)
# from PyQt5.QtGui import *
from PyQt5.QtSql import QSqlDatabase, QSqlTableModel, QSqlQueryModel, QSqlQuery
from PyQt5.QtGui import QPixmap, QPalette, QColor, QClipboard, QGuiApplication
from PyQt5.QtCore import pyqtSignal as Signal, pyqtSlot as Slot
from PyQt5.QtCore import QUrl
from PyQt5.QtGui import QIcon
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
from PyQt5.QtWidgets import QTabWidget, QApplication, QInputDialog, QFileDialog, QPushButton
free_port = '8889'
settings = QtCore.QSettings('WizardAssistant', 'WizardAssistantDesktop')
if settings.contains("wizardwebsshport"):
# there is the key in QSettings
# print('Checking for wizardwebsshport in config')
wizardwebsshport = settings.value('wizardwebsshport')
# print('Found wizardwebsshport port in config:' + wizardwebsshport)
free_port = wizardwebsshport
else:
print('wizardwebsshport not found in config')
pass
try:
ssh_terminal_url = 'http://localhost:' + str(free_port)
print(ssh_terminal_url)
except:
pass
ssh_username = 'example2'
ssh_password = 'somerandompass'
ssh_key_passphrase = ''
ssh_public_key = ''
ssh_private_key = ''
ssh_host = ''
ssh_hostname = 'dev.example.com'
ssh_port = '22'
ssh_proxy_command = ''
ssh_public_key_file = ''
ssh_private_key_file = ''
totp = ''
cmd = """for phpver in $(ls -1 /opt/cpanel/ |grep ea-php | sed 's/ea-php//g') ; do echo "PHP $phpver" ; /opt/cpanel/ea-php$phpver/root/usr/bin/php -i |grep -Ei 'memory_limit|post_max_size|upload_max_filesize|max_execution_time|session.save_path' && echo "" ; done"""
class TabbedTerminal(QTabWidget):
def __init__(self, parent=None):
super(TabbedTerminal, self).__init__(parent)
self.setDocumentMode(True)
self.setTabPosition(QTabWidget.South)
self._new_button = QPushButton(self)
self._new_button.setText("New SSH Session")
self._new_button.clicked.connect(self.add_new_tab)
self.setCornerWidget(self._new_button)
self.tabBarDoubleClicked.connect(self.tab_open_doubleclick)
self.currentChanged.connect(self.current_tab_changed)
self.setTabsClosable(True)
self.setMovable(True)
self.tabCloseRequested.connect(self.close_current_tab)
# Uncomment to disable native menubar on Mac
# self.menuBar().setNativeMenuBar(False)
self.add_new_tab(QUrl(ssh_terminal_url), 'Homepage')
# self.show()
self.setWindowTitle("Wizard Assistant SSH")
self.setWindowIcon(QIcon(os.path.join('images', 'ma-icon-64.png')))
def add_new_tab(self, qurl=ssh_terminal_url, label="Blank"):
# if qurl is None:
# qurl = QUrl('http://localhost:8888/')
qurl = QUrl(ssh_terminal_url)
browser = QWebEngineView()
# self.webSettings = browser.settings()
# self.webSettings.setAttribute(QWebEngineSettings.PluginsEnabled, True)
# self.webSettings.setAttribute(QWebEngineSettings.JavascriptEnabled, True)
# self.webSettings.setAttribute(QWebEngineSettings.LocalStorageEnabled, True)
# self.webSettings.setAttribute(QWebEngineSettings.JavascriptCanAccessClipboard, True)
# self.webSettings.setAttribute(QWebEngineSettings.JavascriptCanPaste, True)
# self.webSettings.setAttribute(QWebEngineSettings.LocalContentCanAccessFileUrls, True)
# self.webSettings.setAttribute(QWebEngineSettings.JavascriptCanOpenWindows, True)
# self.webSettings.setAttribute(QWebEngineSettings.LocalContentCanAccessRemoteUrls, True)
# self.webSettings.setAttribute(QWebEngineSettings.AllowWindowActivationFromJavaScript, True)
browser.setUrl(qurl)
i = self.addTab(browser, label)
self.setCurrentIndex(i)
# More difficult! We only want to update the url when it's from the
# correct tab
# browser.urlChanged.connect(lambda qurl, browser=browser:
# self.update_urlbar(qurl, browser))
browser.loadFinished.connect(lambda _, i=i, browser=browser:
self.setTabText(i, browser.page().title()))
browser.titleChanged.connect(lambda _, i=i, browser=browser:
self.setTabText(i, browser.page().title()))
browser.titleChanged.connect(lambda _, i=i, browser=browser:
self.setTabToolTip(i, browser.page().title()))
def tab_open_doubleclick(self, i):
if i == -1: # No tab under the click
self.add_new_tab()
def current_tab_changed(self, i):
qurl = self.currentWidget().url()
self.update_title(self.currentWidget())
def close_current_tab(self, i):
if self.count() < 2:
return
self.removeTab(i)
def update_title(self, browser):
if browser != self.currentWidget():
# If this signal is not from the current tab, ignore
return
title = self.currentWidget().page().title()
self.setWindowTitle("%s - Wizard Assistant SSH" % title)
def navigate_webssh(self):
self.currentWidget().setUrl(QUrl(ssh_terminal_url))
def navigate_home(self):
self.currentWidget().setUrl(QUrl(ssh_terminal_url))
def navigate_to_url(self): # Does not receive the Url
q = QUrl(self.urlbar.text())
if q.scheme() == "":
q.setScheme("http")
self.currentWidget().setUrl(q)
def open_file(self):
filename, _ = QFileDialog.getOpenFileName(self, "Open file", "",
"SSH Private Key (id_*);;"
"All files (*.*)")
if filename:
with open(filename, 'rb') as f:
sshkeyprivate = f.read()
f.close()
@QtCore.pyqtSlot(bool)
def on_load_finished(self, ok):
if ok:
script = """
// pass an object to wssh.connect
var opts = {
hostname: '%s',
port: '%s',
username: '%s',
password: '%s',
privatekey: '%s',
passphrase: '%s',
totp: '%s'
};
wssh.connect(opts);
""" % (ssh_hostname, ssh_port, ssh_username, ssh_password, ssh_private_key, ssh_key_passphrase, totp)
self.currentWidget().page().runJavaScript(script)
@QtCore.pyqtSlot(bool)
def run_command_via_js(self):
script = """
// var cmd = (function() {/**/}).toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1];
wssh.send(`%s`);
""" % cmd
self.currentWidget().page().runJavaScript(script)
qtCreatorFile = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), "ui",
"sshterminal.ui") # Type your file path
Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)
class build(Ui_MainWindow, QtWidgets.QMainWindow):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
for name, obj in dict(self.__dict__).items():
# print(str(name) + str(obj))
obj_type = str(obj).strip("<PyQt5").rsplit(" ")[0].replace(".", '', 1)
# obj_type = str(obj).strip("<").rsplit(" ")[0]
# print(obj_type)
# obj_type = obj_str.strip("<PyQt5").rsplit(" ")[0].replace(".", '', 1)
label_name = "self." + str(name)
try:
label_name = self.findChild(eval(obj_type), name)
print(str(label_name) + ' created')
except:
pass
if not isinstance(obj_type, QObject):
continue
try:
print('Trying to embed SSH Terminal Widget')
self.sshterminal = TabbedTerminal(self.ssh_placeholder_widget)
self.sshterminal.setObjectName(u"sshterminal")
self.verticalLayout_2.addWidget(self.sshterminal)
self.sshterminal.setStyleSheet("QTabBar::tab { height: 25px; width: 125px; }")
print('Embedded SSH Terminal Widget completed')
except:
print('Unable to embed SSH Terminal Widget')
pass
self.new_session_pushButton.setText('Run command')
self.new_session_pushButton.clicked.connect(self.sshterminal.run_command_via_js)
def start():
app = QtWidgets.QApplication(sys.argv)
QApplication.setStyle("Fusion")
#
# # Now use a palette to switch to dark colors:
palette = QPalette()
palette.setColor(QPalette.Window, QColor(53, 53, 53))
palette.setColor(QPalette.WindowText, Qt.white)
palette.setColor(QPalette.Base, QColor(25, 25, 25))
palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53))
palette.setColor(QPalette.ToolTipBase, QColor(25, 25, 25))
palette.setColor(QPalette.ToolTipText, Qt.white)
palette.setColor(QPalette.Text, Qt.white)
palette.setColor(QPalette.Button, QColor(53, 53, 53))
palette.setColor(QPalette.ButtonText, Qt.white)
palette.setColor(QPalette.BrightText, Qt.red)
palette.setColor(QPalette.Link, QColor(42, 130, 218))
palette.setColor(QPalette.Highlight, QColor(42, 130, 218))
palette.setColor(QPalette.HighlightedText, Qt.black)
QApplication.setPalette(palette)
bld = build()
bld.show()
sys.exit(app.exec_())
if __name__ == '__main__':
start()
Now with this I can do some really slick stuff now not possible in any other way in most terminals
Hopefully this will help someone else out if theyre looking for how to do this with QtWebengine
Resources:
https://doc.qt.io/qtforpython/PySide2/QtWebEngineWidgets/QWebEnginePage.html#id5
http://www.366service.com/jp/qa/be447fc5bf361b1c7ab234de61497a0d
http://python.6.x6.nabble.com/inject-code-to-webpage-using-QWebEnginePage-runJavaScript-td5255473.html