Is there a callback in PJSR when there is 'no more pending window events event?.
There is no such callback, mainly because it cannot be implemented (in a realistic way).
There are much better solutions for the problem that you are facing. One of them is asynchronous event handling. This has been implemented, for example, in Georg's CanonBandingReduction script.
Consider the following script:
#include <pjsr/NumericControl.jsh>
#include <pjsr/FrameStyle.jsh>
/*
* Defines the rate of calls to processEvents(). This value is somewhat
* critical:
*
* - Too low of an event check rate will slow down the process too much.
*
* - Too large of an event rate won't consume GUI events as quickly as needed
* to keep the GUI responsive.
*/
#define EVENT_CHECK_RATE 256
function AsynchronousPolarTransformDialog()
{
this.__base__ = Dialog;
this.__base__();
this.startingAngle = 0;
this.image = new Image;
this.abort = false;
this.busy = false;
this.terminate = false;
/*
* Transformation of an image from Cartesian to polar coordinates.
* Code adapted from the PolarCoordinates example script.
*/
this.cartesianToPolar = function( image )
{
// Gather working parameters
let n = image.numberOfChannels;
let w = image.width;
let h = image.height;
let w2 = w/2;
let h2 = h/2;
let r0 = Math.sqrt( w2*w2 + h2*h2 ); // semi-diagonal
// Total work
let N = n*h;
// Create a working image to store one channel of the target image
let tmp = new Image( w, h, 1 );
// Reset the rectangular selection to the whole image boundaries
image.resetRectSelection();
// For each channel
for ( let c = 0, status = 0; c < n; ++c )
{
tmp.fill( 0 ); // initialize working data with zeros
// Use a sample iterator for faster pixel access.
tmp.initSampleIterator();
// For each row
for ( let i = 0; i < h; ++i, ++status )
{
// Polar angle for the current row
let theta = this.startingAngle + 360*i/h;
if ( theta >= 360 )
theta -= 360;
theta = Math.rad( theta );
// Sine and cosine of the current polar angle
let stheta = Math.sin( theta );
let ctheta = Math.cos( theta );
// For each column
for ( let j = 0, e = 0; j < w; ++j, tmp.nextSample() )
{
// Radial distance for the current column
let r = r0*j/w;
// Horizontal coordinate on the source image
let x = w2 + r*ctheta;
if ( x >= 0 && x < w )
{
// Vertical coordinate on the source image
let y = h2 - r*stheta;
// Copy the source (interpolated) pixel sample to the
// polar-transformed location on the working image.
if ( y >= 0 && y < h )
tmp.setSampleValue( image.interpolate( x, y, c ) );
}
if ( ++e == EVENT_CHECK_RATE )
{
// Process pending GUI events
processEvents();
// Check for abort condition
if ( this.abort )
break;
e = 0;
}
}
// Check for abort condition
if ( this.abort )
break;
this.infoLabel.text = format( "Computing: %d%%", Math.round( 100.0*status/N ) );
}
// Check for abort condition
if ( this.abort )
break;
// Copy our working data to the channel c of image
image.selectedChannel = c;
image.apply( tmp );
}
image.resetChannelSelection();
// Deallocate our working image
tmp.free();
};
/*
* Asynchronous generation routine.
*/
this.generate = function()
{
// If we are already generating data, request job abortion and return.
if ( this.busy )
{
this.abort = true;
return;
}
// Start of job
this.busy = true;
do
{
this.abort = false;
this.image.assign( ImageWindow.activeWindow.currentView.image );
this.cartesianToPolar( this.image );
}
while ( this.abort && !this.terminate );
// End of job
this.busy = false;
if ( !this.terminate )
{
this.infoLabel.text = "Ready.";
this.renderControl.update();
}
};
this.renderControl = new Control( this );
this.renderControl.setMinSize( 400, 400 );
this.renderControl.onPaint = function()
{
var G = new Graphics( this );
if ( this.parent.busy )
G.eraseRect( this.boundsRect );
else
G.drawScaledBitmap( this.boundsRect, this.parent.image.render() );
G.end();
};
this.angleControl = new NumericControl( this );
this.angleControl.real = false;
this.angleControl.setRange( 0, 360 );
this.angleControl.label.text = "Starting angle:";
this.angleControl.slider.setRange( 0, 360 );
this.angleControl.slider.minWidth = 400;
this.angleControl.setValue( this.startingAngle );
this.angleControl.toolTip = "<p>Starting angle of the Cartesian to Polar transform.</p>";
this.angleControl.onValueUpdated = function( value )
{
this.parent.startingAngle = value;
this.parent.generate();
};
this.infoLabel = new Label( this );
this.infoLabel.frameStyle = FrameStyle_Box;
this.infoLabel.margin = 4;
this.infoLabel.text = "";
this.sizer = new VerticalSizer;
this.sizer.margin = 6;
this.sizer.spacing = 6;
this.sizer.add( this.renderControl, 100 );
this.sizer.add( this.angleControl );
this.sizer.add( this.infoLabel );
// Ensure first-time generation
this.onGetFocus = function()
{
if ( this.image.isEmpty ) // but only for the first time
this.generate();
}
// Ensure unconditional job abortion upon dialog termination
this.onHide = function()
{
this.terminate = this.abort = true;
}
this.windowTitle = "Asynchronous PolarTransform";
this.adjustToContents();
}
AsynchronousPolarTransformDialog.prototype = new Dialog;
function main()
{
console.hide();
var dialog = new AsynchronousPolarTransformDialog;
dialog.execute();
}
if ( ImageWindow.activeWindow.isNull )
throw new Error( "I need an image window to work." );
main();
This script generates a polar transform, which is a relatively computing-intensive image transformation. You can move the slider to change the initial polar angle of the transform, or change the value in degrees directly, while the transform is being computed. Each time you change the angle, the current transform is aborted and a new one starts.
Let me know if this helps.