#! /usr/bin/env python
# -*- coding: utf-8 -*-
#-----------------------------------------------------------------------------
# Name: masterlist.py
# Purpose:
#
# Author: Jeremy Arendt
#
# Created: 2004/28/01
# RCS-ID: $Id: masterlist.py,v 1.12 2005/11/30 00:57:59 inigo Exp $
# Copyright: (c) 2002
# Licence: See G3.LICENCE.TXT
#-----------------------------------------------------------------------------
import wx
import sys
from wx.lib.mixins.listctrl import ColumnSorterMixin,ListCtrlAutoWidthMixin
from traceback import print_exc
from images import Images
from time import time,strftime,gmtime
from btconfig import BTConfig
from g3listctrl import G3ListCtrl
from types import StringType
from BitTorrent.encodedata import decodedata
class MasterList(G3ListCtrl, ListCtrlAutoWidthMixin, ColumnSorterMixin):
def __init__(self,
parent,
updateguifunc,
resumefunc,
pausefunc,
stopfunc,
killfunc,
qupfunc,
qdownfunc,
announcefunc,
showTorrentfunc,
orderMngrfunc,
openDataFolderfunc,
trackerMngrfunc,
onSelectfunc,
onChangeCfgfunc,
onRescanFldrfunc,
btconfig,
pos = wx.DefaultPosition,
size = wx.DefaultSize,
style = wx.LC_REPORT | wx.LC_VRULES,
bmps = None):
G3ListCtrl.__init__(self, parent, btconfig, "MasterList", -1, pos, size, style, list_type=1)
ListCtrlAutoWidthMixin.__init__(self)
ColumnSorterMixin.__init__(self, 1)
self.CmdUpdateGUI = updateguifunc
self.CmdResume = resumefunc
self.CmdPause = pausefunc
self.CmdStop = stopfunc
self.CmdKill = killfunc
self.CmdQUp = qupfunc
self.CmdQDown = qdownfunc
self.CmdAnnounce = announcefunc
self.CmdShowTorrent = showTorrentfunc
self.CmdOrderMngr = orderMngrfunc
self.CmdDataFolder = openDataFolderfunc
self.CmdTrackerMngr = trackerMngrfunc
self.CmdChangeCfg = onChangeCfgfunc
self.CmdRescanFldr = onRescanFldrfunc
self.OnSelect = onSelectfunc
self.last_selected = -1
self.itemDataMap = {}
self.btconfig = btconfig
#tt = wx.ToolTip("You can drag and drop .torrent files here")
#self.SetToolTip(tt)
if bmps == None:
bmps = Images()
mask_color = wx.Color(0,0,0)
b_mask_color = wx.Color(255,255,255)
self.images = [1] * 7
self.i_list = wx.ImageList(16, 16)
self.images[0] = self.i_list.AddWithColourMask(bmps.GetImage('blank.bmp'), b_mask_color)
self.images[1] = self.i_list.AddWithColourMask(bmps.GetImage('stop.bmp'), b_mask_color)
self.images[2] = self.i_list.AddWithColourMask(bmps.GetImage('down.png'), b_mask_color)
self.images[3] = self.i_list.AddWithColourMask(bmps.GetImage('pause.bmp'), b_mask_color)
self.images[4] = self.i_list.AddWithColourMask(bmps.GetImage('smile.png'), b_mask_color)
self.images[5] = self.i_list.AddWithColourMask(bmps.GetImage('info.bmp'), b_mask_color)
self.images[6] = self.i_list.AddWithColourMask(bmps.GetImage('conn16.png'), b_mask_color)
self.SetImageList(self.i_list, wx.IMAGE_LIST_SMALL)
cols = [ [True, _("Filename"), wx.LIST_FORMAT_LEFT, 160],
[True, _("Size"), wx.LIST_FORMAT_RIGHT, 60],
[True, "%", wx.LIST_FORMAT_RIGHT, 50],
[True, _("Progress"), wx.LIST_FORMAT_LEFT, 80],
[True, _("KB/s Dn"), wx.LIST_FORMAT_RIGHT, 60],
[True, _("KB/s Up"), wx.LIST_FORMAT_RIGHT, 60],
[True, _("Status"), wx.LIST_FORMAT_LEFT, 80],
[True, _("Time Left"), wx.LIST_FORMAT_LEFT, wx.LIST_AUTOSIZE_USEHEADER],
[True, _("Downloaded"), wx.LIST_FORMAT_RIGHT, wx.LIST_AUTOSIZE_USEHEADER],
[True, _("Uploaded"), wx.LIST_FORMAT_RIGHT, wx.LIST_AUTOSIZE_USEHEADER],
[True, _("U/D Ratio"), wx.LIST_FORMAT_RIGHT, wx.LIST_AUTOSIZE_USEHEADER],
[True, _("Seeds/Leechers"), wx.LIST_FORMAT_RIGHT, wx.LIST_AUTOSIZE_USEHEADER],
[True, _("Seen Copies"), wx.LIST_FORMAT_RIGHT, wx.LIST_AUTOSIZE_USEHEADER],
[True, _("Avg. Prog"), wx.LIST_FORMAT_RIGHT, wx.LIST_AUTOSIZE_USEHEADER],
[True, _("Priority"), wx.LIST_FORMAT_LEFT, wx.LIST_AUTOSIZE_USEHEADER],
[True, _("Tracker URL"), wx.LIST_FORMAT_LEFT, wx.LIST_AUTOSIZE_USEHEADER],
[True, "", wx.LIST_FORMAT_LEFT, wx.LIST_AUTOSIZE_USEHEADER]
]
self.InsertColumns(cols)
self.SetEbedCol(3)
wx.EVT_LIST_ITEM_ACTIVATED(self, -1, self.OnDBLCLick)
wx.EVT_LIST_ITEM_SELECTED(self, -1, self.OnFocusGot)
wx.EVT_LIST_ITEM_DESELECTED(self, -1, self.OnFocusLost)
wx.EVT_LIST_COL_CLICK(self, -1, self.OnColClick)
wx.EVT_COMMAND_RIGHT_CLICK(self, -1, self.OnRightClick) # wxMSW
wx.EVT_RIGHT_UP(self, self.OnRightClick) # wxGTK
def MenuOnAnnounce(self, event):
selected = self.GetSelectedData()
if selected:
self.CmdAnnounce( selected )
def MenuOnRemove(self, event):
selected = self.GetSelectedData()
if selected:
self.CmdKill( selected )
def MenuOnMoveUp(self, event):
selected = self.GetSelectedData()
if selected:
self.CmdQUp( selected )
self.CmdUpdateGUI()
def MenuOnMoveDown(self, event):
selected = self.GetSelectedData()
if selected:
self.CmdQDown( selected, 1)
self.CmdUpdateGUI()
def MenuOnOrderMgr(self, event):
selected = self.GetSelectedData()
if selected:
self.CmdOrderMngr(selected, True)
def MenuOnDataFolder(self, event):
selected = self.GetSelectedData()
if selected:
self.CmdDataFolder()
def MenuOnTrackerMgr(self, event):
selected = self.GetSelectedData()
if selected:
self.CmdTrackerMngr(selected)
def MenuOnRescan(self, event):
print "Rescan"
self.CmdRescanFldr()
def MenuOnShow(self, event):
selected = self.GetSelectedData()
if selected:
self.CmdShowTorrent( selected )
def MenuOnResume(self, event):
selected = self.GetSelectedData()
if selected:
self.CmdResume()
self.CmdUpdateGUI()
def MenuOnPause(self, event):
selected = self.GetSelectedData()
if selected:
self.CmdPause()
self.CmdUpdateGUI()
def MenuOnStop(self, event):
selected = self.GetSelectedData()
if selected:
self.CmdStop()
self.CmdUpdateGUI()
def MenuOnCfgFin(self, event):
selected = self.GetSelectedData()
if selected:
self.CmdChangeCfg(selected, "OnFin")
def MenuOnCfgChoker(self, event):
selected = self.GetSelectedData()
if selected:
self.CmdChangeCfg(selected, "Choker")
def MenuOnCfgDL(self, event):
selected = self.GetSelectedData()
if selected:
self.CmdChangeCfg(selected, "DL")
def MenuOnCfgConn(self, event):
selected = self.GetSelectedData()
if selected:
self.CmdChangeCfg(selected, "Conn")
def OnRightClick(self, event):
if not hasattr(self, "_popup_id"):
self._popup_id = []
for i in range(0, 16):
self._popup_id.append(wx.NewId())
wx.EVT_MENU(self, self._popup_id[0], self.MenuOnResume)
wx.EVT_MENU(self, self._popup_id[1], self.MenuOnPause)
wx.EVT_MENU(self, self._popup_id[2], self.MenuOnStop)
wx.EVT_MENU(self, self._popup_id[3], self.MenuOnRemove)
wx.EVT_MENU(self, self._popup_id[4], self.MenuOnMoveUp)
wx.EVT_MENU(self, self._popup_id[5], self.MenuOnMoveDown)
wx.EVT_MENU(self, self._popup_id[6], self.MenuOnAnnounce)
wx.EVT_MENU(self, self._popup_id[7], self.MenuOnShow)
wx.EVT_MENU(self, self._popup_id[8], self.MenuOnDataFolder)
wx.EVT_MENU(self, self._popup_id[9], self.MenuOnOrderMgr)
wx.EVT_MENU(self, self._popup_id[10], self.MenuOnCfgDL)
wx.EVT_MENU(self, self._popup_id[11], self.MenuOnCfgFin)
wx.EVT_MENU(self, self._popup_id[12], self.MenuOnCfgChoker)
wx.EVT_MENU(self, self._popup_id[13], self.MenuOnCfgConn)
wx.EVT_MENU(self, self._popup_id[14], self.MenuOnTrackerMgr)
wx.EVT_MENU(self, self._popup_id[15], self.MenuOnRescan)
menu = wx.Menu()
submenu1 = wx.Menu()
submenu1.Append(self._popup_id[10], _("Rate options"), "")
submenu1.Append(self._popup_id[11], _("On completion options"), "")
submenu1.Append(self._popup_id[12], _("Choker options"), "")
submenu1.Append(self._popup_id[13], _("Connection options"), "")
menu.Append(self._popup_id[0], _("Resume"), _("Resume selected torrent"))
menu.Append(self._popup_id[1], _("Pause/Requeue"), _("Pause/Requeue selected torrent"))
menu.Append(self._popup_id[2], _("Stop"), _("Stop selected torrent"))
menu.Append(self._popup_id[3], _("Remove"), _("Remove selected torrent"))
menu.Append(self._popup_id[4], _("Move up in queue"), _("Move up in queue"))
menu.Append(self._popup_id[5], _("Move down in queue"), _("Move down in queue"))
menu.AppendSeparator()
menu.Append(self._popup_id[6], _("Poll tracker"), _("Poll the tracker manually"))
menu.AppendSeparator()
menu.AppendMenu(wx.NewId(), _("Change this torrent's options"), submenu1, _("Change options for this torrent only"))
menu.AppendSeparator()
menu.Append(self._popup_id[7], _("Open Progress Dialog"), _("Opens this torrent's dialog window"))
menu.Append(self._popup_id[8], _("Open Torrent's Data Folder"), _("Opens the location this torrent is downloading to"))
menu.Append(self._popup_id[9], _("Multi-File Torrent Manager"), _("Manage the download order of torrents containing multiple files"))
menu.Append(self._popup_id[14], _("Multi-Tracker Manager"), _("Manage/Add the trackers associated with this torrent"))
menu.AppendSeparator()
menu.Append(self._popup_id[15], _("Rescan torrent folder"), _("Rescan the 'torrents' folder for newly downloaded torrents"))
self.PopupMenu(menu, self.ScreenToClient(wx.GetMousePosition()))
menu.Destroy()
def OnRightDown(self, event):
self.x = event.GetX()
self.y = event.GetY()
event.Skip()
def Sort(self):
self.SortListItems(0, True)
def OnColClick(self, event):
self.SortListItems(0, True)
def OnFocusGot(self, event):
if self.OnSelect:
self.OnSelect( self.GetItemData(event.m_itemIndex) )
def OnFocusLost(self, event):
if self.OnSelect:
self.OnSelect( None )
def OnDBLCLick(self, event):
if event.m_itemIndex != -1:
self.CmdShowTorrent( self.GetItemData(event.m_itemIndex) )
def GetSelected(self):
return self.GetFirstSelected()
def GetSelectedData(self):
if self.GetFirstSelected() != -1:
return self.GetItemData( self.GetFirstSelected() )
else:
return None
def GetListCtrl(self):
return self
def GetColumnText(self, index, col):
item = self.GetItem(index, col)
return item.GetText()
def RemoveSession(self, btsession):
id = btsession.GetId()
item_idx = self.FindItemData(-1, id)
self.DeleteItem(item_idx)
if self.itemDataMap.has_key(id):
del self.itemDataMap[id]
def FmtFileSize(self, bytes):
if bytes > 2**30:
return '%.2f GB' % (bytes/1073741824.0)
elif bytes > 2**20:
return '%.1f MB' % (bytes/1048576.0)
elif bytes > 2**10:
return '%.1f KB' % (bytes/1024.0)
else:
return '%d B' % bytes
def AddSession(self, btsession):
d = btsession.GetStatusData()
s = btsession.GetStaticData()
id = btsession.GetId()
ipriority = btsession.GetQRank()
filename = btsession.GetFilename()
if type(filename) is StringType:
filename = decodedata(filename)
ifilesize = btsession.GetFilesize()
if ifilesize > 0:
filesize = self.FmtFileSize( ifilesize )
else:
filesize = ""
icompleted = d['fractionDone']
iurate = d['urate']
idrate = d['drate']
iutotal = d['utotal']
idtotal = d['dtotal']
priority = str(ipriority)
if btsession.IsComplete() and not idtotal:
idtotal = btsession.GetFilesize()
elif idtotal == None:
idtotal = 0
if iutotal == None:
iutotal = 0
dtotal = self.FmtFileSize(idtotal)
utotal = self.FmtFileSize(iutotal)
try:
share_ratio = "%0.2f" % ((float(iutotal)+1) / (float(idtotal)+1))
except ZeroDivisionError:
share_ratio = "0"
if btsession.IsChecking():
activity = _("Checking")
elif btsession.IsComplete() and btsession.IsPaused():
activity = _("Complete")
elif btsession.IsComplete():
activity = _("Seeding")
elif btsession.IsStopped():
activity = _("Stopped")
elif btsession.IsPaused():
activity = _("Queued (Paused)")
elif btsession.IsRunning() and (d['peers'] < 1):
activity = _("Connecting...")
elif btsession.IsRunning() and (d['timeleft'] == None or d['timeleft'] == -1):
activity = _("Connected")
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"
if not btsession.IsComplete():
completed = "%d%c" % (int(icompleted*100), '%')
else:
completed = "100%" # The above would sometimes display 99% on completion
# from scrape
scrape = btsession.GetScrapeData()
if scrape and scrape.get('complete') != None and scrape.get('incomplete') != None:
peers_seeds = "%d(%d)/%d(%d)" % (d['seeds'], scrape['complete'], (d['peers'] - d['seeds']), scrape['incomplete'])
else:
peers_seeds = "%d/%d" % (d['seeds'], d['peers'])
if s != None:
announce = s['announce']
else:
announce = ''
item_idx = self.FindItemData(-1, id)
inserted = False
if item_idx < 0:
inserted = True
item_idx = self.InsertRow(id, [filename, filesize, completed, " ", drate,
urate, activity, timeleft, dtotal, utotal, share_ratio,
peers_seeds, dist_copies, avg_progress, priority, announce, ""])
if btsession.IsComplete():
self.Insert_Ebed_Ctrl(id, -1, [])
else:
self.Insert_Ebed_Ctrl(id, icompleted, d['havelist'])
self.itemDataMap[id] = [ipriority, activity, filename, ifilesize, icompleted,
d['havelist'], d['availlist']]
item = self.GetItem(item_idx)
if inserted:
item.m_state = item.m_state = wx.LIST_STATE_SELECTED
self.m_stateMask = item.m_state = wx.LIST_STATE_SELECTED
# set icons and row color
if btsession.IsComplete() and btsession.IsPaused():
#complete and idle
item.SetImage(self.images[4])
item.SetTextColour(self.btconfig.GetColor('ml_complete'))
elif btsession.IsComplete():
#complete and seeding
item.SetImage(self.images[4])
item.SetTextColour(self.btconfig.GetColor('ml_seeding'))
elif btsession.IsStopped():
# stopped
item.SetImage(self.images[1])
item.SetTextColour(self.btconfig.GetColor('ml_stopped'))
elif btsession.IsPaused():
# paused
item.SetImage(self.images[3])
item.SetTextColour(self.btconfig.GetColor('ml_paused'))
elif btsession.IsChecking():
# checking
item.SetImage(self.images[5])
item.SetTextColour(self.btconfig.GetColor('ml_checking'))
elif btsession.IsRunning() and (d['peers'] < 1):
# connecting...
item.SetImage(self.images[6])
item.SetTextColour(self.btconfig.GetColor('ml_checking'))
elif btsession.IsRunning() and (d['timeleft'] == None or d['timeleft'] == -1):
# connected
item.SetImage(self.images[6])
item.SetTextColour(self.btconfig.GetColor('ml_downloading'))
else:
# downloading
item.SetImage(self.images[2])
item.SetTextColour(self.btconfig.GetColor('ml_downloading'))
self.SetItem(item)
if sys.platform != "win32":
self.OnPaint()
def AddPlaceholder(self, btsession):
id = btsession.GetId()
ipriority = btsession.GetQRank()
filename = btsession.GetFilename()
if type(filename) is StringType:
filename = decodedata(filename)
if btsession.GetFilesize() > 0:
filesize = "%.1f MB" % (float( btsession.GetFilesize()) / 1048576)
else:
filesize = ""
item_idx = self.FindItemData(-1, id)
inserted = False
if item_idx < 0:
inserted = True
item_idx = self.InsertRow(id, [filename, filesize, "", " ", "", "",
_("Queued"), "", "", "", "", "", "", "", str(ipriority), ""])
self.Insert_Ebed_Ctrl(id, -2, [])
self.SetItemData(item_idx, id)
self.itemDataMap[id] = [ipriority]
item = self.GetItem(item_idx)
if inserted:
item.m_state = item.m_state = wx.LIST_STATE_SELECTED
self.m_stateMask = item.m_state = wx.LIST_STATE_SELECTED
if btsession.IsStopped():
item.SetImage(self.images[1])
else:
item.SetImage(self.images[3])
item.SetTextColour(self.btconfig.GetColor('ml_paused'))
self.SetItem(item)
if sys.platform != "win32":
self.OnPaint()
#-----------------------------------------------------------------------------
class TestList(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Test', size = wx.Size(430, 350), style = wx.DEFAULT_FRAME_STYLE)
panel = wx.Panel(self, -1)
self.ppp = MasterList(panel, None, None,None,None,None,None,None,None,None,None,None,None,None,None, None, BTConfig())
testbutton1 = wx.Button(panel, 100, "test1")
testbutton2 = wx.Button(panel, 101, "test2")
wx.EVT_BUTTON(self, 100, self.OnClick1)
wx.EVT_BUTTON(self, 101, self.OnClick2)
sizer = wx.FlexGridSizer(cols=1)
sizer.Add(self.ppp, 1, wx.EXPAND)
sizer.AddGrowableRow(0)
sizer.AddGrowableCol(0)
sizer.Add(testbutton1, 1)
sizer.Add(testbutton2, 1)
panel.SetSizer(sizer)
panel.Layout()
self.Show(True)
def OnClick2(self, event):
self.ppp.AddPlaceholder(DummyBTSession("test1"))
self.ppp.AddPlaceholder(DummyBTSession("test2"))
self.ppp.AddPlaceholder(DummyBTSession("test3"))
pass
def OnClick1(self, event):
pass
class DummyBTSession:
def __init__(self, name="test"):
self.name = name
def GetId(self):
return hash(self.name)
def GetQRank(self):
return 0
def GetFilename(self):
return self.name
def GetFilesize(self):
return 1
def IsStopped(self):
return False
if __name__ == "__main__":
_ = lambda x: x # needed for gettext
app = wx.PySimpleApp()
ppp = TestList()
ppp.Show(True)
app.MainLoop()
|