Perl Tk Formatting Text in a button - perl

The code below creates Perl/Tk buttons filled with each event's details from the hash %events. Is there a way to format the text inside the buttons so that it appears in aligned columns in the manner printf prints it out on line 16.
use strict;
use Tk;
my %events;
$events{"Jazz Fest"}{city} = "New Orleans";
$events{"Jazz Fest"}{state} = "LA";
$events{"Jazz Fest"}{date} = "June 6th, 2023";
$events{"Lollapalooza"}{city} = "Chicago";
$events{"Lollapalooza"}{state} = "IL";
$events{"Lollapalooza"}{date} = "July 25th, 2023";
$events{"Blues Fest"}{city} = "Chicago";
$events{"Blues Fest"}{state} = "IL";
$events{"Blues Fest"}{date} = "Augsut 4th, 2023";
my %data;
foreach my $event (sort keys %events) {
printf("%-15.15s %-20.20s %-2.2s %18.18s\n", $event, $events{$event}{city}, $events{$event}{state}, $events{$event}{date});
$data{$event} = sprintf("%-15.15s %-20.20s %-2.2s %18.18s", $event, $events{$event}{city}, $events{$event}{state}, $events{$event}{date});
}
my $data;
my $mw = MainWindow->new;
$mw->title("Event Clipboard Copy");
$mw->label("Pick Event Details to Copy.");
for(sort keys %data){
my $text = $data{$_};
$mw->Button( -text => $text, -anchor => 'w', -command => sub {
Clipboard->copy( $text );
}
)->pack( -fill => 'x' );
}
MainLoop;

To align the columns, set font of each button to "Courier" (or another monospaced font). There are at least two ways to do this:
when creating the button
assign the button to a variable and then call configure on it.
#Setting font when creating the button
$mw->Button( -text => $text, -anchor => 'w', -font => [-family => 'Courier'], -command => sub {
#Calling configure on the button
my $button = $mw->Button(-text => $text, -anchor => 'w', -command => sub {
...
}->pack( -fill => 'x' );
$button->configure(-font => [-family => 'Courier']);
Here's how it looks with font set to Courier
And without setting font:

Related

Loop to define buttons in Perl / Tkx

I am trying to define a sequences of buttons in Perl with Tkx through a loop; the text and actions of each button is defined in an array of hash tables.
However, no matter what button I click on only the action of the last button is triggered.
This is the code I use.
use strict;
use Tkx;
my #Buttons = (
{ 'descr'=> "Button #0", 'cmd'=> sub { print "TODO: to implement (button #0)\n\n"; } },
{ 'descr'=> "Button #1", 'cmd'=> sub { print "TODO: to implement (button #1)\n\n"; } }
);
my %Widg;
$Widg{"."}= Tkx::widget->new(".");
$Widg{"."}->g_wm_title("List of buttons");
$Widg{"."}->g_wm_minsize(350, 300);
Tkx::font_create("H4", -family => "Helvetica", -size => 8);
$Widg{".fButton"} = $Widg{"."}->new_ttk__frame( -padding => "2 2 5 5");
$Widg{".fButton"}->g_grid(-row => 1, -sticky => "w");
my $id=-1;
foreach my $rh_measure (#Buttons) {
++$id;
$Widg{".fButton.bRun$id"}=$Widg{".fButton"}->new_ttk__button(-text => $rh_measure->{'descr'}, -command => sub { &wrapper($id); });
$Widg{".fButton.bRun$id"}->g_grid( -row => $id, -column => 1, -sticky => "w");
}
Tkx::MainLoop;
sub wrapper {
my $id=shift;
print "Process command for button #$id\n";
&{$Buttons[$id]->{'cmd'}}();
}
Actually, you just have to call the command via reference and the argument not being referenced.
$Widg{".fButton.bRun$id"}=$Widg{".fButton"}->new_ttk__button(-text => $rh_measure->{'descr'}, -command => [\&wrapper, $id]);

perl tk gui to show script stdout and stderr in text widget

I have a GUI that runs a script from a command button, but how can I get it to display output in the text widget?
If I wanted to display the output via a logfile insert, could I have the command on the same button/sub as the run button?
use warnings;
use strict;
use Tk;
use Tk::Text ;
use POSIX 'strftime';
my $DATE = strftime("Report.pl for %dth %b %Y" , localtime());
my $mw = MainWindow->new;
my $filename = "c:\\Temp\\perl.txt";
$mw->geometry("720x500");
$mw->title(" backupadmin ");
my $main_frame = $mw->Frame()->pack(-side => 'top', -fill => 'x');
my $left_frame = $main_frame->Frame(-background => "snow2")->pack(-side => 'left', -fill => 'y');
my $right_frame = $main_frame->Scrolled("Text", -scrollbars => 'se',-background => "black",-foreground => "yellow",-height => '44')->pack(-expand => 1, -fill => 'both');
my $failures_button = $left_frame->Button(-text => " $DATE ",
-command => [\&runscript])->pack;
my $Close_button = $left_frame->Button(-text => ' Close ',
-command => [$mw => 'destroy'])->pack;
my $Help_button = $left_frame->Button(-text => " Help Guide ",
-command => [\&load_file])->pack(-side => "bottom");
my $Close_help = $left_frame->Button(-text => ' Close Help ',
-command => [$right_frame => \&clear_file])->pack(-side => "bottom");
MainLoop;
sub runscript {
system("report.pl");
}
sub load_file {
open (FH, "$filename");
while (<FH>) { $right_frame->insert("end", $_); }
close (FH);
}
sub clear_file {
$right_frame->('quit');
}
If your report.pl script outputs to STDOUT, then you could try something like this in your runscript callback:
sub runscript {
right_frame->delete('1.0','end');
my $text = `report.pl`;
$right_frame->insert('end', $text);
}
Alternatively, if report.pl outputs to c:\temp\perl.txt then you could try the following:
sub runscript {
right_frame->delete('1.0','end');
system("report.pl");
load_file();
}

BioPerl/BioGraphics only prints one value instead of all

I am trying to plot SNPs onto a gene (or below). The code I have is the following:
#!/usr/bin/perl
use strict;
use warnings;
use Bio::Graphics;
use Bio::SeqFeature::Generic;
my #SNPs = "408777 408900 409100 409480";
my $gene_name = "GSTd10";
my $scaffold = "KB668289";
my $gene_start = 408763;
my $gene_end = 409489;
my $length = $gene_end - $gene_start + 50;
open my $png, ">", "$gene_name.png" or die "Cannot open $gene_name.png: $!\n";
#Create a panel for the image#
my $panel=Bio::Graphics::Panel->new(-offset => $gene_start, -length => $length, -width => 1000, -pad_left => 100, -pad_right => 10, -pad_top => 10);
my $track_whole=$panel->add_track(-glyph => 'graded_segments', -label => 1, -bgcolor => 'black', -font2color => 'black',);
my $feature= Bio::SeqFeature::Generic->new(-display_name => $gene_name, -start => $gene_start, -end => $gene_end,);
$track_whole->add_feature($feature);
my $track=$panel->add_track(-glyph => 'graded_segments', -label => 1, -bgcolor =>'blue', -min_score => 0, -max_score => 30, -font2color => 'black');
foreach my $SNP (#SNPs)
{
my $feature= Bio::SeqFeature::Generic->new(-label => $SNP, -start => $SNP, -end => $SNP);
$track->add_feature($feature);
}
#This will print out the final panel i.e. you must have created an object called $panel above
print $png $panel -> png;
Whenever I run this script, I only get printed one line.
Where is the mistake in order to print all values in #SNPs? In addition, is there a way of printing ^ instead of a block?
In this line
my #SNPs = "408777 408900 409100 409480";
You're just creating an array with a single element of that whole string.
Try
my #SNPs = qw(408777 408900 409100 409480);

Perl get value of variable from while(1) loop when sub() change it

I got a small problem.
When I create thread in main block of script, which should get the $txt value in while(1) loop, in the same time program create TopLevel window and there is a Text() object in $txt string.
I want to read values from Text() object, only when Text() object will be created, but not earlier.
In my example $txt should be a global variable, but my thread read $txt variable only by 'undef'.
Is it possible to read variable from while(1) loop, when other subroutines change it?
I must watch $txt var in thread, because when i try start thread in makeTop(), Tk gives me error about non-exist string.
Thanks for advices.
CODE:
use Tk;
use threads;
use warnings;
$mw = new MainWindow;
our $txt = undef;
my $lab = $mw->Label( -text=>"Main window.", -font => "ansi 12 bold")->pack;
my $but = $mw->Button( -text=>"Create Toplevel", -command => \&makeTop)->pack;
my $thr = threads->create('urls_couter');
MainLoop;
sub urls_couter {
while (1) {
if (defined $txt){
$txt->get('1.0','end');
}
}
}
sub makeTop {
my $top = $mw->Toplevel();
$fr = $top->Frame()->grid( -row => 1, -column => 1 );
$fr2 = $top->Frame()->grid( -row => 2, -column => 1 );
my $top_lab = $fr->Label( -text => "URLs (each on a separate line) : ",
-font => "ansi 12 bold")->pack;
$txt = $fr->Text( -width => 44, -height => 20)->pack;
$txt->insert('end', "xxxxxxx");
my $but_close =
$fr2->Button(
-text => "Ready!",
-command => sub { my #urls = split("\n", $txt->get('1.0','end-1c')); },
-relief => "raised",
-font => "ansi 12 bold")->grid( -padx => 100, -row => 1, -column => 1 );
$fr2->Button(
-text => "Close",
-command => sub { destroy $top; },
-relief => "raised",
-font => "ansi 12 bold")->grid( -pady => 10, -row => 1, -column => 2 );
}
According to this
As just mentioned, all variables are, by default, thread local. To use shared variables, you need to also load threads::shared:
thread local means you won't see changes outside your thread, so after you create your thread, each thread (logically) has it's own copy of all the variables.

How do I get the caller widget from a subroutine in Perl/TK?

in perl Tk I want to ask how to get the calling widget when using the -command option.
I have a dialog widget that is dynamically created by a previously unknown number of entries. There are Entry-widget and next to each a button where one can browse possible (useful) strings for the Entry.
Therefore, if the button next to the 2nd Entry is pressed, the result should be stored in the 2nd Entry and so on.
The code looks as follows.
my $count = 0;
my #name = ();
my #val = ();
my #edit = ();
my #button = ();
my $fr = $wind->Frame->pack;
foreach ( #outputs ) {
$name[$count] = helper::trim($_);
$val[$count] = '';
$fr->Label(-text => $name[$count])->grid(-row => $count, -column => 0, -sticky => 'w');
$edit[$count] = $fr->Entry(-textvariable => \$val[$count], -width => 30)->grid(-row => $count, -column => 1);
$button[$count] = $fr->Button(-width => 20, -text => 'Select',
-command => sub { &select(\$val[$count], \$edit[$count]); }
)->grid(-row => $count, -column => 2);
$count++;
}
However, since &select is called after the complete dialog box was built $count is always on the last value and I don't get the info, which button I have pressed. Lines as
-command => sub { &select(\#val, \#edit, $count); }
also didn't work.
Is there some way to get the widget that called the sub-routine (so I can store necessary infos somehow in the respective button) or use the actual value (of $count here) in such a call?
Best regards.
inside the callback:
my $caller = $Tk::widget;