Perl Curses::UI - perl

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.

Related

Vala force refresh progressbar

I've made an aplication with vala where at some point I have to process a lot of files. I've created a window to choose a folder and then I get the paths of files and make some proces on them.
I've added a progress bar to this window to show how many files have been processed but for some reason it remains always empty.
Code about window:
this.files_window = new Gtk.Window();
this.files_window.window_position = Gtk.WindowPosition.CENTER;
this.files_window.destroy.connect (Gtk.main_quit);
// VBox:
Gtk.Box vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 5);
this.files_window.add (vbox);
// Buttons to open and close
Gtk.Button cancel = new Gtk.Button.with_label ("Cancel");
Gtk.Button select = new Gtk.Button.with_label ("Select");
vbox.add (select);
vbox.add (cancel);
// proogress bar
this.progress_bar = new Gtk.ProgressBar();
vbox.add(this.progress_bar);
// conect select to method do_stuff
select.clicked.connect (do_stuff);
this.files_window.show_all ();
As you can see, I connect the button "select" to the method "do_stuff" where I get the paths of selected files and make some process.
I update correctlly the fraction of the progres bar because I've added some prints to know if the value is correct and it is. It's just that the windows is not refreshing, possibly because all the work it is doing with the process of the files. Here is the code about do_stuff() method:
// some proces to get paths of files in the list sfiles
double fraction = 0.0;
this.progress_bar.set_fraction (fraction);
int processed_files = 0;
foreach (string sfile in sfiles) {
do_some_proces_to_file(sfile);
processed_files += 1;
fraction = (double)processed_files/(double)sfiles.length;
this.progress_bar.set_fraction (fraction);
stdout.printf("Real fraction: %f\n", this.progress_bar.get_fraction());
}
The printf shows that the value of the progres bar is being updated but in the window the bar is always empty.
Am I missing something? Is it the correct way to do the progres bar? Should I made another thread to do the stuff?
As #nemequ says, your code is blocking the main loop thread (which handles both user input and scheduling/drawing widget updates), hence it the progress bar is not updated until the method completes.
Using a thread is one way solve the problem, however using threads can lead to a lot of bugs however since it can be difficult to make even simple interactions between threads safe.
An async method avoids this by interleaving the code with the other work being done by the main loop. An async version of your do_stuff() would be pretty straight-forward to write, simply declare it async and put a yield in the for loop somewhere:
public async void do_stuff() {
...
foreach (string sfile in sfiles) {
// all of this is as before
do_some_proces_to_file(sfile);
processed_files += 1;
fraction = (double)processed_files/(double)sfiles.length;
this.progress_bar.set_fraction (fraction);
// Schedule the method to resume when idle, then
// yield control back to the caller
Idle.add(do_stuff.callback);
yield;
}
}
You can then kick it off from your click handler by calling: do_stuff.begin().
Unless there is some relevant code you're not showing, you're blocking the main loop. One option would be to do everything in a thread, and use an idle callback to update the UI. The basic idea is something like:
new GLib.Thread<void*>("file-processor", () => {
foreach (string sfile in sfiles) {
/* do stuff */
GLib.Idle.add(() => {
/* Update progress */
return false;
});
}
return null;
});
Depending on your application you may need to add a mutex to avoid race conditions. You may also need to add some logic for canceling the operation.
A better option might be to use a GLib.ThreadPool. You'd still want to update the UI from an idle callback, but this would allow each task to execute in parallel, which could provide a significant speed-up.
If I were you I'd probably wrap it all up in an async function to keep the API tidy, but you don't really have to.

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

Crossrider : Using mouse position in context menu

We would like to use appAPI.openURL but in place of sending the data.selectedText I woould like to send the text of the element under the mouse. But I can't find the way of getting the mouse position. My idea was to add in the appAPI.ready the following
$().mousemove(function(event) {
myPositionX = event.pageX ;
myPositionY = event.pageY ;
}
And to have two global variable myPositionX and myPositionY which I could access in my background code to transmit as parameters of my URL.
But this doesn't seem to work.
Is what I'm doing crazy?
You'll be pleased to note that your are not crazy but simply missed the selector required to attach the handler to the page. Hence, to make you code work, bind the mousemove handler to the document object per the following tried and tested code:
$(document).mousemove(function(event) {
myPositionX = event.pageX ;
myPositionY = event.pageY ;
});

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 TK with Proc::Background proper usage (keep GUI active?)

I'm trying to build a Toplevel window that will show the progress of a system cmd. I want the GUI to be active (without freezing and "not responding"), so pressing on the "Cancel" button will kill the process, otherwise, when finished, make active the "close" button and disable the "cancel". Following a suggestion to one of my previous questions, I tried to use Proc::Background. The sole way I've found to do it is:
my $proc1;
my $cancel = $toplevel->Button(-text => "Cancel", -command =>sub{$proc1->die;})->pack;
my $close = $toplevel->Button(-text => "Close", -command =>sub{destroy $toplevel;}, -state=>"disabled")->pack;
$proc1 = Proc::Background->new("x264.exe $args");
while ($proc1->alive == 1){
$mw->update();
sleep(1);
}
$cancel->configure(-state=>'disabled');
$close->configure(-state=>'normal');
Is there another, more efficient way to do it (without waiting 1 sec for response)?
Thanks,
Mark.
I use Time::HiRes::usleep.
use Time::HiRes qw(usleep);
while ($proc1->alive == 1){
$mw->update();
usleep(100_000); //0.1 seconds
}
It may be an overkill for this problem, but at some point our UI applications grow and we desperately need to use high resolution timers and asynchronously dispatch and listen events thorough out the application. For this purpose I find the POE framework a great asset.
I particularly use POE with wxWidgets but it is also compatible with Tk:
POE::Loop::Tk
The after method (of any Tk widget) lets you schedule a callback to occur a certain number of milliseconds in the future, and the waitVariable method (you'll need to search in the page) will run the event loop until a variable is set.
my $proc1;
my $cancel = $toplevel->Button(-text => "Cancel", -command =>sub{$proc1->die;})->pack;
$proc1 = Proc::Background->new("x264.exe $args");
my $procDone = 0;
my $timercb = sub {
if ($proc1->alive) {
$toplevel->after(100, $timercb);
} else {
$procDone = 1; # Anything really
}
};
$timercb();
$toplevel->waitVariable(\$procDone) unless ($procDone);
(I'm not sure if this code will work; I don't code very much in Perl these days, so I'm translating what I'd do in another languageā€¦)