#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Written by Bram Cohen and Myers Carpenter
# see BT.LICENSE.txt for license information
from BitTorrent import version
from threading import Event
from time import time
from traceback import print_exc
from btconfig import BTConfig
from peerlist import PeerList
from p2pgauge import *
from optiondlg import *
import sys
import wx
if sys.platform == "win32":
win32_flag = True
else:
win32_flag = False
class DownloadInfoFrame(wx.Frame):
def __init__(self, parent=None, btsession=None, images=None, cachefunc=None, friendfunc=None):
wx.Frame.__init__(self, parent, -1, "Rufus", size = wx.Size(430, 350),
style = wx.DEFAULT_FRAME_STYLE)
self.parent = parent
self.uiflag = Event()
if btsession:
btconfig = btsession.GetCfg()
else:
btconfig = BTConfig()
self.btsession = btsession
self.btconfig = btconfig
panel = wx.Panel(self, -1, size=(100,100))
self.panel = panel
self.last_update_time = 0
colSizer = wx.FlexGridSizer(cols = 1, vgap = 3)
fnsizer = wx.BoxSizer(wx.HORIZONTAL)
# filename text
self.fileNameText = wx.StaticText(panel, -1, "")
fnsizer.Add(self.fileNameText, 1, wx.ALIGN_BOTTOM)
colSizer.Add(fnsizer, 1, wx.EXPAND) #row 0
# add p2p gauge
self.pppgauge = P2P_Gauge(panel, self.btconfig, (-1, 27))
self.pppgauge.SetValueUnknown()
colSizer.Add(self.pppgauge, 1, wx.EXPAND) #row 1
# information grid sizer
gridSizer = wx.FlexGridSizer(cols = 2, vgap = 3, hgap = 4)
self.statusText = wx.StaticText(panel, -1, "")
gridSizer.Add(self.statusText, 1, wx.EXPAND)
gridSizer.Add((1,1))
gridSizer.Add(wx.StaticText(panel, 1, _("Estimated time left:")))
self.timeEstText = wx.StaticText(panel, 1, "")
gridSizer.Add(self.timeEstText, 1, wx.EXPAND)
gridSizer.AddGrowableCol(1)
# rating grid sizer
rategridSizer = wx.FlexGridSizer(cols = 4, vgap = 3, hgap = 8)
rategridSizer.Add(wx.StaticText(panel, 1, _("Download rate:")))
self.downRateText = wx.StaticText(panel, -1, "")
rategridSizer.Add(self.downRateText, 1, wx.EXPAND)
rategridSizer.Add(wx.StaticText(panel, 1, _("Downloaded:")))
self.downTotalText = wx.StaticText(panel, -1, "")
rategridSizer.Add(self.downTotalText, 1, wx.EXPAND)
rategridSizer.Add(wx.StaticText(panel, 1, _("Upload rate:")))
self.upRateText = wx.StaticText(panel, -1, "")
rategridSizer.Add(self.upRateText, 1, wx.EXPAND)
rategridSizer.Add(wx.StaticText(panel, 1, _("Uploaded:")))
self.upTotalText = wx.StaticText(panel, -1, "")
rategridSizer.Add(self.upTotalText, 1, wx.EXPAND)
rategridSizer.AddGrowableCol(1)
rategridSizer.AddGrowableCol(3)
#box for info and ratings
notebox = wx.StaticBox(panel, -1, _("Status"))
notebox.SetBackgroundColour( wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE) )
noteboxer = wx.StaticBoxSizer(notebox, wx.VERTICAL)
noteboxer.Add(gridSizer, 1, wx.EXPAND)
noteboxer.Add(rategridSizer, 1, wx.EXPAND)
colSizer.Add(noteboxer, 1, wx.EXPAND) #row 2
self.peerlist = PeerList(panel, btconfig, bmps=images, friendfunc=friendfunc, cachefunc=cachefunc)
colSizer.Add(self.peerlist, 1, wx.EXPAND) #row 3
colSizer.AddGrowableRow(3)
colSizer.AddGrowableCol(0)
# Options / Cancel / Finished button
buttongrid = wx.FlexGridSizer(cols = 2, vgap = 3, hgap = 10)
self.optionsVisible = False
self.cancelButton = wx.Button(panel, -1, _("Cancel"))
buttongrid.Add(self.cancelButton, 1, wx.ALIGN_RIGHT)
self.optionsButton = wx.Button(panel, -1, _("Options"))
buttongrid.Add(self.optionsButton, 1, wx.ALIGN_LEFT)
buttongrid.AddGrowableCol(0)
buttongrid.AddGrowableCol(1)
buttongrid.AddGrowableRow(0)
wx.EVT_BUTTON(self, self.cancelButton.GetId(), self.Close)
wx.EVT_BUTTON(self, self.optionsButton.GetId(), self.ShowOptions)
if parent != None:
self.cancelButton.SetLabel(_("Hide"))
#add flex grid for buttons
colSizer.Add(buttongrid, 0, wx.EXPAND)
border = wx.BoxSizer(wx.HORIZONTAL)
border.Add(colSizer, 1, wx.EXPAND | wx.ALL, 4)
panel.SetSizer(border)
panel.Layout()
if win32_flag:
self.tbicon = wx.TaskBarIcon()
wx.EVT_CLOSE(self, self.Close)
wx.EVT_SIZE(self, self.OnReSize)
wx.EVT_ERASE_BACKGROUND(self, self.DummyFunc)
wx.EVT_IDLE(self, self.OnIdle)
if win32_flag:
wx.EVT_ICONIZE(self, self.onMinimize)
self.Show(True)
def DummyFunc(self, event):
pass
def Close(self, event):
try:
if self.optionsVisible == True:
self.ShowOptions(event)
self.uiflag.set()
self.btsession.spew = False
except:
print_exc()
if win32_flag:
self.tbicon.Destroy()
self.Destroy()
def CreateIcon(self, fraction = 0):
bgcolor = self.btconfig.g_colors["g_have_color"]
gauge_text = self.btconfig.g_colors["g_text_color"]
gauge = self.btconfig.g_colors["g_overall_color"]
nothave = self.btconfig.g_colors["g_nothave_color"]
bmp = wx.EmptyBitmap(16, 16)
dc = wx.BufferedDC(None, bmp)
dc.BeginDrawing()
dc.SetBackground(wx.Brush(bgcolor, wx.SOLID))
dc.Clear()
if self.btsession.IsPaused():
dc.SetFont(wx.Font(9, wx.SWISS, wx.NORMAL, wx.BOLD))
dc.SetTextForeground(gauge_text)
dc.DrawText("BT", 1, 1)
elif self.btsession.IsComplete():
dc.SetFont(wx.Font(9, wx.SWISS, wx.NORMAL, wx.BOLD))
dc.SetTextForeground(gauge)
dc.DrawText("BT", 1, 1)
else:
dc.SetPen(wx.Pen(bgcolor, 0, wx.TRANSPARENT ))
dc.SetBrush(wx.Brush(nothave,wx.SOLID))
dc.DrawRectangle(0, 0, 3, 16 )
dc.SetBrush(wx.Brush(gauge,wx.SOLID))
dc.DrawRectangle(0, 16-int(16*fraction), 3, 16 )
dc.SetFont(wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL))
dc.SetTextForeground(gauge_text)
dc.DrawText(str(int(100*fraction)).rjust(2), 3, 1)
dc.EndDrawing()
dc = None
icon = wx.Icon("", wx.BITMAP_TYPE_BMP)
icon.CopyFromBitmap(bmp)
return icon
def FixFileName(self, filename):
l = len(filename)
if l <= 30:
return filename
else:
return filename[0:29]
def UpdateIcon(self, fraction):
filename = self.btsession.GetFilename()
filename = self.FixFileName(filename)
if not hasattr(self, "_lastfraction"):
self._lastfraction = 0.0
if win32_flag:
icon = self.CreateIcon(fraction)
if self.IsShown():
self.SetIcon(icon)
if self.tbicon.IsIconInstalled() and ((round(fraction,3) > round(self._lastfraction, 3))):
self.tbicon.SetIcon(icon, "%s - %0.1f%% completed" % (filename, (fraction*100)))
self._lastfraction = fraction
def onTaskBarActivate(self, evt):
if self.IsIconized():
self.Iconize(False)
if not self.IsShown():
self.Show(True)
self.Raise()
if self.tbicon.IsIconInstalled():
self.tbicon.RemoveIcon()
return
def onMinimize(self, evt):
if win32_flag:
icon = self.CreateIcon()
if icon:
if not self.tbicon.IsIconInstalled():
#self.tbicon = wx.TaskBarIcon()
self.tbicon.SetIcon(icon, "Rufus")
wx.EVT_TASKBAR_LEFT_DCLICK(self.tbicon, self.onTaskBarActivate)
self.Hide()
else:
self.Iconize(True)
else:
pass
def OnReSize(self, evt):
self.panel.SetSize(self.GetClientSize())
def ShowOptions(self, evt):
pos = self.optionsButton.ClientToScreen((0,0))
bsize = self.optionsButton.GetSize()
self.optionswin = OptionsPopup(self, (pos[0], pos[1] + bsize.height), self.btconfig)
def OnIdle(self, event):
if not self.btsession:
return
if self.last_update_time + max(0.25, self.btconfig.Get("gui_update_rate")) < time():
if self.IsShown():
self.UpdateGUI()
else:
d = self.btsession.GetStatusData()
self.UpdateIcon(d["fractionDone"])
self.last_update_time = time()
def UpdateGUI(self):
btsession = self.btsession
d = btsession.GetStatusData()
s = btsession.GetStaticData()
id = btsession.GetId()
ipriority = btsession.GetQRank()
filename = self.btsession.GetFilename()
if btsession.GetFilesize() > 0:
filesize = "%.1f MB" % (float( btsession.GetFilesize() ) / 1048576)
else:
filesize = ""
icompleted = d["fractionDone"]
iurate = d["urate"]
idrate = d["drate"]
iutotal = d["utotal"]
idtotal = d["dtotal"]
peers_seeds = "%d/%d" % (d["seeds"], d["peers"])
priority = str(ipriority)
if btsession.IsComplete() and not idtotal:
idtotal = float(btsession.GetFilesize()) / 1048576
elif idtotal == None:
idtotal = 0
if iutotal == None:
iutotal = 0
utotal = "%.1f MB" % (iutotal/1048576.0)
dtotal = "%.1f MB" % (idtotal/1048576.0)
try:
share_ratio = "%0.1f" % ((float(iutotal)+1) / (float(idtotal)+1))
except ZeroDivisionError:
share_ratio = "0"
if btsession.IsComplete() and btsession.IsPaused():
activity = "Complete"
elif btsession.IsComplete():
activity = "Seeding"
elif btsession.IsStopped():
activity = "Stopped"
elif btsession.IsPaused():
activity = "Paused"
elif btsession.IsRunning() and (d["timeleft"] == None or d["timeleft"] == -1):
activity = "Checking"
else:
activity = "Downloading"
if d["timeleft"] == None or d["timeleft"] == -1:
if btsession.IsComplete():
timeleft = "Done"
else:
timeleft = "Unknown"
else:
if d["timeleft"] < 86400:
t = gmtime(int(d["timeleft"]))
timeleft = strftime("%H:%M:%S", t)
else:
timeleft = "%.1f Days" % (float(d["timeleft"]) / 86400)
if idrate > 0:
drate = "%.0f KB/s" % (float(idrate) / (1 << 10))
else:
drate = ""
if iurate > 0:
urate = "%.0f KB/s" % (float(iurate) / (1 << 10))
else:
urate = ""
if d["avg_progress"]:
avg_progress = "%d" % int(d["avg_progress"] * 100)
else:
avg_progress = "0"
if d["dist_copies"]:
dist_copies = "%0.2f" % d["dist_copies"]
else:
dist_copies = "0"
completed = "%d%c" % (int(icompleted*100), "%")
if btsession.IsComplete():
self.pppgauge.SetValueFinished()
elif btsession.IsPaused():
self.pppgauge.SetValuePaused()
self.peerlist.Reset()
elif btsession.HasFailed():
self.pppgauge.SetValueFailed()
elif btsession.IsRunning() and d != None:
self.pppgauge.SetValue(d["fractionDone"],
d["havelist"], d["availlist"])
if btsession.IsRunning() and d != None and s != None:
self.peerlist.Populate(d["spew"], s["info_hash"], btsession.ReChoke)
else:
self.peerlist.Reset()
self.SetTitle("%s %s - Rufus %s" % (completed, filename, version))
self.fileNameText.SetLabel(filename)
self.statusText.SetLabel(activity)
self.timeEstText.SetLabel(timeleft)
self.downRateText.SetLabel(drate)
self.upRateText.SetLabel(urate)
self.downTotalText.SetLabel(dtotal)
self.upTotalText.SetLabel(utotal)
if win32_flag:
self.UpdateIcon(icompleted)
def onInvoke(self, event):
if not self.uiflag.isSet():
apply(event.func, event.args, event.kwargs)
def invokeLater(self, func, args = [], kwargs = {}):
if not self.uiflag.isSet():
wx.PostEvent(self, InvokeEvent(func, args, kwargs))
def hours(self, n):
if n == -1 or n == None:
return "<unknown>"
if n == 0:
return "complete!"
n = int(n)
h, r = divmod(n, 3600)
m, sec = divmod(r, 60)
if h > 1000000:
return "<unknown>"
if h > 0:
return "%d hour %02d min %02d sec" % (h, m, sec)
else:
return "%d min %02d sec" % (m, sec)
def KillDlg(self):
if win32_flag:
self.tbicon.Destroy()
self.Destroy()
if __name__ == "__main__":
_ = lambda x: x # needed for gettext
app = wx.PySimpleApp()
f = DownloadInfoFrame()
app.SetTopWindow(f)
app.MainLoop()
|