0

在一个应用程序中,我想嵌入 VteTerminal 并让用户提供预定义的命令。

在我运行一个需要一些时间的命令后,我想运行另一个命令。但是,即使第一个命令还没有启动,第二个命令也会运行,或者看起来是这样。按钮标签立即更改为“已完成”,在终端中,您可以在第一个命令之后(在第一个命令回显文件之前)看到 echo 命令。

如何让 feed_child 等到命令完成?

以下是示例代码:

#!/usr/bin/env python3

import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Vte', '2.91')

from gi.repository import Gtk, GLib, Vte
from os import environ


class Terminal(Vte.Terminal):
    def __init__(self):
        super(Terminal, self).__init__()
        self.connect_after('child-exited', self.on_child_exited)
        self.create_child()
        
    def create_child(self):
        self.spawn_async(
            Vte.PtyFlags.DEFAULT,
            environ['HOME'],
            ["/bin/bash"],
            [],
            GLib.SpawnFlags.DO_NOT_REAP_CHILD,
            None,
            None,
            -1,
            None,
            None,
            None
            )
    
    def feed(self, command):
        command += '\n'
        self.feed_child(command.encode())
    
    def on_child_exited(self, terminal, status):
        # Create a new child if the user ended the current one
        # with Ctrl-D or typing exit.
        self.create_child()
        
class VteTest(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self, title="Vte Test")
        self.connect('destroy', Gtk.main_quit)
        self.terminal = Terminal()
        self.set_default_size(600, 400)

        # GtkBox
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        # VteTerminal
        sw = Gtk.ScrolledWindow()
        sw.add(self.terminal)
        box.pack_start(sw, True, True, 2)
        # GtkButton
        self.btn = Gtk.Button(label="Run commands")
        self.btn.connect("clicked", self.on_btn_clicked)
        box.pack_start(self.btn, False, True, 2)
        # Add GtkBox to window
        self.add(box)
        
        self.show_all()
        
    def on_btn_clicked(self, widget):
        self.terminal.feed('for F in $(ls); do echo "$F"; sleep 0.2; done')
        # Button label is instantaniously changed to "Finished"
        self.btn.set_label('Working...')
        # TODO: wait until command has finished
        self.terminal.feed('echo "This shoud be the end"')
        self.btn.set_label('Finished')

VteTest()
Gtk.main()
    
4

1 回答 1

1

通过在 feed 函数中创建一个循环来检查终端输出是否有新的提示符($ 或 #),我提出了一个不太理想的解决方案。

如果有人找到更优雅的解决此问题的方法,请告诉我。

以下是调整后的进给功能:

    def feed(self, command, wait_until_done=False):
        command += '\n'
        self.feed_child(command.encode())

        def sleep(seconds=0.1):
            time.sleep(seconds)
            # Update the parent window
            while Gtk.events_pending():
                Gtk.main_iteration()

        # Unfortunately, there is no built-in way to notify the parent
        # that a command has finished or to wait for the command 
        # until it is finished.
        if wait_until_done:
            # This won't work if the user scrolls up.
            # So, disable scrolling while running the command
            parent_is_sensitive = self.get_parent().get_sensitive()
            self.get_parent().set_sensitive(False)
            # First, wait until the last character is not a prompt sign
            while self.get_text(None, None)[0].strip()[-1:] in '$#':
                sleep()
            # Finally, the command is executing - wait until the last
            # character is a prompt sign
            while self.get_text(None, None)[0].strip()[-1:] not in '$#':
                sleep()
            # Make the terminal scrollable again if it was at the start
            if parent_is_sensitive: self.get_parent().set_sensitive(True)
于 2020-10-08T09:37:41.650 回答