gri6507 has asked for the wisdom of the Perl Monks concerning the following question:

Monks,

Sorry for consecutive posts, but I cannot find any starting point on how to do this. I would like the ability to basically have a fully functional terminal/shell in a Tk frame. In other words, the user should be able to type in a command, which would be executed and output displayed. I also need to have the ability to have my script "type" in a command to execute automatically.

Alternatively, I was thinking of having a Tk::Text widget, where the output would be displayed and new commands typed in, but I'm not sure how to trigger the execution of the command (ie, `doCmd` when <Enter> is pressed). However, I am afraid that even this method would not work. The output of the commands could get quite lengthy, creating a very large buffer for the widget.

Just to give this and my previous post some perspective: I need to write a GUI wrapper to a command lined interface program (kind of like Matlab). Where can I start searching? Thanks in advance.

Replies are listed 'Best First'.
Re: tty in Tk app
by zentara (Cardinal) on Jul 31, 2004 at 14:46 UTC
    If I understand what you want to do correctly, the Tk::ExecuteCommand module should do it easily. There are other things you could do too, like embed a real xterm into the frame.
    #!/usr/bin/perl use Tk; use Tk::ExecuteCommand; my $mw = MainWindow->new; $ec = $mw->ExecuteCommand( -command => '', -entryWidth => 50, -height => 10, -label => '', -text => 'Execute', )->pack; $ec->configure( -command => 'dir' ); $ec->execute_command; $ec->bell; $ec->update; MainLoop;

    I'm not really a human, but I play one on earth. flash japh
      > There are other things you could do too, like embed a real xterm into the frame.

      I believe that's exactly what I would like to do. How can that be done?

Re: tty in Tk app
by Avitar (Acolyte) on Jul 30, 2004 at 23:01 UTC
    You can use the Key or KeyPress descriptor and bind it to an event just like a button. Just eval the input to a system call. I technically think that Tk may be a bad method to accomplish what you are trying to do.. It would probably be a better Idea to install Apache and run it through a web page via CGI using passwork protection. If you need help with the Syntax.. http://www.foo.be/docs/tpj/issues/vol2_3/tpj0203-0006.html
Re: tty in Tk app
by Joost (Canon) on Jul 31, 2004 at 12:02 UTC
    Using Tk::Text sounds reasonable to me.

    If you're really worried about buffering the output of the commands, why would you put it in a Tk widget at all? That will take up more memory than just the string output.

    Also: IIRC matlab has a "shell" interface, which means you don't start matlab itself for each command, instead you start it once, and then send commands to its STDIN and read its STDOUT for the output. That can get complicated (mainly because you can run into deadlocks) but it's certainly possible. See perlipc and IPC::Open3 and Tk::fileevent.

    Update: If it's possible in your situation, you can simplify your program by using the Tk::Text (or Tk::ROText) widget for output only, and let the user enter the commands in a seperate text field below the output widget. That way you won't have to do all kinds of funky things with the keyboard events.

Re: tty in Tk app
by zentara (Cardinal) on Aug 01, 2004 at 15:55 UTC
    There are two things I've been able to do with embedding an xterm into a Tk widget. The first is just embedding the xterm, the second is setting up an io-slave so that you can write to that xterm from an external process. The second one I did with Zinc, but you should be able to modify it for a canvas, if you need help let me know.
    #!/usr/bin/perl -w use strict; use Tk; my $mw = MainWindow->new(); my $canv = $mw->Canvas(-bg => 'lightsteelblue', -relief => 'sunken', -width => 500, -height => 400)->pack(-expand => 1, -fill => 'both'); my $xtermWidth = 400; my $xtermHeight = 300; ## this Frame is needed for including the xterm in Tk::Zinc my $xtermContainer = $canv->Frame(-container => 1); my $xtid = $xtermContainer->id(); print "$xtid\n"; # converting the id from HEX to decimal as xterm requires a decimal Id + my ($xtId) = sprintf hex $xtid; print "$xtId\n"; my $label = $canv->createText( 250,10, -text => "Hide xterm", ); my $dcontitem = $canv->createWindow(250,175, -window => $xtermContainer, -width => $xtermWidth+4, -height => $xtermHeight+4, -state => 'normal'); $canv->bind($label, '<1>', \&hideShow); sub hideShow { if ($canv->itemcget($label, -text) =~ /Hide/) { $canv->itemconfigure($label, -fill => 'yellow', -text => "Show xterm"); $canv->itemconfigure($dcontitem, -state => 'hidden'); } else { $canv->itemconfigure($label, -fill => 'black', -text => "Hide xterm"); $canv->itemconfigure($dcontitem, -state => 'normal'); } } my $width = $xtermWidth/10; my $height = $xtermHeight/20; $mw->Button(-text => "Toplevel", -command => \&do_Toplevel)->pack( ); system("xterm -fn 10x20 -geometry ${width}x${height} -into $xtId &"); MainLoop(); sub do_Toplevel { my $tl = $mw->Toplevel(-use=>$xtId ); $tl->title("Toplevel"); $tl->Button(-text => "Close", -command => sub { $tl->withdraw })->pack; } __END__
    and here is an IO-slave example
    #!/usr/bin/perl -w use strict; use Tk; use Tk::Zinc; use Proc::ProcessTable; my $title = time(); my $xtermparentpid; my $pttydev; my $id; my $count = 0; my $mw = MainWindow->new(); my $zinc = $mw->Zinc(-backcolor => 'gray', -relief => 'sunken', -width => 400, -height => 300)->pack(-expand => 1, -fill => 'both'); my $xtermWidth = 300; my $xtermHeight = 200; ## this Frame is needed for including the xterm in Tk::Zinc my $xtermContainer = $zinc->Frame(-container => 1); my $xtid = $xtermContainer->id(); # converting the id from HEX to decimal as xterm requires a decimal Id + my ($xtId) = sprintf hex $xtid; my $label = $zinc->add('text', 1, -text => "Hide xterm", -position => [150, 30]); my $dcontitem = $zinc->add('window', 1, -window => $xtermContainer, -position => [50, 75], -width => $xtermWidth+4, -height => $xtermHeight+4, -visible => 1); $zinc->bind($label, '<1>', \&hideShow); sub hideShow { if ($zinc->itemcget($label, -text) =~ /Hide/) { $zinc->itemconfigure($label, -color => 'yellow', -text => "Show xterm"); $zinc->itemconfigure($dcontitem, -visible => 0); } else { $zinc->itemconfigure($label, -color => 'black', -text => "Hide xterm"); $zinc->itemconfigure($dcontitem, -visible => 1); } } my $width = $xtermWidth/10; my $height = $xtermHeight/20; system("xterm -T $title -fn 10x20 -geometry ${width}x${height} -into +$xtId &"); my $button = $mw->Button(-text => "Start", -command => \&do_slave_writ +e)->pack(); my $exit = $mw->Button(-text => "Exit", -command => sub{Tk::exit})->pa +ck(); MainLoop(); sub do_slave_write{ $button->configure(-text => 'Stop', -command => \&do_slave_stop); $mw->update; my $t = new Proc::ProcessTable; foreach my $p (@{$t->table}) { if($p->cmndline =~ /$title/){$xtermparentpid = $p->pid} } foreach my $p (@{$t->table}) { if($p->ppid == $xtermparentpid){$pttydev = $p->ttydev} } open(FH,">$pttydev") or warn $!; $id = Tk::After->new($mw,1000,'repeat', sub{print STDOUT $count,"\n"; print FH $count.'a',"\n";$count++ +}); } sub do_slave_stop{ $id->cancel; $button->configure(-text => 'Start', -command => \&do_slave_write); $mw->update; # $count=0; close FH; #to reset count }

    I'm not really a human, but I play one on earth. flash japh