3

I want to embed an xterm into a pyqt4 widget and communicate with it. Especially I want to be able to print to it and execute commands on it (such that it returns to normal user prompt after executing the command just like a normal shell would do). Consider the following minimal example. How can I make it work?

#!/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_())
student
  • 1,636
  • 3
  • 29
  • 53

1 Answers1

4

To embed a xterm into one of your windows you should use:

-into windowId Given an X window identifier (a decimal integer), xterm will reparent its top-level shell widget to that window. This is used to embed xterm within other applications.

xterm itself talks to the launched shell (bash etc). So, you have to find a way to talk to that launched shell. You can pass open filedescriptors to xterm via the -Sccn flag:

This option allows xterm to be used as an input and output channel for an existing program and is sometimes used in specialized applications

So, I think you have to create your instance of bash, zsh, whatever you want to send the commands to. Then connect the stdout/stderr fd of that subprocess to your instance of xterm and connect the stdin to your main program which then multiplexes the input coming from the xterm and the commands you want to send to the bash (so they will get executed and be shown in xterm).

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

The manpage of urxvt reveils that urxvt has some similar switches:

-embed windowid
Tells urxvt to embed its windows into an already-existing window, which enables applications to easily embed a terminal. [ ... ] Here is a short Gtk2-perl snippet that illustrates how this option can be used (a longer example is in doc/embed):

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

and

-pty-fd file descriptor
Tells urxvt NOT to execute any commands or create a new pty/tty pair but instead use the given file descriptor as the tty master. This is useful if you want to drive urxvt as a generic terminal emulator without having to run a program within it.

Here is a example in perl that illustrates how this option can be used (a longer example > is in doc/pty-fd):

use IO::Pty;
use Fcntl;

my $pty = new IO::Pty;
fcntl $pty, F_SETFD, 0; # clear close-on-exec
system "urxvt -pty-fd " . (fileno $pty) . "&";
close $pty;

# now communicate with rxvt
my $slave = $pty->slave;
while () { print $slave "got \n" }

To open a PTY from within python the pty module looks promising: http://docs.python.org/2/library/pty.html

Interesting read: http://sqizit.bartletts.id.au/2011/02/14/pseudo-terminals-in-python/

akira
  • 6,050
  • 29
  • 37
  • Can you implement this in my minimal example of my post? – student Feb 08 '13 at 12:21
  • Thanks but I don't get the pieces together. Perhaps someone can give more details targeting on my minimal example or at least on pyqt4. – student Feb 08 '13 at 17:41
  • 1
    which part of my answer you do not understand? – akira Feb 08 '13 at 18:07
  • Honestly most of the infos in your answer I found on google before asking my question. The problem was that I didn't see how to fit it together and where to begin. So I put together a minimal example and just asked how to make it work. If I stick to xterm I think the most relevant part in your answer is to connect my python app first to bash etc. However I have no idea how to do this concretely (see my minimal example). I should also add that I am an absolute python an pyqt4 beginner. – student Feb 09 '13 at 11:29
  • and yeah, if you are a beginner or an expert: does not matter. first make the components work and then combine the components. make a little program that talks to bash. – akira Feb 09 '13 at 11:32
  • Ok, I just add another `QProcess` called `bash` in my example: `self.bash = QProcess(self)` and start it via `self.bash.start('bash')` then I can write to it. I test it for example in my `Ccmd1` function to start for example an xterm via bash: `self.bash.write('xterm \n')`. This works. If I click on the Command1 Button it starts a new xterm (or any other program if I replace xterm with it). However I dont' want to write to some bash but to the bash which is running in my existing, embedded xterm, but how to do this? – student Feb 09 '13 at 11:52
  • so actually you did not understand what i ve written about how the whole thing will work. there is "no way" to get the running process within the xterm. that's why YOU have to provide the file-descriptors to which xterm is talking to. xterm is just a window to whats going on in the process which is actually doing something. and i would not use the qt-api for subprocesses but the native python api for subprocesses. – akira Feb 09 '13 at 12:39
  • Yes I don't understand how to implement this concretely. Can you make a concrete working example of how this will work in python? – student Feb 10 '13 at 11:08
  • 2
    no, because i dont have the time to do the work for you. i still have the feeling you don't understand the concepts i lined out. – akira Feb 10 '13 at 11:19
  • I've gotten quite far in putting this together, but the final step just doesn't work. So I have an "fd" from `pty.fork()` (it equals 9), and I try to give it to `xterm` with a result like this: `xterm -into 33554442 -S019`. The main problem is I have no idea what the `cc` part of `-Sccn` is, so I tried random things and they didn't work. `xterm` just starts "normally" and I get some weird output `4e00010` in the parent terminal. Also tried with `urxvt`, doesn't work. – Oleh Prypin Jul 08 '15 at 08:16