1 # Written by Arno Bakker, Jan David Mol
2 # see LICENSE.txt for license information
8 from BaseLib.__init__ import LIBRARYNAME
11 """ Arno: WARNING: the self.tricolore member is read by the MainThread and
12 written by the network thread. As it is a fixed array with simple values, this
13 concurrency problem is ignored.
19 def __init__(self,numbuckets=100,full=False):
20 self.numbuckets = numbuckets
22 self.movieselector = None
24 self.tricolore = [2] * self.numbuckets
26 def set_numpieces(self,numpieces):
27 self.numpieces = numpieces
28 self.buckets = [0] * self.numbuckets
29 self.tricolore = [0] * self.numbuckets
30 #self.bucketsize = int(ceil(float(self.numpieces) / self.numbuckets))
31 self.bucketsize = float(self.numpieces) / float(self.numbuckets)
32 self.lastbucketsize = self.numpieces - int(float(self.numbuckets-1) * self.bucketsize)
34 def complete( self, piece ):
35 bucket = int(float(piece) / self.bucketsize)
37 #print >>sys.stderr,"BUCKET",bucket,"piece",piece,"bucksize",self.bucketsize
38 # If there is a multi-file torrent that has been partially downloaded before we go
39 # to VOD, it can happen that pieces outside the range of the file selected are
40 # reported as complete here.
41 if bucket < 0 or bucket >= self.numbuckets:
44 self.buckets[bucket] += 1
46 fill = self.buckets[bucket]
47 if bucket == self.numbuckets-1:
48 total = self.lastbucketsize
50 total = int(self.bucketsize)
59 self.tricolore[bucket] = colour
62 def chr( fill, total ):
65 if fill >= int(total):
68 index = int(float(fill*len(self.SOMEFILL))/total)
69 if index >= len(self.SOMEFILL):
70 index = len(self.SOMEFILL)-1
71 return self.SOMEFILL[index]
73 chars = [chr( self.buckets[i], self.bucketsize ) for i in xrange(0,self.numbuckets-1)]
74 chars.append( chr( self.buckets[-1], self.lastbucketsize ) )
78 def set_playable(self):
81 def get_playable(self):
84 def set_movieselector(self,movieselector):
85 self.movieselector = movieselector
87 def get_bitrate(self):
88 if self.movieselector is not None:
89 return self.movieselector.get_bitrate()
99 self.bufferinfo = BufferInfo()
102 def get_bufferinfo(self):
103 return self.bufferinfo
105 def set_callback(self,callback):
106 self.callback = callback
108 def bufferinfo_updated_callback(self):
109 if self.callback is not None:
114 class ProgressBar(wx.Control):
115 #def __init__(self, parent, colours = ["#cfcfcf","#d7ffd7","#00ff00"], *args, **kwargs ):
116 #def __init__(self, parent, colours = ["#cfcfcf","#fde72d","#00ff00"], *args, **kwargs ):
117 #def __init__(self, parent, colours = ["#ffffff","#fde72d","#00ff00"], *args, **kwargs ):
118 def __init__(self, parent, colours = ["#ffffff","#92cddf","#006dc0"], *args, **kwargs ): ## "#ffffff","#CBCBCB","#ff3300"
119 self.colours = colours
120 self.pens = [wx.Pen(c,0) for c in self.colours]
121 self.brushes = [wx.Brush(c) for c in self.colours]
125 wx.Control.__init__(self, parent, -1, style=style)
126 self.SetMaxSize((-1,6))
127 self.SetMinSize((1,6))
128 self.SetBackgroundColour(wx.WHITE)
130 self.Bind(wx.EVT_PAINT, self.OnPaint)
131 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
134 self.progressinf = None
136 def AcceptsFocus(self):
139 def OnEraseBackground(self, event):
142 def OnPaint(self, evt):
145 x,y,maxw,maxh = self.GetClientRect()
146 #dc.DrawRectangle(x,y,)
150 numrect = float(len(self.blocks))
153 w = max(1,maxw/numrect)
156 width, height = self.GetClientSizeTuple()
157 buffer = wx.EmptyBitmap(width, height)
158 #dc = wx.PaintDC(self)
159 dc = wx.BufferedPaintDC(self, buffer)
163 rectangles = [(x+int(i*w),y,int(w)+1,h) for i in xrange(0,int(numrect))]
166 pens = [self.pens[c] for c in self.blocks]
167 brushes = [self.brushes[c] for c in self.blocks]
169 dc.DrawRectangleList(rectangles,pens,brushes)
173 def set_pieces(self, blocks):
174 num = 50 # max number of blocks to show
175 if len(blocks) < num:
176 self.set_blocks([2*int(a) for a in blocks])
180 for i in xrange(num):
181 part = blocks[int(f*i):int(f*(i+1))]
184 if not False in part:
186 self.set_blocks(sblocks)
188 def set_blocks(self,blocks):
189 """ Called by MainThread """
192 def setNormalPercentage(self, perc):
194 self.blocks = ([2]*perc)+([0]* (100-perc))
196 def reset(self,colour=0):
197 self.blocks = [colour] * 100
199 class ProgressSlider(wx.Panel):
201 def __init__(self, parent, utility, colours = ["#ffffff","#CBCBCB","#ff3300"], imgprefix= '', *args, **kwargs ):
202 self.colours = colours
203 #self.backgroundImage = wx.Image('')
205 self.videobuffer = 0.0
206 self.videoPosition = 0
207 self.timeposition = None
208 self.videoLength = None
209 #wx.Control.__init__(self, parent, -1)
210 wx.Panel.__init__(self, parent, -1)
211 self.SetMaxSize((-1,25))
212 self.SetMinSize((1,25))
213 self.SetBackgroundColour(wx.WHITE)
214 self.utility = utility
215 self.bgImage = wx.Bitmap(os.path.join(self.utility.getPath(), LIBRARYNAME,'Video','Images',imgprefix+'background.png')) ## LIBRARYNAME
216 self.dotImage = wx.Bitmap(os.path.join(self.utility.getPath(), LIBRARYNAME,'Video','Images',imgprefix+'sliderDot.png')) ## LIBRARYNAME
217 self.dotImage_dis = wx.Bitmap(os.path.join(self.utility.getPath(), LIBRARYNAME,'Video','Images',imgprefix+'sliderDot_dis.png')) ## LIBRARYNAME
218 self.sliderPosition = None
220 self.rectBorderColour = wx.LIGHT_GREY
223 self.doneColor = "#13bd00" # wx.RED
224 self.bufferColor = "#0b7100" # wx.GREEN
228 self.dragging = False
229 self.allowDragging = False
230 self.Bind(wx.EVT_PAINT, self.OnPaint)
231 self.Bind(wx.EVT_SIZE, self.OnSize)
232 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
233 self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
234 #self.SetSize((-1,self.bgImage.GetSize()[1]))
236 def AcceptsFocus(self):
239 def OnEraseBackground(self, event):
242 def OnSize(self, event):
245 def OnMouse(self, event):
246 if not self.allowDragging:
249 pos = event.GetPosition()
250 if event.ButtonDown():
251 if self.onSliderButton(pos):
252 print >> sys.stderr, 'ProgressSlider: Start drag'
254 elif self.onSlider(pos): # click somewhere on the slider
255 self.setSliderPosition(pos,True)
256 elif event.ButtonUp():
258 print >> sys.stderr, 'ProgressSlider: End drag'
259 self.setSliderPosition(pos, True)
260 self.dragging = False
261 elif event.Dragging():
263 self.setSliderPosition(pos, False)
264 elif event.Leaving():
266 self.setSliderPosition(pos,True)
268 def onSliderButton(self, pos):
269 if not self.sliderPosition:
272 bx, by = self.sliderPosition
273 dotSize = self.dotImage.GetSize()
274 return abs(x-bx) < dotSize[0]/2 and abs(y-by)<dotSize[1]/2
276 def onSlider(self, pos):
278 width, height = self.GetClientSizeTuple()
279 return (x > self.margin and x<= self.margin+self.sliderWidth and \
280 abs(y - height/2) < self.rectHeight/2+4)
282 def setSliderPosition(self, pos, ready):
284 tmp_progress = (x-self.margin)/float(self.sliderWidth)
285 self.progress = min(1.0, max(0.0, tmp_progress))
286 self.videoPosition = self
289 #theEvent = wx.ScrollEvent(pos=self.progress)
290 #self.GetEventHandler().ProcessEvent(theEvent)
291 #print >> sys.stderr, 'Posted event'
292 print >> sys.stderr, 'ProgressSlider: Set progress to : %f' % self.progress
293 self.sliderChangedAction()
295 def sliderChangedAction(self):
296 self.GetParent().Seek()
299 def setSelected(self, sel):
306 def setBufferFromPieces(self, pieces_complete):
307 if not pieces_complete:
308 self.videobuffer = 0.0
310 last_buffered_piece = 0
311 while last_buffered_piece<len(pieces_complete) and pieces_complete[last_buffered_piece]:
312 last_buffered_piece+=1
313 if last_buffered_piece == len(pieces_complete)-1:
314 last_buffered_piece += 1
316 self.videobuffer = last_buffered_piece/float(len(pieces_complete))
317 #print >> sys.stderr, 'progress: %d/%d pieces continuous buffer (frac %f)' % \
318 # (last_buffered_piece, len(pieces_complete), self.videobuffer)
322 def SetValue(self, b):
323 if self.range[0] == self.range[1]:
326 if not self.dragging:
327 self.progress = max(0.0, min((b - self.range[0]) / float(self.range[1] - self.range[0]), 1.0))
331 print >>sys.stderr, 'ProgressSlider: %f, Range (%f, %f)' % (self.progress, self.range[0], self.range[1])
332 return self.progress * (self.range[1] - self.range[0])+ self.range[0]
334 def SetRange(self, a,b):
337 def setVideoBuffer(self, buf):
338 self.videobuffer = buf
340 def SetTimePosition(self, timepos, duration):
341 self.timeposition = timepos
342 self.videoLength = duration
345 self.videoLength = None
346 self.timeposition = None
350 def formatTime(self, s):
351 longformat = time.strftime('%d:%H:%M:%S', time.gmtime(s))
352 if longformat.startswith('01:'):
353 longformat = longformat[3:]
354 while longformat.startswith('00:') and len(longformat) > len('00:00'):
355 longformat = longformat[3:]
358 def OnPaint(self, evt):
359 width, height = self.GetClientSizeTuple()
360 buffer = wx.EmptyBitmap(width, height)
361 #dc = wx.PaintDC(self)
362 dc = wx.BufferedPaintDC(self, buffer)
367 bgSize = self.bgImage.GetSize()
368 for i in xrange(width/bgSize[0]+1):
369 dc.DrawBitmap(self.bgImage, i*bgSize[0],0)
372 self.sliderWidth = width-(3*self.margin+self.textWidth)
373 position = self.sliderWidth * self.progress
374 self.sliderPosition = position+self.margin, height/2
375 self.bufferlength = (self.videobuffer-self.progress) * self.sliderWidth
376 self.bufferlength = min(self.bufferlength, self.sliderWidth-position)
379 if self.videoLength is not None:
380 durationString = self.formatTime(self.videoLength)
382 durationString = '--:--'
383 if self.timeposition is not None:
384 timePositionString = self.formatTime(self.timeposition)
386 timePositionString = '--:--'
388 if width > 3*self.margin+self.textWidth:
390 dc.SetPen(wx.Pen(self.rectBorderColour, 2))
391 dc.DrawRectangle(self.margin,height/2-self.rectHeight/2, self.sliderWidth, self.rectHeight)
392 # Draw slider rect inside
393 dc.SetPen(wx.Pen(self.doneColor, 0))
394 dc.SetBrush(wx.Brush(self.doneColor))
395 smallRectHeight = self.rectHeight - 2
396 dc.DrawRectangle(self.margin,height/2-smallRectHeight/2, position, smallRectHeight)
397 dc.SetBrush(wx.Brush(self.bufferColor))
398 dc.SetPen(wx.Pen(self.bufferColor, 0))
399 dc.DrawRectangle(position+self.margin,height/2-smallRectHeight/2, self.bufferlength, smallRectHeight)
401 dotSize = self.dotImage.GetSize()
402 if self.selected == 2:
403 dc.DrawBitmap(self.dotImage_dis, position+self.margin-dotSize[0]/2, height/2-dotSize[1]/2, True)
405 dc.DrawBitmap(self.dotImage, position+self.margin-dotSize[0]/2, height/2-dotSize[1]/2, True)
406 if width > 2*self.margin+self.textWidth:
408 font = self.GetFont()
411 dc.DrawText('%s / %s' % (timePositionString, durationString), width-self.margin-self.textWidth, height/2-dc.GetCharHeight()/2)
415 def EnableDragging(self):
416 self.allowDragging = True
419 def DisableDragging(self):
420 self.allowDragging = False
424 class VolumeSlider(wx.Panel):
426 def __init__(self, parent, utility, imgprefix=''):
430 #wx.Control.__init__(self, parent, -1)
431 wx.Panel.__init__(self, parent, -1)
432 self.SetMaxSize((150,25))
433 self.SetMinSize((150,25))
434 self.SetBackgroundColour(wx.WHITE)
435 self.utility = utility
436 self.bgImage = wx.Bitmap(os.path.join(self.utility.getPath(), LIBRARYNAME,'Video','Images',imgprefix+'background.png')) ## LIBRARYNAME
437 self.dotImage = wx.Bitmap(os.path.join(self.utility.getPath(), LIBRARYNAME,'Video','Images',imgprefix+'sliderVolume.png')) ## LIBRARYNAME
438 self.sliderPosition = None
440 self.rectBorderColour = wx.LIGHT_GREY
442 self.cursorsize = [4,19]
443 self.doneColor = wx.BLACK #wx.RED
446 self.dragging = False
447 self.Bind(wx.EVT_PAINT, self.OnPaint)
448 self.Bind(wx.EVT_SIZE, self.OnSize)
449 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
450 self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
451 #self.SetSize((-1,self.bgImage.GetSize()[1]))
453 def AcceptsFocus(self):
456 def OnEraseBackground(self, event):
459 def OnSize(self, event):
462 def OnMouse(self, event):
463 pos = event.GetPosition()
464 if event.ButtonDown():
465 if self.onSliderButton(pos):
466 print >> sys.stderr, 'VolumeSlider: Start drag'
468 elif self.onSlider(pos): # click somewhere on the slider
469 self.setSliderPosition(pos,True)
470 elif event.ButtonUp():
472 print >> sys.stderr, 'VolumeSlider: End drag'
473 self.setSliderPosition(pos, True)
474 self.dragging = False
475 elif event.Dragging():
477 self.setSliderPosition(pos, False)
478 elif event.Leaving():
480 self.setSliderPosition(pos,True)
482 def onSliderButton(self, pos):
483 if not self.sliderPosition:
486 bx, by = self.sliderPosition
487 extraGrip = 3 # 3px extra grip on sliderButton
488 return abs(x-bx) < self.cursorsize[0]/2+extraGrip and abs(y-by)<self.cursorsize[1]/2
490 def onSlider(self, pos):
492 width, height = self.GetClientSizeTuple()
493 return (x > self.margin and x<= self.margin+self.sliderWidth and \
494 abs(y - height/2) < self.rectHeight/2+4)
496 def setSliderPosition(self, pos, ready):
498 tmp_progress = (x-self.margin)/float(self.sliderWidth)
499 self.progress = min(1.0, max(0.0, tmp_progress))
500 self.videoPosition = self
503 #theEvent = wx.ScrollEvent(pos=self.progress)
504 #self.GetEventHandler().ProcessEvent(theEvent)
505 #print >> sys.stderr, 'Posted event'
506 print >> sys.stderr, 'VolumeSlider: Set progress to : %f' % self.progress
507 self.sliderChangedAction()
509 def sliderChangedAction(self):
510 self.GetParent().SetVolume()
513 def SetValue(self, b):
514 if not self.dragging:
515 self.progress = min((b - self.range[0]) / float(self.range[1] - self.range[0]), 1.0)
519 print >>sys.stderr, 'VolumeSlider: %f, Range (%f, %f)' % (self.progress, self.range[0], self.range[1])
520 return self.progress * (self.range[1] - self.range[0])+ self.range[0]
522 def SetRange(self, a,b):
525 def OnPaint(self, evt):
526 width, height = self.GetClientSizeTuple()
527 buffer = wx.EmptyBitmap(width, height)
528 #dc = wx.PaintDC(self)
529 dc = wx.BufferedPaintDC(self, buffer)
534 bgSize = self.bgImage.GetSize()
535 for i in xrange(width/bgSize[0]+1):
536 dc.DrawBitmap(self.bgImage, i*bgSize[0],0)
539 self.sliderWidth = width-(2*self.margin)
540 position = self.sliderWidth * self.progress
541 self.sliderPosition = position+self.margin, height/2
544 if width > 2*self.margin:
546 dc.SetPen(wx.Pen(self.rectBorderColour, 2))
547 dc.DrawRectangle(self.margin,height/2-self.rectHeight/2, self.sliderWidth, self.rectHeight)
548 # Draw slider rect inside
549 dc.SetPen(wx.Pen(self.doneColor, 0))
550 dc.SetBrush(wx.Brush(self.doneColor))
551 smallRectHeight = self.rectHeight - 2
552 dc.DrawRectangle(self.margin,height/2-smallRectHeight/2, position, smallRectHeight)
554 dotSize = self.dotImage.GetSize()
555 dc.DrawBitmap(self.dotImage, position+self.margin-dotSize[0]/2, height/2-dotSize[1]/2, True)