PyQt5 альтернатива Qtermwidget - PullRequest
2 голосов
/ 20 июня 2020
• 1000 ОС (ubuntu) его необходимо вручную скомпилировать и переустановить из-за некоторых проблем.

Я пытаюсь сделать настройку моего приложения как можно проще и быстрее, поскольку большинство зависимостей являются простыми пакетами pip или стандартными apt-installs.

Итак, мой вопрос:

Есть ли стандартная библиотека или способ использования терминала, например, ввода / вывода в pyqt? Я подумал о том, чтобы просто создать его в javascript (достаточно просто) и использовать QWebEngineView, но является ли это лучшей альтернативой?

1 Ответ

2 голосов
/ 20 июня 2020

Один из возможных вариантов - написать QTermWidget logi c с чистым python, чтобы сделать его переносимым, но это может занять время, поэтому в этом ответе я буду реализовывать logi c, используя xterm. js с помощью QWebChannel:

index. html

<!doctype html>
  <html>
    <head>
      <style>
      * { padding: 0; margin: 0; }
      html, body, #terminal-container {
          min-height: 100% !important;
          width: 100%;
          height: 100%;
      }
      #terminal-container {
          background: black;
      }
      </style>
      <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@4.5.0/css/xterm.css" />
      <script src="https://cdn.jsdelivr.net/npm/xterm@4.5.0/lib/xterm.js"></script> 
      <script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.3.0/lib/xterm-addon-fit.js"></script> 
      <script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
      <script type="text/javascript" src="index.js"></script>
    </head>
    <body>
      <div id="terminal-container"></div>
    </body>
  </html>

index. js

window.onload = function () {
    const terminal = new Terminal();
    f = new FitAddon.FitAddon();
    terminal.loadAddon(f);
    const container = document.getElementById('terminal-container');
    terminal.open(container);
    terminal.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ ')
    f.fit();
    new QWebChannel(qt.webChannelTransport, function (channel) {
        var socket = channel.objects.socket;
        var resize_listener = channel.objects.resize_listener;
        terminal.onKey(function(e){
            socket.send_data(e.key)
        });
        socket.dataChanged.connect(function(text){
            terminal.write(text)
        });
        resize_listener.resized.connect(function(){
            f.fit();
        });
    });
}

main.py

from functools import cached_property
import os

from PyQt5 import QtCore, QtWidgets, QtNetwork, QtWebEngineWidgets, QtWebChannel

CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))


class TerminalSocket(QtNetwork.QTcpSocket):
    dataChanged = QtCore.pyqtSignal(str)

    def __init__(self, parent=None):
        super().__init__(parent)

        self.readyRead.connect(self._handle_ready_read)
        self.error.connect(self._handle_error)

    @QtCore.pyqtSlot(str)
    def send_data(self, message):
        self.write(message.encode())

    def _handle_ready_read(self):
        data = self.readAll().data()
        self.dataChanged.emit(data.decode())

    def _handle_error(self):
        print(self.errorString())


class ResizeListener(QtCore.QObject):
    resized = QtCore.pyqtSignal()

    def __init__(self, widget):
        super().__init__(widget)
        self._widget = widget
        if isinstance(self.widget, QtWidgets.QWidget):
            self.widget.installEventFilter(self)

    @property
    def widget(self):
        return self._widget

    def eventFilter(self, obj, event):
        if obj is self.widget and event.type() == QtCore.QEvent.Resize:
            QtCore.QTimer.singleShot(100, self.resized.emit)
        return super().eventFilter(obj, event)


class TerminalWidget(QtWebEngineWidgets.QWebEngineView):
    def __init__(self, ipaddr, port, parent=None):
        super().__init__(parent)

        resize_listener = ResizeListener(self)
        self.page().setWebChannel(self.channel)
        self.channel.registerObject("resize_listener", resize_listener)
        self.channel.registerObject("socket", self.socket)
        filename = os.path.join(CURRENT_DIR, "index.html")
        self.load(QtCore.QUrl.fromLocalFile(filename))
        self.socket.connectToHost(ipaddr, port)

    @cached_property
    def socket(self):
        return TerminalSocket()

    @cached_property
    def channel(self):
        return QtWebChannel.QWebChannel()


def main():
    import sys

    app = QtWidgets.QApplication(sys.argv)

    QtCore.QCoreApplication.setApplicationName("QTermWidget Test")
    QtCore.QCoreApplication.setApplicationVersion("1.0")

    parser = QtCore.QCommandLineParser()
    parser.addHelpOption()
    parser.addVersionOption()
    parser.setApplicationDescription(
        "Example(client-side) for remote terminal of QTermWidget"
    )
    parser.addPositionalArgument("ipaddr", "adrress of host")
    parser.addPositionalArgument("port", "port of host")

    parser.process(QtCore.QCoreApplication.arguments())

    requiredArguments = parser.positionalArguments()
    if len(requiredArguments) != 2:
        parser.showHelp(1)
        sys.exit(-1)

    address, port = requiredArguments
    w = TerminalWidget(QtNetwork.QHostAddress(address), int(port))
    w.resize(640, 480)
    w.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...