Using a ListCtrlPrinter
A ListCtrlPrinter takes an ObjectListView
(or plain old wx.ListCtrl
) and
effortlessly turns it into a pretty report. With only two lines of code, you can produce a
nice report like this:
When would I want to use it?
Your shiny new application is finished to perfection — and it’s a week ahead of schedule
(we’re imagining here, so let’s be completely unrealistic). After your demonstration to
The Management, the CEO says, “It’s great! I love it! But I want to have all that
information as a report. And I want to be able to fiddle with the columns in the report,
resize them, any way I want. It has to be sortable, and groupable, just like we can see on
the screen there. And of course, we have to be able to print preview it before we print
it. And can we have it finished by tomorrow?” You consider briefly whether you should
mention the PrintScreen key, but you doubt he would be enthused with that solution.
Management love reports. Programmers aren’t so keen. They often appear as an afterthought
to the requirement. Yet producing nice looking reports is not a trivial task. If you using
other languages, you can buy a commercial product (if you have Blizzard’s budget), but
with wxPython, there are not that many options. You normally have to write it all
yourself, which can be frustrating. wx’s printing scheme can cause even strong
programmers to go weak in the knees – even with Robin’s wonderful book for reference.
For a lazy and vain programmer like myself, what I really want is something that takes no
effort to implement, yet produces wonderful results. I want to be able to go back to my
CEO the same day, show him the nice looking reports that do exactly what he wanted, and
then remind him about my overdue raise.
The ListCtrlPrinter is designed to be just such a solution.
How do I use it in my project?
As always, the goal is for this to be as easy to use as possible. A typical
usage should be as simple as:
printer = ListCtrlPrinter(self.myListCtrl, "My Report Title")
printer.PrintPreview()
This code create a new ListCtrlPrinter
, telling it what ListCtrl
it should print
and what the report should be titled. Then a print preview of the report is opened.
A more complete example might look like this:
printer = ListCtrlPrinter(self.lv, "Graviton Collision Statistics")
printer.ReportFormat = ReportFormat.Normal("Lucida Bright")
printer.ReportFormat.IsColumnHeadingsOnEachPage = True
printer.PageHeader("Monthly Bad Science Report")
printer.PageFooter("Bright Ideas Software", "%(date)s", "%(currentPage)d of %(totalPages)d")
printer.Watermark("Work hard!")
printer.PrintPreview(self)
Primary commands
The ListCtrlPrinter
has commands that match the normal printing commands, which should
be hooked into the menu structure of your application.
PageSetup()
Opens the normal “Page Setup” dialog that allows the user to choose the
page orientation and margins, as well as choose the printer for output.
PrintPreview()
Open a print preview dialog, from which the user can also print the report.
Print()
Opens the “Print” dialog that lets the user choose which printer, the pages and
other details before printing the report.
Working in a structured environment
A report consists of several blocks, each of which stacks vertically. The structure of a report
is like this:
The PageHeader and PageFooter repeat on each page (obviously). The ColumnHeader
repeats on each page, depending on the ReportFormat options.
Controlling the appearance
The formatting of a report is controlled completely by the ReportFormat
object of the
ListCtrlPrinter
. To change the appearance of the report, you change the settings in
this object.
These properties control the appearance of the report as a whole:
IncludeImages
Should images from the ListCtrl
be included in the report?
IsColumnHeadingsOnEachPage
If this is True, the column headers will be repeated at the top of each page.
IsShrinkToFit
If this is True, the report will be shrunk so that all the column of the ListCtrl
can fit within
the width of page.
UseListCtrlTextFormat
If this is True, the format (text font and color) of the rows will be taken
from the ListCtrl
itself, rather than the Cell.BlockFormat
object. Useful if your
ListCtrl
has fancy formatting on the rows that you want to replicate in the printed
version.
As was illustrated above, a report consists of various sections (called “blocks”). Each
of these blocks has a matching BlockFormat
object in the ReportFormat
. To modify
the appearance of a block, you modify its matching BlockFormat
object. So, to
modify the format of the page header, you change the ReportFormat.PageHeader
object.
A ReportFormat
object has the following properties which control the appearance of the matching
sections of the report:
- PageHeader
- ListHeader
- ColumnHeader
- GroupTitle
- Row
- ListFooter
- PageFooter
These properties return BlockFormat
objects, which have the following properties:
CanWrap
If the text for this block cannot fit horizontally, should be wrap to a new line (True)
or should it be truncated (False)?
Font
What font should be used to draw the text of this block
Padding
How much padding should be applied to the block before the text or other decorations
are drawn? This can be a numeric (which will be applied to all sides) or it can be
a collection of the paddings to be applied to the various sides: (left, top, right, bottom).
TextAlignment
How should text be aligned within this block? Can be wx.ALIGN_LEFT, wx.ALIGN_CENTER, or
wx.ALIGN_RIGHT.
TextColor
In what color should be text be drawn?
The blocks that are based on cells (PageHeader, ColumnHeader, Row, PageFooter) can also
have the following properties set:
AlwaysCenter
Will the text in the cells be center aligned, regardless of other settings?
CellPadding
How much padding should be applied to this cell before the text or other decorations
are drawn? This can be a numeric (which will be applied to all sides) or it can be a
collection of the paddings to be applied to the various sides: (left, top, right,
bottom).
GridPen
What Pen will be used to draw the grid lines of the cells?
In addition to these properties, there are some methods which add various decorations to
the blocks:
Background(color=wx.BLUE, toColor=None, space=0)
This gives the block a solid color background (or a gradient background if toColor
is not None). If space is not 0, space pixels will be subtracted from all sides
from the space available to the block.
Frame(pen=None, space=0)
Draw a rectangle around the block in the given pen
Line(side=wx.BOTTOM, color=wx.BLACK, width=1, toColor=None, space=0, pen=None)
Draw a line on a given side of the block. If a pen is given, that is used to draw the
line (and the other parameters are ignored), otherwise a solid line (or a gradient
line is toColor is not None) of width pixels is drawn.
Can’t you just show me what these things do?
Understanding the process
Use The Source Luke (at least until I write this part of the docs)
Other things to be aware of
- A
ListCtrlPrinter
only works on ListCtrls that are in report view. It will ignore
any ListCtrl that is in any other view.
- You can set the left Padding of the ColumnHeader format and Row format to different
values. This results in the column headers not lining up with the rows. This should be
understood as a feature.
- For reasons that are still not clear to me, images that come from BMP files will not
print on some (most?) printers. Images that come from PNG and other formats work fine.
- The
ListCtrlPrinter
is not designed to be general purpose reporting solution. There are
no running totals, macro language, or ODBC data sources. It just prints ListCtrls.