Wxperl create widget but don't place it - perl

How can I create a widget without it being placed on its parent?
Here's a minimal example.
package MyApp;
use strict;
use warnings;
use Wx;
use base 'Wx::App';
sub OnInit {
my ($self) = #_;
my $frame
= Wx::Frame->new( undef, -1, 'Test', [ -1, -1 ], [ 250, 150 ], );
my $sizer = Wx::GridBagSizer->new( 0, 0 );
my $btn1 = Wx::Button->new( $frame, -1, '1' );
my $btn2 = Wx::Button->new( $frame, -1, '2' );
$sizer->Add( $btn1, Wx::GBPosition->new( 2, 2 ) );
$frame->SetSizer($sizer);
$frame->Show(1);
1;
}
package main;
MyApp->new->MainLoop;
This yields
I want only what is placed in the sizer (button 1) to show.

You can hide things by calling $thing->Show(0). I added:
$btn2->Show(0);
The layout is still kinda funny because the space for the widget is still thereā€”it's just not visible. So, it's still "placed". Maybe you want to create the control somewhere else that you can size on its own.
You have to hide the widget before the call to Layout.
See Hiding Controls Using Sizers

All non top-level windows are created shown by default. If you don't want them to appear on the screen, you need to hide them. The best way to do it is to hide them before actually creating the real window, which can be achieved in C++ by creating the window without giving it any parameters and then calling Create() with the same parameters you would normally use when creating it.
I'm not sure if this is exposed in wxPerl. If it is, something like this
my $btn2 = Wx::Button->new();
$btn2->Hide(); # or $btn2->Show(false)
$btn2->Create($frame, -1, '2' );
should work;
If not, you can still hide it and if you do it before showing the frame, it still won't be visible for the user.

Related

Perl Gtk3::ScrolledWindow that contains more than one Gtk3::TreeView child. How do you disable child from scrolling to the top when selecting a row?

I am writing a program in Perl using Gtk3. I have a left sidebar (not using any sidebar widgets) that contains multiple tree views.
I it setup like this:
my $sidebarscrollarea = Gtk3::ScrolledWindow->new( undef, undef );
my $sidebarlabelaccounts = Gtk3::Label->new("Accounts");
$sidebarlabelaccounts->set_halign('GTK_ALIGN_START');
my $sidebarlabelincome = Gtk3::Label->new("Income Envelopes");
$sidebarlabelincome->set_halign('GTK_ALIGN_START');
my $sidebarlabelexpense = Gtk3::Label->new("Expense Envelopes");
$sidebarlabelexpense->set_halign('GTK_ALIGN_START');
# *_create_model() builds the models
my $account_tstore = account_create_model();
my $income_tstore = envelope_create_model();
my $expense_tstore = envelope_create_model();
# populate the models with another subroutine
populate_models();
my $accountslist = Gtk3::TreeView->new();
$accountslist->set_model($account_tstore);
my $incomelist = Gtk3::TreeView->new();
$incomelist->set_model($income_tstore);
my $expenselist = Gtk3::TreeView->new();
$expenselist->set_model($expense_lstore);
# Add columns to model and view via view ( Gtk3::TreeView )
account_add_columns($accountslist);
envelope_add_columns($incomelist);
envelope_add_columns($expenselist);
my $sidebarbox = Gtk3::Box->new('vertical',1);
$sidebarbox->set_border_width(5);
$sidebarbox->pack_start($sidebarlabelaccounts,0,0,5);
$sidebarbox->pack_start($accountslist,0,6,5);
$sidebarbox->pack_start($sidebarlabelincome,0,0,5);
$sidebarbox->pack_start($incomelist,0,6,0);
$sidebarbox->pack_start($sidebarlabelexpense,0,0,5);
$sidebarbox->pack_start($expenselist,0,6,0);
$sidebarscrollarea->add($sidebarbox);
The envelopeslist is very long. When I click a row from that list that is toward the bottom of the window, it scrolls so that the envelopes list at the top of the window. I do not want it to move anywhere regardless of where I click a row. Thanks for your help. I am new to all of this.
Simply adding:
$expenselist->set_can_focus(FALSE);
solves my problem.

wxPerl: add component which resizes automatically when parent frame gets resized

I am relatively new to Perl and I am using wxPerl to create a GUI application. Now, I want to add a Panel into a Frame, possibly using a sizer so that the panel resizes automatically as the frame gets resized.
So here's what I got:
(1) I have to use a BoxSizer, which stretch components in one direction.
(2) I have to pass parameters in the Add subroutines to stretch components in another direction.
I wrote the following code:
package Main;
use Wx;
use parent 'Wx::App';
sub OnInit {
my $frame = Wx::Frame->new(undef, -1, "SimpleCalc ".$Information::VERSION_NO, [-1,-1], [-1,-1]);
my $centerPanel = Wx::Panel->new($frame, -1, [-1,-1], [-1,-1]);
#set red background
$centerPanel->SetBackgroundColour(Wx::Colour->new(255,0,0));
my $frameSizer = Wx::BoxSizer->new(wxHORIZONTAL);
$frameSizer->Add($centerPanel, 1, 0, 0);
$frame->SetSizer($frameSizer);
$frame->Center();
$frame->Show(1);
return 1;
}
my $app = Main->new;
$app->MainLoop;
The unwanted result:
What I want is to stretch the red panel in both (horizontal and vertical) direction, or in short, I want something similar to BorderLayout in Java.
According to some online tutorials, I tried to replace $frameSizer->Add($centerPanel, 1, 0, 0); with
$frameSizer->Add($centerPanel, 1, wxEXPAND, 0);, but the script doesn't run. An error occurs saying that it is unable to resolve overload for Wx::Sizer::Add(Wx::Panel, number, scalar, number). I also tried $frameSizer->Add($centerPanel, 1, 0, 0, wxEXPAND);, but the frame obtained is exactly the same as the frame in the image.
Is it possible to have something similar to Java's BorderLayout in wxPerl? Thanks in advance.
P.S. I know there is a duplicate, but there are no concrete answers...
Update
In case you weren't aware, the default sizer for any child window will make it fill its available space, so to achieve the effect you're asking for all you need is this
use strict;
use warnings;
package Information;
our $VERSION_NO = 9.99;
package Main;
use Wx qw/ :colour /;
use parent 'Wx::App';
sub OnInit {
my $frame = Wx::Frame->new(undef, -1, "SimpleCalc $Information::VERSION_NO");
my $centerPanel = Wx::Panel->new($frame);
$centerPanel->SetBackgroundColour(wxRED);
$frame->Center;
$frame->Show;
return 1;
}
my $app = Main->new;
$app->MainLoop;
Original
It would have helped you a lot if you had use strict and use warnings in place! I and several others have to endlessly encourage people to do that but it seems sometimes that the message will never get across. Please try to make a habit of adding these statements to the top of every Perl program you write, and help us to spread the word
There are two things preventing your program from working
The value wxHORIZONTAL is undefined because you haven't imported it from Wx, so you are passing a value of zero to Wx::BoxSizer->new without any warning being raised
You have used a value of zero for the third parameter to $frameSizer->Add, which prevents the panel from expanding transversly to the direction of the sizer. You need wxEXPAND in there to enable it, and you will also need to import the value of that constant of course
Here's a rewrite of your code that fixes these problems, and also takes advantage of the defaults that will be used for missing parameters. I've also used wxRED instead of creating a new Wx::Colour object. I had to set a value for $Information::VERSION_NO too
This code works as you expected
use strict;
use warnings;
package Information;
our $VERSION_NO = 9.99;
package Main;
use Wx qw/ :sizer :colour /;
use parent 'Wx::App';
sub OnInit {
my $frame = Wx::Frame->new(undef, -1, "SimpleCalc $Information::VERSION_NO");
my $centerPanel = Wx::Panel->new($frame);
$centerPanel->SetBackgroundColour(wxRED);
my $frameSizer = Wx::BoxSizer->new(wxHORIZONTAL);
$frameSizer->Add($centerPanel, 1, wxEXPAND);
$frame->SetSizer($frameSizer);
$frame->Center;
$frame->Show;
return 1;
}
my $app = Main->new;
$app->MainLoop;
output
Fixed WxWidgets screen http://bit.ly/1JNrrEL

perl tk listbox, detect when focus is lost via mouse

I have two listboxes in a small perl/tk script. When I click on one, the other "loses focus" and the clicked one "gains" it. I put that in quotes because unfortunately these events do not trigger "<FocusIn>" or "<FocusOut>". Using the keyboard, ie, the tab key, does trigger these. I have also tried <Enter>/<Leave> and <B1-Enter>/<B1-Leave> as well as <<ListboxSelect>> but none of these achieve what I need. I listed the available events to be triggered, but most are keyboard related.
What I need is to disable a Button when the second ListBox loses that focus (ie, when the first ListBox is clicked on), and enable it when it gains it via the mouse. So how do I do this?
Ok, I found an acceptable solution for this:
my $tmp = ref $my_listbox;
$my_listbox->bind($tmp, '<<ListboxSelect>>', sub { &listbox_bind; } );
sub listbox_bind
{
my ($self) = #_;
if ($self == $my_listbox)
{ $my_button->configure( -state => 'normal' ); }
else
{ $my_button->configure( -state => 'disabled' ); }
}
hope that helps someone out there.

How to clear and refresh a panel in Wx?

I'm building a news reader application and I have a wxScrolledWindow in which I show the news. However, I have categories and when one is clicked, I want to update this panel with the current categorie's news. I achieved that using DeleteChildren on the wxScrolledWindow, but this doesn't seem to work very correctly.
The problem is that there's blinking while the news are regenerating, and also the scrollbars don't appear unless I strech the whole window. Also, sometimes unless I do this manual resizing the news doesn't show. I tried with refresh but it's still the same. Here my code:
our ($self);
sub new {
my ($class, $parent_window) = #_;
$self = $class->SUPER::new($parent_window, -1);
$self->SetScrollRate(10, 10);
my #news = (...);
regenerate_news_list(#news);
return $self;
}
sub regenerate_news_list($) {
my (#news) = #_;
$self->DestroyChildren();
my $vbox = Wx::BoxSizer->new(wxVERTICAL);
for my $news_item (#news) {
my $news_panel = Wx::Panel->new($self, wxID_ANY);
my $news_sizer = Wx::BoxSizer->new(wxVERTICAL);
my $news_title = Wx::HyperlinkCtrl->new($news_panel, wxID_ANY, $news_item{'title'}, $news_item{'url'}, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
my $news_description = Wx::StaticText->new($news_panel, wxID_ANY, $news_item{'description'}, wxDefaultPosition);
$news_description->Wrap(560);
$news_sizer->AddSpacer(5);
$news_sizer->Add($news_title, 0);
$news_sizer->AddSpacer(5);
$news_sizer->Add($news_description, 0);
$news_sizer->AddSpacer(5);
$vbox->Add($news_panel, 0, wxEXPAND|wxALL);
}
$self->SetSizer($vbox);
$vbox->Fit($self);
$self->Refresh();
}
Call $self->Freeze() before DestroyChildren() to stop redraws before you do the updates, then call Thaw() when you're done, after Refresh(). It should be much faster and there won't be any flickering.
Use two panels, one which you show to the user, one where you prepare the new display. When the new display is complete, show the new panel and hide the old one. Alternate.

Perl Curses::UI

I am trying to use the library Curses:UI from http://search.cpan.org/dist/Curses-UI/
to build a UI on linux karmic.
I can create a simple user interface e.g.:
#!usr/usr/bin/perl
use strict;
use Curses;
use Curses::UI;
$ui = new Curses::UI(-color_support=>1,-clear_on_exit=>1,-intellidraw=>1);
my $window = $ui->add('window', 'Window',-intellidraw=>1);
my $message = $window->add(-text=>"Hello!",-intellidraw=>1);
$window->focus();
$ui->mainloop();
Question: I need some way to communicate informatio to the UI i.e. I have a loop which will wait for message to come and change the text in window. Once this message comes a popup will be displayed.
Attempt:
my $ui = new Curses::UI(-color_support=>1,-clear_on_exit=>1,-intellidraw=>1);
my $window = $ui->add('window', 'Window',-intellidraw=>1);
my $message = $window->add(-text=>"Hello!",-intellidraw=>1);
pseudocode
while(true) #implemented a function to wait
{
popup($window->text("Hello how are you?"));
}
$window->focus();
$ui->mainloop();
Problem: The above does not work. I am given a dark screen where my message is displayed. I have read the documentation and when I relocate : $ui->mainloop() above the while loop I am given the user interface but now nothing communicates to the window.
Coincise Question: I need some way of displaying the user interface wait for inputs and display messages.
Could anyone please help me on this? Thank you!
I would just replace $ui->mainloop() with my own eventloop where my own stuff is updated aswell.
For reference $ui->mainloop() is implemented as follows:
sub mainloop {
my ($self) = #_;
# Draw the initial screen.
$self->focus(undef, 1); # 1 = forced focus
$self->draw;
doupdate();
# Inifinite event loop.
while (1) { $self->do_one_event }
}
So I would simply add your own tick() function to the while loop.