Jason S
Jason S

Reputation: 189686

How to display a print progress dialog in Swing?

If I do this in my class that implements Printable,

PrinterJob job = PrinterJob.getPrinterJob();
job.setJobName( /* some name */ );
Book book = new Book();
book.append(this, new PageFormat());
job.setPageable(book);
boolean ok = job.printDialog();
if (ok) {
    try {
        job.print();
    } 
    catch (PrinterException ex) {
        ex.printStackTrace();
    } 
}

it works, but my Printable class gets print() called twice. (presumably for resource allocation purposes by the printer driver)

How can I display a progress bar properly? In my case, I know how many sub-printing tasks need to execute, but if I try to display it, the progress bar will proceed from start to finish twice, which is horrible feedback for a user. And I'd rather not display an indeterminate progress bar....

What should I do?


clarification: I'm printing a bunch of plots on a page. They're somewhat intricate and take a while to print, so I might know that there's 10 objects to render, so I'd like to have my progress bar go from 0 to 10, and increment it after I render each of the 10 objects. (Or, even better, go from 0 to 20, assuming I could somehow know the page was being rendered twice.)

Upvotes: 4

Views: 1219

Answers (2)

Jason S
Jason S

Reputation: 189686

I gave up on the idea of trying to predict or distinguish the # of calls to print().

@Atreys' answer was promising, but the only difference that I could use w/o reflection or importing private sun.* classes was that sun.print.PeekGraphics implements java.awt.image.ImageObserver, whereas sun.awt.windows.WPathGraphics does not. (getClass().getName() counts as reflection in my book)

What I ended up doing was to use my progress bar for each individual call to Printable.print(), e.g. reset the progress to the beginning and advance it for each substep of print.

To keep the users from complaining that the progress bar shows progress twice, I have a label that is changed to "Pass #{k}" whenever print() is called, where {k} is a counter that is incremented each time.


p.s. The proper way to cancel a print job from within Printable.print() is a little tricky. Throwing PrintAbortException doesn't seem to work correctly, as it ends the call to print() but the printer driver keeps going, despite the fact that the Printable javadoc says this:

If the Printable object aborts the print job then it throws a PrinterException.

Instead, you have to cast the print() method's Graphics parameter as a PrintGraphics object, and then call PrintGraphics.getPrinterJob().cancel():

if (graphics instanceof PrinterGraphics)
{
    PrinterGraphics pg = (PrinterGraphics) graphics;
    pg.getPrinterJob().cancel();
}
else
{
    // this is kind of an unexpected circumstance
    // not sure if we should do this or throw IllegalStateException()
    PrinterAbortException pae = new PrinterAbortException();
    pae.initCause(/* something */);
    throw pae;
}

Upvotes: 1

Atreys
Atreys

Reputation: 3761

Looking at the arguments supplied to the print method, the first time it's called (for me at least), the graphics is a PeekGraphics object, and the second time is a WPathGraphics one. On my system, RasterPrinterJob is creating a PeekGraphics to get information about the print job before setting up the graphics for the real print job.

Depending on how you're updating your progress bar, you might be able to just check that the graphics argument is not a PeekGraphics before you give it a tick.

Upvotes: 3

Related Questions