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