]>
Commit | Line | Data |
---|---|---|
d14a1e28 RD |
1 | #---------------------------------------------------------------------------- |
2 | # Name: printout.py | |
3 | # Purpose: preview and printing class -> table/grid printing | |
4 | # | |
5 | # Author: Lorne White (email: lorne.white@telusplanet.net) | |
6 | # | |
7 | # Created: | |
8 | # Version 0.75 | |
9 | # Date: May 15, 2002 | |
10 | # Licence: wxWindows license | |
11 | #---------------------------------------------------------------------------- | |
12 | # Release Notes | |
13 | ||
14 | # fixed bug for string wider than print region | |
15 | # add index to data list after parsing total pages for paging | |
16 | #---------------------------------------------------------------------------- | |
b881fc78 | 17 | # 12/10/2003 - Jeff Grimmett (grimmtooth@softhome.net) |
b881fc78 | 18 | # o 2.5 compatability update. |
02b800ce RD |
19 | #---------------------------------------------------------------------------- |
20 | # 11/23/2004 - Vernon Cole (wnvcole@peppermillcas.com) | |
21 | # o Generalize for non-2-dimensional sequences and non-text data | |
22 | # (can use as a simple text printer by supplying a list of strings.) | |
23 | # o Add a small _main_ for self test | |
24 | ||
b881fc78 | 25 | import copy |
02b800ce | 26 | import types |
b881fc78 | 27 | import wx |
d14a1e28 RD |
28 | |
29 | class PrintBase: | |
30 | def SetPrintFont(self, font): # set the DC font parameters | |
31 | fattr = font["Attr"] | |
32 | if fattr[0] == 1: | |
b881fc78 | 33 | weight = wx.BOLD |
d14a1e28 | 34 | else: |
b881fc78 | 35 | weight = wx.NORMAL |
d14a1e28 RD |
36 | |
37 | if fattr[1] == 1: | |
b881fc78 | 38 | set_style = wx.ITALIC |
d14a1e28 | 39 | else: |
b881fc78 | 40 | set_style = wx.NORMAL |
d14a1e28 RD |
41 | |
42 | underline = fattr[2] | |
43 | fcolour = self.GetFontColour(font) | |
44 | self.DC.SetTextForeground(fcolour) | |
45 | ||
b881fc78 | 46 | setfont = wx.Font(font["Size"], wx.SWISS, set_style, weight, underline) |
d14a1e28 RD |
47 | setfont.SetFaceName(font["Name"]) |
48 | self.DC.SetFont(setfont) | |
49 | ||
50 | def GetFontColour(self, font): | |
51 | fcolour = font["Colour"] | |
b881fc78 | 52 | return wx.Colour(fcolour[0], fcolour[1], fcolour[2]) |
d14a1e28 RD |
53 | |
54 | def OutTextRegion(self, textout, txtdraw = True): | |
55 | textlines = textout.split('\n') | |
56 | y = copy.copy(self.y) + self.pt_space_before | |
57 | for text in textlines: | |
58 | remain = 'X' | |
59 | while remain != "": | |
60 | vout, remain = self.SetFlow(text, self.region) | |
61 | if self.draw == True and txtdraw == True: | |
62 | test_out = self.TestFull(vout) | |
b881fc78 | 63 | if self.align == wx.ALIGN_LEFT: |
d7403ad2 | 64 | self.DC.DrawText(test_out, self.indent+self.pcell_left_margin, y) |
d14a1e28 | 65 | |
b881fc78 | 66 | elif self.align == wx.ALIGN_CENTRE: |
d14a1e28 | 67 | diff = self.GetCellDiff(test_out, self.region) |
d7403ad2 | 68 | self.DC.DrawText(test_out, self.indent+diff/2, y) |
d14a1e28 | 69 | |
b881fc78 | 70 | elif self.align == wx.ALIGN_RIGHT: |
d14a1e28 | 71 | diff = self.GetCellDiff(test_out, self.region) |
d7403ad2 | 72 | self.DC.DrawText(test_out, self.indent+diff, y) |
d14a1e28 RD |
73 | |
74 | else: | |
d7403ad2 | 75 | self.DC.DrawText(test_out, self.indent+self.pcell_left_margin, y) |
d14a1e28 RD |
76 | text = remain |
77 | y = y + self.space | |
78 | return y - self.space + self.pt_space_after | |
79 | ||
80 | def GetCellDiff(self, text, width): # get the remaining cell size for adjustment | |
81 | w, h = self.DC.GetTextExtent(text) | |
82 | diff = width - w | |
83 | if diff < 0: | |
84 | diff = 0 | |
85 | return diff | |
86 | ||
87 | def TestFull(self, text_test): | |
88 | w, h = self.DC.GetTextExtent(text_test) | |
89 | if w > self.region: # trouble fitting into cell | |
90 | return self.SetChar(text_test, self.region) # fit the text to the cell size | |
91 | else: | |
92 | return text_test | |
93 | ||
94 | def SetFlow(self, ln_text, width): | |
95 | width = width - self.pcell_right_margin | |
96 | text = "" | |
97 | split = ln_text.split() | |
98 | if len(split) == 1: | |
99 | return ln_text, "" | |
100 | ||
101 | try: | |
102 | w, h = self.DC.GetTextExtent(" " + split[0]) | |
103 | if w >= width: | |
104 | return ln_text, "" | |
105 | except: | |
106 | pass | |
107 | ||
108 | cnt = 0 | |
109 | for word in split: | |
110 | bword = " " + word # blank + word | |
111 | length = len(bword) | |
112 | ||
113 | w, h = self.DC.GetTextExtent(text + bword) | |
114 | if w < width: | |
115 | text = text + bword | |
116 | cnt = cnt + 1 | |
117 | else: | |
118 | remain = ' '.join(split[cnt:]) | |
119 | text = text.strip() | |
120 | return text, remain | |
121 | ||
122 | remain = ' '.join(split[cnt:]) | |
123 | vout = text.strip() | |
124 | return vout, remain | |
125 | ||
126 | def SetChar(self, ln_text, width): # truncate string to fit into width | |
127 | width = width - self.pcell_right_margin - self.pcell_left_margin | |
128 | text = "" | |
129 | for val in ln_text: | |
130 | w, h = self.DC.GetTextExtent(text + val) | |
131 | if w > width: | |
132 | text = text + ".." | |
133 | return text # fitted text value | |
134 | text = text + val | |
135 | return text | |
136 | ||
137 | def OutTextPageWidth(self, textout, y_out, align, indent, txtdraw = True): | |
138 | textlines = textout.split('\n') | |
139 | y = copy.copy(y_out) | |
140 | ||
141 | pagew = self.parent.page_width * self.pwidth # full page width | |
142 | w, h = self.DC.GetTextExtent(textout) | |
143 | y_line = h | |
144 | ||
145 | for text in textlines: | |
146 | remain = 'X' | |
147 | while remain != "": | |
148 | vout, remain = self.SetFlow(text, pagew) | |
149 | if self.draw == True and txtdraw == True: | |
150 | test_out = vout | |
b881fc78 | 151 | if align == wx.ALIGN_LEFT: |
d7403ad2 | 152 | self.DC.DrawText(test_out, indent, y) |
d14a1e28 | 153 | |
b881fc78 | 154 | elif align == wx.ALIGN_CENTRE: |
d14a1e28 | 155 | diff = self.GetCellDiff(test_out, pagew) |
d7403ad2 | 156 | self.DC.DrawText(test_out, indent+diff/2, y) |
d14a1e28 | 157 | |
b881fc78 | 158 | elif align == wx.ALIGN_RIGHT: |
d14a1e28 | 159 | diff = self.GetCellDiff(test_out, pagew) |
d7403ad2 | 160 | self.DC.DrawText(test_out, indent+diff, y) |
d14a1e28 RD |
161 | |
162 | else: | |
d7403ad2 | 163 | self.DC.DrawText(test_out, indent, y_out) |
d14a1e28 RD |
164 | text = remain |
165 | y = y + y_line | |
166 | return y - y_line | |
167 | ||
168 | def GetDate(self): | |
169 | date, time = self.GetNow() | |
170 | return date | |
171 | ||
172 | def GetDateTime(self): | |
173 | date, time = self.GetNow() | |
174 | return date + ' ' + time | |
175 | ||
176 | def GetNow(self): | |
c5d11c71 RD |
177 | now = wx.DateTime.Now() |
178 | date = now.FormatDate() | |
179 | time = now.FormatTime() | |
d14a1e28 RD |
180 | return date, time |
181 | ||
182 | def SetPreview(self, preview): | |
183 | self.preview = preview | |
184 | ||
185 | def SetPSize(self, width, height): | |
186 | self.pwidth = width/self.scale | |
187 | self.pheight = height/self.scale | |
188 | ||
189 | def SetScale(self, scale): | |
190 | self.scale = scale | |
191 | ||
192 | def SetPTSize(self, width, height): | |
193 | self.ptwidth = width | |
194 | self.ptheight = height | |
195 | ||
196 | def getWidth(self): | |
197 | return self.sizew | |
198 | ||
199 | def getHeight(self): | |
200 | return self.sizeh | |
201 | ||
202 | ||
b881fc78 | 203 | class PrintTableDraw(wx.ScrolledWindow, PrintBase): |
d14a1e28 RD |
204 | def __init__(self, parent, DC, size): |
205 | self.parent = parent | |
206 | self.DC = DC | |
207 | self.scale = parent.scale | |
208 | self.width = size[0] | |
209 | self.height = size[1] | |
210 | self.SetDefaults() | |
211 | ||
212 | def SetDefaults(self): | |
213 | self.page = 1 | |
214 | self.total_pages = None | |
215 | ||
216 | self.page_width = self.parent.page_width | |
217 | self.page_height = self.parent.page_height | |
218 | ||
219 | self.left_margin = self.parent.left_margin | |
220 | self.right_margin = self.parent.right_margin | |
221 | ||
222 | self.top_margin = self.parent.top_margin | |
223 | self.bottom_margin = self.parent.bottom_margin | |
224 | self.cell_left_margin = self.parent.cell_left_margin | |
225 | self.cell_right_margin = self.parent.cell_right_margin | |
226 | ||
227 | self.label_colour = self.parent.label_colour | |
228 | ||
229 | self.row_line_colour = self.parent.row_line_colour | |
230 | self.row_line_size = self.parent.row_line_size | |
231 | ||
232 | self.row_def_line_colour = self.parent.row_def_line_colour | |
233 | self.row_def_line_size = self.parent.row_def_line_size | |
234 | ||
235 | self.column_line_colour = self.parent.column_line_colour | |
236 | self.column_line_size = self.parent.column_line_size | |
237 | ||
238 | self.column_def_line_size = self.parent.column_def_line_size | |
239 | self.column_def_line_colour = self.parent.column_def_line_colour | |
240 | ||
241 | self.text_font = self.parent.text_font | |
242 | ||
243 | self.label_font = self.parent.label_font | |
244 | ||
245 | def AdjustValues(self): | |
246 | self.vertical_offset = self.pheight * self.parent.vertical_offset | |
247 | self.horizontal_offset = self.pheight * self.parent.horizontal_offset | |
248 | ||
249 | self.pcell_left_margin = self.pwidth * self.cell_left_margin | |
250 | self.pcell_right_margin = self.pwidth * self.cell_right_margin | |
251 | self.ptop_margin = self.pheight * self.top_margin | |
252 | self.pbottom_margin = self.pheight * self.bottom_margin | |
253 | ||
254 | self.pheader_margin = self.pheight * self.parent.header_margin | |
255 | self.pfooter_margin = self.pheight * self.parent.footer_margin | |
256 | ||
257 | self.cell_colour = self.parent.set_cell_colour | |
258 | self.cell_text = self.parent.set_cell_text | |
259 | ||
260 | self.column = [] | |
261 | self.column_align = [] | |
262 | self.column_bgcolour = [] | |
263 | self.column_txtcolour = [] | |
264 | ||
265 | set_column_align = self.parent.set_column_align | |
266 | set_column_bgcolour = self.parent.set_column_bgcolour | |
267 | set_column_txtcolour = self.parent.set_column_txtcolour | |
268 | ||
269 | pos_x = self.left_margin * self.pwidth + self.horizontal_offset # left margin | |
270 | self.column.append(pos_x) | |
271 | ||
02b800ce RD |
272 | #module logic expects two dimensional data -- fix input if needed |
273 | if isinstance(self.data,types.StringTypes): | |
274 | self.data = [[copy.copy(self.data)]] # a string becomes a single cell | |
275 | try: | |
276 | rows = len(self.data) | |
277 | except TypeError: | |
278 | self.data = [[str(self.data)]] # a non-iterable becomes a single cell | |
279 | rows = 1 | |
280 | first_value = self.data[0] | |
281 | ||
282 | if isinstance(first_value, types.StringTypes): # a sequence of strings | |
283 | if self.label == [] and self.set_column == []: | |
284 | data = [] | |
285 | for x in self.data: #becomes one column | |
286 | data.append([x]) | |
287 | else: | |
288 | data = [self.data] #becames one row | |
289 | self.data = data | |
290 | first_value = data[0] | |
291 | try: | |
292 | column_total = len(first_value) | |
293 | except TypeError: # a sequence of non-iterables | |
294 | if self.label == [] and self.set_column == []: | |
295 | data = [] #becomes one column | |
296 | for x in self.data: | |
297 | data.append([str(x)]) | |
298 | column_total = 1 | |
299 | else: | |
300 | data = [self.data] #becomes one row | |
301 | column_total = len(self.data) | |
302 | self.data = data | |
303 | first_value = data[0] | |
304 | ||
d14a1e28 RD |
305 | if self.set_column == []: |
306 | table_width = self.page_width - self.left_margin - self.right_margin | |
02b800ce RD |
307 | if self.label == []: |
308 | temp = first_value | |
309 | else: | |
310 | temp = self.label | |
311 | width = table_width/(len(temp)) | |
312 | for val in temp: | |
d14a1e28 RD |
313 | column_width = width * self.pwidth |
314 | pos_x = pos_x + column_width | |
315 | self.column.append(pos_x) # position of each column | |
316 | else: | |
317 | for val in self.set_column: | |
318 | column_width = val * self.pwidth | |
319 | pos_x = pos_x + column_width | |
320 | self.column.append(pos_x) # position of each column | |
321 | ||
322 | if pos_x > self.page_width * self.pwidth: # check if it fits in page | |
323 | print "Warning, Too Wide for Page" | |
324 | return | |
325 | ||
326 | if self.label != []: | |
327 | if len(self.column) -1 != len(self.label): | |
328 | print "Column Settings Incorrect", "\nColumn Value: " + str(self.column), "\nLabel Value: " + str(self.label) | |
329 | return | |
330 | ||
d14a1e28 | 331 | if column_total != len(self.column) -1: |
02b800ce | 332 | print "Cannot fit", first_value, 'in', len(self.column)-1, 'columns.' |
d14a1e28 RD |
333 | return |
334 | ||
d14a1e28 RD |
335 | for col in range(column_total): |
336 | try: | |
337 | align = set_column_align[col] # check if custom column alignment | |
338 | except: | |
b881fc78 | 339 | align = wx.ALIGN_LEFT |
d14a1e28 RD |
340 | self.column_align.append(align) |
341 | ||
342 | try: | |
343 | colour = set_column_bgcolour[col] # check if custom column background colour | |
344 | except: | |
345 | colour = self.parent.column_colour | |
346 | self.column_bgcolour.append(colour) | |
347 | ||
348 | try: | |
349 | colour = set_column_txtcolour[col] # check if custom column text colour | |
350 | except: | |
351 | colour = self.GetFontColour(self.parent.text_font) | |
d14a1e28 RD |
352 | self.column_txtcolour.append(colour) |
353 | ||
d14a1e28 RD |
354 | |
355 | def SetPointAdjust(self): | |
b881fc78 | 356 | f = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL) # setup using 10 point |
d14a1e28 RD |
357 | self.DC.SetFont(f) |
358 | f.SetFaceName(self.text_font["Name"]) | |
359 | x, y = self.DC.GetTextExtent("W") | |
360 | ||
361 | self.label_pt_space_before = self.parent.label_pt_adj_before * y/10 # extra spacing for label per point value | |
362 | self.label_pt_space_after = self.parent.label_pt_adj_after * y/10 | |
363 | ||
364 | self.text_pt_space_before = self.parent.text_pt_adj_before * y/10 # extra spacing for row text per point value | |
365 | self.text_pt_space_after = self.parent.text_pt_adj_after * y/10 | |
366 | ||
367 | def SetPage(self, page): | |
368 | self.page = page | |
369 | ||
370 | def SetColumns(self, col): | |
371 | self.column = col | |
372 | ||
373 | def OutCanvas(self): | |
374 | self.AdjustValues() | |
375 | self.SetPointAdjust() | |
376 | ||
377 | self.y_start = self.ptop_margin + self.vertical_offset | |
378 | self.y_end = self.parent.page_height * self.pheight - self.pbottom_margin + self.vertical_offset | |
379 | ||
380 | self.SetPrintFont(self.label_font) | |
381 | ||
382 | x, y = self.DC.GetTextExtent("W") | |
383 | self.label_space = y | |
384 | ||
385 | self.SetPrintFont(self.text_font) | |
386 | ||
387 | x, y = self.DC.GetTextExtent("W") | |
388 | self.space = y | |
389 | ||
390 | if self.total_pages is None: | |
391 | self.GetTotalPages() # total pages for display/printing | |
392 | ||
393 | self.data_cnt = self.page_index[self.page-1] | |
394 | ||
395 | self.draw = True | |
396 | self.PrintHeader() | |
397 | self.PrintFooter() | |
398 | self.OutPage() | |
399 | ||
400 | def GetTotalPages(self): | |
401 | self.data_cnt = 0 | |
402 | self.draw = False | |
403 | self.page_index = [0] | |
404 | ||
405 | cnt = 0 | |
406 | while 1: | |
407 | test = self.OutPage() | |
408 | self.page_index.append(self.data_cnt) | |
409 | if test == False: | |
410 | break | |
411 | cnt = cnt + 1 | |
412 | ||
413 | self.total_pages = cnt + 1 | |
414 | ||
415 | def OutPage(self): | |
416 | self.y = self.y_start | |
417 | self.end_x = self.column[-1] | |
418 | ||
419 | if self.data_cnt < len(self.data): # if there data for display on the page | |
420 | if self.label != []: # check if header defined | |
421 | self.PrintLabel() | |
422 | else: | |
423 | return False | |
424 | ||
425 | for val in self.data: | |
426 | try: | |
427 | row_val = self.data[self.data_cnt] | |
428 | except: | |
429 | self.FinishDraw() | |
430 | return False | |
431 | ||
432 | max_y = self.PrintRow(row_val, False) # test to see if row will fit in remaining space | |
433 | test = max_y + self.space | |
434 | ||
435 | if test > self.y_end: | |
436 | break | |
437 | ||
438 | self.ColourRowCells(max_y-self.y+self.space) # colour the row/column | |
439 | max_y = self.PrintRow(row_val, True) # row fits - print text | |
440 | self.DrawGridLine() # top line of cell | |
441 | self.y = max_y + self.space | |
442 | ||
443 | if self.y > self.y_end: | |
444 | break | |
445 | ||
446 | self.data_cnt = self.data_cnt + 1 | |
447 | ||
448 | self.FinishDraw() | |
449 | ||
450 | if self.data_cnt == len(self.data): # last value in list | |
451 | return False | |
452 | ||
453 | return True | |
454 | ||
455 | ||
456 | def PrintLabel(self): | |
457 | self.pt_space_before = self.label_pt_space_before # set the point spacing | |
458 | self.pt_space_after = self.label_pt_space_after | |
459 | ||
460 | self.LabelColorRow(self.label_colour) | |
461 | self.SetPrintFont(self.label_font) | |
462 | ||
463 | self.col = 0 | |
464 | max_y = 0 | |
465 | for vtxt in self.label: | |
466 | self.region = self.column[self.col+1] - self.column[self.col] | |
467 | self.indent = self.column[self.col] | |
468 | ||
b881fc78 | 469 | self.align = wx.ALIGN_LEFT |
d14a1e28 RD |
470 | |
471 | max_out = self.OutTextRegion(vtxt, True) | |
472 | if max_out > max_y: | |
473 | max_y = max_out | |
474 | self.col = self.col + 1 | |
475 | ||
476 | self.DrawGridLine() # top line of label | |
477 | self.y = max_y + self.label_space | |
478 | ||
479 | def PrintHeader(self): # print the header array | |
480 | if self.draw == False: | |
481 | return | |
482 | ||
483 | for val in self.parent.header: | |
484 | self.SetPrintFont(val["Font"]) | |
485 | ||
486 | header_indent = val["Indent"] * self.pwidth | |
487 | text = val["Text"] | |
488 | ||
489 | htype = val["Type"] | |
490 | if htype == "Date": | |
491 | addtext = self.GetDate() | |
492 | elif htype == "Date & Time": | |
493 | addtext = self.GetDateTime() | |
494 | else: | |
495 | addtext = "" | |
496 | ||
497 | self.OutTextPageWidth(text+addtext, self.pheader_margin, val["Align"], header_indent, True) | |
498 | ||
499 | def PrintFooter(self): # print the header array | |
500 | if self.draw == False: | |
501 | return | |
502 | ||
503 | footer_pos = self.parent.page_height * self.pheight - self.pfooter_margin + self.vertical_offset | |
504 | for val in self.parent.footer: | |
505 | self.SetPrintFont(val["Font"]) | |
506 | ||
507 | footer_indent = val["Indent"] * self.pwidth | |
508 | text = val["Text"] | |
509 | ||
510 | ftype = val["Type"] | |
511 | if ftype == "Pageof": | |
512 | addtext = "Page " + str(self.page) + " of " + str(self.total_pages) | |
513 | elif ftype == "Page": | |
514 | addtext = "Page " + str(self.page) | |
515 | elif ftype == "Num": | |
516 | addtext = str(self.page) | |
517 | elif ftype == "Date": | |
518 | addtext = self.GetDate() | |
519 | elif ftype == "Date & Time": | |
520 | addtext = self.GetDateTime() | |
521 | else: | |
522 | addtext = "" | |
523 | ||
524 | self.OutTextPageWidth(text+addtext, footer_pos, val["Align"], footer_indent, True) | |
525 | ||
526 | ||
527 | def LabelColorRow(self, colour): | |
b881fc78 | 528 | brush = wx.Brush(colour, wx.SOLID) |
d14a1e28 RD |
529 | self.DC.SetBrush(brush) |
530 | height = self.label_space + self.label_pt_space_before + self.label_pt_space_after | |
d7403ad2 RD |
531 | self.DC.DrawRectangle(self.column[0], self.y, |
532 | self.end_x-self.column[0]+1, height) | |
d14a1e28 RD |
533 | |
534 | def ColourRowCells(self, height): | |
535 | if self.draw == False: | |
536 | return | |
537 | ||
538 | col = 0 | |
539 | for colour in self.column_bgcolour: | |
540 | cellcolour = self.GetCellColour(self.data_cnt, col) | |
541 | if cellcolour is not None: | |
542 | colour = cellcolour | |
543 | ||
b881fc78 | 544 | brush = wx.Brush(colour, wx.SOLID) |
d14a1e28 | 545 | self.DC.SetBrush(brush) |
b881fc78 | 546 | self.DC.SetPen(wx.Pen(wx.NamedColour('WHITE'), 0)) |
d14a1e28 RD |
547 | |
548 | start_x = self.column[col] | |
549 | width = self.column[col+1] - start_x + 2 | |
d7403ad2 | 550 | self.DC.DrawRectangle(start_x, self.y, width, height) |
d14a1e28 RD |
551 | col = col + 1 |
552 | ||
b881fc78 | 553 | def PrintRow(self, row_val, draw = True, align = wx.ALIGN_LEFT): |
d14a1e28 RD |
554 | self.SetPrintFont(self.text_font) |
555 | ||
556 | self.pt_space_before = self.text_pt_space_before # set the point spacing | |
557 | self.pt_space_after = self.text_pt_space_after | |
558 | ||
559 | self.col = 0 | |
560 | max_y = 0 | |
561 | for vtxt in row_val: | |
02b800ce RD |
562 | if not isinstance(vtxt,types.StringTypes): |
563 | vtxt = str(vtxt) | |
d14a1e28 RD |
564 | self.region = self.column[self.col+1] - self.column[self.col] |
565 | self.indent = self.column[self.col] | |
566 | self.align = self.column_align[self.col] | |
567 | ||
568 | fcolour = self.column_txtcolour[self.col] # set font colour | |
02b800ce | 569 | celltext = self.GetCellTextColour(self.data_cnt, self.col) |
d14a1e28 RD |
570 | if celltext is not None: |
571 | fcolour = celltext # override the column colour | |
572 | ||
573 | self.DC.SetTextForeground(fcolour) | |
574 | ||
575 | max_out = self.OutTextRegion(vtxt, draw) | |
576 | if max_out > max_y: | |
577 | max_y = max_out | |
578 | self.col = self.col + 1 | |
579 | return max_y | |
580 | ||
581 | def GetCellColour(self, row, col): # check if custom colour defined for the cell background | |
582 | try: | |
583 | set = self.cell_colour[row] | |
584 | except: | |
585 | return None | |
586 | try: | |
587 | colour = set[col] | |
588 | return colour | |
589 | except: | |
590 | return None | |
591 | ||
02b800ce | 592 | def GetCellTextColour(self, row, col): # check if custom colour defined for the cell text |
d14a1e28 RD |
593 | try: |
594 | set = self.cell_text[row] | |
595 | except: | |
596 | return None | |
597 | try: | |
598 | colour = set[col] | |
599 | return colour | |
600 | except: | |
601 | return None | |
602 | ||
603 | def FinishDraw(self): | |
604 | self.DrawGridLine() # draw last row line | |
605 | self.DrawColumns() # draw all vertical lines | |
606 | ||
607 | def DrawGridLine(self): | |
02b800ce RD |
608 | if self.draw == True \ |
609 | and len(self.column) > 2: #supress grid lines if only one column | |
d14a1e28 RD |
610 | try: |
611 | size = self.row_line_size[self.data_cnt] | |
612 | except: | |
613 | size = self.row_def_line_size | |
614 | ||
615 | try: | |
616 | colour = self.row_line_colour[self.data_cnt] | |
617 | except: | |
618 | colour = self.row_def_line_colour | |
619 | ||
b881fc78 | 620 | self.DC.SetPen(wx.Pen(colour, size)) |
d14a1e28 RD |
621 | |
622 | y_out = self.y | |
623 | # y_out = self.y + self.pt_space_before + self.pt_space_after # adjust for extra spacing | |
d7403ad2 | 624 | self.DC.DrawLine(self.column[0], y_out, self.end_x, y_out) |
d14a1e28 RD |
625 | |
626 | def DrawColumns(self): | |
02b800ce RD |
627 | if self.draw == True \ |
628 | and len(self.column) > 2: #surpress grid line if only one column | |
d14a1e28 RD |
629 | col = 0 |
630 | for val in self.column: | |
631 | try: | |
632 | size = self.column_line_size[col] | |
633 | except: | |
634 | size = self.column_def_line_size | |
635 | ||
636 | try: | |
637 | colour = self.column_line_colour[col] | |
638 | except: | |
639 | colour = self.column_def_line_colour | |
640 | ||
641 | indent = val | |
642 | ||
b881fc78 | 643 | self.DC.SetPen(wx.Pen(colour, size)) |
d7403ad2 | 644 | self.DC.DrawLine(indent, self.y_start, indent, self.y) |
d14a1e28 RD |
645 | col = col + 1 |
646 | ||
647 | def DrawText(self): | |
648 | self.DoRefresh() | |
649 | ||
650 | def DoDrawing(self, DC): | |
b881fc78 | 651 | size = DC.GetSize() |
d14a1e28 RD |
652 | self.DC = DC |
653 | ||
654 | DC.BeginDrawing() | |
655 | self.DrawText() | |
656 | DC.EndDrawing() | |
657 | ||
658 | self.sizew = DC.MaxY() | |
659 | self.sizeh = DC.MaxX() | |
660 | ||
661 | ||
662 | class PrintTable: | |
663 | def __init__(self, parentFrame=None): | |
664 | self.data = [] | |
665 | self.set_column = [] | |
666 | self.label = [] | |
667 | self.header = [] | |
668 | self.footer = [] | |
669 | ||
670 | self.set_column_align = {} | |
671 | self.set_column_bgcolour = {} | |
672 | self.set_column_txtcolour = {} | |
673 | self.set_cell_colour = {} | |
674 | self.set_cell_text = {} | |
675 | self.column_line_size = {} | |
676 | self.column_line_colour = {} | |
677 | self.row_line_size = {} | |
678 | self.row_line_colour = {} | |
679 | ||
680 | self.parentFrame = parentFrame | |
681 | self.SetPreviewSize() | |
682 | ||
b881fc78 | 683 | self.printData = wx.PrintData() |
d14a1e28 RD |
684 | self.scale = 1.0 |
685 | ||
686 | self.SetParms() | |
687 | self.SetColors() | |
688 | self.SetFonts() | |
689 | self.TextSpacing() | |
690 | ||
691 | self.SetPrinterOffset() | |
692 | self.SetHeaderValue() | |
693 | self.SetFooterValue() | |
694 | self.SetMargins() | |
695 | self.SetPortrait() | |
696 | ||
b881fc78 | 697 | def SetPreviewSize(self, position = wx.Point(0, 0), size="Full"): |
d14a1e28 | 698 | if size == "Full": |
b881fc78 RD |
699 | r = wx.GetClientDisplayRect() |
700 | self.preview_frame_size = r.GetSize() | |
701 | self.preview_frame_pos = r.GetPosition() | |
d14a1e28 RD |
702 | else: |
703 | self.preview_frame_size = size | |
704 | self.preview_frame_pos = position | |
705 | ||
706 | def SetPaperId(self, paper): | |
707 | self.printData.SetPaperId(paper) | |
708 | ||
709 | def SetOrientation(self, orient): | |
710 | self.printData.SetOrientation(orient) | |
711 | ||
712 | def SetColors(self): | |
b881fc78 | 713 | self.row_def_line_colour = wx.NamedColour('BLACK') |
d14a1e28 RD |
714 | self.row_def_line_size = 1 |
715 | ||
b881fc78 | 716 | self.column_def_line_colour = wx.NamedColour('BLACK') |
d14a1e28 | 717 | self.column_def_line_size = 1 |
b881fc78 | 718 | self.column_colour = wx.NamedColour('WHITE') |
d14a1e28 | 719 | |
b881fc78 | 720 | self.label_colour = wx.NamedColour('LIGHT GREY') |
d14a1e28 RD |
721 | |
722 | def SetFonts(self): | |
723 | self.label_font = { "Name": self.default_font_name, "Size": 12, "Colour": [0, 0, 0], "Attr": [0, 0, 0] } | |
724 | self.text_font = { "Name": self.default_font_name, "Size": 10, "Colour": [0, 0, 0], "Attr": [0, 0, 0] } | |
725 | ||
726 | def TextSpacing(self): | |
727 | self.label_pt_adj_before = 0 # point adjustment before and after the label text | |
728 | self.label_pt_adj_after = 0 | |
729 | ||
730 | self.text_pt_adj_before = 0 # point adjustment before and after the row text | |
731 | self.text_pt_adj_after = 0 | |
732 | ||
733 | def SetLabelSpacing(self, before, after): # method to set the label space adjustment | |
734 | self.label_pt_adj_before = before | |
735 | self.label_pt_adj_after = after | |
736 | ||
737 | def SetRowSpacing(self, before, after): # method to set the row space adjustment | |
738 | self.text_pt_adj_before = before | |
739 | self.text_pt_adj_after = after | |
740 | ||
741 | def SetPrinterOffset(self): # offset to adjust for printer | |
742 | self.vertical_offset = -0.1 | |
743 | self.horizontal_offset = -0.1 | |
744 | ||
745 | def SetHeaderValue(self): | |
746 | self.header_margin = 0.25 | |
747 | self.header_font = { "Name": self.default_font_name, "Size": 11, "Colour": [0, 0, 0], "Attr": [0, 0, 0] } | |
b881fc78 | 748 | self.header_align = wx.ALIGN_CENTRE |
d14a1e28 RD |
749 | self.header_indent = 0 |
750 | self.header_type = "Text" | |
751 | ||
752 | def SetFooterValue(self): | |
753 | self.footer_margin = 0.7 | |
754 | self.footer_font = { "Name": self.default_font_name, "Size": 11, "Colour": [0, 0, 0], "Attr": [0, 0, 0] } | |
b881fc78 | 755 | self.footer_align = wx.ALIGN_CENTRE |
d14a1e28 RD |
756 | self.footer_indent = 0 |
757 | self.footer_type = "Pageof" | |
758 | ||
759 | def SetMargins(self): | |
02b800ce RD |
760 | self.left_margin = 0.5 |
761 | self.right_margin = 0.5 # only used if no column sizes | |
d14a1e28 RD |
762 | |
763 | self.top_margin = 0.8 | |
764 | self.bottom_margin = 1.0 | |
765 | self.cell_left_margin = 0.1 | |
766 | self.cell_right_margin = 0.1 | |
767 | ||
768 | def SetPortrait(self): | |
b881fc78 RD |
769 | self.printData.SetPaperId(wx.PAPER_LETTER) |
770 | self.printData.SetOrientation(wx.PORTRAIT) | |
d14a1e28 RD |
771 | self.page_width = 8.5 |
772 | self.page_height = 11.0 | |
773 | ||
774 | def SetLandscape(self): | |
b881fc78 | 775 | self.printData.SetOrientation(wx.LANDSCAPE) |
d14a1e28 RD |
776 | self.page_width = 11.0 |
777 | self.page_height = 8.5 | |
778 | ||
779 | def SetParms(self): | |
780 | self.ymax = 1 | |
781 | self.xmax = 1 | |
782 | self.page = 1 | |
783 | self.total_pg = 100 | |
784 | ||
785 | self.preview = None | |
786 | self.page = 0 | |
787 | ||
788 | self.default_font_name = "Arial" | |
789 | self.default_font = { "Name": self.default_font_name, "Size": 10, "Colour": [0, 0, 0], "Attr": [0, 0, 0] } | |
790 | ||
b881fc78 | 791 | def SetColAlignment(self, col, align=wx.ALIGN_LEFT): |
d14a1e28 RD |
792 | self.set_column_align[col] = align |
793 | ||
794 | def SetColBackgroundColour(self, col, colour): | |
795 | self.set_column_bgcolour[col] = colour | |
796 | ||
797 | def SetColTextColour(self, col, colour): | |
798 | self.set_column_txtcolour[col] = colour | |
799 | ||
800 | def SetCellColour(self, row, col, colour): # cell background colour | |
801 | try: | |
802 | set = self.set_cell_colour[row] # test if row already exists | |
803 | try: | |
804 | set[col] = colour # test if column already exists | |
805 | except: | |
806 | set = { col: colour } # create the column value | |
807 | except: | |
808 | set = { col: colour } # create the column value | |
809 | ||
810 | self.set_cell_colour[row] = set # create dictionary item for colour settings | |
811 | ||
812 | def SetCellText(self, row, col, colour): # font colour for custom cells | |
813 | try: | |
814 | set = self.set_cell_text[row] # test if row already exists | |
815 | try: | |
816 | set[col] = colour # test if column already exists | |
817 | except: | |
818 | set = { col: colour } # create the column value | |
819 | except: | |
820 | set = { col: colour } # create the column value | |
821 | ||
822 | self.set_cell_text[row] = set # create dictionary item for colour settings | |
823 | ||
824 | def SetColumnLineSize(self, col, size): # column line size | |
825 | self.column_line_size[col] = size # create dictionary item for column line settings | |
826 | ||
827 | def SetColumnLineColour(self, col, colour): | |
828 | self.column_line_colour[col] = colour | |
829 | ||
830 | def SetRowLineSize(self, row, size): | |
831 | self.row_line_size[row] = size | |
832 | ||
833 | def SetRowLineColour(self, row, colour): | |
834 | self.row_line_colour[row] = colour | |
835 | ||
836 | def GetColour(self, colour): # returns colours based from wxColour value | |
837 | red = colour.Red() | |
838 | blue = colour.Blue() | |
839 | green = colour.Green() | |
840 | return [red, green, blue ] | |
841 | ||
842 | def SetHeader(self, text = "", type = "Text", font=None, align = None, indent = None, colour = None, size = None): | |
843 | set = { "Text": text } | |
844 | ||
845 | if font is None: | |
846 | set["Font"] = copy.copy(self.default_font) | |
847 | else: | |
848 | set["Font"] = font | |
849 | ||
850 | if colour is not None: | |
851 | setfont = set["Font"] | |
852 | setfont["Colour"] = self.GetColour(colour) | |
853 | ||
854 | if size is not None: | |
855 | setfont = set["Font"] | |
856 | setfont["Size"] = size | |
857 | ||
858 | if align is None: | |
859 | set["Align"] = self.header_align | |
860 | else: | |
861 | set["Align"] = align | |
862 | ||
863 | if indent is None: | |
864 | set["Indent"] = self.header_indent | |
865 | else: | |
866 | set["Indent"] = indent | |
867 | ||
868 | if type is None: | |
869 | set["Type"] = self.header_type | |
870 | else: | |
871 | set["Type"] = type | |
872 | ||
873 | self.header.append(set) | |
874 | ||
875 | def SetFooter(self, text = "", type = None, font=None, align = None, indent = None, colour = None, size = None): | |
876 | set = { "Text": text } | |
877 | ||
878 | if font is None: | |
879 | set["Font"] = copy.copy(self.default_font) | |
880 | else: | |
881 | set["Font"] = font | |
882 | ||
883 | if colour is not None: | |
884 | setfont = set["Font"] | |
885 | setfont["Colour"] = self.GetColour(colour) | |
886 | ||
887 | if size is not None: | |
888 | setfont = set["Font"] | |
889 | setfont["Size"] = size | |
890 | ||
891 | if align is None: | |
892 | set["Align"] = self.footer_align | |
893 | else: | |
894 | set["Align"] = align | |
895 | ||
896 | if indent is None: | |
897 | set["Indent"] = self.footer_indent | |
898 | else: | |
899 | set["Indent"] = indent | |
900 | ||
901 | if type is None: | |
902 | set["Type"] = self.footer_type | |
903 | else: | |
904 | set["Type"] = type | |
905 | ||
906 | self.footer.append(set) | |
907 | ||
908 | def Preview(self): | |
02b800ce | 909 | data = wx.PrintDialogData(self.printData) |
d14a1e28 RD |
910 | printout = SetPrintout(self) |
911 | printout2 = SetPrintout(self) | |
02b800ce | 912 | self.preview = wx.PrintPreview(printout, printout2, data) |
d14a1e28 | 913 | if not self.preview.Ok(): |
b881fc78 | 914 | wxMessageBox("There was a problem printing!", "Printing", wx.OK) |
d14a1e28 RD |
915 | return |
916 | ||
917 | self.preview.SetZoom(60) # initial zoom value | |
b881fc78 | 918 | frame = wx.PreviewFrame(self.preview, self.parentFrame, "Print preview") |
d14a1e28 RD |
919 | |
920 | frame.Initialize() | |
921 | if self.parentFrame: | |
922 | frame.SetPosition(self.preview_frame_pos) | |
923 | frame.SetSize(self.preview_frame_size) | |
924 | frame.Show(True) | |
925 | ||
926 | def Print(self): | |
02b800ce | 927 | pdd = wx.PrintDialogData(self.printData) |
b881fc78 | 928 | printer = wx.Printer(pdd) |
d14a1e28 RD |
929 | printout = SetPrintout(self) |
930 | if not printer.Print(self.parentFrame, printout): | |
b881fc78 | 931 | wx.MessageBox("There was a problem printing.\nPerhaps your current printer is not set correctly?", "Printing", wx.OK) |
d14a1e28 | 932 | else: |
02b800ce | 933 | self.printData = wx.PrintData( printer.GetPrintDialogData().GetPrintData() ) |
d14a1e28 RD |
934 | printout.Destroy() |
935 | ||
936 | def DoDrawing(self, DC): | |
b881fc78 | 937 | size = DC.GetSize() |
d14a1e28 RD |
938 | DC.BeginDrawing() |
939 | ||
940 | table = PrintTableDraw(self, DC, size) | |
941 | table.data = self.data | |
942 | table.set_column = self.set_column | |
943 | table.label = self.label | |
944 | table.SetPage(self.page) | |
945 | ||
946 | if self.preview is None: | |
947 | table.SetPSize(size[0]/self.page_width, size[1]/self.page_height) | |
948 | table.SetPTSize(size[0], size[1]) | |
949 | table.SetPreview(False) | |
950 | else: | |
951 | if self.preview == 1: | |
952 | table.scale = self.scale | |
953 | table.SetPSize(size[0]/self.page_width, size[1]/self.page_height) | |
954 | else: | |
955 | table.SetPSize(self.pwidth, self.pheight) | |
956 | ||
957 | table.SetPTSize(self.ptwidth, self.ptheight) | |
958 | table.SetPreview(self.preview) | |
959 | ||
960 | table.OutCanvas() | |
961 | self.page_total = table.total_pages # total display pages | |
962 | ||
963 | DC.EndDrawing() | |
964 | ||
965 | self.ymax = DC.MaxY() | |
966 | self.xmax = DC.MaxX() | |
967 | ||
968 | self.sizeh = size[0] | |
969 | self.sizew = size[1] | |
970 | ||
971 | def GetTotalPages(self): | |
972 | self.page_total = 100 | |
973 | return self.page_total | |
974 | ||
975 | def HasPage(self, page): | |
976 | if page <= self.page_total: | |
977 | return True | |
978 | else: | |
979 | return False | |
980 | ||
981 | def SetPage(self, page): | |
982 | self.page = page | |
983 | ||
984 | def SetPageSize(self, width, height): | |
985 | self.pwidth, self.pheight = width, height | |
986 | ||
987 | def SetTotalSize(self, width, height): | |
988 | self.ptwidth, self.ptheight = width, height | |
989 | ||
990 | def SetPreview(self, preview, scale): | |
991 | self.preview = preview | |
992 | self.scale = scale | |
993 | ||
994 | def SetTotalSize(self, width, height): | |
995 | self.ptwidth = width | |
996 | self.ptheight = height | |
997 | ||
998 | class PrintGrid: | |
999 | def __init__(self, parent, grid, format = [], total_col = None, total_row = None): | |
1000 | if total_row is None: | |
1001 | total_row = grid.GetNumberRows() | |
1002 | if total_col is None: | |
1003 | total_col = grid.GetNumberCols() | |
1004 | ||
1005 | self.total_row = total_row | |
1006 | self.total_col = total_col | |
1007 | self.grid = grid | |
1008 | ||
1009 | data = [] | |
1010 | for row in range(total_row): | |
1011 | row_val = [] | |
1012 | value = grid.GetRowLabelValue(row) | |
1013 | row_val.append(value) | |
1014 | ||
1015 | for col in range(total_col): | |
1016 | value = grid.GetCellValue(row, col) | |
1017 | row_val.append(value) | |
1018 | data.append(row_val) | |
1019 | ||
1020 | label = [""] | |
1021 | for col in range(total_col): | |
1022 | value = grid.GetColLabelValue(col) | |
1023 | label.append(value) | |
1024 | ||
1025 | self.table = PrintTable(parent) | |
1026 | self.table.cell_left_margin = 0.0 | |
1027 | self.table.cell_right_margin = 0.0 | |
1028 | ||
1029 | self.table.label = label | |
1030 | self.table.set_column = format | |
1031 | self.table.data = data | |
1032 | ||
1033 | def GetTable(self): | |
1034 | return self.table | |
1035 | ||
1036 | def SetAttributes(self): | |
1037 | for row in range(self.total_row): | |
1038 | for col in range(self.total_col): | |
1039 | colour = self.grid.GetCellTextColour(row, col-1) | |
1040 | self.table.SetCellText(row, col, colour) | |
1041 | ||
1042 | colour = self.grid.GetCellBackgroundColour(row, col-1) | |
1043 | self.table.SetCellColour(row, col, colour) | |
1044 | ||
1045 | def Preview(self): | |
1046 | self.table.Preview() | |
1047 | ||
1048 | def Print(self): | |
1049 | self.table.Print() | |
1050 | ||
1051 | ||
b881fc78 | 1052 | class SetPrintout(wx.Printout): |
d14a1e28 | 1053 | def __init__(self, canvas): |
b881fc78 | 1054 | wx.Printout.__init__(self) |
d14a1e28 RD |
1055 | self.canvas = canvas |
1056 | self.end_pg = 1000 | |
1057 | ||
1058 | def OnBeginDocument(self, start, end): | |
1059 | return self.base_OnBeginDocument(start, end) | |
1060 | ||
1061 | def OnEndDocument(self): | |
1062 | self.base_OnEndDocument() | |
1063 | ||
1064 | def HasPage(self, page): | |
1065 | try: | |
1066 | end = self.canvas.HasPage(page) | |
1067 | return end | |
1068 | except: | |
1069 | return True | |
1070 | ||
1071 | def GetPageInfo(self): | |
1072 | try: | |
1073 | self.end_pg = self.canvas.GetTotalPages() | |
1074 | except: | |
1075 | pass | |
1076 | ||
1077 | end_pg = self.end_pg | |
1078 | str_pg = 1 | |
1079 | return (str_pg, end_pg, str_pg, end_pg) | |
1080 | ||
1081 | def OnPreparePrinting(self): | |
1082 | self.base_OnPreparePrinting() | |
1083 | ||
1084 | def OnBeginPrinting(self): | |
1085 | dc = self.GetDC() | |
1086 | ||
1087 | self.preview = self.IsPreview() | |
1088 | if (self.preview): | |
1089 | self.pixelsPerInch = self.GetPPIScreen() | |
1090 | else: | |
1091 | self.pixelsPerInch = self.GetPPIPrinter() | |
1092 | ||
b881fc78 | 1093 | (w, h) = dc.GetSize() |
d14a1e28 RD |
1094 | scaleX = float(w) / 1000 |
1095 | scaleY = float(h) / 1000 | |
1096 | self.printUserScale = min(scaleX, scaleY) | |
1097 | ||
1098 | self.base_OnBeginPrinting() | |
1099 | ||
1100 | def GetSize(self): | |
1101 | self.psizew, self.psizeh = self.GetPPIPrinter() | |
1102 | return self.psizew, self.psizeh | |
1103 | ||
1104 | def GetTotalSize(self): | |
1105 | self.ptsizew, self.ptsizeh = self.GetPageSizePixels() | |
1106 | return self.ptsizew, self.ptsizeh | |
1107 | ||
1108 | def OnPrintPage(self, page): | |
1109 | dc = self.GetDC() | |
b881fc78 | 1110 | (w, h) = dc.GetSize() |
d14a1e28 RD |
1111 | scaleX = float(w) / 1000 |
1112 | scaleY = float(h) / 1000 | |
1113 | self.printUserScale = min(scaleX, scaleY) | |
1114 | dc.SetUserScale(self.printUserScale, self.printUserScale) | |
1115 | ||
1116 | self.preview = self.IsPreview() | |
1117 | ||
1118 | self.canvas.SetPreview(self.preview, self.printUserScale) | |
1119 | self.canvas.SetPage(page) | |
1120 | ||
1121 | self.ptsizew, self.ptsizeh = self.GetPageSizePixels() | |
1122 | self.canvas.SetTotalSize(self.ptsizew, self.ptsizeh) | |
1123 | ||
1124 | self.psizew, self.psizeh = self.GetPPIPrinter() | |
1125 | self.canvas.SetPageSize(self.psizew, self.psizeh) | |
1126 | ||
1127 | self.canvas.DoDrawing(dc) | |
1128 | return True | |
1129 | ||
02b800ce RD |
1130 | if __name__ == '__main__': |
1131 | app = wx.PySimpleApp() | |
1132 | frame = wx.Frame(None, -1, "Dummy wx frame for testing printout.py") | |
1133 | frame.Show(True) | |
1134 | ptbl = PrintTable(frame) | |
1135 | ptbl.SetHeader('This is the test HEADER') | |
1136 | # a single sequence will print out as a single column with no borders ... | |
1137 | ptbl.data = ( | |
1138 | 'This is the first line of text.', | |
1139 | 'This is the second line\nand the third. The fourth will be the number "4.0".', | |
1140 | 04.00, | |
1141 | 'This is the fifth line, but by design it is too long to fit in the width of a standard'\ | |
1142 | ' page, so it will be forced to wrap around in order to fit without having '\ | |
1143 | 'some of its verbose verbage truncated.', | |
1144 | 'Here we have the final line.' | |
1145 | ) | |
1146 | #... but, if labels or columns are defined, a single sequence will print out as a single row | |
1147 | ##ptbl.label = ('One','Two','Three','Four','5') | |
1148 | ptbl.Preview() | |
1149 | app.MainLoop() |