Module aggregatedisplay
[hide private]
[frames] | no frames]

Source Code for Module aggregatedisplay

  1  ### BITPIM 
  2  ### 
  3  ### Copyright (C) 2004 Roger Binns <rogerb@rogerbinns.com> 
  4  ### 
  5  ### This program is free software; you can redistribute it and/or modify 
  6  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
  7  ### 
  8  ### $Id: aggregatedisplay.py 3298 2006-05-28 03:10:52Z skyjunky $ 
  9   
 10  "Displays a number of sections each with a number of items" 
 11   
 12  import wx 
 13  import sys 
 14  import guihelper 
 15   
 16  ActivateEvent, EVT_ACTIVATE = wx.lib.newevent.NewCommandEvent() 
 17   
18 -class BufferedPaintDC(wx.BufferedPaintDC):
19 - def __init__(self, *arg, **karg):
20 super(BufferedPaintDC, self).__init__(*arg, **karg) 21 self.view_start=(0,0) 22 self.pos=(0,0)
23
24 -class Display(wx.ScrolledWindow):
25 "This is the view" 26
27 - class HitTestResult:
28 "Where a particular point corresponds to" 29 IN_BACKGROUND=0 30 IN_SECTION=1 31 IN_ITEM=2 32
33 - def __init__(self, **kwds):
34 self.__dict__.update({ 35 'location': self.IN_BACKGROUND, 36 'sectionnum': None, 37 'section': None, 38 'sectionx': None, 39 'sectiony': None, 40 'itemnum': None, 41 'item': None, 42 'itemx': None, 43 'itemy': None, 44 }) 45 self.__dict__.update(kwds)
46
47 - def __str__(self):
48 res="<HitTestResult " 49 if self.location==self.IN_BACKGROUND: 50 res+="IN_BACKGROUND>" 51 elif self.location==self.IN_SECTION: 52 res+="IN_SECTION, sectionnum=%d, section=%s, sectionx=%d, sectiony=%d>" % \ 53 (self.sectionnum, self.section, self.sectionx, self.sectiony) 54 elif self.location==self.IN_ITEM: 55 res+="IN_ITEM, sectionnum=%d, section=%s, itemnum=%d, item=%s, itemx=%d, itemy=%d>" % \ 56 (self.sectionnum, self.section, self.itemnum, self.item, self.itemx, self.itemy) 57 else: 58 assert False, "bad location" 59 res+="ERROR>" 60 return res
61 62 63 # See the long note in OnPaint before touching this code 64 65 VSCROLLPIXELS=3 # how many pixels we scroll by 66 67 ITEMPADDING=5 # how many pixels we pad items by 68
69 - def __init__(self, parent, datasource, watermark=None):
70 wx.ScrolledWindow.__init__(self, parent, id=wx.NewId(), style=wx.FULL_REPAINT_ON_RESIZE|wx.SUNKEN_BORDER) 71 self.exceptioncount=0 72 self.EnableScrolling(False, False) 73 self.datasource=datasource 74 self._bufbmp=None 75 self.active_section=None 76 self._w, self._h=-1,-1 77 self.vheight, self.maxheight=self._h,self._h 78 self.sections=[] 79 self.sectionheights=[] 80 self._scrollbarwidth=wx.SystemSettings_GetMetric(wx.SYS_VSCROLL_X) 81 wx.EVT_SIZE(self, self.OnSize) 82 wx.EVT_PAINT(self, self.OnPaint) 83 wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground) 84 wx.EVT_LEFT_DOWN(self, self.OnLeftDown) 85 wx.EVT_LEFT_DCLICK(self, self.OnLeftDClick) 86 wx.EVT_RIGHT_DOWN(self, self.OnRightDown) 87 if watermark is not None: 88 wx.EVT_SCROLLWIN(self, self.OnScroll) 89 90 bgcolour=wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) 91 if guihelper.IsMac(): 92 bgcolour=wx.WHITE 93 self.bgbrush=wx.TheBrushList.FindOrCreateBrush(bgcolour, wx.SOLID) 94 if watermark: 95 self.watermark=guihelper.getbitmap(watermark) 96 else: 97 self.watermark=None 98 self.UpdateItems()
99
100 - def OnScroll(self, evt):
101 self.Refresh(False) 102 evt.Skip()
103
104 - def OnSize(self, evt):
105 self.Refresh(False)
106
107 - def OnEraseBackground(self, _):
108 pass
109
110 - def SetActiveSection(self, section):
111 if self.active_section!=section: 112 self.active_section=section 113 self.ReLayout() 114 self.Refresh(False)
115
116 - def OnPaint(self, event):
117 # Getting this drawing right involved hours of fighting wxPython/wxWidgets. 118 # Those hours of sweating and cursing reveal the following: 119 # 120 # - The bufferedpaintdc only allocates a bitmap large enough to cover the 121 # viewable window size. That is used irrespective of the scroll position 122 # which means you get garbage for any part of your window beyond the 123 # initial physical viewable size 124 # 125 # - The bufferedpaintdc must have its origin reset back to zero by the time 126 # its destructor is called otherwise it won't draw in the right place 127 # 128 # - The various other calls are very carefully placed (eg size handler, ReLayout 129 # being a callafter etc). Otherwise really silly things happen due to the 130 # order that things get displayed in, scrollbars adjustments causing your 131 # window to get cropped behind the scenes and various other annoyances, setting 132 # scrollbars changing size etc 133 # 134 # - The ReLayout happens very frequently. It may be possible to optimise out 135 # some calls to it. Tread very carefully and test on multiple platforms. 136 # 137 # - wx sometimes misbehaves if the window height isn't set to an exact multiple of the 138 # VSCROLLPIXELS 139 140 sz=self.GetClientSize() 141 _relayout=False 142 if sz != (self._w,self._h): 143 self._w,self._h=sz 144 _relayout=True 145 self.ReLayout() 146 # relayout may change size (scrollbar appearing/going away) so repeat in that case 147 if self.GetClientSize()!=sz: 148 return self.OnPaint(event) 149 150 if self._bufbmp is None or self._bufbmp.GetWidth()<self._w or self._bufbmp.GetHeight()<self.maxheight: 151 self._bufbmp=wx.EmptyBitmap((self._w+64)&~8, (self.maxheight+64)&~8) 152 dc=BufferedPaintDC(self, self._bufbmp, style=wx.BUFFER_VIRTUAL_AREA) 153 try: 154 self.DoPaint(dc, _relayout) 155 except: 156 # only raise one exception - swallow any more 157 self.exceptioncount+=1 158 if self.exceptioncount<=1: 159 print "raise" 160 raise
161
162 - def DoPaint(self, dc, relayout=False):
163 # we redraw everything that is in the visible area of the screen 164 vs=self.GetViewStart() 165 dc.view_start=(vs[0], vs[1]*self.VSCROLLPIXELS) 166 origin=dc.view_start[1] 167 firstvisible=origin 168 lastvisible=origin+self._h 169 170 # clear background 171 dc.SetBackground(self.bgbrush) 172 # only clear area you can see 173 dc.SetClippingRegion(0, firstvisible, self._w+1, lastvisible-firstvisible+1) 174 dc.Clear() 175 # draw watermark 176 if self.watermark: 177 # place in the middle: 178 # dc.DrawBitmap(self.watermark, self._w/2-self.watermark.GetWidth()/2, origin+self._h/2-self.watermark.GetHeight()/2, True) 179 # place in bottom right: 180 dc.DrawBitmap(self.watermark, self._w-self.watermark.GetWidth(), origin+self._h-self.watermark.GetHeight(), True) 181 182 # draw active section 183 cury=0 184 for i, section in enumerate(self.sections): 185 if self.active_section!=None and section.label!=self.active_section: 186 continue 187 if relayout or _isvisible(cury, cury+self.sectionheights[i], firstvisible, lastvisible): 188 dc.SetDeviceOrigin(0, cury ) 189 section.Draw(dc, self._w) 190 cury+=self.sectionheights[i] 191 extrawidth=(self._w-6)-(self.itemsize[i][0]+self.ITEMPADDING)*self.itemsperrow[i] 192 extrawidth/=self.itemsperrow[i] 193 if extrawidth<0: extrawidth=0 194 # now draw items in this section 195 num=0 196 while num<len(self.items[i]): 197 x=(num%self.itemsperrow[i]) 198 y=(num/self.itemsperrow[i]) 199 posy=cury+y*(self.itemsize[i][1]+self.ITEMPADDING) 200 # skip the entire row if it isn't visible 201 if not relayout and \ 202 x==0 and \ 203 not _isvisible(posy, posy+self.itemsize[i][1], firstvisible, lastvisible): 204 num+=self.itemsperrow[i] 205 continue 206 item=self.items[i][num] 207 dc.pos=(3+x*(self.itemsize[i][0]+self.ITEMPADDING+extrawidth), posy) 208 dc.SetDeviceOrigin(*dc.pos) 209 dc.ResetBoundingBox() 210 bb=item.Draw(dc, self.itemsize[i][0]+extrawidth, self.itemsize[i][1], self.selected[i][num]) 211 if bb is None: 212 bb=dc.MinX(), dc.MinY(), dc.MaxX(), dc.MaxY() 213 assert len(bb)==4 214 try: 215 assert bb[0]>=0 and bb[0]<self.itemsize[i][0]+extrawidth 216 assert bb[1]>=0 and bb[1]<self.itemsize[i][1] 217 assert bb[2]>=bb[0] and bb[2]<=self.itemsize[i][0]+extrawidth 218 assert bb[3]>=bb[1] and bb[3]<=self.itemsize[i][1] 219 except: 220 print "bb is",bb 221 print "should be within",(self.itemsize[i][0]+extrawidth,self.itemsize[i][1]) 222 223 self.boundingboxes[i][num]=bb 224 num+=1 225 cury+=(len(self.items[i])+self.itemsperrow[i]-1)/self.itemsperrow[i]*(self.itemsize[i][1]+self.ITEMPADDING) 226 227 # must do this or the world ends ... 228 dc.SetDeviceOrigin(0,0)
229
230 - def OnLeftDown(self, evt):
231 self.SetFocus() 232 actualx,actualy=self.CalcUnscrolledPosition(evt.GetX(), evt.GetY()) 233 res=self.HitTest(actualx, actualy) 234 if res.item is None: 235 if len(self.GetSelection()): 236 self.ClearSelection() 237 return 238 # if neither control or shift is down then clear selection and add this item 239 if not evt.ControlDown() and not evt.ShiftDown(): 240 self.SetSelectedByNumber(res.sectionnum, res.itemnum, False) 241 return 242 # if control and shift are down then treat as just shift 243 if evt.ShiftDown(): 244 self.SetSelectedByNumber(res.sectionnum, res.itemnum, True) 245 return 246 assert evt.ControlDown() 247 # we toggle the item 248 self.selected[res.sectionnum][res.itemnum]=not self.selected[res.sectionnum][res.itemnum] 249 self.Refresh(False)
250
251 - def OnLeftDClick(self, evt):
252 actualx,actualy=self.CalcUnscrolledPosition(evt.GetX(), evt.GetY()) 253 res=self.HitTest(actualx, actualy) 254 if res.item is None: 255 if len(self.GetSelection()): 256 self.ClearSelection() 257 return 258 self.SetSelectedByNumber(res.sectionnum, res.itemnum, False) # make this the only selected item 259 wx.PostEvent(self, ActivateEvent(self.GetId(), item=res.item))
260
261 - def OnRightDown(self, evt):
262 self.SetFocus() 263 actualx,actualy=self.CalcUnscrolledPosition(evt.GetX(), evt.GetY()) 264 res=self.HitTest(actualx, actualy) 265 if res.item is None: 266 self.ClearSelection() 267 else: 268 # if item is not in selection then change selection to item 269 if not self.IsSelectedByNum(res.sectionnum, res.itemnum): 270 self.SetSelectedByNumber(res.sectionnum, res.itemnum)
271
272 - def GetAllItems(self):
273 """Returns all items 274 275 The return value is a list. Each item in the list is a tuple of 276 (sectionnumber, sectionobject, itemnumber, itemobject)""" 277 res=[] 278 for s,section in enumerate(self.sections): 279 if self.active_section!=None and section.label!=self.active_section: 280 continue 281 for i,item in enumerate(self.items[s]): 282 res.append( (s,section,i,item) ) 283 return res
284
285 - def IsSelectedByNum(self, sectionnum, itemnum):
286 "Returns if the itemnum from sectionnum is selected" 287 return self.selected[sectionnum][itemnum]
288
289 - def IsSelectedByObject(self, itemobj):
290 "Returns if the itemobject is selected" 291 for s,section in enumerate(self.sections): 292 if self.active_section!=None and section.label!=self.active_section: 293 continue 294 for i,item in enumerate(self.items[s]): 295 if item is itemobj: 296 return self.IsSelectedByNum(s,i) 297 raise ValueError("no such item "+itemobj)
298
299 - def GetSelection(self):
300 """Returns the selected items 301 302 The return value is a list. Each item in the list is a tuple of 303 (sectionnumber, sectionobject, itemnumber, itemobject)""" 304 res=[] 305 for s,section in enumerate(self.sections): 306 if self.active_section!=None and section.label!=self.active_section: 307 continue 308 for i,item in enumerate(self.items[s]): 309 if self.selected[s][i]: 310 res.append( (s,section,i,item) ) 311 return res
312
313 - def ClearSelection(self):
314 self.selected=[ [False]*len(items) for items in self.items] 315 self.Refresh(False)
316
317 - def SelectAll(self):
318 self.selected=[ [True]*len(items) for items in self.items] 319 self.Refresh(False)
320
321 - def SetSelectedByNumber(self, sectionnum, itemnum, add=False):
322 if not add: 323 self.ClearSelection() 324 self.selected[sectionnum][itemnum]=True 325 self.Refresh(False)
326
327 - def SetSelectedByObject(self, itemobj, add=False):
328 if not add: 329 self.ClearSelection() 330 for s,section in enumerate(self.sections): 331 if self.active_section!=None and section.label!=self.active_section: 332 continue 333 for i,item in enumerate(self.items[s]): 334 if item is itemobj: 335 self.selected[s][i]=True 336 self.Refresh(False) 337 return 338 raise ValueError("no such item "+itemobj)
339 340
341 - def HitTest(self, pointx, pointy):
342 # work out which item this corresponds to 343 origin=self.GetViewStart()[1]*self.VSCROLLPIXELS # used later 344 cury=0 345 for i, section in enumerate(self.sections): 346 if self.active_section!=None and section.label!=self.active_section: 347 continue 348 if cury<=pointy<=cury+self.sectionheights[i]: 349 # in the section 350 return self.HitTestResult(location=self.HitTestResult.IN_SECTION, 351 sectionnum=i, section=section, 352 sectionx=pointx, sectiony=pointy-cury) 353 if cury>pointy: 354 return self.HitTestResult() 355 cury+=self.sectionheights[i] 356 extrawidth=(self._w-6)-(self.itemsize[i][0]+self.ITEMPADDING)*self.itemsperrow[i] 357 extrawidth/=self.itemsperrow[i] 358 if extrawidth<0: extrawidth=0 359 # now find items in this section 360 num=0 361 while num<len(self.items[i]): 362 x=(num%self.itemsperrow[i]) 363 y=(num/self.itemsperrow[i]) 364 posy=cury+y*(self.itemsize[i][1]+self.ITEMPADDING) 365 # skip the entire row if it isn't in range 366 if x==0 and not posy<=pointy<=posy+self.itemsize[i][1]: 367 num+=self.itemsperrow[i] 368 continue 369 item=self.items[i][num] 370 bbox=self.boundingboxes[i][num] 371 if bbox is None: 372 bbox=(0, 0, self.itemsize[i][0]+extrawidth, self.itemsize[i][1]) 373 startx=3+x*(self.itemsize[i][0]+self.ITEMPADDING+extrawidth) 374 startx+=bbox[0] 375 endx=startx+bbox[2]-bbox[0] 376 starty=posy+bbox[1] 377 endy=starty+bbox[3]-bbox[1] 378 if startx<=pointx<=endx and starty<=pointy<=endy: 379 ht=self.HitTestResult(location=self.HitTestResult.IN_ITEM, 380 sectionnum=i, section=section, 381 itemnum=num, item=item, 382 itemx=pointx-startx+bbox[0], 383 itemy=pointy-starty+bbox[1], 384 itemrect=(startx, starty, endx-startx, endy-starty), 385 itemrectscrolled=(startx, starty-origin, endx-startx, endy-starty), 386 ) 387 return ht 388 num+=1 389 cury+=(len(self.items[i])+self.itemsperrow[i]-1)/self.itemsperrow[i]*(self.itemsize[i][1]+self.ITEMPADDING) 390 return self.HitTestResult()
391 392
393 - def ReLayout(self):
394 self.itemsperrow=[] 395 self.vheight=0 396 for i,section in enumerate(self.sections): 397 self.itemsperrow.append(max((self._w-6)/(self.itemsize[i][0]+self.ITEMPADDING),1)) 398 if self.active_section!=None and section.label!=self.active_section: 399 continue 400 self.vheight+=self.sectionheights[-1] 401 rows=(len(self.items[i])+self.itemsperrow[i]-1)/self.itemsperrow[i] 402 self.vheight+=rows*(self.itemsize[i][1]+self.ITEMPADDING) 403 404 # set the height we want wx to think the window is 405 self.maxheight=max(self.vheight,self._h) 406 # adjust scrollbar 407 self.SetScrollbars(0,1,0,self.vheight,0, self.GetViewStart()[1]) 408 self.SetScrollRate(0, self.VSCROLLPIXELS)
409
410 - def UpdateItems(self):
411 "Called if you want the items to be refetched from the model" 412 self.sections=self.datasource.GetSections() 413 self.items=[] 414 self.itemsize=[] 415 self.sectionheights=[] 416 self.selected=[] 417 self.boundingboxes=[] 418 for i,section in enumerate(self.sections): 419 self.sectionheights.append(section.GetHeight()) 420 self.itemsize.append(self.datasource.GetItemSize(i,section)) 421 items=self.datasource.GetItemsFromSection(i,section) 422 self.items.append(items) 423 self.boundingboxes.append( [None]*len(items) ) 424 self.selected.append( [False]*len(items) ) 425 self._w+=1 # cause a relayout to happen 426 self.Refresh(False)
427 428 429
430 -def _isvisible(start, end, firstvisible, lastvisible):
431 return start <= firstvisible <= end <= lastvisible or \ 432 (start >= firstvisible and start <= lastvisible) or \ 433 (start <= firstvisible and end >=lastvisible)
434 435
436 -class SectionHeader(object):
437 "A generic section header implementation" 438
439 - def __init__(self, label):
440 self.label=label 441 self.InitAttributes()
442
443 - def InitAttributes(self):
444 "Initialise our font and colours" 445 self.font=wx.TheFontList.FindOrCreateFont(20, family=wx.SWISS, style=wx.NORMAL, weight=wx.NORMAL) 446 self.font2=wx.TheFontList.FindOrCreateFont(20, family=wx.SWISS, style=wx.NORMAL, weight=wx.LIGHT) 447 self.fontcolour=wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNTEXT) 448 self.font2colour=wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW) 449 dc=wx.MemoryDC() 450 # mac needs a bitmap selected in order to return textextent! 451 if guihelper.IsMac(): 452 dc.SelectObject(wx.EmptyBitmap(100,100)) 453 w,h,d,l=dc.GetFullTextExtent("I", font=self.font) 454 self.height=h+3 455 self.descent=d
456
457 - def GetHeight(self):
458 return self.height
459
460 - def Draw(self, dc, width):
461 oldc=dc.GetTextForeground() 462 oldf=dc.GetFont() 463 dc.DrawLine(3, self.height-self.descent, width-3, self.height-self.descent) 464 dc.SetTextForeground(self.font2colour) 465 dc.SetFont(self.font2) 466 for xoff in (0,1,2): 467 for yoff in (0,1,2): 468 dc.DrawText(self.label, xoff+3, yoff) 469 dc.SetTextForeground(self.fontcolour) 470 dc.SetFont(self.font) 471 dc.DrawText(self.label, 1+3,1) 472 dc.SetTextForeground(oldc) 473 dc.SetFont(oldf)
474
475 -class Item(object):
476 """A generic item implementation. 477 478 You don't need to inherit from this - it is mainly to document what you have to implement.""" 479
480 - def Draw(self, dc, width, height, selected):
481 """Draw yourself into the available space. 0,0 will be your top left. 482 483 Note that the width may be larger than the 484 L{DataSource.GetItemSize} method returned because unused space 485 is spread amongst the items. It will never be smaller than 486 what was returned. You should set the clipping region if 487 necessary. 488 489 The main display code needs to know the bounding box for each item so that it can tell 490 when an item has been clicked on, as opposed to the white space surrounding an item. 491 By default it clears the bounding box and looks at what area you draw on in this 492 function. If you return None, then that is what happens. 493 494 Instead you may also return a 4 item tuple of (minx, miny, maxx, maxy) and that 495 will be used. 496 497 @param dc: The device context to draw into 498 @param width: maximum space to use 499 @param height: maximum space to use 500 @param selected: if the item is currently selected""" 501 raise NotImplementedError()
502 503 504
505 -class DataSource(object):
506 """This is the model 507 508 You don't need to inherit from this - it is mainly to document what you have to implement.""" 509
510 - def GetSections(self):
511 "Return a list of section headers" 512 raise NotImplementedError()
513
514 - def GetItemSize(self, sectionnumber, sectionheader):
515 "Return (width, height of each item)" 516 return (160, 80)
517
518 - def GetItemsFromSection(self,sectionnumber,sectionheader):
519 """Return a list of the items for the section. 520 521 @param sectionnumber: the index into the list returned by L{GetSections} 522 @param sectionheader: the section header object from that list 523 """ 524 raise NotImplementedError()
525 526 if __name__=="__main__": 527 528 529
530 - def demo():
531 import wx 532 app=wx.PySimpleApp() 533 534 import sys 535 import common 536 sys.excepthook=common.formatexceptioneh 537 import wx.html 538 import os 539 import brewcompressedimage 540 import wallpaper 541 import fileinfo 542 import conversions 543 544 545 # find bitpim wallpaper directory 546 config=wx.Config("bitpim", style=wx.CONFIG_USE_LOCAL_FILE) 547 548 p=config.Read("path", "resources") 549 pn=os.path.join(p, "wallpaper") 550 if os.path.isdir(pn): 551 p=pn 552 553 imagespath=p 554 images=[name for name in os.listdir(imagespath) if name[-4:] in (".bci", ".bit", ".bmp", ".jpg", ".png")] 555 images.sort() 556 #images=images[:9] 557 558 print imagespath 559 print images 560 561 class WallpaperManager: 562 563 def GetImageStatInformation(self,name): 564 return wallpaper.statinfo(os.path.join(imagespath, name))
565 566 def GetImageConstructionInformation(self,file): 567 file=os.path.join(imagespath, file) 568 569 if file.endswith(".mp4") or not os.path.isfile(file): 570 return guihelper.getresourcefile('wallpaper.png'), wx.Image 571 if self.isBCI(file): 572 return file, lambda name: brewcompressedimage.getimage(brewcompressedimage.FileInputStream(file)) 573 # LG phones may return a proprietary wallpaper media file, LGBIT 574 fi=fileinfo.identify_imagefile(file) 575 if fi is not None and fi.format=='LGBIT': 576 return file, conversions.convertfilelgbittobmp 577 return file, wx.Image 578 579 def isBCI(self, filename): 580 # is it a bci file? 581 return open(filename, "rb").read(4)=="BCI\x00" 582 583 584 wx.FileSystem_AddHandler(wallpaper.BPFSHandler(WallpaperManager())) 585 586 class TestItem(Item): 587 588 def __init__(self, ds, secnum, itemnum, label): 589 super(TestItem,self).__init__() 590 self.label=label 591 self.ds=ds 592 self.secnum=secnum 593 self.itemnum=itemnum 594 595 def Draw(self, dc, width, height, selected): 596 # uncomment to see exactly what size is given 597 #dc.DrawRectangle(0,0,width,height) 598 us=dc.GetUserScale() 599 dc.SetClippingRegion(0,0,width,height) 600 hdc=wx.html.HtmlDCRenderer() 601 hdc.SetDC(dc, 1) 602 hdc.SetSize(9999, 9999) # width is deliberately wide so that no wrapping happens 603 hdc.SetHtmlText(self.genhtml(selected), '.', True) 604 hdc.Render(0,0) 605 del hdc 606 # restore scale hdc messes 607 dc.SetUserScale(*us) 608 dc.DestroyClippingRegion() 609 610 # Linux gets bounding box wrong, so we deliberately return actual size 611 if guihelper.IsGtk(): 612 return (0,0,width,height) 613 elif guihelper.IsMSWindows(): 614 return max(0,dc.MinX()), max(0, dc.MinY()), min(width, dc.MaxX()), min(height, dc.MaxY()) 615 616 def genhtml(self, selected): 617 if selected: 618 selected='bgcolor="blue"' 619 else: 620 selected="" 621 return """<table %s><tr><td valign=top><p><img src="bpuserimage:%s;width=%d;height=%d;valign=top"><td valign=top><b>%s</b><br>BMP format<br>123x925<br>Camera</tr></table>""" \ 622 % (selected, images[self.itemnum], self.ds.IMGSIZES[self.secnum][0], self.ds.IMGSIZES[self.secnum][1], self.label, ) 623 624 class TestDS(DataSource): 625 626 SECTIONS=("Camera", "Wallpaper", "Oranges", "Lemons") 627 ITEMSIZES=( (240,240), (160,70), (48,48), (160,70) ) 628 IMGSIZES=[ (w-110,h-20) for w,h in ITEMSIZES] 629 IMGSIZES[2]=(16,16) 630 631 def GetSections(self): 632 return [SectionHeader(x) for x in self.SECTIONS] 633 634 def GetItemsFromSection(self, sectionnumber, sectionheader): 635 return [TestItem(self, sectionnumber, i, "%s-#%d" % (sectionheader.label,i)) for i in range(len(images))] 636 637 def GetItemSize(self, sectionnumber, sectionheader): 638 return self.ITEMSIZES[sectionnumber] 639 640 f=wx.Frame(None, title="Aggregate Display Test") 641 ds=TestDS() 642 d=Display(f,ds, "wallpaper-watermark") 643 f.Show() 644 645 app.MainLoop() 646 647 648 if __debug__ and True:
649 - def profile(filename, command):
650 import hotshot, hotshot.stats, os 651 file=os.path.abspath(filename) 652 profile=hotshot.Profile(file) 653 profile.run(command) 654 profile.close() 655 del profile 656 howmany=100 657 stats=hotshot.stats.load(file) 658 stats.strip_dirs() 659 stats.sort_stats('time', 'calls') 660 stats.print_stats(20) 661 stats.sort_stats('cum', 'calls') 662 stats.print_stats(20) 663 stats.sort_stats('calls', 'time') 664 stats.print_stats(20) 665 sys.exit(0)
666 667 profile("aggdisplay.prof", "demo()") 668 669 else: 670 demo() 671