Package Gnumed :: Package wxpython :: Module gmFormPrinter
[frames] | no frames]

Source Code for Module Gnumed.wxpython.gmFormPrinter

  1  # GnuMed form printer design study 
  2   
  3  __doc__ = """ 
  4  Module to print a form using the wx. toolkit. 
  5  includes dialogues for printer calibration, etc. 
  6  and new form wizard. 
  7  """ 
  8  # $Source: /cvsroot/gnumed/gnumed/gnumed/client/wxpython/gmFormPrinter.py,v $ 
  9  # $Id: gmFormPrinter.py,v 1.12 2009/01/15 11:35:41 ncq Exp $ 
 10  __version__ = "$Revision: 1.12 $" 
 11  __author__ = "Ian Haywood" 
 12   
 13  try: 
 14          import wxversion 
 15          import wx 
 16  except ImportError: 
 17          from wxPython import wx 
 18   
 19  import string 
 20   
 21  from Gnumed.pycommon import gmCfg, gmI18N 
 22   
 23  try: 
 24          _('dummy-no-need-to-translate-but-make-epydoc-happy') 
 25  except NameError: 
 26          _ = lambda x:x 
 27   
 28  cache_form = 0 # cached variables to allow repeat of last form 
 29  cache_params = {} 
 30   
 31  SCRIPT = -100 # fake form ID that maps to the default prescription form 
 32                # for this locale 
 33  PATH = -101 
 34  RADIOL = -102 
 35   
36 -class FormPrinter:
37
38 - def __init__ (self):
39 self._cfg = gmCfg.gmDefCfgFile 40 xos = self._cfg.get ("printer", "x_offset") 41 if xos is None: 42 self.printer_unset = 1 43 gpsd = gmPrinterSetupDialog (self) 44 else: 45 self.printer_unset = 0 46 self.x_off = float (xos) # in mm from paper-edge 47 self.y_off = float (self._cfg.get ("printer", "y_offset")) # in mm from paper-edge 48 self.x_scale = float (self._cfg.get ("printer", "x_scaling")) # no. of logical units = 1mm 49 self.y_scale = float (self._cfg.get ("printer", "y_scaling")) # no. logical units = 1mm 50 self._log = gmLog.gmDefLog.Log
51
52 - def save (self):
53 self._cfg.set ("printer", "x_offset", self.x_off) 54 self._cfg.set ("printer", "y_offset", self.y_off) 55 self._cfg.set ("printer", "x_scaling", self.x_scale) 56 self._cfg.set ("printer", "y_scaling", self.x_scale) 57 self._cfg.store ()
58
59 - def printform (id, param):
60 """ 61 Print a form. id is the database ID of the form, params is a dictionary of 62 parameters, defined by the type of form (see gmoffice.sql) 63 """ 64 cached_form = id 65 cached_params = param 66 if self.printer_unset: # printer is unset, cache and return 67 return 68 backend = gmPG.GetConnectionPool () 69 db = backend.GetConnection('office') 70 curs = db.cursor() 71 if id == SCRIPT: 72 curs.execute ("select length, width, fontsize from forms, papersizes where id_papersize = papersizes.id and forms.type = 's' and default") 73 elif id == PATH: 74 curs.execute ("select length, width, fontsize from forms, papersizes where id_papersize = papersizes.id and forms.type = 'p' and default") 75 elif id == RADIOL: 76 curs.execute ("select length, width, fontsize from forms, papersizes where id_papersize = papersizes.id and forms.type = 'r' and default") 77 else: 78 curs.execute('select length, width, fontsize from forms, papersizes where id = %s and id_papersize = papersizes.id' % id) 79 p_len, p_wid, fontsize, font = curs.fetchone() 80 curs.execute('select x, y, wraparound, service, query, page from formfield, queries where id_form = %s and id_query = query.id order by page' % id) 81 curr_page = 1 82 # magic to set up Printer DC 83 pd = wxPrintData () 84 if wxPlatform == '__WXMSW__': 85 dc = wxPrinterDC (pd) 86 else: 87 # use PostScript 88 # FIXME: how do we print under Mac?? 89 dc = wxPostScriptDC (pd) 90 font = wx.Font (fontsize, wxDEFAULT, wx.NORMAL, wx.NORMAL) 91 dc.SetFont (font) 92 dc.SetBrush (wx.BLACK_BRUSH) 93 dc.StartDoc ("") 94 dc.StartPage () 95 for (x, y, wraparound, service, query, page) in curs.fetchall (): 96 qdb = backend.GetConnection (service) 97 qcurs = qdb.cursor () 98 qcurs.execute (self.subst_param (query, params)) 99 if page <> curr_page: 100 # new page 101 dc.EndPage () 102 dc.StartPage () 103 curr_page = page 104 for row in qcurs.fetchall (): 105 text = string.join ([str(i) for i in row], ' ') 106 for line in string.split (text, '\n'): # honour \n in string 107 y = self.printtext (dc, x, y, wraparound, line) 108 qcurs.close () 109 backend.ReleaseConnection (service) 110 curs.close() 111 dc.EndPage () 112 dc.EndDoc () 113 del dc 114 115 116 def subst_param (query, param): 117 for (name, value) in param.items (): 118 query = replace (query, '$' + name, value)
119 120 def printtext (dc, x, y, wrap, text): 121 """ 122 Prints text, with word wrapping. 123 Returns where it leaves y (the next virtual line) 124 """ 125 w, h = dc.GetTextExtent (text) 126 nextline = "" 127 while w/float (self.x_scale) > wrap: # text is too wide 128 text.strip () 129 # nibble from the end of text and add to nextline 130 # until we encounter whitespace 131 # re-calculate text width 132 pos = string.rfind (text, ' ') # returns -1 if cannot find 133 nextline = text[pos:] + nextline 134 text = text[:pos] 135 w, h = dc.GetTextExtent (text) 136 px = (x+self.x_off)*self.x_scale 137 py = (y+self.y_off)*self.y_scale 138 dc.DrawText (px, py, text) 139 y += (h/float (self.y_scale))*1.2 # advance one line, give 20% space 140 # do the wrapped line if neccessary 141 if len (nextline) > 0: 142 y = self.printtext (dc, x, y, wrap, nextline) 143 # return new y position 144 return y
145
146 -class gmPrinterSetupDialog (wx.Dialog):
147 - def __init__(self, formprinter):
148 # begin wxGlade: __init__ 149 wx.Dialog.__init__(self, None, -1, _("Printer Setup")) 150 self.formprinter = formprinter 151 self.label_1 = wx.StaticText(self, -1, "Horiz. Offset") 152 self.horiz_off_spin = wx.SpinCtrl(self, -1, min=0, max=100, initial=0) 153 self.label_2 = wx.StaticText(self, -1, "Vert. Offset") 154 self.vert_off_spin = wx.SpinCtrl(self, -1, min=0, max=100, initial=0, style=wx.SP_ARROW_KEYS) 155 self.label_3 = wx.StaticText(self, -1, "Horiz. Scaling") 156 self.horiz_scale_spin = wx.SpinCtrl(self, -1, min=0, max=100, initial=0, style=wx.SP_ARROW_KEYS) 157 self.label_4 = wx.StaticText(self, -1, "Vert. Scaling") 158 self.vert_scale_spin = wx.SpinCtrl(self, -1, min=0, max=100, initial=0) 159 REPRINT_ID = wx.NewId () 160 self.reprint_button = wx.Button(self, REPRINT_ID, "Re-print") 161 CALIB_ID = wx.NewId () 162 self.calib_button = wx.Button(self, CALIB_ID, "Re-calibrate") 163 DISMISS_ID = wx.NewId () 164 self.dismiss_button = wx.Button(self, DISMISS_ID, "Dismiss") 165 self.text_ctrl_1 = wxTextCtrl(self, -1, "You need to enter parameters so forms print properly on this printer", style=wx.TE_MULTILINE|wx.TE_READONLY) 166 167 self.__set_properties() 168 self.__do_layout() 169 # end wxGlade 170 if not self.formprinter.printer_unset: 171 self.horiz_off_spin.SetValue (self.formprinter.x_off) 172 self.vert_off_spin.SetValue (self.formprinter.y_off) 173 self.horiz_scale_spin.SetValue (self.formprinter.x_scale) 174 self.vert_scale_spin.SetValue (self.formprinter.y_scale) 175 else: 176 self.horiz_off_spin.SetValue (0) 177 self.horiz_scale_spin.SetValue (28.3) 178 self.vert_off_spin.SetValue (0) # this is a sensible value on UNIX 179 self.vert_scale_spin.SetValue (28.3) 180 wx.EVT_BUTTON (self, REPRINT_ID, self.OnReprint) 181 wx.EVT_BUTTON (self, CALIB_ID, self.OnRecalibrate) 182 wx.EVT_BUTTON (self, DISMISS_ID, self.OnDismiss) 183 self.Show ()
184
185 - def OnDismiss (self, event):
186 # load spins back into form engine 187 self.formprinter.x_off = self.horiz_off_spin.GetValue () 188 self.formprinter.y_off = self.vert_off_spin.GetValue () 189 self.formprinter.x_scale = self.horiz_scale_spin.GetValue () 190 self.formprinter.y_scale = self.vert_scale_spin.GetValue () 191 self.formprinter.save () 192 self.Destroy ()
193
194 - def OnReprint (self, event):
195 # load spins back into form engine 196 self.formprinter.x_off = self.horiz_off_spin.GetValue () 197 self.formprinter.y_off = self.vert_off_spin.GetValue () 198 self.formprinter.x_scale = self.horiz_scale_spin.GetValue () 199 self.formprinter.y_scale = self.vert_scale_spin.GetValue () 200 self.formprinter.printer_unset = 0 201 if cache_form != 0: 202 self.formprinter.printform (cache_form, cache_params)
203
204 - def OnRecalibrate (self, event):
205 dialog = gmCalibrationDialog () 206 pd = wxPrintData () 207 pd.SetPrinterCommand ("lpr") 208 if wxPlatform == '__WXMSW__': 209 dc = wxPrinterDC (pd) 210 else: 211 dc = wxPostScriptDC (pd) 212 dc.StartDoc ("") 213 dc.StartPage () 214 dc.SetBrush (wx.BLACK_BRUSH) 215 dc.DrawRectangle (1000, 1000, 200, 200) 216 dc.DrawRectangle (2000, 2000, 200, 200) 217 dc.EndPage () 218 dc.EndDoc () 219 del dc 220 dialog.ShowModal () 221 x1, y1, x2, y2 = dialog.GetValues () 222 dialog.Destroy () 223 self.formprinter.x_scale = (x2-x1)/1000.0 224 self.formprinter.y_scale = (y2-y1)/1000.0 225 self.formprinter.x_off = x1-(x2-x1) 226 self.formprinter.y_off = y1-(y2-y1) 227 self.formprinter.printer_unset = 0 228 self.horiz_off_spin.SetValue (self.formprinter.x_off) 229 self.vert_off_spin.SetValue (self.formprinter.y_off) 230 self.horiz_scale_spin.SetValue (self.formprinter.x_scale) 231 self.vert_scale_spin.SetValue (self.formprinter.y_scale) 232 self.formprinter.save ()
233
234 - def __set_properties(self):
235 # begin wxGlade: __set_properties 236 self.SetTitle("Setup Printer for Forms") 237 self.vert_off_spin.SetToolTipString("Move text down (in millimetres)") 238 self.horiz_scale_spin.SetToolTipString("Horizontal scaling (units per mm)") 239 self.vert_scale_spin.SetToolTipString("Vertical scaling (units per mm)") 240 self.reprint_button.SetToolTipString("Re-print the last printed form") 241 self.calib_button.SetToolTipString("Print a table to calibrate this printer") 242 self.dismiss_button.SetToolTipString("Dismiss this dialog box")
243 # end wxGlade 244
245 - def __do_layout(self):
246 # begin wxGlade: __do_layout 247 sizer_1 = wx.BoxSizer(wx.HORIZONTAL) 248 sizer_2 = wx.BoxSizer(wx.VERTICAL) 249 sizer_3 = wx.BoxSizer(wx.VERTICAL) 250 sizer_7 = wx.BoxSizer(wx.HORIZONTAL) 251 sizer_6 = wx.BoxSizer(wx.HORIZONTAL) 252 sizer_5 = wx.BoxSizer(wx.HORIZONTAL) 253 sizer_4 = wx.BoxSizer(wx.HORIZONTAL) 254 sizer_4.Add(self.label_1, 0, wx.ALL, 10) 255 sizer_4.Add(self.horiz_off_spin, 0, wx.ALL, 10) 256 sizer_3.Add(sizer_4, 1, wx.EXPAND, 0) 257 sizer_5.Add(self.label_2, 0, wx.ALL, 10) 258 sizer_5.Add(self.vert_off_spin, 0, wx.ALL, 10) 259 sizer_3.Add(sizer_5, 1, wx.EXPAND, 0) 260 sizer_6.Add(self.label_3, 0, wx.ALL, 10) 261 sizer_6.Add(self.horiz_scale_spin, 0, wx.ALL, 10) 262 sizer_3.Add(sizer_6, 1, wx.EXPAND, 0) 263 sizer_7.Add(self.label_4, 0, wx.ALL, 10) 264 sizer_7.Add(self.vert_scale_spin, 0, wx.ALL, 10) 265 sizer_3.Add(sizer_7, 1, wx.EXPAND, 0) 266 sizer_1.Add(sizer_3, 1, wx.EXPAND, 0) 267 sizer_2.Add(self.reprint_button, 0, wx.ALL|wx.EXPAND, 10) 268 sizer_2.Add(self.calib_button, 0, wx.ALL|wx.EXPAND, 10) 269 sizer_2.Add(self.dismiss_button, 0, wx.ALL|wx.EXPAND, 10) 270 sizer_2.Add(self.text_ctrl_1, 1, wx.EXPAND, 0) 271 sizer_1.Add(sizer_2, 1, wxALL|wx.EXPAND|wx.ALIGN_RIGHT, 30) 272 self.SetAutoLayout(1) 273 self.SetSizer(sizer_1) 274 sizer_1.Fit(self) 275 self.Layout()
276 # end wxGlade 277 278 # end of class Printer 279
280 -class gmCalibrationDialog(wx.Dialog):
281 - def __init__(self):
282 # begin wxGlade: __init__ 283 #kwds["style"] = wxDIALOG_MODAL|wxCAPTION 284 wx.Dialog.__init__(self, None, -1, _("Calibration")) 285 self.label_9 = wx.StaticText(self, -1, """Calibration Page now printing.\n 286 Measure the position of the boxes and enter""") 287 self.label_5 = wx.StaticText(self, -1, "Distance of first box from top of page") 288 self.first_top_spin = wx.SpinCtrl(self, -1, min=0, max=100, initial=0) 289 self.label_6 = wx.StaticText(self, -1, "Distance of first box from left of page") 290 self.first_left_spin = wx.SpinCtrl(self, -1, min=0, max=100, initial=0) 291 self.label_7 = wx.StaticText(self, -1, "Distance of second box of top of page") 292 self.sec_top_spin = wx.SpinCtrl(self, -1, min=0, max=100, initial=0) 293 self.label_8 = wx.StaticText(self, -1, "Distance of second box from left of page") 294 self.sec_left_spin = wx.SpinCtrl(self, -1, min=0, max=100, initial=0) 295 ID = wx.NewId () 296 self.ok_button = wx.Button(self, ID, "OK") 297 wx.EVT_BUTTON (self, ID, self.OnOK) 298 self.__set_properties() 299 self.__do_layout() 300 # end wxGlade 301 self.Show ()
302
303 - def __set_properties(self):
304 # begin wxGlade: __set_properties 305 self.SetTitle("Calibration")
306 # end wxGlade 307
308 - def __do_layout(self):
309 # begin wxGlade: __do_layout 310 sizer_8 = wx.BoxSizer(wx.VERTICAL) 311 grid_sizer_2 = wx.FlexGridSizer(4, 2, 0, 0) 312 sizer_8.Add(self.label_9, 0, wx.ALL|wx.EXPAND, 10) 313 grid_sizer_2.Add(self.label_5, 0, 0, 0) 314 grid_sizer_2.Add(self.first_top_spin, 0, 0, 0) 315 grid_sizer_2.Add(self.label_6, 0, 0, 0) 316 grid_sizer_2.Add(self.first_left_spin, 0, 0, 0) 317 grid_sizer_2.Add(self.label_7, 0, 0, 0) 318 grid_sizer_2.Add(self.sec_top_spin, 0, 0, 0) 319 grid_sizer_2.Add(self.label_8, 0, 0, 0) 320 grid_sizer_2.Add(self.sec_left_spin, 0, 0, 0) 321 grid_sizer_2.AddGrowableRow(0) 322 grid_sizer_2.AddGrowableRow(1) 323 grid_sizer_2.AddGrowableRow(2) 324 grid_sizer_2.AddGrowableRow(3) 325 grid_sizer_2.AddGrowableCol(0) 326 sizer_8.Add(grid_sizer_2, 1, wx.EXPAND, 0) 327 sizer_8.Add(self.ok_button, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 10) 328 self.SetAutoLayout(1) 329 self.SetSizer(sizer_8) 330 sizer_8.Fit(self) 331 self.Layout()
332 # end wxGlade 333
334 - def OnOK (self, event):
335 self.Hide ()
336
337 - def GetValues (self):
338 return (self.first_top_spin.GetValue (), 339 self.first_left_spin.GetValue (), 340 self.sec_top_spin.GetValue (), 341 self.sec_left_spin.GetValue ())
342 343 # end of class gmCalibrationDialog 344 345 fp = FormPrinter () 346 psd = gmPrinterSetupDialog (fp) 347 348 #================================================= 349 # $Log: gmFormPrinter.py,v $ 350 # Revision 1.12 2009/01/15 11:35:41 ncq 351 # - cleanup 352 # 353 # Revision 1.11 2008/03/06 18:29:29 ncq 354 # - standard lib logging only 355 # 356 # Revision 1.10 2006/10/25 07:21:57 ncq 357 # - no more gmPG 358 # 359 # Revision 1.9 2006/01/03 12:12:03 ncq 360 # - make epydoc happy re _() 361 # 362 # Revision 1.8 2005/09/28 21:27:30 ncq 363 # - a lot of wx2.6-ification 364 # 365 # Revision 1.7 2005/09/28 15:57:48 ncq 366 # - a whole bunch of wx.Foo -> wx.Foo 367 # 368 # Revision 1.6 2005/09/26 18:01:50 ncq 369 # - use proper way to import wx26 vs wx2.4 370 # - note: THIS WILL BREAK RUNNING THE CLIENT IN SOME PLACES 371 # - time for fixup 372 # 373 # Revision 1.5 2004/06/20 16:01:05 ncq 374 # - please epydoc more carefully 375 # 376