Вставьте xterm в QWidget и общайтесь с ним

Я хочу встроить 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_())

person student    schedule 08.02.2013    source источник


Ответы (1)


Чтобы встроить xterm в одно из ваших окон, вы должны использовать:

-into windowId Учитывая идентификатор окна X (десятичное целое число), xterm переназначит свой виджет оболочки верхнего уровня этому окну. Это используется для встраивания xterm в другие приложения.

xterm сам общается с запущенной оболочкой (bash и т.д.). Итак, вы должны найти способ поговорить с этой запущенной оболочкой. Вы можете передать дескрипторы открытых файлов в xterm с помощью флага -Sccn:

Эта опция позволяет использовать xterm в качестве канала ввода и вывода для существующей программы и иногда используется в специализированных приложениях.

Итак, я думаю, вам нужно создать свой экземпляр bash, zsh, все, на что вы хотите отправлять команды. Затем подключите stdout/stderr fd этого подпроцесса к вашему экземпляру xterm и подключите стандартный ввод к вашей основной программе, которая затем мультиплексирует ввод, поступающий от 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 (реализовать => sub { my $xid = $_[0]->window->get_xid;
system "urxvt -embed $xid &";
});

а также

-pty-fd файловый дескриптор
Сообщает urxvt НЕ выполнять какие-либо команды или создавать новую пару pty/tty, а вместо этого использовать данный файловый дескриптор в качестве мастера tty. Это полезно, если вы хотите управлять urxvt как общим эмулятором терминала, не запуская в нем программу.

Вот пример на Perl, иллюстрирующий, как можно использовать эту опцию (более длинный пример > находится в doc/pty-fd):

используйте IO::Pty;
используйте Fcntl;

мой $pty = новый IO::Pty;
fcntl $pty, F_SETFD, 0; # очистить систему закрытия при выполнении
"urxvt -pty-fd " . (файл без $pty) . "&";
закрыть $pty;

# теперь связываемся с rxvt
my $slave = $pty->slave;
while () { print $slave "got\n" }

Чтобы открыть PTY из Python, модуль pty выглядит многообещающе: http://docs.python.org/2/library/pty.html

Интересно почитать: http://sqizit.bartletts.id.au/2011/02/14/pseudo-terminals-in-python/

person akira    schedule 08.02.2013
comment
Можете ли вы реализовать это в моем минимальном примере моего поста? - person student; 08.02.2013
comment
Спасибо, но я не собираю кусочки вместе. Возможно, кто-то может дать более подробную информацию о таргетинге на моем минимальном примере или хотя бы на pyqt4. - person student; 08.02.2013
comment
какая часть моего ответа вам не понятна? - person akira; 08.02.2013
comment
Честно говоря, большую часть информации в вашем ответе я нашел в Google, прежде чем задать свой вопрос. Проблема заключалась в том, что я не видел, как это совместить и с чего начать. Поэтому я собрал минимальный пример и просто спросил, как заставить его работать. Если я буду придерживаться xterm, я думаю, что наиболее важной частью вашего ответа является подключение моего приложения python сначала к bash и т. д. Однако я понятия не имею, как это сделать конкретно (см. Мой минимальный пример). Я также должен добавить, что я абсолютный питон и новичок в pyqt4. - person student; 09.02.2013
comment
и да, если вы новичок или эксперт: не имеет значения. сначала заставьте компоненты работать, а затем объедините компоненты. сделать небольшую программу, которая разговаривает с bash. - person akira; 09.02.2013
comment
Хорошо, я просто добавляю еще один QProcess с именем bash в моем примере: self.bash = QProcess(self) и запускаю его через self.bash.start('bash'), после чего я могу писать в него. Я тестирую это, например, в моей функции Ccmd1, чтобы запустить, например, xterm через bash: self.bash.write('xterm \n'). Это работает. Если я нажму кнопку Command1, она запустит новый xterm (или любую другую программу, если я заменю ею xterm). Однако я не хочу писать в какой-то bash, а в bash, который работает в моем существующем встроенном xterm, но как это сделать? - person student; 09.02.2013
comment
так что на самом деле вы не поняли, что я написал о том, как все это будет работать. нет способа получить запущенный процесс в xterm. вот почему ВЫ должны предоставить файловые дескрипторы, с которыми общается xterm. xterm — это просто окно того, что происходит в процессе, который на самом деле что-то делает. и я бы не использовал qt-api для подпроцессов, а собственный API-интерфейс python для подпроцессов. - person akira; 09.02.2013
comment
Да, я не понимаю, как это реализовать конкретно. Можете ли вы привести конкретный рабочий пример того, как это будет работать в python? - person student; 10.02.2013
comment
нет, потому что у меня нет времени делать работу за вас. У меня все еще есть ощущение, что вы не понимаете концепции, которые я изложил. - person akira; 10.02.2013
comment
Я довольно далеко продвинулся в этом, но последний шаг просто не работает. Итак, у меня есть fd от pty.fork() (оно равно 9), и я пытаюсь передать его xterm с таким результатом: xterm -into 33554442 -S019. Основная проблема в том, что я понятия не имею, что такое cc часть -Sccn, поэтому я пробовал случайные вещи, и они не работали. xterm просто запускается нормально, и я получаю какой-то странный вывод 4e00010 в родительском терминале. Также пробовал с urxvt, не работает. - person Oleh Prypin; 08.07.2015