mike2750 | 2020-06-23 14:18:07 UTC | #1
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: https://stackoverflow.com/questions/41474647/run-a-foreign-exe-inside-a-python-gui-pyqt https://stackoverflow.com/questions/32703557/qx11embedcontainer-equivalent-in-qt5 https://stackoverflow.com/questions/52123693/open-window-within-main-window-pyqt5 https://stackoverflow.com/questions/29112349/how-to-use-a-terminal-embedded-in-a-pyqt-gui
This looks promising but only applies to the cef browser. https://github.com/cztomczak/cefpython/issues/325 https://github.com/cztomczak/cefpython/blob/79a8ebcc51ab7e96adbb8d0d44eec4c89c7c1cc9/examples/qt.py
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_())
mike2750 | 2020-06-30 03:55:05 UTC | #2
Here are all the different things i tried code wise.
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.
martin | 2020-06-30 11:24:10 UTC | #3
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?
mike2750 | 2020-06-30 16:17:59 UTC | #4
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
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.
- 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. https://gitlab.com/mikeramsey/py3qtermwidget/-/blob/master/py3qterm/backend.py#L110
Cursor is pretty fugly. not end of the world.
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. https://github.com/Axel-Erfurt/QTerminal
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:
To support developers in [[ countryRegion ]] I give a [[ localizedDiscount[couponCode] ]]% discount with the code [[ couponCode ]] — Enjoy!
For [[ activeDiscount.description ]] I'm giving a [[ activeDiscount.discount ]]% discount with the code [[ couponCode ]] — Enjoy!