Perl TK with Proc::Background proper usage (keep GUI active?) - perl

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ā€¦)

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.

Protractor: Wait for angular to finish animation

I have a slider menu on my page which appear after an interval of time through an animation. I would to know how can I handle click action without using browser.wait(condition, timeout) since depending on the network traffic to fetch data in a remote database.
The page rendering can take long time thus protractor is triggering timeouts error. I have been trying as bellow to use jQuery in order to wait for all transition and animation event to finish, but it is still not working.
BasePage.prototype.clickMenuButton = function(menuName) {
var link = element(by.linkText(menuName));
browser.ignoreSynchronization = true;
browser.executeScript("jQuery('html > *').one('animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd', function(){})").then(
function() {
link.click();
},
function (error) { console.log("Error : ", error); }
);
}
Does anyone know a way to wait for angularjs to finish rendering and animation without using browser.wait and timeouts?
It's a Protractor "incompatibility" with Angular JS. Most of the people calls this a bug, but nobody is sure about this.
However, if this issue occurs, the Angular and Protractor teams recommends in order to fix the $timeout awaits, to replace the $timeout function with a $interval function. They're almost the same, they both achieve the same thing.
$interval function fixes the protractor $timeout issue.
Official documentation states that You should use the $interval for anything that polls continuously (introduced in Angular 1.2rc3).
In this case, $timeout it's not a good option for Front End Developers(AngularJS developers) neither for JavaScript Automation Developers(Protractor developers).
Another option would be to either use a timeout in your test application, either turn the browser sync off.
Here's my example for ignoreSynchronization function.
browser.ignoreSynchronization = true;
browser.wait(toast.isPresent(), 3000).then(
function(arr) {
if (arr) {
toast.getText().then(function(txt) {
// console.log(txt);
expect(txt).toContain(caption);
});
} else {
toast.getText().then(function(txt) {
console.log('current toast: ' + txt);
console.log('modal not catched. see bug with $timeout \n ' +
caption + " \n" +
"==> Error! NOT PRESENT");
});
}
}
);
browser.ignoreSynchronization = false;
You could also have a look over their official github issue tracker. This page states that if you have a $timeout function, that the developer cannot change this(this seems a bit unlikely, and seems like one of the bad QA/dev relationship) you should use the ignoreSynchronization function.
The main conclusion is that, in order to fix protractor $timeout awaits for Angular the Front End developer should easily replace $timeout with $interval.
Let me know if it helps.

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.

InDesign CS5 Script: How can I close all modal dialog windows in a document?

When a document does not have any modal dialog window messages, app.activeDocument.close(SaveOptions.no) works fine.
However, I have some InDesign documents that do have such windows appear, showing error messages about links that need to be updated, or incorrect styles. The above statement won't work in this case, as the windows prevent the script from accessing the document.
So, is there a way to iterate through all of the modal-dialogs in the active document? Here is what I have tried so far, which is not working:
if(xmlFile == "")
{
//alert("There is no linked XML file in this document,\n\ttry a different document.");
for(var i = 0; i < app.activeDocument.Windows.length; i++)
{
app.activeDocument.Windows[i].close();
}
app.activeDocument.close(SaveOptions.no);
exit();
}
Ok, so the "user interaction level" of the application needs to be changed to "NEVER_INTERACT" to ignore all modal dialog windows. Here is the modified code, which is now working:
if(xmlFile == "")
{
alert("There is no linked XML file in this document,\n\ttry a different document.");
// the original interaction and warning settings of the application
var oldInteractionPrefs = app.scriptPreferences.userInteractionLevel;
// prevent interaction and warnings from interrupting script
app.scriptPreferences.userInteractionLevel = UserInteractionLevels.NEVER_INTERACT;
// close the active document without saving
app.activeDocument.close(SaveOptions.no);
// reset interactions to original settings
app.scriptPreferences.userInteractionLevel = oldInteractionPrefs;
exit();
}
Did you try it ?
app.activeDocument.windows.everyItem.close(SaveOptions.no);

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.