将xterm嵌入QWidget并与之通信

3 投票
1 回答
2806 浏览
提问于 2025-04-17 15:14

我想把一个 xterm 嵌入到一个 pyqt4 的小部件里,并且和它进行交流。特别是我希望能够向它打印信息,并在里面执行命令(这样在执行完命令后,它能像普通的命令行那样返回到正常的用户提示符)。请看下面这个简单的例子。我该怎么做才能让它工作呢?

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

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


class embedxterm(QWidget):

    def __init__(self):
        QWidget.__init__(self)
        self.setMinimumWidth(900)
        self.setMinimumHeight(400)
        self.process = QProcess(self)

        self.terminal = QWidget(self)
        self.terminal.setMinimumHeight(300)

        self.cmd1 = QPushButton('Command1',self)
        self.cmd2 = QPushButton('Command2',self)
        self.hello = QPushButton('Print Hello World',self)

        layout = QVBoxLayout(self)

        layoutH = QHBoxLayout(self)

        layoutH.addWidget(self.cmd1)
        layoutH.addWidget(self.cmd2)
        layoutH.addWidget(self.hello)


        layout.addLayout(layoutH)
        layout.addWidget(self.terminal)


        self.process.start(
            'xterm',['-into', str(self.terminal.winId())])

        self.cmd1.clicked.connect(self.Ccmd1)
        self.cmd2.clicked.connect(self.Ccmd2)
        self.hello.clicked.connect(self.Chello)

    def Ccmd1(self):
        self.process.write('ls -l')
        # Should execute ls -l on this terminal

    def Ccmd2(self):
        self.process.write('ping www.google.com')
        # should execute ping www.google.com on this terminal

    def Chello(self):
        self.process.write('Hello World')
        # should just print "Hello World" on this terminal

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

1 个回答

4

如果你想把一个 xterm 嵌入到你的某个窗口里,可以使用以下方法:

-into windowId 这个参数需要一个 X 窗口的标识符(一个十进制整数),这样 xterm 就会把它的顶层窗口嵌入到那个窗口中。这通常用来把 xterm 嵌入到其他应用程序里。

xterm 本身是和启动的 shell(比如 bash)进行沟通的。所以,你需要找到一种方法来和这个启动的 shell 进行交流。你可以通过 -Sccn 这个选项把打开的文件描述符传递给 xterm

这个选项允许 xterm 作为一个现有程序的输入和输出通道,有时在一些特殊应用中会用到。

所以,我觉得你需要创建一个 bash、zsh 或者你想发送命令的其他 shell 的实例。然后把那个子进程的 stdout/stderr 文件描述符连接到你的 xterm 实例,并把 stdin 连接到你的主程序,这样主程序就可以处理来自 xterm 的输入和你想发送给 bash 的命令(这样它们就会被执行并显示在 xterm 中)。

bash ----------------------> xterm
    \--< your.py <----------/

urxvt 的手册页显示,urxvt 也有一些类似的选项:

-embed windowid
这个选项告诉 urxvt 把它的窗口嵌入到一个已经存在的窗口中,这样应用程序就可以很方便地嵌入一个终端。
[ ... ]
这里有一个简短的 Gtk2-perl 代码片段,展示了这个选项如何使用(更长的例子在 doc/embed 中):

my $rxvt = new Gtk2::Socket;
$rxvt->signal_connect_after (realize => sub {
my $xid = $_[0]->window->get_xid;
system "urxvt -embed $xid &";
});

还有:

-pty-fd file descriptor
这个选项告诉 urxvt 不要执行任何命令或创建新的 pty/tty 对,而是使用给定的文件描述符作为 tty 主控。这在你想把 urxvt 作为一个通用的终端模拟器使用,而不需要在里面运行程序时非常有用。

这里有一个 perl 的例子,展示了这个选项如何使用(更长的例子在 doc/pty-fd 中):

use IO::Pty;
use Fcntl;

my $pty = new IO::Pty;
fcntl $pty, F_SETFD, 0; # 清除执行时关闭
system "urxvt -pty-fd " . (fileno $pty) . "&";
close $pty;

# 现在和 rxvt 通信
my $slave = $pty->slave;
while () { print $slave "got \n" }

如果你想在 Python 中打开一个 PTY,pty 模块看起来很有前途: http://docs.python.org/2/library/pty.html

有趣的阅读: http://sqizit.bartletts.id.au/2011/02/14/pseudo-terminals-in-python/

撰写回答