Re: stdout/sterr to GtkTextView at runtime



On 11 December 2015 at 06:14,  <orangensaftx web de> wrote:
How can i achieve to get all output at runtime in my textview? And how do i
check if test_output is done running? I found an old solution using Gtk+
itself but my perl skills aren't good enough to reproduce this:
https://mail.gnome.org/archives/gtk-list/2006-February/msg00040.html

Below is the relevant code which gscan2pdf uses to read the output
from scanimage. The following callbacks are run as the process starts,
while it is running, and when it finished, respectively:

$options{started_callback}, $options{running_callback},
$options{finished_callback}

The following callbacks are triggered when data is available from the
appropriate stream:

$options{out_callback}, $options{err_callback}

Obviously, the command is passed in.

$options{cmd}

You use the above hooks to update the GUI, which is therefore not
blocked by the process.

The code is for Gtk2, but I assume it should work very similarly in
Gtk3, and should also be independent of the shell that is being used.

HTH

Jeff

use IPC::Open3;
use IO::Handle;

sub _watch_cmd {
    my (%options) = @_;

    my $out_finished = FALSE;
    my $err_finished = FALSE;
    my $error_flag   = FALSE;
    $logger->info( $options{cmd} );

    if ( defined $options{running_callback} ) {
        my $timer = Glib::Timeout->add(
            $_POLL_INTERVAL,
            sub {
                $options{running_callback}->();
                return Glib::SOURCE_REMOVE
                  if ( $out_finished or $err_finished );
                return Glib::SOURCE_CONTINUE;
            }
        );
    }

    my ( $write, $read );
    my $error = IO::Handle->new;
    my $pid = IPC::Open3::open3( $write, $read, $error, $options{cmd} );

    if ( defined $options{started_callback} ) { $options{started_callback}->() }
    my ( $stdout, $stderr, $error_message );

    _add_watch(
        $read,
        sub {
            my ($line) = @_;
            $stdout .= $line;
            if ( defined $options{out_callback} ) {
                $options{out_callback}->($line);
            }
        },
        sub {

          # Don't flag this until after the callback to avoid the race condition
          # where stdout is truncated by stderr prematurely reaping the process
            $out_finished = TRUE;
        },
        sub {
            ($error_message) = @_;
            $error_flag = TRUE;
        }
    );
    _add_watch(
        $error,
        sub {
            my ($line) = @_;
            $stderr .= $line;
            if ( defined $options{err_callback} ) {
                $options{err_callback}->($line);
            }
        },
        sub {

          # Don't flag this until after the callback to avoid the race condition
          # where stderr is truncated by stdout prematurely reaping the process
            $err_finished = TRUE;
        },
        sub {
            ($error_message) = @_;
            $error_flag = TRUE;
        }
    );

    # Watch for the process to hang up before running the finished callback
    Glib::Child->watch_add(
        $pid,
        sub {

          # Although the process has hung up, we may still have output to read,
          # so wait until the _watch_add flags that the process has ended first.
            my $timer = Glib::Timeout->add(
                $_POLL_INTERVAL,
                sub {
                    if ($error_flag) {
                        if ( defined $options{error_callback} ) {
                            $options{error_callback}->($error_message);
                        }
                        return Glib::SOURCE_REMOVE;
                    }
                    elsif ( $out_finished and $err_finished ) {

                        if ( defined $options{finished_callback} ) {
                            $options{finished_callback}->( $stdout, $stderr );
                        }
                        waitpid $ALL_PENDING_ZOMBIE_PROCESSES, WNOHANG );
                        return Glib::SOURCE_REMOVE;
                    }
                    return Glib::SOURCE_CONTINUE;
                }
            );
        }
    );
    return;
}

sub _add_watch {
    my ( $fh, $line_callback, $finished_callback, $error_callback ) = @_;
    my $line;
    Glib::IO->add_watch(
        fileno($fh),
        [ 'in', 'hup' ],
        sub {
            my ( $fileno, $condition ) = @_;
            my $buffer;
            if ( $condition & 'in' ) { # bit field operation. >= would also work

                # Only reading one buffer, rather than until sysread gives EOF
                # because things seem to be strange for stderr
                sysread $fh, $buffer, $_1KB;
                if ($buffer) { $line .= $buffer }

                while ( $line =~ /([\r\n])/xsm ) {
                    my $le = $1;
                    if ( defined $line_callback ) {
                        $line_callback->(
                            substr $line, 0, index( $line, $le ) + 1
                        );
                    }
                    $line = substr $line, index( $line, $le ) + 1, length $line;
                }
            }

            # Only allow the hup if sure an empty buffer has been read.
            if (
                ( $condition & 'hup' ) # bit field operation. >= would also work
                and ( not defined $buffer or $buffer eq $EMPTY )
              )
            {
                if ( close $fh ) {
                    $finished_callback->();
                }
                elsif ( defined $error_callback ) {
                    $error_callback->('Error closing filehandle');
                }
                return Glib::SOURCE_REMOVE;
            }
            return Glib::SOURCE_CONTINUE;
        }
    );
    return;
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]