= How to use printing framework - Part 2 (Phoenix) = '''Keywords :''' Printing, Printout, Preview, Printer, PrintData, PrintDialogData, PageSetupDialogData, PageSetupDialog, PrintPreview, PreviewFrame, Canvas, Grid, HtmlEasyPrinting, PDFViewer, PDFWindow, ReportLab. <<TableOfContents>> -------- = Introduction = The Printer framework for wx.Windows / wx.Python is quite complicated. Because of this, `wx.HtmlEasyPrinting` is provided to help simplify basic printing functionality. This Recipe covers a simplified approach to using both `wx.HtmlEasyPrinting` and the more complicated `wx.Printout`. -------- = What Objects are Involved = The following classes will be used in these recipes : * ` wx.HtmlEasyPrinting` -- The simple way to print :-) * ` wx.Printout` -- Base printing class that will be inherited from. Various control functions handling printing events are defined here. * ` wx.PrintData` -- Printer / page configuration data * ` wx.PrintDialogData` -- (same as (`wx.PrintData`) except what is used by Page Setup window ??) * ` wx.Printer` -- Software interface to the printer * ` wx.MessageBox` -- Used for alerting the user to any problems * ` wx.PrintPreview` -- Used for print preview * ` wx.PrintDialog` -- Window that pops up, asking how many pages to print, etc.. -------- = Demonstrating : = __'''''Tested''' py3.x, wx4.x and Win10. ''__ Printing is an essential element for your programs, here we show you how to print. Are you ready to use some samples ? ;) Test, modify, correct, complete, improve and share your discoveries ! (!) -------- == Multiline printing == {{attachment:img_sample_one.png}} {{{#!python # sample_one.py #------------------------------------------------------------------------------- # Name: printout.py # Purpose: preview and printing class -> table/grid printing # # Author: Lorne White (email: lorne.white@telusplanet.net) # # Created: # Version: 0.75 # Date: May 15, 2002 # Licence: wxWindows license #------------------------------------------------------------------------------- # Link: # https://raw.githubusercontent.com/wxWidgets/wxPython/master/wx/lib/printout.py #------------------------------------------------------------------------------- # Release Notes : # fixed bug for string wider than print region # add index to data list after parsing total pages for paging #------------------------------------------------------------------------------- # 12/10/2003 - Jeff Grimmett (grimmtooth@softhome.net) # o 2.5 compatability update. #------------------------------------------------------------------------------- # 11/23/2004 - Vernon Cole (wnvcole@peppermillcas.com) # o Generalize for non-2-dimensional sequences and non-text data # (can use as a simple text printer by supplying a list of strings.) # o Add a small _main_ for self test #------------------------------------------------------------------------------- import os import copy import wx # class PrintBase # class PrintTableDraw # class PrintTable # class PrintGrid # class SetPrintout # class Frame # class App #------------------------------------------------------------------------------- if os.name == "posix": print("\nPlatform : UNIX - Linux") elif os.name in ['nt', 'dos', 'ce']: print("\nPlatform : Windows") else: print("\nPlatform : ", platform.system()) #------------------------------------------------------------------------------- class PrintBase(object): """ ... """ def SetPrintFont(self, font): # set the DC font parameters fattr = font["Attr"] if fattr[0] == 1: weight = wx.BOLD else: weight = wx.NORMAL if fattr[1] == 1: set_style = wx.ITALIC else: set_style = wx.NORMAL underline = fattr[2] fcolour = self.GetFontColour(font) self.DC.SetTextForeground(fcolour) setfont = wx.Font(font["Size"], wx.SWISS, set_style, weight, underline) setfont.SetFaceName(font["Name"]) self.DC.SetFont(setfont) #--------------------------------------------------------------------------- def GetFontColour(self, font): fcolour = font["Colour"] return wx.Colour(fcolour[0], fcolour[1], fcolour[2]) def OutTextRegion(self, textout, txtdraw = True): textlines = textout.split('\n') y = copy.copy(self.y) + self.pt_space_before for text in textlines: remain = 'X' while remain != "": vout, remain = self.SetFlow(text, self.region) if self.draw == True and txtdraw == True: test_out = self.TestFull(vout) if self.align == wx.ALIGN_LEFT: self.DC.DrawText(test_out, int(self.indent+self.pcell_left_margin), int(y)) elif self.align == wx.ALIGN_CENTRE: diff = self.GetCellDiff(test_out, self.region) self.DC.DrawText(test_out, int(self.indent+diff/2), y) elif self.align == wx.ALIGN_RIGHT: diff = self.GetCellDiff(test_out, self.region) self.DC.DrawText(test_out, int(self.indent+diff), y) else: self.DC.DrawText(test_out, int(self.indent+self.pcell_left_margin), int(y)) text = remain y = y + self.space return y - self.space + self.pt_space_after def GetCellDiff(self, text, width): # get the remaining cell size for adjustment w, h = self.DC.GetTextExtent(text) diff = width - w if diff < 0: diff = 0 return diff def TestFull(self, text_test): w, h = self.DC.GetTextExtent(text_test) if w > self.region: # trouble fitting into cell return self.SetChar(text_test, self.region) # fit the text to the cell size else: return text_test def SetFlow(self, ln_text, width): width = width - self.pcell_right_margin text = "" split = ln_text.split() if len(split) == 1: return ln_text, "" try: w, h = self.DC.GetTextExtent(" " + split[0]) if w >= width: return ln_text, "" except: pass cnt = 0 for word in split: bword = " " + word # blank + word length = len(bword) w, h = self.DC.GetTextExtent(text + bword) if w < width: text = text + bword cnt = cnt + 1 else: remain = ' '.join(split[cnt:]) text = text.strip() return text, remain remain = ' '.join(split[cnt:]) vout = text.strip() return vout, remain def SetChar(self, ln_text, width): # truncate string to fit into width width = width - self.pcell_right_margin - self.pcell_left_margin text = "" for val in ln_text: w, h = self.DC.GetTextExtent(text + val) if w > width: text = text + ".." return text # fitted text value text = text + val return text def OutTextPageWidth(self, textout, y_out, align, indent, txtdraw = True): textlines = textout.split('\n') y = copy.copy(y_out) pagew = self.parent.page_width * self.pwidth # full page width w, h = self.DC.GetTextExtent(textout) y_line = h for text in textlines: remain = 'X' while remain != "": vout, remain = self.SetFlow(text, pagew) if self.draw == True and txtdraw == True: test_out = vout if align == wx.ALIGN_LEFT: self.DC.DrawText(test_out, indent, y) elif align == wx.ALIGN_CENTRE: diff = self.GetCellDiff(test_out, pagew) self.DC.DrawText(test_out, int(indent+diff/2), int(y)) elif align == wx.ALIGN_RIGHT: diff = self.GetCellDiff(test_out, pagew) self.DC.DrawText(test_out, int(indent+diff), int(y)) else: self.DC.DrawText(test_out, indent, y_out) text = remain y = y + y_line return y - y_line def GetDate(self): date, time = self.GetNow() return date def GetDateTime(self): date, time = self.GetNow() return date + ' ' + time def GetNow(self): now = wx.DateTime.Now() date = now.FormatDate() time = now.FormatTime() return date, time def SetPreview(self, preview): self.preview = preview def SetPSize(self, width, height): self.pwidth = width/self.scale self.pheight = height/self.scale def SetScale(self, scale): self.scale = scale def SetPTSize(self, width, height): self.ptwidth = width self.ptheight = height def getWidth(self): return self.sizew def getHeight(self): return self.sizeh #------------------------------------------------------------------------------- class PrintTableDraw(wx.ScrolledWindow, PrintBase): """ ... """ def __init__(self, parent, DC, size): self.parent = parent self.DC = DC self.scale = parent.scale self.width = size[0] self.height = size[1] self.SetDefaults() #--------------------------------------------------------------------------- def SetDefaults(self): self.page = 1 self.total_pages = None self.page_width = self.parent.page_width self.page_height = self.parent.page_height self.left_margin = self.parent.left_margin self.right_margin = self.parent.right_margin self.top_margin = self.parent.top_margin self.bottom_margin = self.parent.bottom_margin self.cell_left_margin = self.parent.cell_left_margin self.cell_right_margin = self.parent.cell_right_margin self.label_colour = self.parent.label_colour self.row_line_colour = self.parent.row_line_colour self.row_line_size = self.parent.row_line_size self.row_def_line_colour = self.parent.row_def_line_colour self.row_def_line_size = self.parent.row_def_line_size self.column_line_colour = self.parent.column_line_colour self.column_line_size = self.parent.column_line_size self.column_def_line_size = self.parent.column_def_line_size self.column_def_line_colour = self.parent.column_def_line_colour self.text_font = self.parent.text_font self.label_font = self.parent.label_font def AdjustValues(self): self.vertical_offset = self.pheight * self.parent.vertical_offset self.horizontal_offset = self.pheight * self.parent.horizontal_offset self.pcell_left_margin = self.pwidth * self.cell_left_margin self.pcell_right_margin = self.pwidth * self.cell_right_margin self.ptop_margin = self.pheight * self.top_margin self.pbottom_margin = self.pheight * self.bottom_margin self.pheader_margin = self.pheight * self.parent.header_margin self.pfooter_margin = self.pheight * self.parent.footer_margin self.cell_colour = self.parent.set_cell_colour self.cell_text = self.parent.set_cell_text self.column = [] self.column_align = [] self.column_bgcolour = [] self.column_txtcolour = [] set_column_align = self.parent.set_column_align set_column_bgcolour = self.parent.set_column_bgcolour set_column_txtcolour = self.parent.set_column_txtcolour pos_x = self.left_margin * self.pwidth + self.horizontal_offset # left margin self.column.append(pos_x) # Module logic expects two dimensional data -- fix input if needed if isinstance(self.data, str): self.data = [[copy.copy(self.data)]] # a string becomes a single cell try: rows = len(self.data) except TypeError: self.data = [[str(self.data)]] # a non-iterable becomes a single cell rows = 1 first_value = self.data[0] if isinstance(first_value, str): # A sequence of strings if self.label == [] and self.set_column == []: data = [] for x in self.data: # Becomes one column data.append([x]) else: data = [self.data] # Becames one row self.data = data first_value = data[0] try: column_total = len(first_value) except TypeError: # A sequence of non-iterables if self.label == [] and self.set_column == []: data = [] # Becomes one column for x in self.data: data.append([str(x)]) column_total = 1 else: data = [self.data] # Becomes one row column_total = len(self.data) self.data = data first_value = data[0] if self.set_column == []: table_width = self.page_width - self.left_margin - self.right_margin if self.label == []: temp = first_value else: temp = self.label width = table_width/(len(temp)) for val in temp: column_width = width * self.pwidth pos_x = pos_x + column_width self.column.append(pos_x) # position of each column else: for val in self.set_column: column_width = val * self.pwidth pos_x = pos_x + column_width self.column.append(pos_x) # position of each column if pos_x > self.page_width * self.pwidth: # check if it fits in page print("Warning, Too Wide for Page") return if self.label != []: if len(self.column) -1 != len(self.label): print("Column Settings Incorrect", "\nColumn Value: " + str(self.column), "\nLabel Value: " + str(self.label)) return if column_total != len(self.column) -1: print("Cannot fit", first_value, 'in', len(self.column)-1, 'columns.') return for col in range(column_total): try: align = set_column_align[col] # check if custom column alignment except: align = wx.ALIGN_LEFT self.column_align.append(align) try: colour = set_column_bgcolour[col] # check if custom column background colour except: colour = self.parent.column_colour self.column_bgcolour.append(colour) try: colour = set_column_txtcolour[col] # check if custom column text colour except: colour = self.GetFontColour(self.parent.text_font) self.column_txtcolour.append(colour) def SetPointAdjust(self): f = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL) # setup using 10 point self.DC.SetFont(f) f.SetFaceName(self.text_font["Name"]) x, y = self.DC.GetTextExtent("W") self.label_pt_space_before = self.parent.label_pt_adj_before * y/10 # extra spacing for label per point value self.label_pt_space_after = self.parent.label_pt_adj_after * y/10 self.text_pt_space_before = self.parent.text_pt_adj_before * y/10 # extra spacing for row text per point value self.text_pt_space_after = self.parent.text_pt_adj_after * y/10 def SetPage(self, page): self.page = page def SetColumns(self, col): self.column = col def OutCanvas(self): self.AdjustValues() self.SetPointAdjust() self.y_start = self.ptop_margin + self.vertical_offset self.y_end = self.parent.page_height * self.pheight - self.pbottom_margin + self.vertical_offset self.SetPrintFont(self.label_font) x, y = self.DC.GetTextExtent("W") self.label_space = y self.SetPrintFont(self.text_font) x, y = self.DC.GetTextExtent("W") self.space = y if self.total_pages is None: self.GetTotalPages() # total pages for display/printing self.data_cnt = self.page_index[self.page-1] self.draw = True self.PrintHeader() self.PrintFooter() self.OutPage() def GetTotalPages(self): self.data_cnt = 0 self.draw = False self.page_index = [0] cnt = 0 while 1: test = self.OutPage() self.page_index.append(self.data_cnt) if test == False: break cnt = cnt + 1 self.total_pages = cnt + 1 def OutPage(self): self.y = self.y_start self.end_x = self.column[-1] if self.data_cnt < len(self.data): # if there data for display on the page if self.label != []: # check if header defined self.PrintLabel() else: return False for val in self.data: try: row_val = self.data[self.data_cnt] except: self.FinishDraw() return False max_y = self.PrintRow(row_val, False) # test to see if row will fit in remaining space test = max_y + self.space if test > self.y_end: break self.ColourRowCells(max_y-self.y+self.space) # colour the row/column max_y = self.PrintRow(row_val, True) # row fits - print text self.DrawGridLine() # top line of cell self.y = max_y + self.space if self.y > self.y_end: break self.data_cnt = self.data_cnt + 1 self.FinishDraw() if self.data_cnt == len(self.data): # last value in list return False return True def PrintLabel(self): self.pt_space_before = self.label_pt_space_before # set the point spacing self.pt_space_after = self.label_pt_space_after self.LabelColorRow(self.label_colour) self.SetPrintFont(self.label_font) self.col = 0 max_y = 0 for vtxt in self.label: self.region = self.column[self.col+1] - self.column[self.col] self.indent = self.column[self.col] self.align = wx.ALIGN_LEFT max_out = self.OutTextRegion(vtxt, True) if max_out > max_y: max_y = max_out self.col = self.col + 1 self.DrawGridLine() # top line of label self.y = max_y + self.label_space def PrintHeader(self): # print the header array if self.draw == False: return for val in self.parent.header: self.SetPrintFont(val["Font"]) header_indent = val["Indent"] * self.pwidth text = val["Text"] htype = val["Type"] if htype == "Date": addtext = self.GetDate() elif htype == "Date & Time": addtext = self.GetDateTime() else: addtext = "" self.OutTextPageWidth(text+addtext, self.pheader_margin, val["Align"], header_indent, True) def PrintFooter(self): # print the header array if self.draw == False: return footer_pos = self.parent.page_height * self.pheight - self.pfooter_margin + self.vertical_offset for val in self.parent.footer: self.SetPrintFont(val["Font"]) footer_indent = val["Indent"] * self.pwidth text = val["Text"] ftype = val["Type"] if ftype == "Pageof": addtext = "Page " + str(self.page) + " of " + str(self.total_pages) elif ftype == "Page": addtext = "Page " + str(self.page) elif ftype == "Num": addtext = str(self.page) elif ftype == "Date": addtext = self.GetDate() elif ftype == "Date & Time": addtext = self.GetDateTime() else: addtext = "" self.OutTextPageWidth(text+addtext, footer_pos, val["Align"], footer_indent, True) def LabelColorRow(self, colour): brush = wx.Brush(colour, wx.SOLID) self.DC.SetBrush(brush) height = self.label_space + self.label_pt_space_before + self.label_pt_space_after self.DC.DrawRectangle(self.column[0], self.y, self.end_x-self.column[0]+1, height) def ColourRowCells(self, height): if self.draw == False: return col = 0 for colour in self.column_bgcolour: cellcolour = self.GetCellColour(self.data_cnt, col) if cellcolour is not None: colour = cellcolour brush = wx.Brush(colour, wx.SOLID) self.DC.SetBrush(brush) self.DC.SetPen(wx.Pen(wx.Colour('WHITE'), 0)) start_x = self.column[col] width = self.column[col+1] - start_x + 2 self.DC.DrawRectangle(int(start_x), int(self.y), int(width), int(height)) col = col + 1 def PrintRow(self, row_val, draw = True, align = wx.ALIGN_LEFT): self.SetPrintFont(self.text_font) self.pt_space_before = self.text_pt_space_before # set the point spacing self.pt_space_after = self.text_pt_space_after self.col = 0 max_y = 0 for vtxt in row_val: if not isinstance(vtxt, str): vtxt = str(vtxt) self.region = self.column[self.col+1] - self.column[self.col] self.indent = self.column[self.col] self.align = self.column_align[self.col] fcolour = self.column_txtcolour[self.col] # set font colour celltext = self.GetCellTextColour(self.data_cnt, self.col) if celltext is not None: fcolour = celltext # override the column colour self.DC.SetTextForeground(fcolour) max_out = self.OutTextRegion(vtxt, draw) if max_out > max_y: max_y = max_out self.col = self.col + 1 return max_y def GetCellColour(self, row, col): # check if custom colour defined for the cell background try: set = self.cell_colour[row] except: return None try: colour = set[col] return colour except: return None def GetCellTextColour(self, row, col): # check if custom colour defined for the cell text try: set = self.cell_text[row] except: return None try: colour = set[col] return colour except: return None def FinishDraw(self): self.DrawGridLine() # draw last row line self.DrawColumns() # draw all vertical lines def DrawGridLine(self): if self.draw == True \ and len(self.column) > 2: #supress grid lines if only one column try: size = self.row_line_size[self.data_cnt] except: size = self.row_def_line_size if size < 1: return try: colour = self.row_line_colour[self.data_cnt] except: colour = self.row_def_line_colour self.DC.SetPen(wx.Pen(colour, size)) y_out = self.y #y_out = self.y + self.pt_space_before + self.pt_space_after # adjust for extra spacing self.DC.DrawLine(self.column[0], y_out, self.end_x, y_out) def DrawColumns(self): if self.draw == True \ and len(self.column) > 2: #surpress grid line if only one column col = 0 for val in self.column: try: size = self.column_line_size[col] except: size = self.column_def_line_size if size < 1: continue try: colour = self.column_line_colour[col] except: colour = self.column_def_line_colour indent = val self.DC.SetPen(wx.Pen(colour, size)) self.DC.DrawLine(indent, self.y_start, indent, self.y) col = col + 1 def DrawText(self): self.DoRefresh() def DoDrawing(self, DC): size = DC.GetSize() self.DC = DC self.DrawText() self.sizew = DC.MaxY() self.sizeh = DC.MaxX() #------------------------------------------------------------------------------- class PrintTable(object): """ ... """ def __init__(self, parentFrame=None): self.data = [] self.set_column = [] self.label = [] self.header = [] self.footer = [] self.set_column_align = {} self.set_column_bgcolour = {} self.set_column_txtcolour = {} self.set_cell_colour = {} self.set_cell_text = {} self.column_line_size = {} self.column_line_colour = {} self.row_line_size = {} self.row_line_colour = {} self.parentFrame = parentFrame self.SetPreviewSize() self.printData = wx.PrintData() self.scale = 1.0 self.SetParms() self.SetColors() self.SetFonts() self.TextSpacing() self.SetPrinterOffset() self.SetHeaderValue() self.SetFooterValue() self.SetMargins() self.SetPortrait() #--------------------------------------------------------------------------- def SetPreviewSize(self, position = wx.Point(0, 0), size="Full"): if size == "Full": r = wx.GetClientDisplayRect() self.preview_frame_size = r.GetSize() self.preview_frame_pos = r.GetPosition() else: self.preview_frame_size = size self.preview_frame_pos = position def SetPaperId(self, paper): self.printData.SetPaperId(paper) def SetOrientation(self, orient): self.printData.SetOrientation(orient) def SetColors(self): self.row_def_line_colour = wx.Colour('BLACK') self.row_def_line_size = 1 self.column_def_line_colour = wx.Colour('BLACK') self.column_def_line_size = 1 self.column_colour = wx.Colour('WHITE') self.label_colour = wx.Colour('LIGHT GREY') def SetFonts(self): self.label_font = { "Name": self.default_font_name, "Size": 12, "Colour": [0, 0, 0], "Attr": [0, 0, 0] } self.text_font = { "Name": self.default_font_name, "Size": 10, "Colour": [0, 0, 0], "Attr": [0, 0, 0] } def TextSpacing(self): self.label_pt_adj_before = 0 # point adjustment before and after the label text self.label_pt_adj_after = 0 self.text_pt_adj_before = 0 # point adjustment before and after the row text self.text_pt_adj_after = 0 def SetLabelSpacing(self, before, after): # method to set the label space adjustment self.label_pt_adj_before = before self.label_pt_adj_after = after def SetRowSpacing(self, before, after): # method to set the row space adjustment self.text_pt_adj_before = before self.text_pt_adj_after = after def SetPrinterOffset(self): # offset to adjust for printer self.vertical_offset = -0.1 self.horizontal_offset = -0.1 def SetHeaderValue(self): self.header_margin = 0.25 self.header_font = { "Name": self.default_font_name, "Size": 11, "Colour": [0, 0, 0], "Attr": [0, 0, 0] } self.header_align = wx.ALIGN_CENTRE self.header_indent = 0 self.header_type = "Text" def SetFooterValue(self): self.footer_margin = 0.7 self.footer_font = { "Name": self.default_font_name, "Size": 11, "Colour": [0, 0, 0], "Attr": [0, 0, 0] } self.footer_align = wx.ALIGN_CENTRE self.footer_indent = 0 self.footer_type = "Pageof" def SetMargins(self): self.left_margin = 0.5 self.right_margin = 0.5 # only used if no column sizes self.top_margin = 0.8 self.bottom_margin = 1.0 self.cell_left_margin = 0.1 self.cell_right_margin = 0.1 def SetPortrait(self): self.printData.SetPaperId(wx.PAPER_LETTER) self.printData.SetOrientation(wx.PORTRAIT) self.page_width = 8.5 self.page_height = 11.0 def SetLandscape(self): self.printData.SetOrientation(wx.LANDSCAPE) self.page_width = 11.0 self.page_height = 8.5 def SetParms(self): self.ymax = 1 self.xmax = 1 self.page = 1 self.total_pg = 100 self.preview = None self.page = 0 self.default_font_name = "Arial" self.default_font = { "Name": self.default_font_name, "Size": 10, "Colour": [0, 0, 0], "Attr": [0, 0, 0] } def SetColAlignment(self, col, align=wx.ALIGN_LEFT): self.set_column_align[col] = align def SetColBackgroundColour(self, col, colour): self.set_column_bgcolour[col] = colour def SetColTextColour(self, col, colour): self.set_column_txtcolour[col] = colour def SetCellColour(self, row, col, colour): # cell background colour try: set = self.set_cell_colour[row] # test if row already exists try: set[col] = colour # test if column already exists except: set = { col: colour } # create the column value except: set = { col: colour } # create the column value self.set_cell_colour[row] = set # create dictionary item for colour settings def SetCellText(self, row, col, colour): # font colour for custom cells try: set = self.set_cell_text[row] # test if row already exists try: set[col] = colour # test if column already exists except: set = { col: colour } # create the column value except: set = { col: colour } # create the column value self.set_cell_text[row] = set # create dictionary item for colour settings def SetColumnLineSize(self, col, size): # column line size self.column_line_size[col] = size # create dictionary item for column line settings def SetColumnLineColour(self, col, colour): self.column_line_colour[col] = colour def SetRowLineSize(self, row, size): self.row_line_size[row] = size def SetRowLineColour(self, row, colour): self.row_line_colour[row] = colour def GetColour(self, colour): # returns colours based from wxColour value red = colour.Red() blue = colour.Blue() green = colour.Green() return [red, green, blue ] def SetHeader(self, text = "", type = "Text", font=None, align = None, indent = None, colour = None, size = None): set = { "Text": text } if font is None: set["Font"] = copy.copy(self.default_font) else: set["Font"] = font if colour is not None: setfont = set["Font"] setfont["Colour"] = self.GetColour(colour) if size is not None: setfont = set["Font"] setfont["Size"] = size if align is None: set["Align"] = self.header_align else: set["Align"] = align if indent is None: set["Indent"] = self.header_indent else: set["Indent"] = indent if type is None: set["Type"] = self.header_type else: set["Type"] = type self.header.append(set) def SetFooter(self, text = "", type = None, font=None, align = None, indent = None, colour = None, size = None): set = { "Text": text } if font is None: set["Font"] = copy.copy(self.default_font) else: set["Font"] = font if colour is not None: setfont = set["Font"] setfont["Colour"] = self.GetColour(colour) if size is not None: setfont = set["Font"] setfont["Size"] = size if align is None: set["Align"] = self.footer_align else: set["Align"] = align if indent is None: set["Indent"] = self.footer_indent else: set["Indent"] = indent if type is None: set["Type"] = self.footer_type else: set["Type"] = type self.footer.append(set) def Preview(self): data = wx.PrintDialogData(self.printData) text = self.parentFrame.tc.GetValue() printout = SetPrintout(self, text) printout2 = SetPrintout(self, text) self.preview = wx.PrintPreview(printout, printout2, data) if not self.preview.IsOk(): wx.MessageBox("There was a problem printing!", "Printing", wx.OK) return self.preview.SetZoom(60) # initial zoom value frame = wx.PreviewFrame(self.preview, self.parentFrame, "Print preview") frame.Initialize() if self.parentFrame: frame.SetPosition(self.preview_frame_pos) frame.SetSize(self.preview_frame_size) frame.Show(True) def Print(self): pdd = wx.PrintDialogData(self.printData) printer = wx.Printer(pdd) text = self.parentFrame.tc.GetValue() printout = SetPrintout(self, text) if not printer.Print(self.parentFrame, printout): if wx.Printer.GetLastError() == wx.PRINTER_ERROR: wx.MessageBox("There was a problem printing.\n" "Perhaps your current printer is not set correctly?", "Printing", wx.OK) else: self.printData = wx.PrintData( printer.GetPrintDialogData().GetPrintData() ) printout.Destroy() def DoDrawing(self, DC): size = DC.GetSize() table = PrintTableDraw(self, DC, size) table.data = self.data table.set_column = self.set_column table.label = self.label table.SetPage(self.page) if self.preview is None: table.SetPSize(size[0]/self.page_width, size[1]/self.page_height) table.SetPTSize(size[0], size[1]) table.SetPreview(False) else: if self.preview == 1: table.scale = self.scale table.SetPSize(size[0]/self.page_width, size[1]/self.page_height) else: table.SetPSize(self.pwidth, self.pheight) table.SetPTSize(self.ptwidth, self.ptheight) table.SetPreview(self.preview) table.OutCanvas() self.page_total = table.total_pages # total display pages self.ymax = DC.MaxY() self.xmax = DC.MaxX() self.sizeh = size[0] self.sizew = size[1] def GetTotalPages(self): self.page_total = 100 return self.page_total def HasPage(self, page): if page <= self.page_total: return True else: return False def SetPage(self, page): self.page = page def SetPageSize(self, width, height): self.pwidth, self.pheight = width, height def SetTotalSize(self, width, height): self.ptwidth, self.ptheight = width, height def SetPreview(self, preview, scale): self.preview = preview self.scale = scale def SetTotalSize(self, width, height): self.ptwidth = width self.ptheight = height #------------------------------------------------------------------------------- class PrintGrid(object): """ ... """ def __init__(self, parent, grid, format = [], total_col = None, total_row = None): if total_row is None: total_row = grid.GetNumberRows() if total_col is None: total_col = grid.GetNumberCols() self.total_row = total_row self.total_col = total_col self.grid = grid data = [] for row in range(total_row): row_val = [] value = grid.GetRowLabelValue(row) row_val.append(value) for col in range(total_col): value = grid.GetCellValue(row, col) row_val.append(value) data.append(row_val) label = [""] for col in range(total_col): value = grid.GetColLabelValue(col) label.append(value) self.table = PrintTable(parent) self.table.cell_left_margin = 0.0 self.table.cell_right_margin = 0.0 self.table.label = label self.table.set_column = format self.table.data = data #--------------------------------------------------------------------------- def GetTable(self): return self.table def SetAttributes(self): for row in range(self.total_row): for col in range(self.total_col): colour = self.grid.GetCellTextColour(row, col-1) self.table.SetCellText(row, col, colour) colour = self.grid.GetCellBackgroundColour(row, col-1) self.table.SetCellColour(row, col, colour) def Preview(self): self.table.Preview() def Print(self): self.table.Print() #------------------------------------------------------------------------------- class SetPrintout(wx.Printout): """ ... """ def __init__(self, canvas, text): wx.Printout.__init__(self) self.canvas = canvas self.end_pg = 1000 self.lines = text #--------------------------------------------------------------------------- def OnBeginDocument(self, start, end): return super(SetPrintout, self).OnBeginDocument(start, end) def OnEndDocument(self): super(SetPrintout, self).OnEndDocument() def HasPage(self, page): try: end = self.canvas.HasPage(page) return end except: return True def GetPageInfo(self): try: self.end_pg = self.canvas.GetTotalPages() except: pass end_pg = self.end_pg str_pg = 1 return (str_pg, end_pg, str_pg, end_pg) def OnPreparePrinting(self): super(SetPrintout, self).OnPreparePrinting() def OnBeginPrinting(self): dc = self.GetDC() self.preview = self.IsPreview() if (self.preview): self.pixelsPerInch = self.GetPPIScreen() else: self.pixelsPerInch = self.GetPPIPrinter() (w, h) = dc.GetSize() scaleX = float(w) / 1000 scaleY = float(h) / 1000 self.printUserScale = min(scaleX, scaleY) super(SetPrintout, self).OnBeginPrinting() def GetSize(self): self.psizew, self.psizeh = self.GetPPIPrinter() return self.psizew, self.psizeh def GetTotalSize(self): self.ptsizew, self.ptsizeh = self.GetPageSizePixels() return self.ptsizew, self.ptsizeh def OnPrintPage(self, page): dc = self.GetDC() (w, h) = dc.GetSize() scaleX = float(w) / 1000 scaleY = float(h) / 1000 self.printUserScale = min(scaleX, scaleY) dc.SetUserScale(self.printUserScale, self.printUserScale) self.preview = self.IsPreview() self.canvas.SetPreview(self.preview, self.printUserScale) self.canvas.SetPage(page) self.ptsizew, self.ptsizeh = self.GetPageSizePixels() self.canvas.SetTotalSize(self.ptsizew, self.ptsizeh) self.psizew, self.psizeh = self.GetPPIPrinter() self.canvas.SetPageSize(self.psizew, self.psizeh) self.canvas.DoDrawing(dc) return True #------------------------------------------------------------------------------- class Frame(wx.Frame): """ ... """ def __init__(self, parent, id, title=""): wx.Frame.__init__(self, parent, id, title, size=(600, 450), style=wx.DEFAULT_FRAME_STYLE) #------------ # Simplified init method. self.SetProperties() self.CreateMenu() self.CreateCtrls() self.CreatePrintData() self.BindEvents() self.DoLayout() #------------ self.CenterOnScreen() #--------------------------------------------------------------------------- def SetProperties(self): """ Set the main frame properties (title, icon...). """ frameicon = wx.Icon("Icons/wxWidgets.ico") self.SetIcon(frameicon) #------------ self.SetTitle("Dummy wx frame for testing printout.py") def CreateMenu(self): """ ... """ menub = wx.MenuBar() fmenu = wx.Menu() fmenu.Append(wx.ID_PREVIEW, "Print pre&view\tCtrl+V") fmenu.Append(wx.ID_PRINT, "&Print\tCtrl+P") fmenu.AppendSeparator() fmenu.Append(wx.ID_EXIT, "E&xit\tCtrl+X") menub.Append(fmenu, "&File") self.SetMenuBar(menub) def CreateCtrls(self): """ ... """ font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) font.SetWeight(wx.BOLD) font.SetPointSize(10) #------------ # First create the controls. self.panel = wx.Panel(self, -1, style=wx.BORDER_THEME| wx.TAB_TRAVERSAL) self.demo = wx.StaticText(self.panel, id=-1, label="Demonstrating :") self.demo.SetFont(font) self.info = wx.StaticText(self.panel, id=-1, label="1) Direct printing,\n" "2) Printout class,\n" "3) PrintTable class,\n" "4) Preview,\n" "5) Menu") self.info.SetForegroundColour("red") font.SetWeight(wx.NORMAL) self.info.SetFont(font) text = ('This the first line of text.\n'\ 'This is the second line\nand the third. The fourth will be the number 4.0.\n'\ '4.0\n'\ 'This is the fifth line, but by design it is too long to fit in the width of a standard'\ 'page, so it will be forced to wrap around in order to fit without having'\ 'some of its verbose verbage truncated.\n'\ 'Here we have the final line.') self.tc = wx.TextCtrl(self.panel, id=-1, size=(200, -1), value=text, style=wx.TE_MULTILINE|wx.TE_DONTWRAP) self.btnPreview = wx.Button(self.panel, id=wx.ID_PREVIEW, label="Print pre&view") self.btnPreview.SetFocus() self.btnPrint = wx.Button(self.panel, id=wx.ID_PRINT, label="&Print") self.btnClose = wx.Button(self.panel, id=wx.ID_CLOSE, label="E&xit") def CreatePrintData(self): """ Create printing data. """ self.ptbl = PrintTable(self) self.ptbl.SetHeader('This is the test HEADER') self.ptbl.SetFooter() #------------ # a single sequence will print out as a single column with no borders ... self.ptbl.data = (self.tc.GetValue()) def BindEvents(self): """ Bind all the events related to my application. """ # Bind some menu events to an events handler. self.Bind(wx.EVT_MENU, self.OnPrintPreview, id=wx.ID_PREVIEW) self.Bind(wx.EVT_MENU, self.OnPrint, id=wx.ID_PRINT) self.Bind(wx.EVT_MENU, self.OnClose, id=wx.ID_EXIT) # Bind the close event to an event handler. self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) # Bind some buttons events to an events handler. self.Bind(wx.EVT_BUTTON, self.OnPrintPreview, self.btnPreview) self.Bind(wx.EVT_BUTTON, self.OnPrint, self.btnPrint) self.Bind(wx.EVT_BUTTON, self.OnClose, self.btnClose) def DoLayout(self): """ Manage widgets Layout. """ # MainSizer is the top-level one that manages everything. mainSizer = wx.BoxSizer(wx.VERTICAL) #------------ hBox1 = wx.BoxSizer(wx.HORIZONTAL) hBox1.Add(self.info, 0, wx.ALL, 15) #------------ hBox2 = wx.BoxSizer(wx.HORIZONTAL) hBox2.Add(self.btnPreview, 0, wx.ALL, 10) hBox2.Add(self.btnPrint, 0, wx.ALL, 10) hBox2.Add(self.btnClose, 0, wx.ALL, 10) #------------ mainSizer.Add(self.demo, 0, wx.ALL, 10) mainSizer.Add(wx.StaticLine(self.panel), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5) mainSizer.Add(self.tc, 1, wx.EXPAND | wx.ALL, 15) mainSizer.Add(hBox1, 0, wx.ALL, 5) mainSizer.Add(hBox2, 0, wx.ALL, 5) #------------ # Finally, tell the panel to use the mainSizer for layout. self.panel.SetSizer(mainSizer) def OnPrintPreview(self, event): self.ptbl.data = (self.tc.GetValue()) self.ptbl.Preview() def OnPrint(self, event): self.ptbl.data = (self.tc.GetValue()) self.ptbl.Print() def OnClose(self, evt): self.Close() def OnCloseWindow(self, event): """ ... """ self.Destroy() #------------------------------------------------------------------------------- class App(wx.App): """ ... """ def OnInit(self): #------------ self.locale = wx.Locale(wx.LANGUAGE_ENGLISH) #------------ frame = Frame(None, id=-1) self.SetTopWindow(frame) frame.Show(True) return True #------------------------------------------------------------------------------- def main(): app = App(False) app.MainLoop() #------------------------------------------------------------------------------- if __name__ == "__main__" : main() }}} -------- = Download source = [[attachment:source.zip]] -------- = Additional Information = '''Link :''' https://wiki.wxpython.org/MoreCommentsOnPrinting https://wiki.wxpython.org/PrintingWithReportGenerators http://www.blog.pythonlibrary.org/2012/06/17/an-intro-to-rst2pdf-changing-restructured-text-into-pdfs-with-python/ http://www.blog.pythonlibrary.org/2010/05/15/manipulating-pdfs-with-python-and-pypdf/ https://www.blog.pythonlibrary.org/2010/02/14/python-windows-and-printers/ - - - - - https://wiki.wxpython.org/TitleIndex https://docs.wxpython.org/ -------- = Thanks to = Lorne White, Jeff Grimmett, Vernon Cole, Robin Dunn, Andy Robinson / Robin Becker / The ReportLab team, David Hughe (PDFViewer), Ruikai Liu / Jorj X. McKie (PyMuPDF), Cody Precord, Mike Driscoll, Pascal Faut., Dietmar Schwertberger, Chris Barker, the wxPython community... Thanks also to all known contributors or anonymous that I forgot. And finally, congratulations to the many forums and blogs for all the available examples and the help which is the strength of wxPython. ;) -------- = About this page = Date (d/m/y) Person (bot) Comments : 00/00/00 - Sean McKay (originally Created). 10/10/18 - Ecco (Updated page and published examples for wxPython Phoenix). -------- = Comments = - blah, blah, blah....