in reply to Realtime update of a textbuffer from STDOUT/STDERR in Gtk2

Thank you both. The option to tail definately handles the 'slow' part. Now I just need to handle the fragmented part. I think my problem is in the fact that everytime new data is in the filehandle, the buffer is overwritten. Is there a way to append data to the end of the buffer as it comes in? I'm sorry I didn't post the full program before, here it is (Excuse the sloppiness - it's been a learning experience):
#!/usr/bin/perl use strict; use warnings; use Gtk2 '-init'; use Glib qw/TRUE FALSE/; use FileHandle; use Gtk2::Helper; use Linux::CDROM; my $cddrive = "/dev/cdrom"; my $mountedcd = "/media/cdrom1"; our $cd = Linux::CDROM->new("$cddrive"); sub give_err { my ($fh, $msg) = @_; print $fh $msg; $cd->close; exit(1); } sub ripCD { my ($button, $passed) = @_; my $pid = fork(); if ($pid != 0) { return; } my $data = $passed->[0]; my $label =$passed->[1]; my $writefh = $passed->[2]; my $buffer = $passed->[3]; $cd->drive_status == 4 or give_err ($writefh, "No CD in drive! Insert CD and try + again. " . $cd->disc_status); my $case_number = $data->get_text; if ($case_number eq "") { $case_number = "temp"; } my $datestamp = `date +%Y%m%d%H%M%S `; my $id = ("$case_number.$datestamp"); chomp($id); system("echo 'Start of new job. ID=$id' >> /usr/rip/cdimport.log" +); system("echo '---------------------------------------------------- +---------------------------' >> /usr/rip/cdimport.log"); print $writefh "ID is $id"; system("mkdir /tmp/$id/"); chdir "/tmp/$id"; my $cdtype = $cd->disc_status; if ($cdtype==CDS_AUDIO) { print $writefh "Status: Ripping Audio CD..."; system("cdparanoia -B -e 2>> /usr/rip/cdimport.log") == 0 or give_err ($writefh, "ERROR: CDParanoia Failed: $?"); print $writefh "Status: Converting to MP3..."; system("find /tmp/$id/ -iname \"*.wav\" -print0 | xargs -0 -n +1 -I '{}' ffmpeg -i '{}' -f mp3 '{}'.mp3 2>> /usr/rip/cdimport.log") +== 0 or give_err ($writefh, "ERROR: Failed to convert WAV to M +P3: $?"); system("find /tmp/$id/ -iname \"*.mp3\" -print0 | xargs -0 -n +10 rename 's/\.cdda\.wav\.mp3/\.mp3/i' 1>> /usr/rip/cdimport.log") == + 0 or give_err ($writefh, "ERROR: Failed to rename files: $? +"); system("find /tmp/$id/ -iname \"*.mp3\" -print0 | xargs -0 -n +10 rename 's/track/$id-/i' 1>> /usr/rip/cdimport.log") == 0 or give_err ($writefh, "ERROR: Failed to rename files: $? +"); system("find /tmp/$id/ -iname \"*.wav\" -print0 | xargs -0 -n +10 rm -rf 1>> /usr/rip/cdimport.log") == 0 or give_err ($writefh, "ERROR: Failed to delete WAV Files +: $?"); } else { print $writefh "Status: Mounting CD-ROM..."; system("umount $cddrive 1>> /usr/rip/cdimport.log"); system("mount $cddrive 1>> /usr/rip/cdimport.log") == 0 or give_err ($writefh, "ERROR: Failed to mount CD-Rom: $?" +); print $writefh "Copying CD..."; system("mkdir -v /tmp/$id/ 1>> /usr/rip/cdimport.log"); system("cp $mountedcd/* -vR /tmp/$id/ 1>> /usr/rip/cdimport.lo +g") == 0 or give_err ($writefh, "ERROR: Failed to copy files: $?") +; system("chmod -v +w /tmp/$id -R 1>> /usr/rip/cdimport.log") == + 0 or give_err ($writefh, "ERROR: Failed to give write permi +ssion: $?"); print $writefh "Converting any WAV files to MP3..."; if (system("find /tmp/$id/ -iname \"*.wav\" -print0 | xargs -0 + -n 1 -I '{}' ffmpeg -i '{}' -f mp3 '{}'.mp3 1>> /usr/rip/cdimport.lo +g") == 0) { system("find /tmp/$id/ -iname \"*.mp3\" -print0 | xarg +s -0 -n 10 rename -v 's/\.wav\.mp3/\.mp3/i' 1>> /usr/rip/cdimport.log +") == 0 or give_err ($writefh, "ERROR: Failed to rename f +iles: $?"); system("find /tmp/$id/ -iname \"*.wav\" -print0 | xarg +s -0 -n 10 rm -vrf 1>> /usr/rip/cdimport.log") == 0 or give_err ($writefh, "ERROR: Failed to delete W +AV Files: $?"); } else { print $writefh "Failed to convert WAV to MP3 - Skippi +ng"; } system("umount -v $cddrive 1>> /usr/rip/cdimport.log"); } my ($start, $end) = $buffer->get_bounds; open INPUT, "> /tmp/$id/notes.txt"; print INPUT $buffer->get_text($start, $end, TRUE); close INPUT; print $writefh "Creating Archive..."; chdir("/tmp/"); system("tar --verbose --create --bzip2 --file=/usr/rip/pending/$id +.bz2 $id/ 1>> /usr/rip/cdimport.log") == 0 or give_err ($writefh, "ERROR: Failed to compress files: $?") +; print $writefh "Cleaning up old files..."; system("rm -rfv /tmp/$id/ 1>> /usr/rip/cdimport.log") == 0 or give_err ($writefh, "ERROR: Failed to delete old files: $? +"); $cd->eject; $cd->close; print $writefh "Ready for next CD"; exit(0); } sub watch_console { my ($fd, $fh, $buffer) = @_; my $sysbuffer; if ( not sysread($fh, $sysbuffer, 4096) ) { # obviously the connected pipe was closed Gtk2::Helper->remove_watch ($fd) or die "couldn't remove watcher"; close($fh); return 1; } #$buffer->set_text($sysbuffer); $buffer->set_text($sysbuffer); #always return TRUE to continue the callback return 1; } sub updateStatus { my ($fh, $tag, $label) = @_; my $buffer; if ( not sysread($fh, $buffer, 4096) ) { # obviously the connected pipe was closed Gtk2::Helper->remove_watch ($tag) or die "couldn't remove watcher"; # close($fh); return 1; } $label->set_text("$buffer"); return 1; } sub delete_event { Gtk2->main_quit; return FALSE; } # See if this was called with audio or data my $cdtype = $ARGV[0]; my ($readfh, $writefh) = FileHandle::pipe; my $fh = FileHandle->new(); #open ($fh, "/usr/rip/cdimport.log"); open ($fh, "tail -f -s .1 /usr/rip/cdimport.log |"); # Create widgets # Set up main window my $window = Gtk2::Window->new('toplevel'); $window->set_title("Metropolitan Interpreters and Translators"); $window->signal_connect(delete_event => \&delete_event); $window->set_border_width(10); $window->set_size_request( 800, 400); my $box1 = Gtk2::VBox->new(FALSE,0); $window->add($box1); # Create status bar my $label = Gtk2::Label->new("Status: Not running."); $label->set_alignment(0.0, 0.0); $box1->pack_end($label, TRUE, FALSE, 0); # Create Case Number text box my $frame = Gtk2::Frame->new('Enter in the Case Number for this CD'); my $entry = Gtk2::Entry->new; $frame->add($entry); $frame->set_border_width(0); $box1->pack_start($frame, TRUE, TRUE, 0); # Create textview box for notes $frame = Gtk2::Frame->new('Enter in any relevant notes'); my $textBox = Gtk2::TextView->new; my $scroll = Gtk2::ScrolledWindow->new; my $event_box = Gtk2::EventBox->new; my $color = Gtk2::Gdk::Color->parse ("black"); $event_box->modify_bg ('normal', $color); my $align = Gtk2::Alignment->new (0.5, 0.5, 1.0, 1.0); $align->set_border_width (1); $scroll->add($event_box); $event_box->add ($align); $align->add ($textBox); my $buffer = $textBox->get_buffer; my $end_mark = $buffer->create_mark( 'end', $buffer->get_end_iter, FAL +SE ); $buffer->signal_connect( insert_text => sub { $textBox->scroll_to_mark( $end_mark, 0.0, TRUE, 0.0, 0.0 ); } ); $frame->add($scroll); $box1->pack_start($frame, TRUE, TRUE, 0); $textBox->set_buffer($buffer); # Create console text box $frame = Gtk2::Frame->new('Console Window'); my $ctextBox = Gtk2::TextView->new; my $cscroll = Gtk2::ScrolledWindow->new; my $cevent_box = Gtk2::EventBox->new; $color = Gtk2::Gdk::Color->parse ("black"); $cevent_box->modify_bg ('normal', $color); my $calign = Gtk2::Alignment->new (0.5, 0.5, 1.0, 1.0); $calign->set_border_width (1); $cevent_box->add ($calign); $calign->add ($ctextBox); $cscroll->add($cevent_box); my $cbuffer = $ctextBox->get_buffer; my $cend_mark = $cbuffer->create_mark( 'end', $cbuffer->get_end_iter, +FALSE ); $cbuffer->signal_connect( insert_text => sub { $ctextBox->scroll_to_mark( $cend_mark, 0.0, TRUE, 0.0, 0.0 ); } ); $frame->add($cscroll); $box1->pack_end($frame, TRUE, TRUE, 0); $ctextBox->set_buffer($cbuffer); $ctextBox->set_editable(FALSE); $cbuffer->set_text("Console window"); # Create buttons my $box2 = Gtk2::HBox->new(FALSE, 0); my $button = Gtk2::Button->new(" Start "); $button->signal_connect(clicked => \&ripCD, [$entry, $label, $writefh, + $buffer, $cdtype]); $box2->pack_start($button, TRUE, FALSE, 10); $button = Gtk2::Button->new(" Cancel "); $button->signal_connect(clicked => \&delete_event); $box2->pack_start($button,TRUE, FALSE, 0); $box1->pack_start($box2, TRUE, FALSE, 0); my $tag; $tag = Gtk2::Helper->add_watch ( $readfh->fileno, 'in', sub { updateStatus ($readfh, $tag, $label); }); my $ctag; $ctag = Gtk2::Helper->add_watch ( $fh->fileno, 'in', sub { watch_console ($ctag, $fh, $cbuffer); }); $window->show_all(); Gtk2->main; system("killall tail");

Replies are listed 'Best First'.
Re^2: Realtime update of a textbuffer from STDOUT/STDERR in Gtk2
by zentara (Cardinal) on Aug 31, 2007 at 19:12 UTC
    Your code has too much in it, for me to run. I need to get Linux::CDROM, setup a bunch of directories, run as root, etc. If you want answers to questions, make the simplest possible script that demonstrates the problem. That way people can run it, and easily see the glitch. Usually when you do this simplification, you will answer your own question. This script just has too much clutter for me to make sense out of.

    I think my problem is in the fact that everytime new data is in the filehandle, the buffer is overwritten. Is there a way to append data to the end of the buffer as it comes in?

    Well, you are overwriting it with set_text. You can make a global variable, that you append to

    my $buftext =''; ..... $buftext .= $sysbuffer; $buffer->set_text($buftext); # or better.....insert at the end_iter $buffer->insert( $buffer->get_end_iter, $sysbuffer );

    The end_mark and end_iter concepts are hard to get right, it's best to make a few simple examples for yourself to play with them.

    Also, if you read the warning messages, you should change 'add' to 'add_with_viewport' at the lines specified; it will make the scrollbars work better.


    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
      Thank you, that change works like a charm. Your help is much appreciated!