| 1 | #---------------------------------------------------------------------- |
| 2 | # Name: wx.lib.ticker |
| 3 | # Purpose: A news-ticker style scrolling text control |
| 4 | # |
| 5 | # Author: Chris Mellon |
| 6 | # |
| 7 | # Created: 29-Aug-2004 |
| 8 | # RCS-ID: $Id$ |
| 9 | # Copyright: (c) 2004 by Chris Mellon |
| 10 | # Licence: wxWindows license |
| 11 | #---------------------------------------------------------------------- |
| 12 | |
| 13 | """News-ticker style scrolling text control |
| 14 | |
| 15 | * Can scroll from right to left or left to right. |
| 16 | |
| 17 | * Speed of the ticking is controlled by two parameters: |
| 18 | |
| 19 | - Frames per Second(FPS): How many times per second the ticker updates |
| 20 | |
| 21 | - Pixels per Frame(PPF): How many pixels the text moves each update |
| 22 | |
| 23 | Low FPS with high PPF will result in "jumpy" text, lower PPF with higher FPS |
| 24 | is smoother (but blurrier and more CPU intensive) text. |
| 25 | """ |
| 26 | |
| 27 | import wx |
| 28 | |
| 29 | #---------------------------------------------------------------------- |
| 30 | |
| 31 | class Ticker(wx.PyControl): |
| 32 | def __init__(self, |
| 33 | parent, |
| 34 | id=-1, |
| 35 | text=wx.EmptyString, #text in the ticker |
| 36 | fgcolor = wx.BLACK, #text/foreground color |
| 37 | bgcolor = wx.WHITE, #background color |
| 38 | start=True, #if True, the ticker starts immediately |
| 39 | ppf=2, #pixels per frame |
| 40 | fps=20, #frames per second |
| 41 | direction="rtl", #direction of ticking, rtl or ltr |
| 42 | pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.NO_BORDER, |
| 43 | name="Ticker" |
| 44 | ): |
| 45 | wx.PyControl.__init__(self, parent, id=id, pos=pos, size=size, style=style, name=name) |
| 46 | self.timer = wx.Timer(owner=self) |
| 47 | self._extent = (-1, -1) #cache value for the GetTextExtent call |
| 48 | self._offset = 0 |
| 49 | self._fps = fps #frames per second |
| 50 | self._ppf = ppf #pixels per frame |
| 51 | self.SetDirection(direction) |
| 52 | self.SetText(text) |
| 53 | self.SetInitialSize(size) |
| 54 | self.SetForegroundColour(fgcolor) |
| 55 | self.SetBackgroundColour(bgcolor) |
| 56 | wx.EVT_TIMER(self, -1, self.OnTick) |
| 57 | wx.EVT_PAINT(self, self.OnPaint) |
| 58 | wx.EVT_ERASE_BACKGROUND(self, self.OnErase) |
| 59 | if start: |
| 60 | self.Start() |
| 61 | |
| 62 | |
| 63 | def Stop(self): |
| 64 | """Stop moving the text""" |
| 65 | self.timer.Stop() |
| 66 | |
| 67 | |
| 68 | def Start(self): |
| 69 | """Starts the text moving""" |
| 70 | if not self.timer.IsRunning(): |
| 71 | self.timer.Start(1000 / self._fps) |
| 72 | |
| 73 | |
| 74 | def IsTicking(self): |
| 75 | """Is the ticker ticking? ie, is the text moving?""" |
| 76 | return self.timer.IsRunning() |
| 77 | |
| 78 | |
| 79 | def SetFPS(self, fps): |
| 80 | """Adjust the update speed of the ticker""" |
| 81 | self._fps = fps |
| 82 | self.Stop() |
| 83 | self.Start() |
| 84 | |
| 85 | |
| 86 | def GetFPS(self): |
| 87 | """Update speed of the ticker""" |
| 88 | return self._fps |
| 89 | |
| 90 | |
| 91 | def SetPPF(self, ppf): |
| 92 | """Set the number of pixels per frame the ticker moves - ie, how "jumpy" it is""" |
| 93 | self._ppf = ppf |
| 94 | |
| 95 | |
| 96 | def GetPPF(self): |
| 97 | """Pixels per frame""" |
| 98 | return self._ppf |
| 99 | |
| 100 | |
| 101 | def SetFont(self, font): |
| 102 | self._extent = (-1, -1) |
| 103 | wx.Control.SetFont(self, font) |
| 104 | |
| 105 | |
| 106 | def SetDirection(self, dir): |
| 107 | """Sets the direction of the ticker: right to left(rtl) or left to right (ltr)""" |
| 108 | if dir == "ltr" or dir == "rtl": |
| 109 | if self._offset <> 0: |
| 110 | #Change the offset so it's correct for the new direction |
| 111 | self._offset = self._extent[0] + self.GetSize()[0] - self._offset |
| 112 | self._dir = dir |
| 113 | else: |
| 114 | raise TypeError |
| 115 | |
| 116 | |
| 117 | def GetDirection(self): |
| 118 | return self._dir |
| 119 | |
| 120 | |
| 121 | def SetText(self, text): |
| 122 | """Set the ticker text.""" |
| 123 | self._text = text |
| 124 | self._extent = (-1, -1) |
| 125 | if not self._text: |
| 126 | self.Refresh() #Refresh here to clear away the old text. |
| 127 | |
| 128 | |
| 129 | def GetText(self): |
| 130 | return self._text |
| 131 | |
| 132 | |
| 133 | def UpdateExtent(self, dc): |
| 134 | """Updates the cached text extent if needed""" |
| 135 | if not self._text: |
| 136 | self._extent = (-1, -1) |
| 137 | return |
| 138 | if self._extent == (-1, -1): |
| 139 | self._extent = dc.GetTextExtent(self.GetText()) |
| 140 | |
| 141 | |
| 142 | def DrawText(self, dc): |
| 143 | """Draws the ticker text at the current offset using the provided DC""" |
| 144 | dc.SetTextForeground(self.GetForegroundColour()) |
| 145 | dc.SetFont(self.GetFont()) |
| 146 | self.UpdateExtent(dc) |
| 147 | if self._dir == "ltr": |
| 148 | offx = self._offset - self._extent[0] |
| 149 | else: |
| 150 | offx = self.GetSize()[0] - self._offset |
| 151 | offy = (self.GetSize()[1] - self._extent[1]) / 2 #centered vertically |
| 152 | dc.DrawText(self._text, offx, offy) |
| 153 | |
| 154 | |
| 155 | def OnTick(self, evt): |
| 156 | self._offset += self._ppf |
| 157 | w1 = self.GetSize()[0] |
| 158 | w2 = self._extent[0] |
| 159 | if self._offset >= w1+w2: |
| 160 | self._offset = 0 |
| 161 | self.Refresh() |
| 162 | |
| 163 | |
| 164 | def OnPaint(self, evt): |
| 165 | dc = wx.BufferedPaintDC(self) |
| 166 | brush = wx.Brush(self.GetBackgroundColour()) |
| 167 | dc.SetBackground(brush) |
| 168 | dc.Clear() |
| 169 | self.DrawText(dc) |
| 170 | |
| 171 | |
| 172 | def OnErase(self, evt): |
| 173 | """Noop because of double buffering""" |
| 174 | pass |
| 175 | |
| 176 | |
| 177 | def AcceptsFocus(self): |
| 178 | """Non-interactive, so don't accept focus""" |
| 179 | return False |
| 180 | |
| 181 | |
| 182 | def DoGetBestSize(self): |
| 183 | """Width we don't care about, height is either -1, or the character |
| 184 | height of our text with a little extra padding |
| 185 | """ |
| 186 | if self._extent == (-1, -1): |
| 187 | if not self._text: |
| 188 | h = self.GetCharHeight() |
| 189 | else: |
| 190 | h = self.GetTextExtent(self.GetText())[1] |
| 191 | else: |
| 192 | h = self._extent[1] |
| 193 | return (100, h+5) |
| 194 | |
| 195 | |
| 196 | def ShouldInheritColours(self): |
| 197 | """Don't get colours from our parent...""" |
| 198 | return False |
| 199 | |
| 200 | |
| 201 | |
| 202 | #testcase/demo |
| 203 | if __name__ == '__main__': |
| 204 | app = wx.PySimpleApp() |
| 205 | f = wx.Frame(None) |
| 206 | p = wx.Panel(f) |
| 207 | t = Ticker(p, text="Some sample ticker text") |
| 208 | #set ticker properties here if you want |
| 209 | s = wx.BoxSizer(wx.VERTICAL) |
| 210 | s.Add(t, flag=wx.GROW, proportion=0) |
| 211 | p.SetSizer(s) |
| 212 | f.Show() |
| 213 | app.MainLoop() |
| 214 | |
| 215 | |