| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754 |
- # --------------------------------------------------------------------------- #
- # TOASTERBOX wxPython IMPLEMENTATION
- # Ported And Enhanced From wxWidgets Contribution (Aj Bommarito) By:
- #
- # Andrea Gavana, @ 16 September 2005
- # Latest Revision: 31 Oct 2007, 21.30 CET
- #
- #
- # TODO/Caveats List
- #
- # 1. Any Idea?
- #
- #
- # For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please
- # Write To Me At:
- #
- # [email protected]
- # [email protected]
- #
- # Or, Obviously, To The wxPython Mailing List!!!
- #
- #
- # End Of Comments
- # --------------------------------------------------------------------------- #
- """Description:
- ToasterBox Is A Cross-Platform Library To Make The Creation Of MSN Style "Toaster"
- Popups Easier. The Syntax Is Really Easy Especially If You Are Familiar With The
- Syntax Of wxPython.
- It Has 2 Main Styles:
- - TB_SIMPLE: Using This Style, You Will Be Able To Specify A Background Image For
- ToasterBox, Text Properties As Text Colour, Font And Label.
- - TB_COMPLEX: This Style Will Allow You To Put Almost Any Control Inside A
- ToasterBox. You Can Add A Panel In Which You Can Put All The Controls
- You Like.
- Both Styles Support The Setting Of ToasterBox Position (On Screen Coordinates),
- Size, The Time After Which The ToasterBox Is Destroyed (Linger), And The Scroll
- Speed Of ToasterBox.
- ToasterBox Has Been Tested On The Following Platforms:
- Windows (Verified on Windows XP, 2000)
- Latest Revision: Andrea Gavana @ 31 Oct 2007, 21.30 CET
- """
- import textwrap
- import wx
- import sys
- from wx.lib.statbmp import GenStaticBitmap as StaticBitmap
- # Define Window List, We Use It Globally
- winlist = []
- TB_SIMPLE = 1
- TB_COMPLEX = 2
- DEFAULT_TB_STYLE = wx.SIMPLE_BORDER | wx.STAY_ON_TOP | wx.FRAME_NO_TASKBAR
- TB_CAPTION = DEFAULT_TB_STYLE | wx.CAPTION | wx.SYSTEM_MENU | wx.CLOSE_BOX | wx.FRAME_TOOL_WINDOW
- TB_ONTIME = 1
- TB_ONCLICK = 2
- # scroll from up to down
- TB_SCR_TYPE_UD = 1
- # scroll from down to up
- TB_SCR_TYPE_DU = 2
- # ------------------------------------------------------------------------------ #
- # Class ToasterBox
- # Main Class Implementation. It Is Basically A wx.Timer. It Creates And
- # Displays Popups And Handles The "Stacking".
- # ------------------------------------------------------------------------------ #
- class ToasterBox(wx.Timer):
- def __init__(self, parent, tbstyle=TB_SIMPLE, windowstyle=DEFAULT_TB_STYLE,
- closingstyle=TB_ONTIME, scrollType=TB_SCR_TYPE_DU):
- """Deafult Class Constructor.
- ToasterBox.__init__(self, tbstyle=TB_SIMPLE, windowstyle=DEFAULT_TB_STYLE)
- Parameters:
- - tbstyle: This Parameter May Have 2 Values:
- (a) TB_SIMPLE: A Simple ToasterBox, With Background Image And Text
- Customization Can Be Created;
- (b) TB_COMPLEX: ToasterBoxes With Different Degree Of Complexity Can
- Be Created. You Can Add As Many Controls As You Want, Provided
- That You Call The AddPanel() Method And Pass To It A Dummy Frame
- And A wx.Panel. See The Demo For Details.
- - windowstyle: This Parameter Influences The Visual Appearance Of ToasterBox:
- (a) DEFAULT_TB_STYLE: Default Style, No Caption Nor Close Box;
- (b) TB_CAPTION: ToasterBox Will Have A Caption, With The Possibility To
- Set A Title For ToasterBox Frame, And A Close Box;
- - closingstyle: Set This Value To TB_ONCLICK If You Want To Be Able To Close
- ToasterBox By A Mouse Click Anywhere In The ToasterBox Frame.
- """
- self._parent = parent
- self._sleeptime = 10
- self._pausetime = 1700
- self._popuptext = "default"
- self._popupposition = wx.Point(100,100)
- self._popuptop = wx.Point(0,0)
- self._popupsize = wx.Size(150, 170)
- self._backgroundcolour = wx.WHITE
- self._foregroundcolour = wx.BLACK
- if sys.platform != "darwin":
- self._textfont = wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL, False, "Verdana")
- else:
- self._textfont = wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, "Monaco")
- self._bitmap = None
- self._tbstyle = tbstyle
- self._windowstyle = windowstyle
- self._closingstyle = closingstyle
- self._panel = None
- self._bottomright = wx.Point(wx.GetDisplaySize().GetWidth(),
- wx.GetDisplaySize().GetHeight())
- parent.Bind(wx.EVT_ICONIZE, lambda evt: [w.Hide() for w in winlist])
- self._tb = ToasterBoxWindow(self._parent, self, self._tbstyle, self._windowstyle,
- self._closingstyle, scrollType=scrollType)
- def SetPopupPosition(self, pos):
- """ Sets The ToasterBox Position On Screen. """
- self._popupposition = pos
- def SetPopupPositionByInt(self, pos):
- """ Sets The ToasterBox Position On Screen, At One Of The Screen Corners. """
- self._bottomright = wx.Point(wx.GetDisplaySize().GetWidth(),
- wx.GetDisplaySize().GetHeight())
- # top left
- if pos == 0:
- popupposition = wx.Point(0,0)
- # top right
- elif pos == 1:
- popupposition = wx.Point(wx.GetDisplaySize().GetWidth() -
- self._popupsize[0], 0)
- # bottom left
- elif pos == 2:
- popupposition = wxPoint(0, wx.GetDisplaySize().GetHeight() -
- self._popupsize[1])
- # bottom right
- elif pos == 3:
- popupposition = wx.Point(self._bottomright.x - self._popupsize[0],
- self._bottomright.y - self._popupsize[1])
- self._bottomright = wx.Point(popupposition.x + self._popupsize[0],
- popupposition.y + self._popupsize[1])
- def SetPopupBackgroundColor(self, colour=None):
- """ Sets The ToasterBox Background Colour. Use It Only For ToasterBoxes Created
- With TB_SIMPLE Style. """
- if colour is None:
- colour = wx.WHITE
- self._backgroundcolour = colour
- def SetPopupTextColor(self, colour=None):
- """ Sets The ToasterBox Foreground Colour. Use It Only For ToasterBoxes Created
- With TB_SIMPLE Style. """
- if colour is None:
- colour = wx.BLACK
- self._foregroundcolour = colour
- def SetPopupTextFont(self, font=None):
- """ Sets The ToasterBox Text Font. Use It Only For ToasterBoxes Created With
- TB_SIMPLE Style. """
- if font is None:
- if sys.platform != "darwin":
- font = wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL, False, "Verdana")
- else:
- font = wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, "Monaco")
- self._textfont = font
- def SetPopupSize(self, size):
- """ Sets The ToasterBox Size. """
- self._popupsize = size
- def SetPopupPauseTime(self, pausetime):
- """ Sets The Time After Which The ToasterBox Is Destroyed (Linger). """
- self._pausetime = pausetime
- def SetPopupBitmap(self, bitmap=None):
- """ Sets The ToasterBox Background Image. Use It Only For ToasterBoxes
- Created With TB_SIMPLE Style. """
- if bitmap is not None:
- bitmap = wx.Bitmap(bitmap, wx.BITMAP_TYPE_BMP)
- self._bitmap = bitmap
- def SetPopupScrollSpeed(self, speed):
- """ Sets The ToasterBox Scroll Speed. The Speed Parameter Is The Pause
- Time (In ms) For Every Step In The ScrollUp() Method."""
- self._sleeptime = speed
- def SetPopupText(self, text):
- """ Sets The ToasterBox Text. Use It Only For ToasterBoxes Created With
- TB_SIMPLE Style. """
- self._popuptext = text
- def AddPanel(self, panel):
- """ Adds A Panel To The ToasterBox. Use It Only For ToasterBoxes Created
- With TB_COMPLEX Style. """
- if not self._tbstyle & TB_COMPLEX:
- raise "\nERROR: Panel Can Not Be Added When Using TB_SIMPLE ToasterBox Style"
- return
- self._panel = panel
- def Play(self):
- """ Creates The ToasterBoxWindow, That Does All The Job. """
- # create new window
- self._tb.SetPopupSize((self._popupsize[0], self._popupsize[1]))
- self._tb.SetPopupPosition((self._popupposition[0], self._popupposition[1]))
- self._tb.SetPopupPauseTime(self._pausetime)
- self._tb.SetPopupScrollSpeed(self._sleeptime)
- if self._tbstyle == TB_SIMPLE:
- self._tb.SetPopupTextColor(self._foregroundcolour)
- self._tb.SetPopupBackgroundColor(self._backgroundcolour)
- self._tb.SetPopupTextFont(self._textfont)
- if self._bitmap is not None:
- self._tb.SetPopupBitmap(self._bitmap)
- self._tb.SetPopupText(self._popuptext)
- if self._tbstyle == TB_COMPLEX:
- if self._panel is not None:
- self._tb.AddPanel(self._panel)
- # clean up the list
- self.CleanList()
- # check to see if there is already a window displayed
- # by looking at the linked list
- if len(winlist) > 0:
- # there ARE other windows displayed already
- # reclac where it should display
- self.MoveAbove(self._tb)
- # shift new window on to the list
- winlist.append(self._tb)
- if not self._tb.Play():
- # if we didn't show the window properly, remove it from the list
- winlist.remove(winlist[-1])
- # delete the object too
- self._tb.Destroy()
- return
- def MoveAbove(self, tb):
- """ If A ToasterBox Already Exists, Move The New One Above. """
- # recalc where to place this popup
- self._tb.SetPopupPosition((self._popupposition[0], self._popupposition[1] -
- self._popupsize[1]*len(winlist)))
- def GetToasterBoxWindow(self):
- """ Returns The ToasterBox Frame. """
- return self._tb
- def SetTitle(self, title):
- """ Sets The ToasterBox Title If It Was Created With TB_CAPTION Window Style. """
- self._tb.SetTitle(title)
- def Notify(self):
- """ It's Time To Hide A ToasterBox! """
- if len(winlist) == 0:
- return
- # clean the window list
- self.CleanList()
- # figure out how many blanks we have
- try:
- node = winlist[0]
- except:
- return
- if not node:
- return
- # move windows to fill in blank space
- for i in xrange(node.GetPosition()[1], self._popupposition[1], 4):
- if i > self._popupposition[1]:
- i = self._popupposition[1]
- # loop through all the windows
- for j in xrange(0, len(winlist)):
- ourNewHeight = i - (j*self._popupsize[1] - 8)
- tmpTb = winlist[j]
- # reset where the object THINKS its supposed to be
- tmpTb.SetPopupPosition((self._popupposition[0], ourNewHeight))
- # actually move it
- tmpTb.SetDimensions(self._popupposition[0], ourNewHeight, tmpTb.GetSize().GetWidth(),
- tmpTb.GetSize().GetHeight())
- wx.Usleep(self._sleeptime)
- def CleanList(self):
- """ Clean The Window List. """
- if len(winlist) == 0:
- return
- node = winlist[0]
- while node:
- if not node.IsShown():
- winlist.remove(node)
- try:
- node = winlist[0]
- except:
- node = 0
- else:
- indx = winlist.index(node)
- try:
- node = winlist[indx+1]
- except:
- node = 0
- # ------------------------------------------------------------------------------ #
- # Class ToasterBoxWindow
- # This Class Does All The Job, By Handling Background Images, Text Properties
- # And Panel Adding. Depending On The Style You Choose, ToasterBoxWindow Will
- # Behave Differently In Order To Handle Widgets Inside It.
- # ------------------------------------------------------------------------------ #
- class ToasterBoxWindow(wx.Frame):
- def __init__(self, parent, parent2, tbstyle, windowstyle,
- closingstyle, scrollType=TB_SCR_TYPE_DU):
- """Default Class Constructor.
- Used Internally. Do Not Call Directly This Class In Your Application!
- """
- wx.Frame.__init__(self, parent, wx.ID_ANY, "window", wx.DefaultPosition,
- wx.DefaultSize, style=windowstyle | wx.CLIP_CHILDREN)
- self._starttime = wx.GetLocalTime()
- self._parent2 = parent2
- self._parent = parent
- self._sleeptime = 10
- self._step = 4
- self._pausetime = 1700
- self._textcolour = wx.BLACK
- self._popuptext = "Change Me!"
- # the size we want the dialog to be
- framesize = wx.Size(150, 170)
- self._count = 1
- self._tbstyle = tbstyle
- self._windowstyle = windowstyle
- self._closingstyle = closingstyle
- self._scrollType = scrollType
- if tbstyle == TB_COMPLEX:
- self.sizer = wx.BoxSizer(wx.VERTICAL)
- else:
- self._staticbitmap = None
- if self._windowstyle == TB_CAPTION:
- self.Bind(wx.EVT_CLOSE, self.OnClose)
- self.SetTitle("")
- if self._closingstyle & TB_ONCLICK and self._windowstyle != TB_CAPTION:
- self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseDown)
- self._bottomright = wx.Point(wx.GetDisplaySize().GetWidth(),
- wx.GetDisplaySize().GetHeight())
- self.SetDimensions(self._bottomright.x, self._bottomright.y,
- framesize.GetWidth(), framesize.GetHeight())
- def OnClose(self, event):
- self.NotifyTimer(None)
- event.Skip()
- def OnMouseDown(self, event):
- self.NotifyTimer(None)
- event.Skip()
- def SetPopupBitmap(self, bitmap):
- """ Sets The ToasterBox Background Image. Use It Only For ToasterBoxes
- Created With TB_SIMPLE Style. """
- bitmap = bitmap.ConvertToImage()
- xsize, ysize = self.GetSize()
- bitmap = bitmap.Scale(xsize, ysize)
- bitmap = bitmap.ConvertToBitmap()
- self._staticbitmap = StaticBitmap(self, -1, bitmap, pos=(0,0))
- if self._closingstyle & TB_ONCLICK and self._windowstyle != TB_CAPTION:
- self._staticbitmap.Bind(wx.EVT_LEFT_DOWN, self.OnMouseDown)
- def SetPopupSize(self, size):
- """ Sets The ToasterBox Size. """
- self.SetDimensions(self._bottomright.x, self._bottomright.y, size[0], size[1])
- def SetPopupPosition(self, pos):
- """ Sets The ToasterBox Position On Screen. """
- self._bottomright = wx.Point(pos[0] + self.GetSize().GetWidth(),
- pos[1] + self.GetSize().GetHeight())
- self._dialogtop = pos
- def SetPopupPositionByInt(self, pos):
- """ Sets The ToasterBox Position On Screen, At One Of The Screen Corners. """
- self._bottomright = wx.Point(wx.GetDisplaySize().GetWidth(),
- wx.GetDisplaySize().GetHeight())
- # top left
- if pos == 0:
- popupposition = wx.Point(0,0)
- # top right
- elif pos == 1:
- popupposition = wx.Point(wx.GetDisplaySize().GetWidth() -
- self._popupsize[0], 0)
- # bottom left
- elif pos == 2:
- popupposition = wx.Point(0, wx.GetDisplaySize().GetHeight() -
- self._popupsize[1])
- # bottom right
- elif pos == 3:
- popupposition = wx.Point(self._bottomright.x - self._popupsize[0],
- self._bottomright.y - self._popupsize[1])
- self._bottomright = wx.Point(popupposition.x + self._popupsize[0],
- popupposition.y + self._popupsize[1])
- self._dialogtop = popupposition
- def SetPopupPauseTime(self, pausetime):
- """ Sets The Time After Which The ToasterBox Is Destroyed (Linger). """
- self._pausetime = pausetime
- def SetPopupScrollSpeed(self, speed):
- """ Sets The ToasterBox Scroll Speed. The Speed Parameter Is The Pause
- Time (In ms) For Every Step In The ScrollUp() Method."""
- self._sleeptime = speed
- def AddPanel(self, panel):
- """ Adds A Panel To The ToasterBox. Use It Only For ToasterBoxes Created
- With TB_COMPLEX Style. """
- if not self._tbstyle & TB_COMPLEX:
- raise "\nERROR: Panel Can Not Be Added When Using TB_SIMPLE ToasterBox Style"
- return
- self.sizer.Add(panel, 1, wx.EXPAND)
- self.sizer.Layout()
- self.SetSizer(self.sizer)
- if self._closingstyle & TB_ONCLICK and self._windowstyle != TB_CAPTION:
- panel.Bind(wx.EVT_LEFT_DOWN, self.OnMouseDown)
- def SetPopupText(self, text):
- """ Sets The ToasterBox Text. Use It Only For ToasterBoxes Created With
- TB_SIMPLE Style. """
- self._popuptext = text
- def SetPopupTextFont(self, font):
- """ Sets The ToasterBox Text Font. Use It Only For ToasterBoxes Created With
- TB_SIMPLE Style. """
- self._textfont = font
- def GetPopupText(self):
- """ Returns The ToasterBox Text. Use It Only For ToasterBoxes Created With
- TB_SIMPLE Style. """
- return self._popuptext
- def Play(self):
- """ Creates The ToasterBoxWindow, That Does All The Job. """
- # do some checks to make sure this window is valid
- if self._bottomright.x < 1 or self._bottomright.y < 1:
- return False
- if self.GetSize().GetWidth() < 50 or self.GetSize().GetWidth() < 50:
- # toasterbox launches into a endless loop for some reason
- # when you try to make the window too small.
- return False
- self.ScrollUp()
- timerid = wx.NewId()
- self.showtime = wx.Timer(self, timerid)
- self.showtime.Start(self._pausetime)
- self.Bind(wx.EVT_TIMER, self.NotifyTimer, id=timerid)
- return True
- def NotifyTimer(self, event):
- """ Hides Gradually The ToasterBoxWindow. """
- self.showtime.Stop()
- del self.showtime
- self.ScrollDown()
- def SetPopupBackgroundColor(self, colour):
- """ Sets The ToasterBox Background Colour. Use It Only For ToasterBoxes Created
- With TB_SIMPLE Style. """
- self.SetBackgroundColour(colour)
- def SetPopupTextColor(self, colour):
- """ Sets The ToasterBox Foreground Colour. Use It Only For ToasterBoxes Created
- With TB_SIMPLE Style. """
- self._textcolour = colour
- def ScrollUp(self):
- """ Scrolls The ToasterBox Up, Which Means Gradually Showing The ToasterBox. """
- self.Show(True)
- # walk the Y value up in a raise motion
- xpos = self.GetPosition().x
- ypos = self._bottomright[1]
- windowsize = 0
- # checking the type of the scroll (from up to down or from down to up)
- if self._scrollType == TB_SCR_TYPE_UD:
- start = self._dialogtop[1]
- stop = ypos
- step = self._step
- elif self._scrollType == TB_SCR_TYPE_DU:
- start = ypos
- stop = self._dialogtop[1]
- step = -self._step
- else:
- errMsg = ("scrollType not supported (in ToasterBoxWindow.ScrollUp): %s" %
- self._scrollType)
- raise ValueError(errMsg)
- for i in xrange(start, stop, step):
- if i < self._dialogtop[1]:
- i = self._dialogtop[1]
- windowsize = windowsize + self._step
- # checking the type of the scroll (from up to down or from down to up)
- if self._scrollType == TB_SCR_TYPE_UD:
- dimY = self._dialogtop[1]
- elif self._scrollType == TB_SCR_TYPE_DU:
- dimY = i
- else:
- errMsg = ("scrollType not supported (in ToasterBoxWindow.ScrollUp): %s" %
- self._scrollType)
- raise ValueError(errMsg)
- self.SetDimensions(self._dialogtop[0], dimY, self.GetSize().GetWidth(),
- windowsize)
- if self._tbstyle == TB_SIMPLE:
- self.DrawText()
- wx.Usleep(self._sleeptime)
- self.Update()
- self.Refresh()
- self.Update()
- if self._tbstyle == TB_SIMPLE:
- self.DrawText()
- # self.SetFocus()
- def ScrollDown(self):
- """ Scrolls The ToasterBox Down, Which Means Gradually Hiding The ToasterBox. """
- # walk down the Y value
- windowsize = self.GetSize().GetHeight()
- # checking the type of the scroll (from up to down or from down to up)
- if self._scrollType == TB_SCR_TYPE_UD:
- start = self._bottomright.y
- stop = self._dialogtop[1]
- step = -self._step
- elif self._scrollType == TB_SCR_TYPE_DU:
- start = self._dialogtop[1]
- stop = self._bottomright.y
- step = self._step
- else:
- errMsg = ("scrollType not supported (in ToasterBoxWindow.ScrollUp): %s" %
- self._scrollType)
- raise ValueError(errMsg)
- for i in xrange(start, stop, step):
- if i > self._bottomright.y:
- i = self._bottomright.y
- windowsize = windowsize - self._step
- # checking the type of the scroll (from up to down or from down to up)
- if self._scrollType == TB_SCR_TYPE_UD:
- dimY = self._dialogtop[1]
- elif self._scrollType == TB_SCR_TYPE_DU:
- dimY = i
- else:
- errMsg = ("scrollType not supported (in ToasterBoxWindow.ScrollUp): %s" %
- self._scrollType)
- raise ValueError(errMsg)
- self.SetDimensions(self._dialogtop[0], dimY,
- self.GetSize().GetWidth(), windowsize)
- wx.Usleep(self._sleeptime)
- self.Refresh()
- self.Hide()
- if self._parent2:
- self._parent2.Notify()
- def DrawText(self):
- if self._staticbitmap is not None:
- dc = wx.ClientDC(self._staticbitmap)
- else:
- dc = wx.ClientDC(self)
- dc.SetFont(self._textfont)
- if not hasattr(self, "text_coords"):
- self._getTextCoords(dc)
- dc.DrawTextList(*self.text_coords)
- def _getTextCoords(self, dc):
- """ Draw The User Specified Text Using The wx.DC. Use It Only For ToasterBoxes
- Created With TB_SIMPLE Style. """
- # border from sides and top to text (in pixels)
- border = 7
- # how much space between text lines
- textPadding = 2
- pText = self.GetPopupText()
- max_len = len(pText)
- tw, th = self._parent2._popupsize
- if self._windowstyle == TB_CAPTION:
- th = th - 20
- while 1:
- lines = textwrap.wrap(pText, max_len)
- for line in lines:
- w, h = dc.GetTextExtent(line)
- if w > tw - border * 2:
- max_len -= 1
- break
- else:
- break
- fh = 0
- for line in lines:
- w, h = dc.GetTextExtent(line)
- fh += h + textPadding
- y = (th - fh) / 2; coords = []
- for line in lines:
- w, h = dc.GetTextExtent(line)
- x = (tw - w) / 2
- coords.append((x, y))
- y += h + textPadding
- self.text_coords = (lines, coords)
|