Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can the size of the tk embedded xterm be dynamic?

One can write a simple python script to embed an xterm into a tk frame:

from Tkinter import *
import subprocess

root = Tk()
termf = Frame(root, height=800, width=1000)

termf.pack(fill=BOTH, expand=YES)
wid = termf.winfo_id()

After the window is established

proc = subprocess.Popen('xterm -into %d -sb ' % wid,shell=True)
root.mainloop()

On my computer it looks like embedded xterm How can I make the embedded xterm be dynamically sized to the size of the termf frame even when resizing the frame(by dragging the corner)?

Response to answer by @tinmarino tk frame with xterm embedded, but at wrong size, not completely filling the frame

like image 920
esmit Avatar asked Oct 31 '16 23:10

esmit


2 Answers

small

resized

Implementation of @EthanField solution. 3 functions:

  1. main create gui, xterm and plug the pipes
  2. on_resize is called when the frame containing the terminal is resized
  3. get_xterm_pts is parsing the output for the pts to feed stdin to xterm
import tkinter as tk
import subprocess as sp
from re import match
from threading import Thread
from queue import Queue


def main():
    # Init
    root = tk.Tk()
    queue = Queue()

    # Pack main frame
    termf = tk.Frame(root, width=800, height=800)
    termf.pack(fill=tk.BOTH, expand=tk.YES, padx=0, pady=0)
    wid = termf.winfo_id()

    # Allow window resize
    sp.Popen("""echo '*VT100.allowWindowOps: true' | xrdb -merge""", shell=True)

    # Craft command
    cmd = (
        # Create into me
        f'xterm -into {wid} -geometry 100x50 '
        # Log to stdout
        r'-sb -l -lc -lf /dev/stdout '
        # Launch `ps` command: output, tty, = for remove header
        """-e /bin/bash -c "ps -o tt=;bash" """
        r'| tee'
    )
    print('Launching:', cmd)

    # Spawn Xterm
    process = sp.Popen(
        cmd, shell=True, stdout=sp.PIPE, stderr=sp.PIPE)
    print('Xterm pid:', process.pid)

    # Get pts
    thread = Thread(target=lambda: get_xterm_pts(termf, process, queue))
    thread.start()

    # Set resize callback
    termf.bind("<Configure>", lambda event: on_resize(event, queue))

    # Start
    root.mainloop()


def on_resize(event, queue):
    """On resize: send escape sequence to pts"""
    # Magic && Check
    magic_x, magic_y = 6.1, 13
    print('Resize (w, h):', event.width, event.height)
    if not queue.queue: return

    # Calculate
    width = int(event.width / magic_x)
    height = int(event.height / magic_y)
    print('To (lin,col):', height, width)
    ctl = f"\u001b[8;{height};{width}t"

    # Send to pts
    with open(queue.queue[0], 'w') as f:
        f.write(ctl)


def get_xterm_pts(parent, process, queue):
    """Retrieve pts(`process`) -> `queue`"""
    while True:
        out = process.stdout.readline().decode()
        print('Xterm out' + out)

        match_pts = match(r'pts/\d+', out)
        if match_pts:
            pts = '/dev/' + match_pts.group(0)
            print('-----------> pts:', pts)
            queue.put(pts)
            break

        if out == b'' and process.poll() is not None:
            break

    # Resize now
    fake_event = tk.Event()
    fake_event.width = parent.winfo_width()
    fake_event.height = parent.winfo_height()
    on_resize(fake_event, queue)


if __name__ == '__main__':
    main()

The search of the pts of the new shell in a new thread seems a lot, but we want the pts of the interactive shell and not the sh which spawn it. I actually tried pip -> pts with some Popen ps, and it was less resilient.

11844 pts/1    00:00:00 sh      <- pid returned to Popen
11847 pts/1    00:00:00 xterm   <- then ...
11848 pts/1    00:00:00 tee     <- that is how a pipe work, first plug, then spawn (apparently) (seems logical)
11854 pts/35   00:00:00 bash    <- then favorite shell is Michel 
like image 147
Tinmarino Avatar answered Oct 22 '22 21:10

Tinmarino


As was pointed out in the comments above you can use .bind() on the Tk() window in order to get a call back every time the window is configured.

from tkinter import *

root = Tk()

def callback(event):
    print(event.width, event.height)

root.bind("<Configure>", callback)

root.mainloop()

I don't have the ability to test how you can use this to resize the xterm window on Windows 10. So I'll leave this answer here for anyone who wants to edit it or use this answer to build their own.

like image 42
Ethan Field Avatar answered Oct 22 '22 22:10

Ethan Field