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

Hello monks,

In a Prima application I would like to define a widget which allows to set a bunch of configuration variables. So I have several lines, each consisting of a Prima::Label describing the entry and a Prima::InputLine where users can enter their values.

Is there a decent way to get the InputLine widgets aligned vertically, like in a HTML table?

Counting pixels and specifing the size attribute for the widgets works, sort of, but might break if the text in the Label widgets isn't known in advance, for example when it is pulled from some I18N framework. I would also prefer not to count pixels for the line height, which might differ for different platforms (or fonts).

Any idea or pointer to a code example is welcome!

Replies are listed 'Best First'.
Re: Prima: How to get a "table" layout?
by dk (Chaplain) on Jan 20, 2025 at 17:48 UTC
    Hello Harald,

    I believe the code by AnonymousMonk from Jan 19 2025 19:07, with text "Adjusting cells height per row and, thus, simulating a table.." is the best approach if you want to use the packer geometry. Said that, I don't think that there's anything wrong in counting pixels; the code might be less elegant, that's true, but gives you all the flexibility when a geometry manager cannot do what you want. It comes down basically to knowing the height() of your first $label and first $input and just go all the way down from there. If you are concerned about the various font heights and how this will look like with other widgets with sizes not related to the default font (f ex an image that is always 100x100), you indeed can test that with Prima::Stress, and you can also use the .designScale() property and safely design your window layout the way you see it on your own screen - just find out what is your default widget font width and height and set these in the .designScale() property.

    There's another thing though that I think can be tricky, multiline labels. Labels are flexible and can both adapt your text in a given size, and adapt their sizes after the text - you would most probably need .autoWidth and .wordWrap.

    But I must admit that neither of these solution is as elegant as html's table, or Tk::grid for that matter. If there is interest, I might just add this feature to Prima, like I did back then with the placer and the packer.

    Hope that makes sense

      I believe the code by AnonymousMonk from Jan 19 2025 19:07, with text "Adjusting cells height per row and, thus, simulating a table.." is the best approach if you want to use the packer geometry.

      Indeed, that's probably what I will do. I might still keep my original widget hierarchy and adjust cell widths per column instead of cell heights per row, though, because adding variables is easier that way (add one row instead of one cell in every row). It is also closer to the HTML table model.

      All that said: It is neither urgent nor important. I was just hoping that the problem has already been solved by someone else.

      Thank you a lot for maintaining Prima!

Re: Prima: How to get a "table" layout?
by GrandFather (Saint) on Jan 17, 2025 at 06:05 UTC

    Would you like to show us some short example code that we can run and play with? I'm sure I could whip something up, but I'm too lazy.

    Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
      Sure:
      use 5.032; use Prima qw(Application Label InputLine); my $main = Prima::MainWindow->create( text => 'Configuration', backColor => cl::White, menuItems => [ ['~File' => [['~Quit' => 'Ctrl+Q', '^Q', sub { $::application->close } ], ], ], ] ); my $fps = $main->insert( Widget => pack => { side => 'top' }, ); $fps->insert( Label => text => 'Refresh rate: ', pack => { side => 'left' }, ); $fps->insert( InputLine => text => '60', pack => { side => 'left' }, ); $fps->insert( Label => text => 'FPS', pack => { side => 'left' }, ); my $res = $main->insert( Widget => pack => { side => 'top' }, ); $res->insert( Label => text => "Spectral resolution", pack => { side => 'left' }, ); $res->insert( InputLine => text => '10', pack => { side => 'left' }, ); $res->insert( Label => text => 'Hz', pack => { side => 'left' }, ); my $limit = $main->insert( Widget => pack => { side => 'top' }, ); $limit->insert( Label => text => "Show spectrum up to", pack => { side => 'left' }, ); $limit->insert( InputLine => text => '5000', pack => { side => 'left' }, ); $limit->insert( Label => text => 'Hz', pack => { side => 'left' }, ); Prima->run;

        I had a quick look over the weekend, but couldn't get Prima to install against Strawberry Perl. Not mission critical for me, so I didn't chase it down.

        Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
          A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Prima: How to get a "table" layout?
by Anonymous Monk on Jan 19, 2025 at 17:07 UTC

    Adjusting cells height per row and, thus, simulating a table isn't too difficult, but perhaps what follows has its own hidden drawbacks. Thanks for reminding about Prima and GUIs in general, nice time it was.

    use strict; use warnings; use feature 'say'; use Prima qw(Application Label InputLine); # use Prima::Stress; my $main = Prima::MainWindow-> create( text => 'Configuration', backColor => cl::White, menuItems => [ ['~File' => [['~Quit' => 'Ctrl+Q', '^Q', sub { $::application->close } ], ], ], ] ); $main-> hide; my $table = $main-> insert( Widget => pack => { side => 'top' }, ); my @cols = map $table-> insert( Widget => pack => { side => 'left', anchor => 'n' }, ), 0 .. 1; # let's pretend columns do have different scale $cols[ 1 ]-> font({ size => 5 + $cols[ 0 ]-> font-> size }); for ( 0 .. 9 ) { my $label = $cols[0]-> insert( Label => text => substr( 'Label 1234567890', 0, 7 + $_ ) . ': ', pack => { side => 'top', anchor => 'e' }, ); my $input = $cols[1]-> insert( InputLine => text => '12345', pack => { side => 'top' }, ); my $h_L = $cols[0]-> height; my $h_IL = $cols[1]-> height; if ( $h_L > $h_IL ) { $input-> pack( pady => $h_L - $h_IL ) } else { $label-> pack( pady => $h_IL - $h_L ) } } $main-> show; Prima-> run;
      Adjusting cells height per row and, thus, simulating a table isn't too difficult,

      That's true. It isn't too difficult, and I'll probably end up with something like that. I had a vague hope that there was a way to do it without running pack twice.

      Also, thanks for remninding me of the hide> - show trick which makes sure that users don't see Prima doing the packing!

Re: Prima: How to get a "table" layout?
by etj (Priest) on Jan 20, 2025 at 16:26 UTC
Re: Prima: How to get a "table" layout?
by Marshall (Canon) on Jan 19, 2025 at 08:54 UTC
    I haven't used Prima and my Perl version is so ancient that I couldn't easily get that library running on my machine. I have constructed a Tk way of doing this as shown below.

    In Tk there is a "Frame" widget which usually has invisible borders. Things inside of a Frame widget get grouped. In the example below, there is a text label on the left; all of them are stuffed into a Frame. And then within that Frame, they are right justified and anchored to the east. The Frame will expand to accommodate the longest label.

    In Prima, I see that there is a FrameSet gizmo. This may get you similar function to a Tk Frame.

    Run this and let us know how close it is to your requirements.

    Added:
    I did set one width manually, the input field width, I forget what the units are but you can play around with it.

    se warnings; use strict; use Tk; my $mw = MainWindow->new(); $mw->configure(-title=> "Some Title"); $mw->minsize(qw(700 200)); my $mainDataFrame = $mw->Frame()->pack(-side=>'top', -fill=>'x'); my $labelFrame = $mainDataFrame->Frame->pack(-side=>'left'); my $dataEntryFrame = $mainDataFrame->Frame->pack(-side=>'left'); my $unitsFrame = $mainDataFrame->Frame->pack(-side=>'left'); my $refreshRate; my $spectralRes; my $showSpectrumHz; my @tableConfig = ( ['Refresh rate:','60',\$refreshRate,'FPS'], ["Spectral resolution",'10',\$spectralRes,'Hz'], ["Show spectrum up to",'5000',\$showSpectrumHz,'Hz +']); for my $rowRef ( @tableConfig ) { $labelFrame->Label(-text => "$rowRef->[0]", -anchor=>'e', -justify => 'right')->pack(-side=>'top', -fill +=> 'x'); my $dataRef = $rowRef->[2]; # put default value into textvariable $$dataRef = $rowRef->[1]; $dataEntryFrame->Entry(-width => 8, -textvariable => $dataRef, )->pack(-side =>'top', -fill => 'x'); $unitsFrame->Label(-text => "$rowRef->[3]", -anchor=>'w', -justify => 'left')->pack(-side=>'top', -fill = +> 'x'); } MainLoop();

      Thanks for that example, but sorry, it is not what I am looking for. The Tk layout has nicely aligned columns, but now the lines are misaligned: A Tk::Entry is slightly higher than a label (same for Prima). It is visible with three lines, but becomes really bad for a realistic number of configuration variables and off by a whole line for ~20 variables.

      BTW: Prima::FrameSet is a different thing. In Prima I use plain Prima::Widget for containers.

        In Tk I would use the 'grid' geometry manager, like the following example.

        I don't have Prima on my machine, but I noticed in CPAN that there is a Prima::Grid. Perhaps it is similar.

        #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11163718 use warnings; use Tk; my $mw = MainWindow->new(); $mw->configure(-title=> "Configuration"); my ($refreshRate, $spectralRes, $showSpectrumHz); my @tableConfig = ( ['Refresh rate:','60',\$refreshRate,'FPS'], ["Spectral resolution",'10',\$spectralRes,'Hz'], ["Show spectrum up to",'5000',\$showSpectrumHz,'Hz +']); $mw->Button(-text => 'Done', -command => sub{ $mw->destroy }, )->pack(-side => 'bottom', -fill => 'x'); my $table = $mw->Frame->pack; my $row = 1; for ( @tableConfig ) { my ($label, $value, $variable, $units) = @$_; $table->Label(-text => $label, )->grid(-row => $row, -column => 1, -sticky => 'e'); $$variable = $value; $table->Entry(-textvariable => $variable, -width => 8, -justify => 'right', )->grid(-row => $row, -column => 2, -sticky => 'ew'); $table->Label(-text => $units, )->grid(-row => $row, -column => 3, -sticky => 'w'); $row++; } MainLoop(); use Text::ASCIITable; my $t = Text::ASCIITable->new({ headingText => 'Configuration' }); $t->setCols(qw(Item Value Units)); $t->addRow( [ map [ $_->[0], ${$_->[2]}, $_->[3] ], @tableConfig ] ); print $t;
        How interesting. This can be one of the problems with GUI's. On My machine, all lines look just fine even with 20 rows on my display. I am unable to reproduce your symptoms. This perhaps could have something to do with the font's that your machine is using? Is anybody else here able to reproduce Haj's symptom?

        Added: I am using default fonts. Are you running my code verbatim as shown? This could be an issue if you are using UTF-8 or unicode. You could try explicitly configuring the font and font size for each label and entry widget. These widgets are designed with exactly this "label: Entry"side by side usage in mind. My lines line up to the exact pixel. There are other ways to do this. In Tk, there is TableMatrix which can produce a spreadsheet like appearance. I've used that with 2,000 rows.

        I am using 64 bit Perl 5.24 on Win10.