Pyqt5 embed external program into qwidget

I do not know if anyone here has done this but I have been trying off and off for last few months to be allow a user to specify their desired terminal and have that embedded into a widget in my app.

I have done some extensive research but have not been able to find a way to make this really work despite there being some almost there examples.

Wondering if anyone here had any advise.

Resources:




This looks promising but only applies to the cef browser.


Rough idea
#!/usr/bin/env python
# -- coding:utf-8 --
import psutil
import os
import platform
import sys
from pathlib import Path
from subprocess import call

from PySide2 import QtCore
from PySide2.QtCore import *
from PySide2.QtWidgets import QWidget, QVBoxLayout, QPushButton, QApplication

platform = platform.system()
print(str(platform))

term_dir = Path(os.path.abspath(os.path.dirname(__file__))) / 'terminus'

if platform == 'Windows':
    term_bin = str(term_dir) + '/' + str(platform.lower()) + '/' + 'terminus.exe'

elif platform == 'Linux':
    term_bin = str(term_dir) + '/' + str(platform.lower()) + '/' + 'terminus'

print(term_bin)


class embeddedTerminal(QWidget):

    def __init__(self):
        QWidget.__init__(self)
        self._processes = []
        self.resize(800, 600)
        self.terminal = QWidget(self)
        layout = QVBoxLayout(self)
        layout.addWidget(self.terminal)
        self._stop_process()
        self._start_process(
            'xterm',
            ['-into', str(self.terminal.winId()),
             '-e', 'tmux', 'new', '-s', 'my_session']
        )
        button = QPushButton('List files')
        layout.addWidget(button)
        button.clicked.connect(self._list_files)

    def _start_process(self, prog, args):
        child = QProcess()
        self._processes.append(child)
        child.start(prog, args)

    def _list_files(self):
        self._start_process(
            'tmux', ['send-keys', '-t', 'my_session:0', 'ls', 'Enter'])

    @classmethod
    def _stop_process(self):
        call(["tmux", "kill-session", "-t", "my_session"])


if __name__ == "__main__":
    app = QApplication(sys.argv)
    main = embeddedTerminal()
    main.show()
    sys.exit(app.exec_())

Here are all the different things i tried code wise.

embed_terminal_1.py
https://pastebin.com/vixqvyeZ

embed_terminal_2.py
https://pastebin.com/hu3jPkaq

embed_terminal_3.py
https://pastebin.com/3UXq8edc

embed_terminal_4.py
https://pastebin.com/1x1XTzhW

embed_terminal_5.py
https://pastebin.com/hWNcPGbi

embed_terminal_6.py
https://pastebin.com/mgpRVzdp

embed_terminal_7_urxvt.py
https://pastebin.com/SFMrhWkG

embed_terminal_xterm.py
https://pastebin.com/s5Wq92WR

Zip file with all the different code for ease of use.
https://wizardassistant.com/embedded_terminal/pyqt_embed_terminal_attempts.zip

Honestly the objective it to embed a native bash/xterm/ or user selectable terminal executable but if i could get something like the below to work properly with better context menu support would probably work.

The only issue i have with the websockets thing is the pyqt webengine doesn’t play nice on some older systems even when compiled with older version.

What are the main problems you’re having with the py3qterm approach? Is it getting it to run full stop, or handling the communication with the running terminal once it’s up? The screenshots show something, but is that for an older version?

With the Py3term approach it does attach to a local bash terminal fine in linux and does work for htop/top stuff which is pretty huge considering how lightweight this is compared to websockets approach.


pros: term ptt curses and env variables all work

cons:

The main issue is how it works with mouse text selection and doesn’t feature a right click context menu. This is going to be used cross platform thats going to be weird for alot of people not having a standard context menu(copy/paste) if i were to switch to this. I haven’t really been able to figure out the key events and context menu overrides although i have read the chapters in the book about it. I gotta really experiment more as thats probably fixable.

Also when i ported this to python3 awhile back while mostly functional there are some weird oddities.

  1. like not being able to use the tilde “~” symbol unless i type it in twice or paste it in twice for some reason. This is a huge deal breaker as tons of the commands i use are based on variable expansion and homedir functions of bash and i cannot copy and paste if its going to just silently discard it the first time and have unpredictable results. There are some other characters this happens to but i forgot which they were the tilde one i remember well due to how frustrated i got with it.

I think that has something to do with this portion of how original author did stuff. I’m not that advanced in my knowledge of pyqt or backend terminal coding mechanics to understand why this is needed.

  1. Cursor is pretty fugly. not end of the world.

  2. selection is a bit rough too.

For comparison:
the current xterm.js websocket is pretty slick but due pyqt webgine requirements its heavy and requires more effort to connect to stuff vs attaching to a local bash session and then jumping via ssh to desired server which would simplify things for people on Linux/Mac and Windows with the linux subsystem and bash installed.

I did find another project which text and mouse stuff works really nice but it is not a proper terminal and ptty so it doesn’t support the interactive or environmental variables.

If there was a way to basically splice the interface of this project to the backend of the py3term that would be near perfect. I know there tons of other people looking for a proper Pyqt terminal without any drawbacks just not at that level of skill to make it happen yet :frowning: