如何使tk嵌入的xterm窗口大小动态调整?

11

可以编写一个简单的 Python 脚本,将 xterm 嵌入到 tk 框架中:

from Tkinter import *
import subprocess

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

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

窗口建立后

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

在我的电脑上看起来像这样: 嵌入式xterm 如何使嵌入式xterm动态调整大小以适应termf帧的大小,甚至在调整框架大小时(通过拖动角落)也能适应大小? @tinmarino的答案回复 带有嵌入式xterm的tk框架,但尺寸不正确,不能完全填充框架

询问根节点的大小,并将其作为Frame()函数的参数,进行适当的调整。 - wallyk
我编辑了我的问题以反映尽管我可以像@wallyk建议的那样将xterm实例化为Frame的正确大小,但我想知道如何在拖动其角落调整大小的情况下继续使其与Frame的大小相匹配。 - esmit
您需要使用事件绑定来不断更新大小。 - Mike - SMT
虽然不太灵活,但是 xterm 可以使用命令进行调整大小,例如在 xterm 中输入 echo -e '\e[4;600;800t'。请参考 http://invisible-island.net/xterm/ctlseqs/ctlseqs.html 中的窗口操作。 - esmit
2个回答

4

small

resized

这是对@EthanField的方案的实现,包括三个函数:

  1. main函数用于创建图形用户界面、xterm终端和连接管道
  2. on_resize函数会在包含终端的窗口大小调整时被调用
  3. get_xterm_pts函数用于解析输出以获取pts,以便将stdin提供给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()

在新线程中查找新shell的pts似乎有点多,但我们想要交互式shell的pts而不是生成它的sh。我曾尝试使用一些Popen ps将管道连接到pts,但这种方式并不可靠。

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 

1
请用 fake_event = tk.Event() 替换 fake_event = Namespace() - stovfl
不太对。我的窗口仍然没有以正确的大小启动,因此它无法正确地调整大小(尽管它正在接收调整大小命令,这是一个好的开始)。我会在原始问题中放一张图片,因为似乎在这里无法粘贴。 - esmit
它是否会自动调整大小(即xterm的大小是否会改变)?我放了一些魔法magic_x,magic_y = 6.1,13。在你的情况下,你必须增加像:magic_x,magic_y = 7,15。你可以更改它。也许应该一开始就放置。感谢反馈。 - Tinmarino
1
从除了创建tk.Tk()的线程之外的线程调用tkinter函数并不是一个好的做法。在极少数情况下,它可能会导致python崩溃而没有错误消息/回溯。 - TheLizzard

1
如上面的评论所指出的,您可以在Tk()窗口上使用.bind(),以便在每次窗口配置时得到回调。
from tkinter import *

root = Tk()

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

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

root.mainloop()

我没有能力测试如何在Windows 10上使用此方法调整xterm窗口大小。因此,我会将这个答案留在这里,供任何想要编辑它或使用此答案构建自己的解决方案的人参考。

我知道如何编写回调函数。那个回调函数如何与打开的xterm通信? - esmit
回调函数会给你小部件的大小。你肯定可以利用这个来改变xterm的大小吧? - Ethan Field
谢谢,这很有帮助(尤其是来自于一个Windows机器的人,你可能不会弄对)!我不会进行编辑,因为我的工作代码更长(包括转义序列、同步和打印)。 - Tinmarino

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