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.
Contents
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
1 # sample_one.py
2
3 #-------------------------------------------------------------------------------
4 # Name: printout.py
5 # Purpose: preview and printing class -> table/grid printing
6 #
7 # Author: Lorne White (email: lorne.white@telusplanet.net)
8 #
9 # Created:
10 # Version: 0.75
11 # Date: May 15, 2002
12 # Licence: wxWindows license
13 #-------------------------------------------------------------------------------
14 # Link:
15 # https://raw.githubusercontent.com/wxWidgets/wxPython/master/wx/lib/printout.py
16 #-------------------------------------------------------------------------------
17 # Release Notes :
18 # fixed bug for string wider than print region
19 # add index to data list after parsing total pages for paging
20 #-------------------------------------------------------------------------------
21 # 12/10/2003 - Jeff Grimmett (grimmtooth@softhome.net)
22 # o 2.5 compatability update.
23 #-------------------------------------------------------------------------------
24 # 11/23/2004 - Vernon Cole (wnvcole@peppermillcas.com)
25 # o Generalize for non-2-dimensional sequences and non-text data
26 # (can use as a simple text printer by supplying a list of strings.)
27 # o Add a small _main_ for self test
28 #-------------------------------------------------------------------------------
29
30 import os
31 import copy
32 import wx
33
34 # class PrintBase
35 # class PrintTableDraw
36 # class PrintTable
37 # class PrintGrid
38 # class SetPrintout
39 # class Frame
40 # class App
41
42 #-------------------------------------------------------------------------------
43
44 if os.name == "posix":
45 print("\nPlatform : UNIX - Linux")
46 elif os.name in ['nt', 'dos', 'ce']:
47 print("\nPlatform : Windows")
48 else:
49 print("\nPlatform : ", platform.system())
50
51 #-------------------------------------------------------------------------------
52
53 class PrintBase(object):
54 """
55 ...
56 """
57 def SetPrintFont(self, font): # set the DC font parameters
58 fattr = font["Attr"]
59 if fattr[0] == 1:
60 weight = wx.BOLD
61 else:
62 weight = wx.NORMAL
63
64 if fattr[1] == 1:
65 set_style = wx.ITALIC
66 else:
67 set_style = wx.NORMAL
68
69 underline = fattr[2]
70 fcolour = self.GetFontColour(font)
71 self.DC.SetTextForeground(fcolour)
72
73 setfont = wx.Font(font["Size"], wx.SWISS, set_style, weight, underline)
74 setfont.SetFaceName(font["Name"])
75 self.DC.SetFont(setfont)
76
77 #---------------------------------------------------------------------------
78
79 def GetFontColour(self, font):
80 fcolour = font["Colour"]
81 return wx.Colour(fcolour[0], fcolour[1], fcolour[2])
82
83
84 def OutTextRegion(self, textout, txtdraw = True):
85 textlines = textout.split('\n')
86 y = copy.copy(self.y) + self.pt_space_before
87 for text in textlines:
88 remain = 'X'
89 while remain != "":
90 vout, remain = self.SetFlow(text, self.region)
91 if self.draw == True and txtdraw == True:
92 test_out = self.TestFull(vout)
93 if self.align == wx.ALIGN_LEFT:
94 self.DC.DrawText(test_out, int(self.indent+self.pcell_left_margin), int(y))
95
96 elif self.align == wx.ALIGN_CENTRE:
97 diff = self.GetCellDiff(test_out, self.region)
98 self.DC.DrawText(test_out, int(self.indent+diff/2), y)
99
100 elif self.align == wx.ALIGN_RIGHT:
101 diff = self.GetCellDiff(test_out, self.region)
102 self.DC.DrawText(test_out, int(self.indent+diff), y)
103
104 else:
105 self.DC.DrawText(test_out, int(self.indent+self.pcell_left_margin), int(y))
106 text = remain
107 y = y + self.space
108 return y - self.space + self.pt_space_after
109
110
111 def GetCellDiff(self, text, width): # get the remaining cell size for adjustment
112 w, h = self.DC.GetTextExtent(text)
113 diff = width - w
114 if diff < 0:
115 diff = 0
116 return diff
117
118
119 def TestFull(self, text_test):
120 w, h = self.DC.GetTextExtent(text_test)
121 if w > self.region: # trouble fitting into cell
122 return self.SetChar(text_test, self.region) # fit the text to the cell size
123 else:
124 return text_test
125
126
127 def SetFlow(self, ln_text, width):
128 width = width - self.pcell_right_margin
129 text = ""
130 split = ln_text.split()
131 if len(split) == 1:
132 return ln_text, ""
133
134 try:
135 w, h = self.DC.GetTextExtent(" " + split[0])
136 if w >= width:
137 return ln_text, ""
138 except:
139 pass
140
141 cnt = 0
142 for word in split:
143 bword = " " + word # blank + word
144 length = len(bword)
145
146 w, h = self.DC.GetTextExtent(text + bword)
147 if w < width:
148 text = text + bword
149 cnt = cnt + 1
150 else:
151 remain = ' '.join(split[cnt:])
152 text = text.strip()
153 return text, remain
154
155 remain = ' '.join(split[cnt:])
156 vout = text.strip()
157 return vout, remain
158
159
160 def SetChar(self, ln_text, width): # truncate string to fit into width
161 width = width - self.pcell_right_margin - self.pcell_left_margin
162 text = ""
163 for val in ln_text:
164 w, h = self.DC.GetTextExtent(text + val)
165 if w > width:
166 text = text + ".."
167 return text # fitted text value
168 text = text + val
169 return text
170
171
172 def OutTextPageWidth(self, textout, y_out, align, indent, txtdraw = True):
173 textlines = textout.split('\n')
174 y = copy.copy(y_out)
175
176 pagew = self.parent.page_width * self.pwidth # full page width
177 w, h = self.DC.GetTextExtent(textout)
178 y_line = h
179
180 for text in textlines:
181 remain = 'X'
182 while remain != "":
183 vout, remain = self.SetFlow(text, pagew)
184 if self.draw == True and txtdraw == True:
185 test_out = vout
186 if align == wx.ALIGN_LEFT:
187 self.DC.DrawText(test_out, indent, y)
188
189 elif align == wx.ALIGN_CENTRE:
190 diff = self.GetCellDiff(test_out, pagew)
191 self.DC.DrawText(test_out, int(indent+diff/2), int(y))
192
193 elif align == wx.ALIGN_RIGHT:
194 diff = self.GetCellDiff(test_out, pagew)
195 self.DC.DrawText(test_out, int(indent+diff), int(y))
196
197 else:
198 self.DC.DrawText(test_out, indent, y_out)
199 text = remain
200 y = y + y_line
201 return y - y_line
202
203
204 def GetDate(self):
205 date, time = self.GetNow()
206 return date
207
208
209 def GetDateTime(self):
210 date, time = self.GetNow()
211 return date + ' ' + time
212
213
214 def GetNow(self):
215 now = wx.DateTime.Now()
216 date = now.FormatDate()
217 time = now.FormatTime()
218 return date, time
219
220
221 def SetPreview(self, preview):
222 self.preview = preview
223
224
225 def SetPSize(self, width, height):
226 self.pwidth = width/self.scale
227 self.pheight = height/self.scale
228
229
230 def SetScale(self, scale):
231 self.scale = scale
232
233
234 def SetPTSize(self, width, height):
235 self.ptwidth = width
236 self.ptheight = height
237
238
239 def getWidth(self):
240 return self.sizew
241
242
243 def getHeight(self):
244 return self.sizeh
245
246 #-------------------------------------------------------------------------------
247
248 class PrintTableDraw(wx.ScrolledWindow, PrintBase):
249 """
250 ...
251 """
252 def __init__(self, parent, DC, size):
253 self.parent = parent
254 self.DC = DC
255 self.scale = parent.scale
256 self.width = size[0]
257 self.height = size[1]
258 self.SetDefaults()
259
260 #---------------------------------------------------------------------------
261
262 def SetDefaults(self):
263 self.page = 1
264 self.total_pages = None
265
266 self.page_width = self.parent.page_width
267 self.page_height = self.parent.page_height
268
269 self.left_margin = self.parent.left_margin
270 self.right_margin = self.parent.right_margin
271
272 self.top_margin = self.parent.top_margin
273 self.bottom_margin = self.parent.bottom_margin
274 self.cell_left_margin = self.parent.cell_left_margin
275 self.cell_right_margin = self.parent.cell_right_margin
276
277 self.label_colour = self.parent.label_colour
278
279 self.row_line_colour = self.parent.row_line_colour
280 self.row_line_size = self.parent.row_line_size
281
282 self.row_def_line_colour = self.parent.row_def_line_colour
283 self.row_def_line_size = self.parent.row_def_line_size
284
285 self.column_line_colour = self.parent.column_line_colour
286 self.column_line_size = self.parent.column_line_size
287
288 self.column_def_line_size = self.parent.column_def_line_size
289 self.column_def_line_colour = self.parent.column_def_line_colour
290
291 self.text_font = self.parent.text_font
292
293 self.label_font = self.parent.label_font
294
295
296 def AdjustValues(self):
297 self.vertical_offset = self.pheight * self.parent.vertical_offset
298 self.horizontal_offset = self.pheight * self.parent.horizontal_offset
299
300 self.pcell_left_margin = self.pwidth * self.cell_left_margin
301 self.pcell_right_margin = self.pwidth * self.cell_right_margin
302 self.ptop_margin = self.pheight * self.top_margin
303 self.pbottom_margin = self.pheight * self.bottom_margin
304
305 self.pheader_margin = self.pheight * self.parent.header_margin
306 self.pfooter_margin = self.pheight * self.parent.footer_margin
307
308 self.cell_colour = self.parent.set_cell_colour
309 self.cell_text = self.parent.set_cell_text
310
311 self.column = []
312 self.column_align = []
313 self.column_bgcolour = []
314 self.column_txtcolour = []
315
316 set_column_align = self.parent.set_column_align
317 set_column_bgcolour = self.parent.set_column_bgcolour
318 set_column_txtcolour = self.parent.set_column_txtcolour
319
320 pos_x = self.left_margin * self.pwidth + self.horizontal_offset # left margin
321 self.column.append(pos_x)
322
323 # Module logic expects two dimensional data -- fix input if needed
324 if isinstance(self.data, str):
325 self.data = [[copy.copy(self.data)]] # a string becomes a single cell
326 try:
327 rows = len(self.data)
328 except TypeError:
329 self.data = [[str(self.data)]] # a non-iterable becomes a single cell
330 rows = 1
331 first_value = self.data[0]
332
333 if isinstance(first_value, str): # A sequence of strings
334 if self.label == [] and self.set_column == []:
335 data = []
336 for x in self.data: # Becomes one column
337 data.append([x])
338 else:
339 data = [self.data] # Becames one row
340 self.data = data
341 first_value = data[0]
342 try:
343 column_total = len(first_value)
344 except TypeError: # A sequence of non-iterables
345 if self.label == [] and self.set_column == []:
346 data = [] # Becomes one column
347 for x in self.data:
348 data.append([str(x)])
349 column_total = 1
350 else:
351 data = [self.data] # Becomes one row
352 column_total = len(self.data)
353 self.data = data
354 first_value = data[0]
355
356 if self.set_column == []:
357 table_width = self.page_width - self.left_margin - self.right_margin
358 if self.label == []:
359 temp = first_value
360 else:
361 temp = self.label
362 width = table_width/(len(temp))
363 for val in temp:
364 column_width = width * self.pwidth
365 pos_x = pos_x + column_width
366 self.column.append(pos_x) # position of each column
367 else:
368 for val in self.set_column:
369 column_width = val * self.pwidth
370 pos_x = pos_x + column_width
371 self.column.append(pos_x) # position of each column
372
373 if pos_x > self.page_width * self.pwidth: # check if it fits in page
374 print("Warning, Too Wide for Page")
375 return
376
377 if self.label != []:
378 if len(self.column) -1 != len(self.label):
379 print("Column Settings Incorrect", "\nColumn Value: " + str(self.column), "\nLabel Value: " + str(self.label))
380 return
381
382 if column_total != len(self.column) -1:
383 print("Cannot fit", first_value, 'in', len(self.column)-1, 'columns.')
384 return
385
386 for col in range(column_total):
387 try:
388 align = set_column_align[col] # check if custom column alignment
389 except:
390 align = wx.ALIGN_LEFT
391 self.column_align.append(align)
392
393 try:
394 colour = set_column_bgcolour[col] # check if custom column background colour
395 except:
396 colour = self.parent.column_colour
397 self.column_bgcolour.append(colour)
398
399 try:
400 colour = set_column_txtcolour[col] # check if custom column text colour
401 except:
402 colour = self.GetFontColour(self.parent.text_font)
403 self.column_txtcolour.append(colour)
404
405
406 def SetPointAdjust(self):
407 f = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL) # setup using 10 point
408 self.DC.SetFont(f)
409 f.SetFaceName(self.text_font["Name"])
410 x, y = self.DC.GetTextExtent("W")
411
412 self.label_pt_space_before = self.parent.label_pt_adj_before * y/10 # extra spacing for label per point value
413 self.label_pt_space_after = self.parent.label_pt_adj_after * y/10
414
415 self.text_pt_space_before = self.parent.text_pt_adj_before * y/10 # extra spacing for row text per point value
416 self.text_pt_space_after = self.parent.text_pt_adj_after * y/10
417
418
419 def SetPage(self, page):
420 self.page = page
421
422
423 def SetColumns(self, col):
424 self.column = col
425
426
427 def OutCanvas(self):
428 self.AdjustValues()
429 self.SetPointAdjust()
430
431 self.y_start = self.ptop_margin + self.vertical_offset
432 self.y_end = self.parent.page_height * self.pheight - self.pbottom_margin + self.vertical_offset
433
434 self.SetPrintFont(self.label_font)
435
436 x, y = self.DC.GetTextExtent("W")
437 self.label_space = y
438
439 self.SetPrintFont(self.text_font)
440
441 x, y = self.DC.GetTextExtent("W")
442 self.space = y
443
444 if self.total_pages is None:
445 self.GetTotalPages() # total pages for display/printing
446
447 self.data_cnt = self.page_index[self.page-1]
448
449 self.draw = True
450 self.PrintHeader()
451 self.PrintFooter()
452 self.OutPage()
453
454
455 def GetTotalPages(self):
456 self.data_cnt = 0
457 self.draw = False
458 self.page_index = [0]
459
460 cnt = 0
461 while 1:
462 test = self.OutPage()
463 self.page_index.append(self.data_cnt)
464 if test == False:
465 break
466 cnt = cnt + 1
467
468 self.total_pages = cnt + 1
469
470
471 def OutPage(self):
472 self.y = self.y_start
473 self.end_x = self.column[-1]
474
475 if self.data_cnt < len(self.data): # if there data for display on the page
476 if self.label != []: # check if header defined
477 self.PrintLabel()
478 else:
479 return False
480
481 for val in self.data:
482 try:
483 row_val = self.data[self.data_cnt]
484 except:
485 self.FinishDraw()
486 return False
487
488 max_y = self.PrintRow(row_val, False) # test to see if row will fit in remaining space
489 test = max_y + self.space
490
491 if test > self.y_end:
492 break
493
494 self.ColourRowCells(max_y-self.y+self.space) # colour the row/column
495 max_y = self.PrintRow(row_val, True) # row fits - print text
496 self.DrawGridLine() # top line of cell
497 self.y = max_y + self.space
498
499 if self.y > self.y_end:
500 break
501
502 self.data_cnt = self.data_cnt + 1
503
504 self.FinishDraw()
505
506 if self.data_cnt == len(self.data): # last value in list
507 return False
508
509 return True
510
511
512 def PrintLabel(self):
513 self.pt_space_before = self.label_pt_space_before # set the point spacing
514 self.pt_space_after = self.label_pt_space_after
515
516 self.LabelColorRow(self.label_colour)
517 self.SetPrintFont(self.label_font)
518
519 self.col = 0
520 max_y = 0
521 for vtxt in self.label:
522 self.region = self.column[self.col+1] - self.column[self.col]
523 self.indent = self.column[self.col]
524
525 self.align = wx.ALIGN_LEFT
526
527 max_out = self.OutTextRegion(vtxt, True)
528 if max_out > max_y:
529 max_y = max_out
530 self.col = self.col + 1
531
532 self.DrawGridLine() # top line of label
533 self.y = max_y + self.label_space
534
535
536 def PrintHeader(self): # print the header array
537 if self.draw == False:
538 return
539
540 for val in self.parent.header:
541 self.SetPrintFont(val["Font"])
542
543 header_indent = val["Indent"] * self.pwidth
544 text = val["Text"]
545
546 htype = val["Type"]
547 if htype == "Date":
548 addtext = self.GetDate()
549 elif htype == "Date & Time":
550 addtext = self.GetDateTime()
551 else:
552 addtext = ""
553
554 self.OutTextPageWidth(text+addtext, self.pheader_margin, val["Align"], header_indent, True)
555
556
557 def PrintFooter(self): # print the header array
558 if self.draw == False:
559 return
560
561 footer_pos = self.parent.page_height * self.pheight - self.pfooter_margin + self.vertical_offset
562 for val in self.parent.footer:
563 self.SetPrintFont(val["Font"])
564
565 footer_indent = val["Indent"] * self.pwidth
566 text = val["Text"]
567
568 ftype = val["Type"]
569 if ftype == "Pageof":
570 addtext = "Page " + str(self.page) + " of " + str(self.total_pages)
571 elif ftype == "Page":
572 addtext = "Page " + str(self.page)
573 elif ftype == "Num":
574 addtext = str(self.page)
575 elif ftype == "Date":
576 addtext = self.GetDate()
577 elif ftype == "Date & Time":
578 addtext = self.GetDateTime()
579 else:
580 addtext = ""
581
582 self.OutTextPageWidth(text+addtext, footer_pos, val["Align"], footer_indent, True)
583
584
585 def LabelColorRow(self, colour):
586 brush = wx.Brush(colour, wx.SOLID)
587 self.DC.SetBrush(brush)
588 height = self.label_space + self.label_pt_space_before + self.label_pt_space_after
589 self.DC.DrawRectangle(self.column[0], self.y,
590 self.end_x-self.column[0]+1, height)
591
592
593 def ColourRowCells(self, height):
594 if self.draw == False:
595 return
596
597 col = 0
598 for colour in self.column_bgcolour:
599 cellcolour = self.GetCellColour(self.data_cnt, col)
600 if cellcolour is not None:
601 colour = cellcolour
602
603 brush = wx.Brush(colour, wx.SOLID)
604 self.DC.SetBrush(brush)
605 self.DC.SetPen(wx.Pen(wx.Colour('WHITE'), 0))
606
607 start_x = self.column[col]
608 width = self.column[col+1] - start_x + 2
609 self.DC.DrawRectangle(int(start_x), int(self.y), int(width), int(height))
610 col = col + 1
611
612
613 def PrintRow(self, row_val, draw = True, align = wx.ALIGN_LEFT):
614 self.SetPrintFont(self.text_font)
615
616 self.pt_space_before = self.text_pt_space_before # set the point spacing
617 self.pt_space_after = self.text_pt_space_after
618
619 self.col = 0
620 max_y = 0
621 for vtxt in row_val:
622 if not isinstance(vtxt, str):
623 vtxt = str(vtxt)
624 self.region = self.column[self.col+1] - self.column[self.col]
625 self.indent = self.column[self.col]
626 self.align = self.column_align[self.col]
627
628 fcolour = self.column_txtcolour[self.col] # set font colour
629 celltext = self.GetCellTextColour(self.data_cnt, self.col)
630 if celltext is not None:
631 fcolour = celltext # override the column colour
632
633 self.DC.SetTextForeground(fcolour)
634
635 max_out = self.OutTextRegion(vtxt, draw)
636 if max_out > max_y:
637 max_y = max_out
638 self.col = self.col + 1
639 return max_y
640
641
642 def GetCellColour(self, row, col): # check if custom colour defined for the cell background
643 try:
644 set = self.cell_colour[row]
645 except:
646 return None
647 try:
648 colour = set[col]
649 return colour
650 except:
651 return None
652
653
654 def GetCellTextColour(self, row, col): # check if custom colour defined for the cell text
655 try:
656 set = self.cell_text[row]
657 except:
658 return None
659 try:
660 colour = set[col]
661 return colour
662 except:
663 return None
664
665
666 def FinishDraw(self):
667 self.DrawGridLine() # draw last row line
668 self.DrawColumns() # draw all vertical lines
669
670
671 def DrawGridLine(self):
672 if self.draw == True \
673 and len(self.column) > 2: #supress grid lines if only one column
674 try:
675 size = self.row_line_size[self.data_cnt]
676 except:
677 size = self.row_def_line_size
678
679 if size < 1: return
680
681 try:
682 colour = self.row_line_colour[self.data_cnt]
683 except:
684 colour = self.row_def_line_colour
685
686 self.DC.SetPen(wx.Pen(colour, size))
687
688 y_out = self.y
689 #y_out = self.y + self.pt_space_before + self.pt_space_after # adjust for extra spacing
690 self.DC.DrawLine(self.column[0], y_out, self.end_x, y_out)
691
692
693 def DrawColumns(self):
694 if self.draw == True \
695 and len(self.column) > 2: #surpress grid line if only one column
696 col = 0
697 for val in self.column:
698 try:
699 size = self.column_line_size[col]
700 except:
701 size = self.column_def_line_size
702
703 if size < 1: continue
704
705 try:
706 colour = self.column_line_colour[col]
707 except:
708 colour = self.column_def_line_colour
709
710 indent = val
711
712 self.DC.SetPen(wx.Pen(colour, size))
713 self.DC.DrawLine(indent, self.y_start, indent, self.y)
714 col = col + 1
715
716
717 def DrawText(self):
718 self.DoRefresh()
719
720
721 def DoDrawing(self, DC):
722 size = DC.GetSize()
723 self.DC = DC
724
725 self.DrawText()
726
727 self.sizew = DC.MaxY()
728 self.sizeh = DC.MaxX()
729
730 #-------------------------------------------------------------------------------
731
732 class PrintTable(object):
733 """
734 ...
735 """
736 def __init__(self, parentFrame=None):
737 self.data = []
738 self.set_column = []
739 self.label = []
740 self.header = []
741 self.footer = []
742
743 self.set_column_align = {}
744 self.set_column_bgcolour = {}
745 self.set_column_txtcolour = {}
746 self.set_cell_colour = {}
747 self.set_cell_text = {}
748 self.column_line_size = {}
749 self.column_line_colour = {}
750 self.row_line_size = {}
751 self.row_line_colour = {}
752
753 self.parentFrame = parentFrame
754 self.SetPreviewSize()
755
756 self.printData = wx.PrintData()
757 self.scale = 1.0
758
759 self.SetParms()
760 self.SetColors()
761 self.SetFonts()
762 self.TextSpacing()
763
764 self.SetPrinterOffset()
765 self.SetHeaderValue()
766 self.SetFooterValue()
767 self.SetMargins()
768 self.SetPortrait()
769
770 #---------------------------------------------------------------------------
771
772 def SetPreviewSize(self, position = wx.Point(0, 0), size="Full"):
773 if size == "Full":
774 r = wx.GetClientDisplayRect()
775 self.preview_frame_size = r.GetSize()
776 self.preview_frame_pos = r.GetPosition()
777 else:
778 self.preview_frame_size = size
779 self.preview_frame_pos = position
780
781
782 def SetPaperId(self, paper):
783 self.printData.SetPaperId(paper)
784
785
786 def SetOrientation(self, orient):
787 self.printData.SetOrientation(orient)
788
789
790 def SetColors(self):
791 self.row_def_line_colour = wx.Colour('BLACK')
792 self.row_def_line_size = 1
793
794 self.column_def_line_colour = wx.Colour('BLACK')
795 self.column_def_line_size = 1
796 self.column_colour = wx.Colour('WHITE')
797
798 self.label_colour = wx.Colour('LIGHT GREY')
799
800
801 def SetFonts(self):
802 self.label_font = { "Name": self.default_font_name, "Size": 12, "Colour": [0, 0, 0], "Attr": [0, 0, 0] }
803 self.text_font = { "Name": self.default_font_name, "Size": 10, "Colour": [0, 0, 0], "Attr": [0, 0, 0] }
804
805
806 def TextSpacing(self):
807 self.label_pt_adj_before = 0 # point adjustment before and after the label text
808 self.label_pt_adj_after = 0
809
810 self.text_pt_adj_before = 0 # point adjustment before and after the row text
811 self.text_pt_adj_after = 0
812
813
814 def SetLabelSpacing(self, before, after): # method to set the label space adjustment
815 self.label_pt_adj_before = before
816 self.label_pt_adj_after = after
817
818
819 def SetRowSpacing(self, before, after): # method to set the row space adjustment
820 self.text_pt_adj_before = before
821 self.text_pt_adj_after = after
822
823
824 def SetPrinterOffset(self): # offset to adjust for printer
825 self.vertical_offset = -0.1
826 self.horizontal_offset = -0.1
827
828
829 def SetHeaderValue(self):
830 self.header_margin = 0.25
831 self.header_font = { "Name": self.default_font_name, "Size": 11, "Colour": [0, 0, 0], "Attr": [0, 0, 0] }
832 self.header_align = wx.ALIGN_CENTRE
833 self.header_indent = 0
834 self.header_type = "Text"
835
836
837 def SetFooterValue(self):
838 self.footer_margin = 0.7
839 self.footer_font = { "Name": self.default_font_name, "Size": 11, "Colour": [0, 0, 0], "Attr": [0, 0, 0] }
840 self.footer_align = wx.ALIGN_CENTRE
841 self.footer_indent = 0
842 self.footer_type = "Pageof"
843
844
845 def SetMargins(self):
846 self.left_margin = 0.5
847 self.right_margin = 0.5 # only used if no column sizes
848
849 self.top_margin = 0.8
850 self.bottom_margin = 1.0
851 self.cell_left_margin = 0.1
852 self.cell_right_margin = 0.1
853
854
855 def SetPortrait(self):
856 self.printData.SetPaperId(wx.PAPER_LETTER)
857 self.printData.SetOrientation(wx.PORTRAIT)
858 self.page_width = 8.5
859 self.page_height = 11.0
860
861
862 def SetLandscape(self):
863 self.printData.SetOrientation(wx.LANDSCAPE)
864 self.page_width = 11.0
865 self.page_height = 8.5
866
867
868 def SetParms(self):
869 self.ymax = 1
870 self.xmax = 1
871 self.page = 1
872 self.total_pg = 100
873
874 self.preview = None
875 self.page = 0
876
877 self.default_font_name = "Arial"
878 self.default_font = { "Name": self.default_font_name, "Size": 10, "Colour": [0, 0, 0], "Attr": [0, 0, 0] }
879
880
881 def SetColAlignment(self, col, align=wx.ALIGN_LEFT):
882 self.set_column_align[col] = align
883
884
885 def SetColBackgroundColour(self, col, colour):
886 self.set_column_bgcolour[col] = colour
887
888
889 def SetColTextColour(self, col, colour):
890 self.set_column_txtcolour[col] = colour
891
892
893 def SetCellColour(self, row, col, colour): # cell background colour
894 try:
895 set = self.set_cell_colour[row] # test if row already exists
896 try:
897 set[col] = colour # test if column already exists
898 except:
899 set = { col: colour } # create the column value
900 except:
901 set = { col: colour } # create the column value
902
903 self.set_cell_colour[row] = set # create dictionary item for colour settings
904
905
906 def SetCellText(self, row, col, colour): # font colour for custom cells
907 try:
908 set = self.set_cell_text[row] # test if row already exists
909 try:
910 set[col] = colour # test if column already exists
911 except:
912 set = { col: colour } # create the column value
913 except:
914 set = { col: colour } # create the column value
915
916 self.set_cell_text[row] = set # create dictionary item for colour settings
917
918
919 def SetColumnLineSize(self, col, size): # column line size
920 self.column_line_size[col] = size # create dictionary item for column line settings
921
922
923 def SetColumnLineColour(self, col, colour):
924 self.column_line_colour[col] = colour
925
926
927 def SetRowLineSize(self, row, size):
928 self.row_line_size[row] = size
929
930
931 def SetRowLineColour(self, row, colour):
932 self.row_line_colour[row] = colour
933
934
935 def GetColour(self, colour): # returns colours based from wxColour value
936 red = colour.Red()
937 blue = colour.Blue()
938 green = colour.Green()
939 return [red, green, blue ]
940
941
942 def SetHeader(self, text = "", type = "Text", font=None, align = None, indent = None, colour = None, size = None):
943 set = { "Text": text }
944
945 if font is None:
946 set["Font"] = copy.copy(self.default_font)
947 else:
948 set["Font"] = font
949
950 if colour is not None:
951 setfont = set["Font"]
952 setfont["Colour"] = self.GetColour(colour)
953
954 if size is not None:
955 setfont = set["Font"]
956 setfont["Size"] = size
957
958 if align is None:
959 set["Align"] = self.header_align
960 else:
961 set["Align"] = align
962
963 if indent is None:
964 set["Indent"] = self.header_indent
965 else:
966 set["Indent"] = indent
967
968 if type is None:
969 set["Type"] = self.header_type
970 else:
971 set["Type"] = type
972
973 self.header.append(set)
974
975
976 def SetFooter(self, text = "", type = None, font=None, align = None, indent = None, colour = None, size = None):
977 set = { "Text": text }
978
979 if font is None:
980 set["Font"] = copy.copy(self.default_font)
981 else:
982 set["Font"] = font
983
984 if colour is not None:
985 setfont = set["Font"]
986 setfont["Colour"] = self.GetColour(colour)
987
988 if size is not None:
989 setfont = set["Font"]
990 setfont["Size"] = size
991
992 if align is None:
993 set["Align"] = self.footer_align
994 else:
995 set["Align"] = align
996
997 if indent is None:
998 set["Indent"] = self.footer_indent
999 else:
1000 set["Indent"] = indent
1001
1002 if type is None:
1003 set["Type"] = self.footer_type
1004 else:
1005 set["Type"] = type
1006
1007 self.footer.append(set)
1008
1009
1010 def Preview(self):
1011 data = wx.PrintDialogData(self.printData)
1012
1013 text = self.parentFrame.tc.GetValue()
1014
1015 printout = SetPrintout(self, text)
1016 printout2 = SetPrintout(self, text)
1017 self.preview = wx.PrintPreview(printout, printout2, data)
1018 if not self.preview.IsOk():
1019 wx.MessageBox("There was a problem printing!", "Printing", wx.OK)
1020 return
1021
1022 self.preview.SetZoom(60) # initial zoom value
1023 frame = wx.PreviewFrame(self.preview, self.parentFrame, "Print preview")
1024
1025 frame.Initialize()
1026 if self.parentFrame:
1027 frame.SetPosition(self.preview_frame_pos)
1028 frame.SetSize(self.preview_frame_size)
1029 frame.Show(True)
1030
1031
1032 def Print(self):
1033 pdd = wx.PrintDialogData(self.printData)
1034 printer = wx.Printer(pdd)
1035
1036 text = self.parentFrame.tc.GetValue()
1037
1038 printout = SetPrintout(self, text)
1039 if not printer.Print(self.parentFrame, printout):
1040 if wx.Printer.GetLastError() == wx.PRINTER_ERROR:
1041 wx.MessageBox("There was a problem printing.\n"
1042 "Perhaps your current printer is not set correctly?",
1043 "Printing", wx.OK)
1044 else:
1045 self.printData = wx.PrintData( printer.GetPrintDialogData().GetPrintData() )
1046 printout.Destroy()
1047
1048
1049 def DoDrawing(self, DC):
1050 size = DC.GetSize()
1051
1052 table = PrintTableDraw(self, DC, size)
1053 table.data = self.data
1054 table.set_column = self.set_column
1055 table.label = self.label
1056 table.SetPage(self.page)
1057
1058 if self.preview is None:
1059 table.SetPSize(size[0]/self.page_width, size[1]/self.page_height)
1060 table.SetPTSize(size[0], size[1])
1061 table.SetPreview(False)
1062 else:
1063 if self.preview == 1:
1064 table.scale = self.scale
1065 table.SetPSize(size[0]/self.page_width, size[1]/self.page_height)
1066 else:
1067 table.SetPSize(self.pwidth, self.pheight)
1068
1069 table.SetPTSize(self.ptwidth, self.ptheight)
1070 table.SetPreview(self.preview)
1071
1072 table.OutCanvas()
1073 self.page_total = table.total_pages # total display pages
1074
1075 self.ymax = DC.MaxY()
1076 self.xmax = DC.MaxX()
1077
1078 self.sizeh = size[0]
1079 self.sizew = size[1]
1080
1081
1082 def GetTotalPages(self):
1083 self.page_total = 100
1084 return self.page_total
1085
1086
1087 def HasPage(self, page):
1088 if page <= self.page_total:
1089 return True
1090 else:
1091 return False
1092
1093
1094 def SetPage(self, page):
1095 self.page = page
1096
1097
1098 def SetPageSize(self, width, height):
1099 self.pwidth, self.pheight = width, height
1100
1101
1102 def SetTotalSize(self, width, height):
1103 self.ptwidth, self.ptheight = width, height
1104
1105
1106 def SetPreview(self, preview, scale):
1107 self.preview = preview
1108 self.scale = scale
1109
1110
1111 def SetTotalSize(self, width, height):
1112 self.ptwidth = width
1113 self.ptheight = height
1114
1115 #-------------------------------------------------------------------------------
1116
1117 class PrintGrid(object):
1118 """
1119 ...
1120 """
1121 def __init__(self, parent, grid, format = [], total_col = None, total_row = None):
1122 if total_row is None:
1123 total_row = grid.GetNumberRows()
1124 if total_col is None:
1125 total_col = grid.GetNumberCols()
1126
1127 self.total_row = total_row
1128 self.total_col = total_col
1129 self.grid = grid
1130
1131 data = []
1132 for row in range(total_row):
1133 row_val = []
1134 value = grid.GetRowLabelValue(row)
1135 row_val.append(value)
1136
1137 for col in range(total_col):
1138 value = grid.GetCellValue(row, col)
1139 row_val.append(value)
1140 data.append(row_val)
1141
1142 label = [""]
1143 for col in range(total_col):
1144 value = grid.GetColLabelValue(col)
1145 label.append(value)
1146
1147 self.table = PrintTable(parent)
1148 self.table.cell_left_margin = 0.0
1149 self.table.cell_right_margin = 0.0
1150
1151 self.table.label = label
1152 self.table.set_column = format
1153 self.table.data = data
1154
1155 #---------------------------------------------------------------------------
1156
1157 def GetTable(self):
1158 return self.table
1159
1160
1161 def SetAttributes(self):
1162 for row in range(self.total_row):
1163 for col in range(self.total_col):
1164 colour = self.grid.GetCellTextColour(row, col-1)
1165 self.table.SetCellText(row, col, colour)
1166
1167 colour = self.grid.GetCellBackgroundColour(row, col-1)
1168 self.table.SetCellColour(row, col, colour)
1169
1170
1171 def Preview(self):
1172 self.table.Preview()
1173
1174
1175 def Print(self):
1176 self.table.Print()
1177
1178 #-------------------------------------------------------------------------------
1179
1180 class SetPrintout(wx.Printout):
1181 """
1182 ...
1183 """
1184 def __init__(self, canvas, text):
1185 wx.Printout.__init__(self)
1186
1187 self.canvas = canvas
1188 self.end_pg = 1000
1189 self.lines = text
1190
1191 #---------------------------------------------------------------------------
1192
1193 def OnBeginDocument(self, start, end):
1194 return super(SetPrintout, self).OnBeginDocument(start, end)
1195
1196
1197 def OnEndDocument(self):
1198 super(SetPrintout, self).OnEndDocument()
1199
1200
1201 def HasPage(self, page):
1202 try:
1203 end = self.canvas.HasPage(page)
1204 return end
1205 except:
1206 return True
1207
1208
1209 def GetPageInfo(self):
1210 try:
1211 self.end_pg = self.canvas.GetTotalPages()
1212 except:
1213 pass
1214
1215 end_pg = self.end_pg
1216 str_pg = 1
1217 return (str_pg, end_pg, str_pg, end_pg)
1218
1219
1220 def OnPreparePrinting(self):
1221 super(SetPrintout, self).OnPreparePrinting()
1222
1223
1224 def OnBeginPrinting(self):
1225 dc = self.GetDC()
1226
1227 self.preview = self.IsPreview()
1228 if (self.preview):
1229 self.pixelsPerInch = self.GetPPIScreen()
1230 else:
1231 self.pixelsPerInch = self.GetPPIPrinter()
1232
1233 (w, h) = dc.GetSize()
1234 scaleX = float(w) / 1000
1235 scaleY = float(h) / 1000
1236 self.printUserScale = min(scaleX, scaleY)
1237
1238 super(SetPrintout, self).OnBeginPrinting()
1239
1240
1241 def GetSize(self):
1242 self.psizew, self.psizeh = self.GetPPIPrinter()
1243 return self.psizew, self.psizeh
1244
1245
1246 def GetTotalSize(self):
1247 self.ptsizew, self.ptsizeh = self.GetPageSizePixels()
1248 return self.ptsizew, self.ptsizeh
1249
1250
1251 def OnPrintPage(self, page):
1252 dc = self.GetDC()
1253 (w, h) = dc.GetSize()
1254 scaleX = float(w) / 1000
1255 scaleY = float(h) / 1000
1256 self.printUserScale = min(scaleX, scaleY)
1257 dc.SetUserScale(self.printUserScale, self.printUserScale)
1258
1259 self.preview = self.IsPreview()
1260
1261 self.canvas.SetPreview(self.preview, self.printUserScale)
1262 self.canvas.SetPage(page)
1263
1264 self.ptsizew, self.ptsizeh = self.GetPageSizePixels()
1265 self.canvas.SetTotalSize(self.ptsizew, self.ptsizeh)
1266
1267 self.psizew, self.psizeh = self.GetPPIPrinter()
1268 self.canvas.SetPageSize(self.psizew, self.psizeh)
1269
1270 self.canvas.DoDrawing(dc)
1271 return True
1272
1273 #-------------------------------------------------------------------------------
1274
1275 class Frame(wx.Frame):
1276 """
1277 ...
1278 """
1279 def __init__(self, parent, id, title=""):
1280 wx.Frame.__init__(self,
1281 parent,
1282 id,
1283 title,
1284 size=(600, 450),
1285 style=wx.DEFAULT_FRAME_STYLE)
1286
1287 #------------
1288
1289 # Simplified init method.
1290 self.SetProperties()
1291 self.CreateMenu()
1292 self.CreateCtrls()
1293 self.CreatePrintData()
1294 self.BindEvents()
1295 self.DoLayout()
1296
1297 #------------
1298
1299 self.CenterOnScreen()
1300
1301 #---------------------------------------------------------------------------
1302
1303 def SetProperties(self):
1304 """
1305 Set the main frame properties (title, icon...).
1306 """
1307
1308 frameicon = wx.Icon("Icons/wxWidgets.ico")
1309 self.SetIcon(frameicon)
1310
1311 #------------
1312
1313 self.SetTitle("Dummy wx frame for testing printout.py")
1314
1315
1316 def CreateMenu(self):
1317 """
1318 ...
1319 """
1320
1321 menub = wx.MenuBar()
1322
1323 fmenu = wx.Menu()
1324 fmenu.Append(wx.ID_PREVIEW, "Print pre&view\tCtrl+V")
1325 fmenu.Append(wx.ID_PRINT, "&Print\tCtrl+P")
1326 fmenu.AppendSeparator()
1327 fmenu.Append(wx.ID_EXIT, "E&xit\tCtrl+X")
1328 menub.Append(fmenu, "&File")
1329
1330 self.SetMenuBar(menub)
1331
1332
1333 def CreateCtrls(self):
1334 """
1335 ...
1336 """
1337
1338 font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
1339 font.SetWeight(wx.BOLD)
1340 font.SetPointSize(10)
1341
1342 #------------
1343
1344 # First create the controls.
1345 self.panel = wx.Panel(self, -1,
1346 style=wx.BORDER_THEME|
1347 wx.TAB_TRAVERSAL)
1348
1349 self.demo = wx.StaticText(self.panel,
1350 id=-1,
1351 label="Demonstrating :")
1352 self.demo.SetFont(font)
1353
1354 self.info = wx.StaticText(self.panel,
1355 id=-1,
1356 label="1) Direct printing,\n"
1357 "2) Printout class,\n"
1358 "3) PrintTable class,\n"
1359 "4) Preview,\n"
1360 "5) Menu")
1361 self.info.SetForegroundColour("red")
1362 font.SetWeight(wx.NORMAL)
1363 self.info.SetFont(font)
1364
1365 text = ('This the first line of text.\n'\
1366 'This is the second line\nand the third. The fourth will be the number 4.0.\n'\
1367 '4.0\n'\
1368 'This is the fifth line, but by design it is too long to fit in the width of a standard'\
1369 'page, so it will be forced to wrap around in order to fit without having'\
1370 'some of its verbose verbage truncated.\n'\
1371 'Here we have the final line.')
1372
1373 self.tc = wx.TextCtrl(self.panel,
1374 id=-1,
1375 size=(200, -1),
1376 value=text,
1377 style=wx.TE_MULTILINE|wx.TE_DONTWRAP)
1378
1379 self.btnPreview = wx.Button(self.panel,
1380 id=wx.ID_PREVIEW,
1381 label="Print pre&view")
1382 self.btnPreview.SetFocus()
1383
1384 self.btnPrint = wx.Button(self.panel,
1385 id=wx.ID_PRINT,
1386 label="&Print")
1387
1388 self.btnClose = wx.Button(self.panel,
1389 id=wx.ID_CLOSE,
1390 label="E&xit")
1391
1392
1393 def CreatePrintData(self):
1394 """
1395 Create printing data.
1396 """
1397
1398 self.ptbl = PrintTable(self)
1399 self.ptbl.SetHeader('This is the test HEADER')
1400 self.ptbl.SetFooter()
1401
1402 #------------
1403
1404 # a single sequence will print out as a single column with no borders ...
1405 self.ptbl.data = (self.tc.GetValue())
1406
1407
1408 def BindEvents(self):
1409 """
1410 Bind all the events related to my application.
1411 """
1412
1413 # Bind some menu events to an events handler.
1414 self.Bind(wx.EVT_MENU, self.OnPrintPreview, id=wx.ID_PREVIEW)
1415 self.Bind(wx.EVT_MENU, self.OnPrint, id=wx.ID_PRINT)
1416 self.Bind(wx.EVT_MENU, self.OnClose, id=wx.ID_EXIT)
1417
1418 # Bind the close event to an event handler.
1419 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
1420
1421 # Bind some buttons events to an events handler.
1422 self.Bind(wx.EVT_BUTTON, self.OnPrintPreview, self.btnPreview)
1423 self.Bind(wx.EVT_BUTTON, self.OnPrint, self.btnPrint)
1424 self.Bind(wx.EVT_BUTTON, self.OnClose, self.btnClose)
1425
1426
1427 def DoLayout(self):
1428 """
1429 Manage widgets Layout.
1430 """
1431
1432 # MainSizer is the top-level one that manages everything.
1433 mainSizer = wx.BoxSizer(wx.VERTICAL)
1434
1435 #------------
1436
1437 hBox1 = wx.BoxSizer(wx.HORIZONTAL)
1438 hBox1.Add(self.info, 0, wx.ALL, 15)
1439
1440 #------------
1441
1442 hBox2 = wx.BoxSizer(wx.HORIZONTAL)
1443 hBox2.Add(self.btnPreview, 0, wx.ALL, 10)
1444 hBox2.Add(self.btnPrint, 0, wx.ALL, 10)
1445 hBox2.Add(self.btnClose, 0, wx.ALL, 10)
1446
1447 #------------
1448
1449 mainSizer.Add(self.demo, 0, wx.ALL, 10)
1450 mainSizer.Add(wx.StaticLine(self.panel),
1451 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5)
1452 mainSizer.Add(self.tc, 1, wx.EXPAND | wx.ALL, 15)
1453 mainSizer.Add(hBox1, 0, wx.ALL, 5)
1454 mainSizer.Add(hBox2, 0, wx.ALL, 5)
1455
1456 #------------
1457
1458 # Finally, tell the panel to use the mainSizer for layout.
1459 self.panel.SetSizer(mainSizer)
1460
1461
1462 def OnPrintPreview(self, event):
1463 self.ptbl.data = (self.tc.GetValue())
1464 self.ptbl.Preview()
1465
1466
1467 def OnPrint(self, event):
1468 self.ptbl.data = (self.tc.GetValue())
1469 self.ptbl.Print()
1470
1471
1472 def OnClose(self, evt):
1473 self.Close()
1474
1475
1476 def OnCloseWindow(self, event):
1477 """
1478 ...
1479 """
1480
1481 self.Destroy()
1482
1483 #-------------------------------------------------------------------------------
1484
1485 class App(wx.App):
1486 """
1487 ...
1488 """
1489 def OnInit(self):
1490
1491 #------------
1492
1493 self.locale = wx.Locale(wx.LANGUAGE_ENGLISH)
1494
1495 #------------
1496
1497 frame = Frame(None, id=-1)
1498 self.SetTopWindow(frame)
1499 frame.Show(True)
1500
1501 return True
1502
1503 #-------------------------------------------------------------------------------
1504
1505 def main():
1506 app = App(False)
1507 app.MainLoop()
1508
1509 #-------------------------------------------------------------------------------
1510
1511 if __name__ == "__main__" :
1512 main()
Download source
Additional Information
Link :
https://wiki.wxpython.org/MoreCommentsOnPrinting
https://wiki.wxpython.org/PrintingWithReportGenerators
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
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....