ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
# 第十七章 wxPython的打印构架 本章内容 * 用`wxPython`打印 * 创建和显示打印对话框 * 创建和显示页面设置对话框 * 在你的程序中执行打印 * 执行一个打印预览 在第16章中,我们已经关注了`wxPython`的一打印方法:使用`wx.HtmlEasyPrinting`。如果你用该方法打印`HTML`(或某些容易转换为`HTML`的文件)的话,这个方法将会工作的很好,但是要作为一个完善打印办法还是不够的。在`wxPython`中还有一个更为通用的打印构架,你可以用它来打印你想打印的任何东西。基本上,该构架使你能够使用设备上下文和绘制操作来执行打印。你也可以创建打印预览。 本章将讨论该构架中最重要的类:`wx.Printout`,它管理实际的图片部分。打印输出实例可以由一个代表打印机的`wx.Printer`对象或用于打印预览的`wx.PrintPreview`对象来管理。多们也将讨论几个管理与打印相关的数据的类,以及用来显示信息给用户的对话框。 ## 如何用wxPython打印? 我们将以类`wx.Printout`作为开始。首先你要创建你自定义的`wx.Printout`的子类。接着你要覆盖`wx.Printout`的方法以定义你自定义的打印行为。`wx.Printout`有7个你可以覆盖以自定义打印行为的方法。这些方法在一个打印会话过程期间被`wxPython`自动调用。图17.1其中的六个方法,它们被特定的事件触发。在大多数情况下,你不需要全部覆盖它们。 **图17.1** ![](https://box.kancloud.cn/2016-08-21_57b9964835d0c.gif) ### 理解打印输出的生命周期 你通过创建一个你的打印输出对象的实例和一个类`wx.Printer`的实例启动一个打印会话: ``` wx.Printer(data=None) ``` 可选的`data`参数是`wx.PrintDialogData`的一个实例。要开始实际的打印,需要调用`wx.Printer`的`Print(parent, printout, prompt=True)`方法。参数`parent`是父窗口(它被用作对话框的窗口中)。参数`printout`是你的`wx.Printout`实例。如果参数`prompt`为`True`,那么在打印之前,`wxPython`将显示打印对话框,否则不显示。 在`Print()`方法开始后,它调用`wx.Printout`的第一个可被覆盖的方法`OnPreparePrint()`。`OnPreparePrint()`方法在`wx.Printout`实例做任何其它的事之前被确保调用,因此该方法是放置收集你的数据或做那些必须在打印开始之前所要做的计算的一个好的地方。实际的打印使用`OnBeginPrinting()`方法开始,你可以对该方法进行覆盖,以自定主你想要的行为——默认情况下,该方法什么也不做。`OnBeginPrinting()`在整个打印会话中只会被调用一次。 你希望打印的文档的每个单独的拷贝触发对`OnBeginDocument(startPage, endPage)`的一个调用,其中参数`startPage, endPage`告诉`wxPython`打印的起始页和最后一页。这两个参数都应该指定。如果你想覆盖这个方法,那么你必须调用它的基类的方法,因为基类的方法要做一些重要的工作(如调用`wx.DC.StartDoc()`)。在`wxPython`中,你可以使用`base_OnBeginDocument(startPage, endPage)`来调用其父类的方法。如果`OnBeginDocument`返回`False`,那么将取消打印工作。 你最有可能去覆盖的方法是`OnPrintPage(pageNum)`,该方法是你放置关于每一页的绘制命令的地方。参数`pageNum`是要打印的页的页码。在这个方法中,你调用`GetDC()`,`GetDC()`根据你当前的系统平台返回一个适当的设备上下文。对于实际的打印,如果你是在一个微软的`Windows`系统上的话,那么`GetDC()`返回的是类`wx.PrinterDC`的实例。对于其它的系统,返回的是类`wx.PostScriptDC`的实例。如果你是处在一个打印预览操作中,那么对于任何的操作系统,`GetDC()`返回的都是一个`wx.MemoryDC`。一旦你有了设备上下文,你就可以做你想做的设备上下文绘制操作,并且它们将被打印或预览。 在一个文档的副本打印结束后,一个`OnEndDocument()`调用被触发。另外,如果你要覆盖`OnEndDocument()`方法,那么你必须调用其基类的方法`base_OnEndDocument()`。`base_OnEndDocument()`将调用`wx.DC.EndDoc()`方法。当你的所有的副本被打印完后,`OnEndPrinting()`方法被调用,这样就结束了打印会话。 `wx.Printout`还有另一个可被覆盖的方法:`HasPage(pageNum)`。该方法通常需要被覆盖,它被打印架构用于循环控制。如果参数`pageNum`存在于文档中,那么该方法返回`True`,否则返回`False`。 ### 实战打印构架 下面我们将通过一个例子来展示打印构架实际上是如何工作的。这个例子由一个简单的用于打印文本文件的构架组成,并且应用程序让你能够键入简单的文本。图17.2显示了这个应用程序的结果。 **图17.1** ![](https://box.kancloud.cn/2016-08-21_57b9964848fe1.gif) 例17.1显示了我们已经讨论过的打印构架和我们将要接触的打印对话框机制。 **例17.1** **打印构架的一个较长的例子** ``` import wx import os FONTSIZE = 10 class TextDocPrintout(wx.Printout): """ A printout class that is able to print simple text documents. Does not handle page numbers o titles, and it assumes that no lines are longer than what will fit within the page width. Those features are left as an exercise for the reader. ;-) """ def __init__(self, text, title, margins): wx.Printout.__init__(self, title) self.lines = text.split('\n') self.margins = margins def HasPage(self, page): return page = self.numPages def GetPageInfo(self): return (1, self.numPages, 1, self.numPages) def CalculateScale(self, dc): # Scale the DC such that the printout is roughly the same as # the screen scaling. ppiPrinterX, ppiPrinterY = self.GetPPIPrinter() ppiScreenX, ppiScreenY = self.GetPPIScreen() logScale = float(ppiPrinterX)/float(ppiScreenX) # Now adjust if the real page size is reduced (such as when # drawing on a scaled wx.MemoryDC in the Print Preview.) If # page width == DC width then nothing changes, otherwise we # scale down for the DC. pw, ph = self.GetPageSizePixels() dw, dh = dc.GetSize() scale = logScale * float(dw)/float(pw) # Set the DC's scale. dc.SetUserScale(scale, scale) # Find the logical units per millimeter (for calculating the # margins) self.logUnitsMM = float(ppiPrinterX)/(logScale*25.4) def CalculateLayout(self, dc): # Determine the position of the margins and the # page/line height topLeft, bottomRight = self.margins dw, dh = dc.GetSize() self.x1 = topLeft.x * self.logUnitsMM self.y1 = topLeft.y * self.logUnitsMM self.x2 = dc.DeviceToLogicalXRel(dw) - bottomRight.x * self.logUnitsMM self.y2 = dc.DeviceToLogicalYRel(dh) - bottomRight.y * self.logUnitsMM # use a 1mm buffer around the inside of the box, and a few # pixels between each line self.pageHeight = self.y2 - self.y1 - 2*self.logUnitsMM font = wx.Font(FONTSIZE, wx.TELETYPE, wx.NORMAL, wx.NORMAL) dc.SetFont(font) self.lineHeight = dc.GetCharHeight() self.linesPerPage = int(self.pageHeight/self.lineHeight) def OnPreparePrinting(self): # calculate the number of pages dc = self.GetDC() self.CalculateScale(dc) self.CalculateLayout(dc) self.numPages = len(self.lines) / self.linesPerPage if len(self.lines) % self.linesPerPage != 0: self.numPages += 1 def OnPrintPage(self, page): dc = self.GetDC() self.CalculateScale(dc) self.CalculateLayout(dc) # draw a page outline at the margin points dc.SetPen(wx.Pen("black", 0)) dc.SetBrush(wx.TRANSPARENT_BRUSH) r = wx.RectPP((self.x1, self.y1), (self.x2, self.y2)) dc.DrawRectangleRect(r) dc.SetClippingRect(r) # Draw the text lines for this page line = (page-1) * self.linesPerPage x = self.x1 + self.logUnitsMM y = self.y1 + self.logUnitsMM while line (page * self.linesPerPage): dc.DrawText(self.lines[line], x, y) y += self.lineHeight line += 1 if line = len(self.lines): break return True class PrintFrameworkSample(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, size=(640, 480), title="Print Framework Sample") self.CreateStatusBar() # A text widget to display the doc and let it be edited self.tc = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE|wx.TE_DONTWRAP) self.tc.SetFont(wx.Font(FONTSIZE, wx.TELETYPE, wx.NORMAL, wx.NORMAL)) filename = os.path.join(os.path.dirname(__file__), "sample-text.txt") self.tc.SetValue(open(filename).read()) self.tc.Bind(wx.EVT_SET_FOCUS, self.OnClearSelection) wx.CallAfter(self.tc.SetInsertionPoint, 0) # Create the menu and menubar menu = wx.Menu() item = menu.Append(-1, "Page Setup...\tF5", "Set up page margins and etc.") self.Bind(wx.EVT_MENU, self.OnPageSetup, item) item = menu.Append(-1, "Print Setup...\tF6", "Set up the printer options, etc.") self.Bind(wx.EVT_MENU, self.OnPrintSetup, item) item = menu.Append(-1, "Print Preview...\tF7", "View the printout on-screen") self.Bind(wx.EVT_MENU, self.OnPrintPreview, item) item = menu.Append(-1, "Print...\tF8", "Print the document") self.Bind(wx.EVT_MENU, self.OnPrint, item) menu.AppendSeparator() item = menu.Append(-1, "E ", "Close this application") self.Bind(wx.EVT_MENU, self.OnExit, item) menubar = wx.MenuBar() menubar.Append(menu, " ") self.SetMenuBar(menubar) # initialize the print data and set some default values self.pdata = wx.PrintData() self.pdata.SetPaperId(wx.PAPER_LETTER) self.pdata.SetOrientation(wx.PORTRAIT) self.margins = (wx.Point(15,15), wx.Point(15,15)) def OnExit(self, evt): self.Close() def OnClearSelection(self, evt): evt.Skip() wx.CallAfter(self.tc.SetInsertionPoint, self.tc.GetInsertionPoint()) def OnPageSetup(self, evt): data = wx.PageSetupDialogData() data.SetPrintData(self.pdata) data.SetDefaultMinMargins(True) data.SetMarginTopLeft(self.margins[0]) data.SetMarginBottomRight(self.margins[1]) dlg = wx.PageSetupDialog(self, data) if dlg.ShowModal() == wx.ID_OK: data = dlg.GetPageSetupData() self.pdata = wx.PrintData(data.GetPrintData()) # force a copy self.pdata.SetPaperId(data.GetPaperId()) self.margins = (data.GetMarginTopLeft(), data.GetMarginBottomRight()) dlg.Destroy() def OnPrintSetup(self, evt): data = wx.PrintDialogData(self.pdata) dlg = wx.PrintDialog(self, data) dlg.GetPrintDialogData().SetSetupDialog(True) dlg.ShowModal(); data = dlg.GetPrintDialogData() self.pdata = wx.PrintData(data.GetPrintData()) # force a copy dlg.Destroy() def OnPrintPreview(self, evt): data = wx.PrintDialogData(self.pdata) text = self.tc.GetValue() printout1 = TextDocPrintout(text, "title", self.margins) printout2 = None #TextDocPrintout(text, "title", self.margins) preview = wx.PrintPreview(printout1, printout2, data) if not preview.Ok(): wx.MessageBox("Unable to create PrintPreview!", "Error") else: # create the preview frame such that it overlays the app frame frame = wx.PreviewFrame(preview, self, "Print Preview", pos=self.GetPosition(), size=self.GetSize()) frame.Initialize() frame.Show() def OnPrint(self, evt): data = wx.PrintDialogData(self.pdata) printer = wx.Printer(data) text = self.tc.GetValue() printout = TextDocPrintout(text, "title", self.margins) useSetupDialog = True if not printer.Print(self, printout, useSetupDialog) \ and printer.GetLastError() == wx.PRINTER_ERROR: wx.MessageBox( "There was a problem printing.\n" "Perhaps your current printer is not set correctly?", "Printing Error", wx.OK) else: data = printer.GetPrintDialogData() self.pdata = wx.PrintData(data.GetPrintData()) # force a copy printout.Destroy() app = wx.PySimpleApp() frm = PrintFrameworkSample() frm.Show() app.MainLoop() ``` 例17.2中的打印输出类能够打印简单的文本文档,但是不能处理页码或标题,并且它创假设了行的宽度没有超过页面的宽度。对于例子的完善就留给读者作为一个练习。 上面最重要的代码片断是在构架的`OnPreparePrinting()`和`OnPrintPage()`以及示例窗口的`OnPrint()`方法中。 ### 使用wx.Printout的方法工作 在`wx.Printout`中有几个`get`*方法,它们使你能够获取当前打印环境的有关信息。表17.1列出了这些方法。 **表17.1** **`wx.Printout`的信息获取方法** | | | | --- | --- | | `GetDC()` | 该方法返回关于打印机或打印预览的用于绘制文档的设备上下文。 | | `GetPageInfo()` | 返回一个含有四个元素的元组(`minPage, maxPage, pageFrom, pageTo)`。`minPage, maxPage`分别是所允许的最小和最大页码,默认是1和32000。`pageFrom, pageTo`是必须被打印的范围,默认为1。你可以在你的子类中覆盖这个方法。 | | `GetPageSizeMM()` | 返回包含一个页面的宽度和高度的一个(`w, h)`元组,以毫米为单位。 | | `GetPageSizePixels()` | 返回一个页面的宽度和高度的一个(`w, h)`元组,以像素为单位。如果打印输出被用于打印预览,那么像素数将反应当前的缩放比列,意思就是说像素将会随缩放比列而变。 | | `GetPPIPrinter()` | 返回当前打印机在垂直和水平方向上每英寸的像素的一个(`w, h)`元组。在预览中,这个值也是始终一致的,即使打印预览的缩放比列变化了。 | | `GetPPIScreen()` | 返回当前屏幕在垂直和水平方向上每英寸的像素的一个(`w, h)`元组。在预览中,这个值也是始终一致的,即使打印预览的缩放比列变化了。 | | `GetTitle()` | 返回打印输出的标题。 | 在后面的几节中,我们将讨论如何呈现打印对话框给用户。 ## 如何显示打印对话框? 诸如要打印那些面面,要打印多少副本这些关于打印工作的数据是由标准的打印对话框来管理的。打印对话框是与字体和颜色对话框类似的,`wxPython`中的打印对话框实例仅仅是对本地控件和一个储存了对话框数据的分离的数据对象的简单封装。 ### 创建一个打印对话框 图17.3显示了打印设置对话框的样例。 **图17.3** ![](https://box.kancloud.cn/2016-08-21_57b996485e431.gif) 这里的对话框是类`wx.PrintDialog`的一个实例,你可以使用下面的构造函数来得到: ``` wx.PrintDialog(parent, data=None) ``` 其中,参数`parent`是对话框的父框架,参数`data`是一个预先存在的`wx.PrintDialogData`实例,它用于对话框的初始数据。 **使用方法** 一旦你有了打印对话框,你就可以使用标准的`ShowModal()`方法来显示它,`ShowModal()`方法将根据用户关闭对话框的方式而返回`wx.ID_OK`或`wx.ID_CANCEL`。 在你关闭了对话框之后,你可以使用`GetPrintDialogData()`方法来得到用户输入的数据。你也可以使用`GetPrintDC()`方法得到与数据相关联的打印机的设备上下文,如果还没有内容被创建,那么`GetPrintDC()`方法返回`None`。例17.1中的`OnPrintSetup()`方法显示了实际上对话框是如何被获取的。 **使用属性** 这个数据对象本身有几个属性,其中的一个是对`wx.PrintData`类型的一个对象的引用,`wx.PrintData`有更多的属性。你可以使用构造函数`wx.PrintDialogData()`来创建你的`wx.PrintDialogData`对象。这使得你能够在打开对话框之前预设属性。 `wx.PrintDialogData`对象有四个属性用于控制打印对话框的各个部分的有效性。方法`EnableHelp(enable)`用于开关帮助性能。至于对话框的其它部分,`EnablePageNumbers(enable)`与页面数量输入框相关,`EnablePrintToFile(enable)`管理实际的打印按钮,`EnableSelection(enable)`在打印所有和仅打印被选项之间作切换。 表17.2显示了对话框数据对象的其它属性,它们使你能够管理有关打印请求的信息。 **表17.2** **`wx.PrintDialogData`的属性** | | | | --- | --- | | `GetAllPages()` | 如果用户选择了打印整个文档这一选项,则返回`True`。 | | `SetCollate(flag)` | | | `GetCollate()` | 如果用户选择了核对打印的页,则返回`True`。 | | `SetFromPage(page)` | | | `GetFromPage()` | 如果用户选择从某一页打印,那么方法返回打印的第一页的整数页码。 | | `SetMaxPage(page)` | | | `GetMaxPage()` | 返回文档中最大的页码。 | | `SetMinPage(page)` | | | `GetMinPage()` | 返回文档中最小的页码。 | | `SetNoCopies()` | | | `GetNoCopies()` | 返回用户选择要打印的副本的数量。 | | `SetPrintData(printData)` | | | `GetPrintData()` | 返回与对话框相关联的`wx.PrintData`对象。 | | `SetPrintToFile(flag)` | | | `GetPrintToFile()` | 如果用户已经选择了打印到一个文件这一项,那么返回`True`。“打印到文件”这一机制由`wxPython`管理。 | | `SetSelection(flag)` | | | `GetSelection()` | 如果用户已经选择了只打印当前的选择这一项,那么返回`True`。 | | `SetToPage(page)` | | | `GetToPage()` | 如果用户指定了一个范围,那么返回打印的最后一页的页码。 | 被`GetPrintData()`方法返回的`wx.PrintData`实例提供了有关打印的更进一步的信息。通常这些信息是在你的打印对话框的打印设置子对话框中的。表17.3列出了`wx.PrintData`对象的属性。 **表17.3** **`wx.PrintData`的属性** | | | | --- | --- | | `SetColour(flag)` | | `GetColour()` | 如果当前的打印是用于颜色打印的,那么返回`True`。 | | `SetDuplex(mode)` | | `GetDuplex()` | 返回当前关于双面打印的设置。值可以是`wx.DUPLEX_SIMPLE`(非双面打印),`wx.DUPLEX_HORIZONTAL`(横向双面打印),`wx.DUPLEX_VERTICAL`(纵向双面打印)。 | | `SetOrientation(orientation)` | | `GetOrientation()` | 返回纸张的打印定位(肖像或风景)。值可以是`wx.LANDSCAPE`和`wx.PORTRAIT`。 | | `SetPaperId(paperId)` | | `GetPaperId()` | 返回匹配纸张类型的标识符。通常的值有`wx.PAPER_LETTER, wx.PAPER_LEGAL, `和`wx.PAPER_A4`。完整的页面(纸张)`ID`的列表见`wxWidgets`文档。 | | `SetPrinterName(printerName)` | | `GetPrinterName()` | 返回被系统引用的当前打印机的名字。如果该值为空字符串,那么默认打印机被使用。 | | `SetQuality(quality)` | | `GetQuality()` | 返回打印机的当前品质值。`set`*方法仅接受如下取值 | `wx.PRINT_QUALITY_DRAFT, wx.PRINT_QUALITY_HIGH, wx.PRINT_QUALITY_MEDIUM, `或`wx.PRINT_QUALITY_LOW`。`get`*方法将返回上面的这些值之一,或一个代表每英寸点数设置的正整数。 | ## 如何显示页面设置对话框? 图17.4显示了页面设置对话框是如何让用户来设置与页面尺寸相关的数据的。 **图17.4** ![](https://box.kancloud.cn/2016-08-21_57b996485e431.gif) ### 创建页面设置对话框 你可以通过实例化一个`wx.PageSetupDialog`类来创建一个页面设置对话框。 `wx.PageSetupDialog(parent, data=None)` 参数`parent`是新的对话框的父窗口。参数`data`是`wx.PageSetupDialogData`的一个实例默认为`None`。一旦页面设置对话框被创建了,那么这个对话框的行为就和其它任何模式对话框一样,并且你可以使用`ShowModal()`来显示它。通常,返回值表明了用户是否是使用`wx.ID_OK`或`wx.ID_CANCEL`按钮关闭的对话框窗口。在对话框关闭后,你可以通过调用`GetPageSetupDialogData()`来取得对数据对象的访问, `GetPageSetupDialogData()`返回类`wx.PageSetupDialogData`的一个实例。 ### 使用页面设置属性工作 `wx.PageSetupDialogData`类有几个必须与页面设置一起使用的属性。表17.4展示了控制对话框自身显示的属性。除非有其它的指定,否则所有这些属性都默认为`True`。 **表17.4** **`wx.PageSetupDialogData`的对话框控制属性** | | | | --- | --- | | `GetDefaultMinMargins()` | | `SetDefaultMinMargins(flag)` | 如果这个属性为`True`,并且你是在微软的`Windows`系统上,那么页面设置将使用默认打印机的当前属性作为默认认的最小化页边距。否则,它将使用系统默认值。 | | `GetDefaultInfo()` | | `SetDefaultInfo(flag)` | 如果这个属性为`True`,并且你是在微软的`Windows`系统上,那么这个页面设置对话框不会被显示。替而代之,当前打印机的所有默认值都将被放入数据对象。 | | `EnableHelp(flag)` | | `GetEnableHelp()` | 如果为`True`,那么对话框的帮助部分是有效的。 | | `EnableMargins(flag)` | | `GetEnableMargins()` | 如果为`True`,那么对话框的用于调整页边距的部分是有效的。 | | `EnableOrientation(flag)` | | `GetEnableOrientation()` | 如果为`True`,那么对话框的用于改变页面定位的部分是有效的。 | | `EnablePaper(flag)` | | `GetEnablePaper()` | 如果为`True`,那么对话框的用于允许用户改变页面(纸张)类型的部分是效的。 | | `EnablePrinter(flag)` | | `GetEnablePrinter()` | 如果为`True`,那么允许用户设置打印机的按钮是有效的。 | 表17.5显示了`wx.PageSetupDialogData`类的附加的属性,这些属性用于控制页面的边距和尺寸。 **表17.5** **`wx.PageSetupDialogData`的页边距和尺寸属性** | | | | --- | --- | | `GetMarginTopLeft()` | | `SetMarginTopLeft(pt)` | `get`*方法返回一个`wx.Point`,其中的值x是当前的左边距,y是当前的上边距。`set`*方法允许你使用一个`wx.Point`或一个`Python`元组来改变这些值。 | | `GetMarginBottomRight()` | | `SetMarginBottomRight(pt)` | `get`*方法返回一个`wx.Point`,其中的值x是当前的右边距,y是当前的下边距。`set`*方法允许你使用一个`wx.Point`或一个`Python`元组来改变这些值。 | | `GetMinMarginTopLeft()` | | `SetMinMarginTopLeft(pt)` | 同`GetMarginTopLeft()`中的一样,只是值是所允许的最小左边距和上边距。 | | `GetMinMarginBottomRight()` | | `SetMinMarginBottomRight(pt)` | 同`GetMarginBottomRight()`中的一样,只是值是所允许的最小右边距和下边距。 | | `GetPaperId()` | | `SetPaperId(id)` | 返回关于当前页面类型的`wxPython`标识符。同`wx.PrinterData`的属性。 | | `GetPaperSize()` | | `SetPaperSize(size)` | `get`*方法返回包含页面的水平和坚直方向尺寸的一个`wx.Size`实例。单位是毫米。 | | `GetPrintData()` | | `SetPrintData(printData)` | `get`*方法返回与当前打印会话相关的`wx.PrintData`实例。 | 到目前为止,我们已经讨论了所有关于数据对话框的整改,下面我们将重点放在打印上面。 ## 如何打印? 到目前为止,我们已经见过了打印构架的所有部分,现是我们打印一些东西的时候了。实际的打印部分是由`wx.Printer`类的一个实例来控制的。与已经说明的其它部分相比,打印并不更简单。接下来,我们将对在例17.1中的`OnPrint()`中的步骤作介绍。 第一步 按顺序得到你的所有数据 这至少应该包括带有打印机命令的`wx.Printout`对象,通常也要包括一个`wx.PrintDialogData`实例。 第二步 创建一个`wx.Printer`实例 创建该实例,要使用构造器`wx.Printer(data=None)`。可选参数`data`是一个`wx.PrintDialogData`实例。该数据控制打印,通常,你会想使用它。 第三步 使用`wx.Printer`的`Print ()`方法打印 `Print()`方法如下: ``` Print(parent, printout, prompt=True) ``` 其中参数`parent`是当打印时所触发的对话框的父窗口。`printout`是用于打印的`wx.Printout`对象。如果参数`prompt`为`True`,那么在打印之前显示打印对话框,否则将立即;启动打印。 如果打印成功,则`Print()`方法返回`True`。你能够调用`GetLastError()`方法来得到下列常量之一:`wx.PRINTER_CANCELLED`(如果失败是由于用户取消了打印所引起的),`wx.PRINTER_ERROR`(如果失败在打印期间由打印自身所引起的),或`wx.PRINTER_NO_ERROR`(如果`Print()`返回`True`且没有错误发生)。 这儿还有另外两个你可以使用一个`wx.Printer`实例做的事: * 你可以使用`CreateAbortWindow(parent,printout)`来显示中止对话框,其中参数`parent`和`printout`同`Print()`方法中的。如果用户已经中止打印任务,你能够通过调用`Abort()`来发现,该方法在这种情况下返回`True`。 * 你可以使用`PrintDialog(parent)`来显式地显示打印对话框,并且你可以使用`GetPrintDialogData()`来得到活动的打印数据对象。 ## 如何实现一个打印预览? 使用设备上下文的一个好处就是很容易管理打印预览,你可以使用一个屏幕设备上下文来代替打印机设备上下文。接下来的三个部分将讨论打印预览的过程。 第一步 创建预览实例 在一个打印预览中的第一步是创建类`wx.PrintPreview`的一个实例,`wx.PrintPreview`类似`wx.Printer`。构造器如下: ``` wx.PrintPreview(printout, printoutForPrinting, data=None) ``` 其中参数`printout`是一个`wx.Printout`对象,用于管理预览。参数`printoutForPrinting`是另一个`wx.Printout`对象。如果它不是`None`,那么当显示的时候,该打印预览窗口包含一`Print`按钮,该按钮启动打印。`printoutForPrinting`用于实际的打印。如果参数`printoutForPrinting`为`None`,那么`Print`按钮不显示。当然,你可以传递同一个实例或你的自定义打印输出类的相同版本的两个实例给参数`printout`和`printoutForPrinting`。参数`data`可以是一个`wx.PrintData`对象或一个`wx.PrintDialogData`对象。如果参数`data`指定了的话,那么它被用于控制该打印预览。在例17.1中,我们显示了一个在`OnPrintPreview()`方法中使用打印预览的例子。 第二步 创建预览框架 一旦你有了你的`wx.PrintPreview`,你就需要一框架以在其中观看你的`wx.PrintPreview`。该框架由类`wx.PreviewFrame`提供,`wx.PreviewFrame`是`wx.Frame`的一个子类,`wx.Frame`为预览提供基本的用户交互控件。`wx.PreviewFrame`的构造器如下: ``` wx.PreviewFrame(preview, parent, title, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE, name="frame") ``` 其中真正有意义的参数是`preview`,它是要被预览的`wx.PrintPreview`实例。其它的参数都是标准的`wx.Frame`中的。`wx.PreviewFrame`不定义任何自定义的样式或事件。 第三步 初始化框架 在你显示你的`wx.PreviewFrame`之前,你需要调用`Initialize()`方法,该方法创建窗口的内部的部件并做其它的内部的计算。一旦你`Show()`了该框架,那么如果你想再改变预览窗口的感观,你可以使用考虑`CreateControlBar()`和`CreateCanvas()`方法,它们分别创建类`wx.PreviewControlBar`和`wx.PreviewCanvas`的对象。覆盖这些方法以创建你自己的画布(`canvas)`和/或控制栏对象,使得你能够定制你的打印预览窗口的感观。 ## 本章小结 1、这是`wxPython`中的一个通用的打印构架,它不仅可以打印`HTML`,还可以打印任何能够被绘制到设备上下文的东西。这个架构中的主要的类是`wx.Printout`,但是`wx.Printer`和`wx.PrintPreview`也是重要的。 2、`wx.Printout`类管理图形打印的细节,并且它包含几个可以被覆盖来定制打印会话期间的行为和使用的数据的方法。打印发生在`OnPrintPage()`方法期间。 3、用于打印机设置和页面设置的标准的对话框是可以从`wxPython`来访问的。打印机设置对话框是`wx.PrintDialog`的一个实例,页面设置对话框是`wx.PageSetupDialog`的一个实例。这两个对话框都有相关的数据类,数据类使你的程序可以处理所有显示在对话框中的值。 4、一旦有了数据,那么实际将数据传送给打印机则是`wx.Printer`类的相对简单的应用。你可以使用`wx.PrintPreview`类来管理一个打印预览会话,该类包括一个打印预览框架,和根据该框架指定通常打印行为的选项。