如何在PyQt GUI中使用嵌入式终端

6

现有一个可通过Bash终端使用的环境和框架,我想要创建一个GUI。我的设想如下:

  • 在Bash会话中,设置框架环境。这将导致会话中设置了从环境变量到身份验证的所有内容。
  • 运行Python GUI脚本,以便包装现有会话并使其更容易运行后续步骤。
  • GUI出现,一侧显示嵌入式终端中的Bash会话,另一侧显示对应于可以在现有框架环境中运行的各种命令的一组按钮。
  • 可以在GUI中按下按钮,导致运行某些Bash命令。运行结果显示在嵌入式终端中。

如何创建这样的GUI呢?我意识到与现有环境交互可能会很棘手。如果特别棘手,我可以在GUI的会话中重新创建环境。无论如何,GUI如何与嵌入式终端交互?按下GUI按钮时,如何运行并在嵌入式终端中显示命令?

GUI的基本启动(具有嵌入式终端)如下:

#!/usr/bin/env python
#-*- coding:utf-8 -*-

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *

class embeddedTerminal(QWidget):

    def __init__(self):

        QWidget.__init__(self)
        self.resize(800, 600)
        self.process  = QProcess(self)
        self.terminal = QWidget(self)
        layout = QVBoxLayout(self)
        layout.addWidget(self.terminal)
        self.process.start(
            'xterm',
            ['-into', str(self.terminal.winId())]
        )

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

我怎样才可以在GUI按下按钮后,在这个嵌入式终端上运行top

你能详细解释一下为什么你希望这样处理你的问题,而不是使用更传统的 GUI 吗?是为了节省编写 GUI 的工作量,还是为了节省修改现有命令的工作量?还是两者兼而有之,或者其他原因?这些命令是否交互式的,或者在输入命令行并按下回车后是否需要进一步的用户输入就能完成运行?你说“bash 命令”,我猜想实际情况是需要运行某些特定的进程,而 bash 实际上只是一种运行它们的方式? - Croad Langshan
嘿,感谢您的考虑。基本上,有一个巨大的基础设施,用户需要通过终端进行交互。典型的用法可能涉及调用多个命令,对应于多种语言中的脚本,大多数都具有复杂的命令行参数。我知道如何自动创建要在终端中运行的各种命令,并希望通过为终端添加GUI并提供按钮来自动生成和运行某些命令,从而有效地简化用户的操作。 - d3pd
实际上,我想要一个交互式终端,并将一个简单的PyQt GUI 添加到它上面(或者将其添加到GUI上),以便GUI也可以与终端进行交互(输入命令并运行它们,并获取命令的输出)。 - d3pd
那么它实际上需要是一个终端+ bash,而不是你自己实现的简单行编辑器 - 在这种情况下,您可以使用QProcess或子进程自己运行命令。管道和作业控制经常使用吗?如果只有少数情况,请考虑用几个新命令包装这些情况,以便没有剩余的情况。 - Croad Langshan
在我上面的评论提问的不同行上,只是为了展示可能性/灵感的范围(我并不建议您使用此内容或类似内容):https://github.com/mseaborn/coconut-shell(这是一个组合的 shell 和终端,用 Python 编写 - 因此可以扩展为与您喜欢的方式进行交互) - Croad Langshan
2个回答

7
如果必须使用真正的终端和真正的shell(而不仅仅是接受一行输入,运行一些命令,然后显示输出)-如何考虑使用tmux?您可以使用像tee这样的东西将输出返回到程序中。请注意,tmux会话可能会持续存在于程序运行期间,因此您需要了解其工作原理以及如何控制它。
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *

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._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'])

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

更多信息请参考:https://superuser.com/questions/492266/run-or-send-a-command-to-a-tmux-pane-in-a-running-tmux-session


我终于考虑完成了。非常感谢您在这个问题上提供的所有非常有帮助的指导。也许一个新建的Python终端是一种更清洁的解决方案,但tmux方法基本上正是我想要的。谢谢! :) - d3pd
如果不清楚的话,答案顶部的评论并不是在将此答案与椰壳进行比较 - 而是将其与用户在行编辑器中键入命令的更简单的想法进行比较,当他们按下回车时运行该命令(例如使用QProcess),并在文本区域中打印输出。这个想法类似于浏览器使用它们的JS shell(但不需要那么花哨)。 - Croad Langshan
这太棒了。谢谢。 - answerSeeker

0

如果其他人遇到此问题并对其进行了轻微修改以关闭已存在的tmux会话,因为先前的方法在退出时没有关闭它。还设置了PySide2。

现在唯一需要的就是调整大小支持。

#!/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_())


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接