3
# Written by Bram Cohen and Myers Carpenter
4
# Modifications by various people
5
# see LICENSE.txt for license information
7
from BitTornado import PSYCO
11
assert psyco.__version__ >= 0x010100f0
16
from sys import argv, version, exit
17
assert version >= '2', "Install Python 2.0 or greater"
22
print 'wxPython is either not installed or has not been installed properly.'
24
from BitTornado.download_bt1 import BT1Download, defaults, parse_params, get_usage, get_response
25
from BitTornado.RawServer import RawServer, UPnP_ERROR
26
from random import seed
27
from socket import error as socketerror
28
from BitTornado.ConnChoice import *
29
from BitTornado.ConfigReader import configReader
30
from BitTornado.bencode import bencode, bdecode
31
from BitTornado.natpunch import UPnP_test
32
from threading import Event, Thread
35
from time import strftime, time, localtime, sleep
36
from BitTornado.clock import clock
37
from webbrowser import open_new
38
from traceback import print_exc
39
from StringIO import StringIO
43
from BitTornado import version, createPeerID, report_email
54
# Note to packagers: edit OLDICONPATH in BitTornado/ConfigDir.py
58
return 'download complete'
61
assert n >= 0 and n < 5184000 # 60 days
67
return '%d hour(s) %02d min %02d sec' % (h, m, s)
69
return '%d min %02d sec' % (m, s)
75
r = str(int(s/1024)) + 'KiB'
76
elif (s < 1073741824L):
77
r = str(int(s/1048576)) + 'MiB'
78
elif (s < 1099511627776L):
79
r = str(int((s/1073741824.0)*100.0)/100.0) + 'GiB'
81
r = str(int((s/1099511627776.0)*100.0)/100.0) + 'TiB'
86
for i in range(len(r)-3, 0, -3):
90
hexchars = '0123456789abcdef'
93
x = hexchars[(i&0xF0)/16]+hexchars[i&0x0F]
99
r.append(hexmap[ord(c)])
102
wxEVT_INVOKE = wx.NewEventType()
104
def EVT_INVOKE(win, func):
105
win.Connect(-1, -1, wxEVT_INVOKE, func)
107
class InvokeEvent(wx.PyEvent):
108
def __init__(self, func = None, args = None, kwargs = None):
109
wx.PyEvent.__init__(self)
110
self.SetEventType(wxEVT_INVOKE)
117
class DownloadInfoFrame:
118
def __init__(self, flag, configfile):
119
self._errorwindow = None
121
self.FONT = configfile.config['gui_font']
122
self.default_font = wx.Font(self.FONT, wx.DEFAULT, wx.NORMAL, wx.NORMAL, False)
123
frame = wx.Frame(None, -1, 'BitTorrent ' + version + ' download',
124
style = wx.DEFAULT_FRAME_STYLE|wx.FULL_REPAINT_ON_RESIZE)
126
self.configfile = configfile
127
self.configfileargs = configfile.config
128
self.uiflag = Event()
131
self.detailBox = None
133
self.creditsBox = None
134
self.statusIconHelpBox = None
135
self.reannouncelast = 0
139
self.spewwait = clock()
141
self.updateSpinnerFlag = 0
142
self.updateSliderFlag = 0
143
self.statusIconValue = ' '
145
self.taskbaricon = False
147
self.activity = 'Starting up...'
148
self.firstupdate = True
149
self.shuttingdown = False
150
self.ispaused = False
151
self.bgalloc_periods = 0
152
self.gui_fractiondone = None
154
self.lastexternalannounce = ''
155
self.refresh_details = False
156
self.lastuploadsettings = 0
157
self.old_download = 0
159
self.old_ratesettings = None
160
self.current_ratesetting = None
161
self.gaugemode = None
162
self.autorate = False
166
if sys.platform == 'win32':
167
self.invokeLaterEvent = InvokeEvent()
168
self.invokeLaterList = []
170
wx.InitAllImageHandlers()
171
self.basepath = self.configfile.getIconDir()
172
self.icon = wx.Icon(os.path.join(self.basepath,'icon_bt.ico'), wx.BITMAP_TYPE_ICO)
173
self.finicon = wx.Icon(os.path.join(self.basepath,'icon_done.ico'), wx.BITMAP_TYPE_ICO)
174
self.statusIconFiles={
175
'startup':os.path.join(self.basepath,'white.ico'),
176
'disconnected':os.path.join(self.basepath,'black.ico'),
177
'noconnections':os.path.join(self.basepath,'red.ico'),
178
'nocompletes':os.path.join(self.basepath,'blue.ico'),
179
'noincoming':os.path.join(self.basepath,'yellow.ico'),
180
'allgood':os.path.join(self.basepath,'green.ico'),
183
self.filestatusIcons = wx.ImageList(16, 16)
184
self.filestatusIcons.Add(wx.Bitmap(os.path.join(self.basepath,'black1.ico'),wx.BITMAP_TYPE_ICO))
185
self.filestatusIcons.Add(wx.Bitmap(os.path.join(self.basepath,'yellow1.ico'), wx.BITMAP_TYPE_ICO))
186
self.filestatusIcons.Add(wx.Bitmap(os.path.join(self.basepath,'green1.ico'), wx.BITMAP_TYPE_ICO))
188
self.allocbuttonBitmap = wx.Bitmap(os.path.join(self.basepath,'alloc.gif'), wx.BITMAP_TYPE_GIF)
190
self.starttime = clock()
194
self.frame.SetIcon(self.icon)
198
panel = wx.Panel(frame, -1)
199
self.bgcolor = panel.GetBackgroundColour()
201
def StaticText(text, font = self.FONT-1, underline = False, color = None, panel = panel):
202
x = wx.StaticText(panel, -1, text, style = wx.ALIGN_LEFT)
203
x.SetFont(wx.Font(font, wx.DEFAULT, wx.NORMAL, wx.NORMAL, underline))
204
if color is not None:
205
x.SetForegroundColour(color)
208
colSizer = wx.FlexGridSizer(cols = 1, vgap = 3)
210
border = wx.BoxSizer(wx.HORIZONTAL)
211
border.Add(colSizer, 1, wx.EXPAND | wx.ALL, 4)
212
panel.SetSizer(border)
213
panel.SetAutoLayout(True)
215
topboxsizer = wx.FlexGridSizer(cols = 3, vgap = 0)
216
topboxsizer.AddGrowableCol (0)
218
fnsizer = wx.FlexGridSizer(cols = 1, vgap = 0)
219
fnsizer.AddGrowableCol (0)
220
fnsizer.AddGrowableRow (1)
222
fileNameText = StaticText('', self.FONT+4)
223
fnsizer.Add(fileNameText, 1, wx.ALIGN_BOTTOM|wx.EXPAND)
224
self.fileNameText = fileNameText
226
fnsizer2 = wx.FlexGridSizer(cols = 8, vgap = 0)
227
fnsizer2.AddGrowableCol (0)
229
fileSizeText = StaticText('')
230
fnsizer2.Add(fileSizeText, 1, wx.ALIGN_BOTTOM|wx.EXPAND)
231
self.fileSizeText = fileSizeText
233
fileDetails = StaticText('Details', self.FONT, True, 'Blue')
234
fnsizer2.Add(fileDetails, 0, wx.ALIGN_BOTTOM)
236
fnsizer2.Add(StaticText(' '))
238
advText = StaticText('Advanced', self.FONT, True, 'Blue')
239
fnsizer2.Add(advText, 0, wx.ALIGN_BOTTOM)
240
fnsizer2.Add(StaticText(' '))
242
prefsText = StaticText('Prefs', self.FONT, True, 'Blue')
243
fnsizer2.Add(prefsText, 0, wx.ALIGN_BOTTOM)
244
fnsizer2.Add(StaticText(' '))
246
aboutText = StaticText('About', self.FONT, True, 'Blue')
247
fnsizer2.Add(aboutText, 0, wx.ALIGN_BOTTOM)
249
fnsizer2.Add(StaticText(' '))
250
fnsizer.Add(fnsizer2,0,wx.EXPAND)
251
topboxsizer.Add(fnsizer,0,wx.EXPAND)
252
topboxsizer.Add(StaticText(' '))
254
self.statusIcon = wx.EmptyBitmap(32,32)
255
statidata = wx.MemoryDC()
256
statidata.SelectObject(self.statusIcon)
257
statidata.SetPen(wx.TRANSPARENT_PEN)
258
statidata.SetBrush(wx.Brush(self.bgcolor,wx.SOLID))
259
statidata.DrawRectangle(0,0,32,32)
260
self.statusIconPtr = wx.StaticBitmap(panel, -1, self.statusIcon)
261
topboxsizer.Add(self.statusIconPtr)
263
self.fnsizer = fnsizer
264
self.fnsizer2 = fnsizer2
265
self.topboxsizer = topboxsizer
266
colSizer.Add(topboxsizer, 0, wx.EXPAND)
268
self.gauge = wx.Gauge(panel, -1, range = 1000, style = wx.GA_SMOOTH)
269
colSizer.Add(self.gauge, 0, wx.EXPAND)
271
timeSizer = wx.FlexGridSizer(cols = 2)
272
timeSizer.Add(StaticText('Time elapsed / estimated : '))
273
self.timeText = StaticText(self.activity+' ')
274
timeSizer.Add(self.timeText)
275
timeSizer.AddGrowableCol(1)
276
colSizer.Add(timeSizer)
278
destSizer = wx.FlexGridSizer(cols = 2, hgap = 8)
279
self.fileDestLabel = StaticText('Download to:')
280
destSizer.Add(self.fileDestLabel)
281
self.fileDestText = StaticText('')
282
destSizer.Add(self.fileDestText, flag = wx.EXPAND)
283
destSizer.AddGrowableCol(1)
284
colSizer.Add(destSizer, flag = wx.EXPAND)
285
self.destSizer = destSizer
287
statSizer = wx.FlexGridSizer(cols = 3, hgap = 8)
289
self.ratesSizer = wx.FlexGridSizer(cols = 2)
290
self.infoSizer = wx.FlexGridSizer(cols = 2)
292
self.ratesSizer.Add(StaticText(' Download rate: '))
293
self.downRateText = StaticText('0 kB/s ')
294
self.ratesSizer.Add(self.downRateText, flag = wx.EXPAND)
296
self.downTextLabel = StaticText('Downloaded: ')
297
self.infoSizer.Add(self.downTextLabel)
298
self.downText = StaticText('0.00 MiB ')
299
self.infoSizer.Add(self.downText, flag = wx.EXPAND)
301
self.ratesSizer.Add(StaticText(' Upload rate: '))
302
self.upRateText = StaticText('0 kB/s ')
303
self.ratesSizer.Add(self.upRateText, flag = wx.EXPAND)
305
self.upTextLabel = StaticText('Uploaded: ')
306
self.infoSizer.Add(self.upTextLabel)
307
self.upText = StaticText('0.00 MiB ')
308
self.infoSizer.Add(self.upText, flag = wx.EXPAND)
310
shareSizer = wx.FlexGridSizer(cols = 2, hgap = 8)
311
shareSizer.Add(StaticText('Share rating:'))
312
self.shareRatingText = StaticText('')
313
shareSizer.AddGrowableCol(1)
314
shareSizer.Add(self.shareRatingText, flag = wx.EXPAND)
316
statSizer.Add(self.ratesSizer)
317
statSizer.Add(self.infoSizer)
318
statSizer.Add(shareSizer, flag = wx.ALIGN_CENTER_VERTICAL)
319
colSizer.Add (statSizer)
321
torrentSizer = wx.FlexGridSizer(cols = 1)
322
self.peerStatusText = StaticText('')
323
torrentSizer.Add(self.peerStatusText, 0, wx.EXPAND)
324
self.seedStatusText = StaticText('')
325
torrentSizer.Add(self.seedStatusText, 0, wx.EXPAND)
326
torrentSizer.AddGrowableCol(0)
327
colSizer.Add(torrentSizer, 0, wx.EXPAND)
328
self.torrentSizer = torrentSizer
330
self.errorTextSizer = wx.FlexGridSizer(cols = 1)
331
self.errorText = StaticText('', self.FONT, False, 'Red')
332
self.errorTextSizer.Add(self.errorText, 0, wx.EXPAND)
333
colSizer.Add(self.errorTextSizer, 0, wx.EXPAND)
335
cancelSizer=wx.GridSizer(cols = 2, hgap = 40)
336
self.pauseButton = wx.Button(panel, -1, 'Pause')
337
cancelSizer.Add(self.pauseButton, 0, wx.ALIGN_CENTER)
339
self.cancelButton = wx.Button(panel, -1, 'Cancel')
340
cancelSizer.Add(self.cancelButton, 0, wx.ALIGN_CENTER)
341
colSizer.Add(cancelSizer, 0, wx.ALIGN_CENTER)
345
slideSizer = wx.FlexGridSizer(cols = 7, hgap = 0, vgap = 5)
349
self.connChoiceLabel = StaticText('Settings for ')
350
slideSizer.Add (self.connChoiceLabel, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)
351
self.connChoice = wx.Choice (panel, -1, (-1, -1), (self.FONT*12, -1),
352
choices = connChoiceList)
353
self.connChoice.SetFont(self.default_font)
354
self.connChoice.SetSelection(0)
355
slideSizer.Add (self.connChoice, 0, wx.ALIGN_CENTER)
356
self.rateSpinnerLabel = StaticText(' Upload rate (kB/s) ')
357
slideSizer.Add (self.rateSpinnerLabel, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
361
self.rateSpinner = wx.SpinCtrl (panel, -1, "", (-1,-1), (50, -1))
362
self.rateSpinner.SetFont(self.default_font)
363
self.rateSpinner.SetRange(0,5000)
364
self.rateSpinner.SetValue(0)
365
slideSizer.Add (self.rateSpinner, 0, wx.ALIGN_CENTER|wx.ALIGN_CENTER_VERTICAL)
367
self.rateLowerText = StaticText(' %5d' % (0))
368
self.rateUpperText = StaticText('%5d' % (5000))
369
self.rateslider = wx.Slider(panel, -1, 0, 0, 5000, (-1, -1), (80, -1))
371
slideSizer.Add(self.rateLowerText, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
372
slideSizer.Add(self.rateslider, 0, wx.ALIGN_CENTER|wx.ALIGN_CENTER_VERTICAL)
373
slideSizer.Add(self.rateUpperText, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)
375
slideSizer.Add(StaticText(''), 0, wx.ALIGN_LEFT)
377
self.bgallocText = StaticText('', self.FONT+2, False, 'Red')
378
slideSizer.Add(self.bgallocText, 0, wx.ALIGN_LEFT)
382
self.connSpinnerLabel = StaticText(' Max uploads ')
383
slideSizer.Add (self.connSpinnerLabel, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
384
self.connSpinner = wx.SpinCtrl (panel, -1, "", (-1,-1), (50, -1))
385
self.connSpinner.SetFont(self.default_font)
386
self.connSpinner.SetRange(4,100)
387
self.connSpinner.SetValue(4)
388
slideSizer.Add (self.connSpinner, 0, wx.ALIGN_CENTER|wx.ALIGN_CENTER_VERTICAL)
390
self.connLowerText = StaticText(' %5d' % (4))
391
self.connUpperText = StaticText('%5d' % (100))
392
self.connslider = wx.Slider(panel, -1, 4, 4, 100, (-1, -1), (80, -1))
394
slideSizer.Add(self.connLowerText, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
395
slideSizer.Add(self.connslider, 0, wx.ALIGN_CENTER|wx.ALIGN_CENTER_VERTICAL)
396
slideSizer.Add(self.connUpperText, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)
398
colSizer.Add(slideSizer, 1, wx.ALL|wx.ALIGN_CENTER|wx.EXPAND, 0)
400
self.unlimitedLabel = StaticText('0 kB/s means unlimited. Tip: your download rate is proportional to your upload rate', self.FONT-2)
401
colSizer.Add(self.unlimitedLabel, 0, wx.ALIGN_CENTER)
403
self.priorityIDs = [wx.NewId(),wx.NewId(),wx.NewId(),wx.NewId()]
404
self.prioritycolors = [ wx.Colour(160,160,160),
407
wx.Colour(64,64,255) ]
410
wx.EVT_LEFT_DOWN(aboutText, self.about)
411
wx.EVT_LEFT_DOWN(fileDetails, self.details)
412
wx.EVT_LEFT_DOWN(self.statusIconPtr,self.statusIconHelp)
413
wx.EVT_LEFT_DOWN(advText, self.advanced)
414
wx.EVT_LEFT_DOWN(prefsText, self.openConfigMenu)
415
wx.EVT_CLOSE(frame, self.done)
416
wx.EVT_BUTTON(frame, self.pauseButton.GetId(), self.pause)
417
wx.EVT_BUTTON(frame, self.cancelButton.GetId(), self.done)
418
EVT_INVOKE(frame, self.onInvoke)
419
wx.EVT_SCROLL(self.rateslider, self.onRateScroll)
420
wx.EVT_SCROLL(self.connslider, self.onConnScroll)
421
wx.EVT_CHOICE(self.connChoice, -1, self.onConnChoice)
422
wx.EVT_SPINCTRL(self.connSpinner, -1, self.onConnSpinner)
423
wx.EVT_SPINCTRL(self.rateSpinner, -1, self.onRateSpinner)
424
if (sys.platform == 'win32'):
425
self.frame.tbicon = wx.TaskBarIcon()
426
wx.EVT_ICONIZE(self.frame, self.onIconify)
427
wx.EVT_TASKBAR_LEFT_DCLICK(self.frame.tbicon, self.onTaskBarActivate)
428
wx.EVT_TASKBAR_RIGHT_UP(self.frame.tbicon, self.onTaskBarMenu)
429
wx.EVT_MENU(self.frame.tbicon, self.TBMENU_RESTORE, self.onTaskBarActivate)
430
wx.EVT_MENU(self.frame.tbicon, self.TBMENU_CLOSE, self.done)
431
colSizer.AddGrowableCol (0)
432
colSizer.AddGrowableRow (6)
438
self.addwidth = aboutText.GetBestSize().GetWidth() + fileDetails.GetBestSize().GetWidth() + (self.FONT*16)
439
self.fnsizer = fnsizer
440
self.colSizer = colSizer
441
minsize = self.colSizer.GetSize()
442
minsize.SetWidth (minsize.GetWidth())
443
minsize.SetHeight (minsize.GetHeight())
444
self.colSizer.SetMinSize (minsize)
445
self.colSizer.Fit(self.frame)
450
if sys.platform == 'win32': # windows-only optimization
451
def onInvoke(self, event):
452
while self.invokeLaterList:
453
func,args,kwargs = self.invokeLaterList[0]
454
if self.uiflag.isSet():
457
apply(func,args,kwargs)
460
del self.invokeLaterList[0]
462
def invokeLater(self, func, args = [], kwargs = {}):
463
if not self.uiflag.isSet():
464
self.invokeLaterList.append((func,args,kwargs))
465
if len(self.invokeLaterList) == 1:
466
wx.PostEvent(self.frame, self.invokeLaterEvent)
468
def onInvoke(self, event):
469
if not self.uiflag.isSet():
471
apply(event.func, event.args, event.kwargs)
475
def invokeLater(self, func, args = [], kwargs = {}):
476
if not self.uiflag.isSet():
477
wx.PostEvent(self.frame, InvokeEvent(func, args, kwargs))
480
def getStatusIcon(self, name, bitmap=False):
481
if self.statusIcons.has_key(name):
482
i = self.statusIcons[name]
483
if type(i) == type(self.icon) and not bitmap:
486
i = wx.Bitmap(self.statusIconFiles[name], wx.BITMAP_TYPE_ICO)
488
i = wx.Icon(self.statusIconFiles[name], wx.BITMAP_TYPE_ICO)
489
self.statusIcons[name] = i
493
def setStatusIcon(self, name):
494
if name == self.statusIconValue:
496
self.statusIconValue = name
497
statidata = wx.MemoryDC()
498
statidata.SelectObject(self.statusIcon)
499
statidata.BeginDrawing()
501
statidata.DrawIcon(self.getStatusIcon(name),0,0)
503
statidata.DrawBitmap(self.getStatusIcon(name,True),0,0,True)
504
statidata.EndDrawing()
505
statidata.SelectObject(wx.NullBitmap)
506
self.statusIconPtr.Refresh()
509
def createStatusIcon(self, name):
510
iconbuffer = wx.EmptyBitmap(32,32)
511
bbdata = wx.MemoryDC()
512
bbdata.SelectObject(iconbuffer)
513
bbdata.SetPen(wx.TRANSPARENT_PEN)
514
bbdata.SetBrush(wx.Brush(self.bgcolor,wx.SOLID))
515
bbdata.DrawRectangle(0,0,32,32)
517
bbdata.DrawIcon(self.getStatusIcon(name),0,0)
519
bbdata.DrawBitmap(self.getStatusIcon(name,True),0,0,True)
523
def setgaugemode(self, selection):
524
if selection is None:
525
selection = self.gaugemode
526
elif selection == self.gaugemode:
529
self.gaugemode = selection
531
self.gauge.SetForegroundColour(self.configfile.getcheckingcolor())
532
self.gauge.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENU))
534
self.gauge.SetForegroundColour(self.configfile.getdownloadcolor())
535
self.gauge.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENU))
537
self.gauge.SetForegroundColour(self.configfile.getseedingcolor())
538
self.gauge.SetBackgroundColour(self.configfile.getdownloadcolor())
541
def onIconify(self, evt):
543
if self.configfileargs['win32_taskbar_icon']:
545
self.frame.tbicon.SetIcon(self.finicon, "BitTorrent")
547
self.frame.tbicon.SetIcon(self.icon, "BitTorrent")
549
self.taskbaricon = True
556
def onTaskBarActivate(self, evt):
558
if self.frame.IsIconized():
559
self.frame.Iconize(False)
560
if not self.frame.IsShown():
561
self.frame.Show(True)
563
self.frame.tbicon.RemoveIcon()
564
self.taskbaricon = False
565
except wx.PyDeadObjectError:
570
TBMENU_RESTORE = 1000
573
def onTaskBarMenu(self, evt):
575
menu.Append(self.TBMENU_RESTORE, "Restore BitTorrent")
576
menu.Append(self.TBMENU_CLOSE, "Close")
577
self.frame.tbicon.PopupMenu(menu)
581
def _try_get_config(self):
582
if self.config is None:
584
self.config = self.dow.getConfig()
587
return self.config != None
589
def onRateScroll(self, event):
593
if not self._try_get_config():
595
if (self.scrollock == 0):
597
self.updateSpinnerFlag = 1
598
self.dow.setUploadRate(self.rateslider.GetValue()
599
* connChoices[self.connChoice.GetSelection()]['rate'].get('div',1))
604
def onConnScroll(self, event):
608
if not self._try_get_config():
610
self.connSpinner.SetValue (self.connslider.GetValue ())
611
self.dow.setConns(self.connslider.GetValue())
615
def onRateSpinner(self, event = None):
619
if not self._try_get_config():
621
if (self.spinlock == 0):
623
spinnerValue = self.rateSpinner.GetValue()
624
div = connChoices[self.connChoice.GetSelection()]['rate'].get('div',1)
626
if spinnerValue > (self.config['max_upload_rate']):
630
newValue = int((spinnerValue + round_up) / div) * div
631
if newValue != spinnerValue:
632
self.rateSpinner.SetValue(newValue)
634
newValue = spinnerValue
635
self.dow.setUploadRate(newValue)
636
self.updateSliderFlag = 1
641
def onDownRateSpinner(self, event=None):
643
if not self._try_get_config():
645
spinnerValue = self.downrateSpinner.GetValue()
646
self.dow.setDownloadRate(self.downrateSpinner.GetValue())
650
def onConnSpinner(self, event = None):
654
if not self._try_get_config():
656
self.connslider.SetValue (self.connSpinner.GetValue())
657
self.dow.setConns(self.connslider.GetValue())
661
def onConnChoice(self, event, cons=None, rate=None):
663
if not self._try_get_config():
665
num = self.connChoice.GetSelection()
666
choice = connChoices[num]
667
if choice.has_key('super-seed'): # selecting super-seed is now a toggle
668
self.dow.set_super_seed() # one way change, don't go back
669
self.connChoice.SetSelection(self.lastuploadsettings)
671
self.lastuploadsettings = num
672
self.current_ratesetting = self.connChoice.GetStringSelection()
674
rate = choice['rate']['def']
675
self.rateSpinner.SetRange (choice['rate']['min'],
676
choice['rate']['max'])
677
self.rateSpinner.SetValue(rate)
678
self.rateslider.SetRange(
679
choice['rate']['min']/choice['rate'].get('div',1),
680
choice['rate']['max']/choice['rate'].get('div',1))
681
self.rateslider.SetValue (rate/choice['rate'].get('div',1))
682
self.rateLowerText.SetLabel (' %d' % (choice['rate']['min']))
683
self.rateUpperText.SetLabel ('%d' % (choice['rate']['max']))
685
cons = choice['conn']['def']
686
self.connSpinner.SetRange (choice['conn']['min'],
687
choice['conn']['max'])
688
self.connSpinner.SetValue (cons)
689
self.connslider.SetRange (choice['conn']['min'],
690
choice['conn']['max'])
691
self.connslider.SetValue (cons)
692
self.connLowerText.SetLabel (' %d' % (choice['conn']['min']))
693
self.connUpperText.SetLabel ('%d' % (choice['conn']['max']))
694
self.onConnScroll (0)
695
self.onRateScroll (0)
696
self.dow.setInitiate(choice.get('initiate', 40))
697
if choice.has_key('automatic'):
698
if not self.autorate:
700
self.rateSpinner.Enable(False)
701
self.connSpinner.Enable(False)
702
self.dow.setUploadRate(-1)
705
self.autorate = False
706
self.rateSpinner.Enable(True)
707
self.connSpinner.Enable(True)
714
def about(self, event):
716
if (self.aboutBox is not None):
718
self.aboutBox.Close ()
719
except wx.PyDeadObjectError, e:
722
self.aboutBox = wx.Frame(None, -1, 'About BitTorrent', size = (1,1),
723
style = wx.DEFAULT_FRAME_STYLE|wx.FULL_REPAINT_ON_RESIZE)
725
self.aboutBox.SetIcon(self.icon)
729
panel = wx.Panel(self.aboutBox, -1)
731
def StaticText(text, font = self.FONT, underline = False, color = None, panel = panel):
732
x = wx.StaticText(panel, -1, text, style = wx.ALIGN_LEFT)
733
x.SetFont(wx.Font(font, wx.DEFAULT, wx.NORMAL, wx.NORMAL, underline))
734
if color is not None:
735
x.SetForegroundColour(color)
738
colSizer = wx.FlexGridSizer(cols = 1, vgap = 3)
740
titleSizer = wx.BoxSizer(wx.HORIZONTAL)
741
aboutTitle = StaticText('BitTorrent ' + version + ' ', self.FONT+4)
742
titleSizer.Add (aboutTitle)
743
linkDonate = StaticText('Donate to Bram', self.FONT, True, 'Blue')
744
titleSizer.Add (linkDonate, 1, wx.ALIGN_BOTTOM&wx.EXPAND)
745
colSizer.Add(titleSizer, 0, wx.EXPAND)
747
colSizer.Add(StaticText('created by Bram Cohen, Copyright 2001-2003,'))
748
colSizer.Add(StaticText('experimental version maintained by John Hoffman 2003'))
749
colSizer.Add(StaticText('modified from experimental version by Eike Frost 2003'))
750
credits = StaticText('full credits\n', self.FONT, True, 'Blue')
751
colSizer.Add(credits);
753
si = ( 'exact Version String: ' + version + '\n' +
754
'Python version: ' + sys.version + '\n' +
755
'wxPython version: ' + wx.VERSION_STRING + '\n' )
757
si += 'Psyco version: ' + hex(psyco.__version__)[2:] + '\n'
760
colSizer.Add(StaticText(si))
762
babble1 = StaticText(
763
'This is an experimental, unofficial build of BitTorrent.\n' +
764
'It is Free Software under an MIT-Style license.')
765
babble2 = StaticText('BitTorrent Homepage (link)', self.FONT, True, 'Blue')
766
babble3 = StaticText("TheSHAD0W's Client Homepage (link)", self.FONT, True, 'Blue')
767
babble4 = StaticText("Eike Frost's Client Homepage (link)", self.FONT, True, 'Blue')
768
babble6 = StaticText('License Terms (link)', self.FONT, True, 'Blue')
769
colSizer.Add (babble1)
770
colSizer.Add (babble2)
771
colSizer.Add (babble3)
772
colSizer.Add (babble4)
773
colSizer.Add (babble6)
775
okButton = wx.Button(panel, -1, 'Ok')
776
colSizer.Add(okButton, 0, wx.ALIGN_RIGHT)
777
colSizer.AddGrowableCol(0)
779
border = wx.BoxSizer(wx.HORIZONTAL)
780
border.Add(colSizer, 1, wx.EXPAND | wx.ALL, 4)
781
panel.SetSizer(border)
782
panel.SetAutoLayout(True)
784
def donatelink(self):
785
Thread(target = open_new('https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=bram@bitconjurer.org&item_name=BitTorrent&amount=5.00&submit=donate')).start()
786
wx.EVT_LEFT_DOWN(linkDonate, donatelink)
788
Thread(target = open_new('http://bitconjurer.org/BitTorrent/')).start()
789
wx.EVT_LEFT_DOWN(babble2, aboutlink)
791
Thread(target = open_new('http://www.bittornado.com/')).start()
792
wx.EVT_LEFT_DOWN(babble3, shadlink)
794
Thread(target = open_new('http://ei.kefro.st/projects/btclient/')).start()
795
wx.EVT_LEFT_DOWN(babble4, explink)
796
def licenselink(self):
797
Thread(target = open_new('http://ei.kefro.st/projects/btclient/LICENSE.TXT')).start()
798
wx.EVT_LEFT_DOWN(babble6, licenselink)
799
wx.EVT_LEFT_DOWN(credits, self.credits)
801
def closeAbout(e, self = self):
803
self.aboutBox.Close()
804
wx.EVT_BUTTON(self.aboutBox, okButton.GetId(), closeAbout)
805
def kill(e, self = self):
807
self.aboutBox.RemoveIcon()
810
self.aboutBox.Destroy()
812
wx.EVT_CLOSE(self.aboutBox, kill)
821
def details(self, event):
823
if not self.dow or not self.filename:
825
metainfo = self.dow.getResponse()
828
if metainfo.has_key('announce'):
829
announce = metainfo['announce']
832
if metainfo.has_key('announce-list'):
833
announce_list = metainfo['announce-list']
836
info = metainfo['info']
837
info_hash = self.dow.infohash
838
piece_length = info['piece length']
839
fileselector = self.dow.fileselector
841
if (self.detailBox is not None):
843
self.detailBox.Close()
844
except wx.PyDeadObjectError, e:
845
self.detailBox = None
847
self.detailBox = wx.Frame(None, -1, 'Torrent Details ', size = wx.Size(405,230),
848
style = wx.DEFAULT_FRAME_STYLE|wx.FULL_REPAINT_ON_RESIZE)
850
self.detailBox.SetIcon(self.icon)
854
panel = wx.Panel(self.detailBox, -1, size = wx.Size (400,220))
856
def StaticText(text, font = self.FONT-1, underline = False, color = None, panel = panel):
857
x = wx.StaticText(panel, -1, text, style = wx.ALIGN_CENTER_VERTICAL)
858
x.SetFont(wx.Font(font, wx.DEFAULT, wx.NORMAL, wx.NORMAL, underline))
859
if color is not None:
860
x.SetForegroundColour(color)
863
colSizer = wx.FlexGridSizer(cols = 1, vgap = 3)
864
colSizer.AddGrowableCol(0)
866
titleSizer = wx.BoxSizer(wx.HORIZONTAL)
867
aboutTitle = StaticText('Details about ' + self.filename, self.FONT+4)
869
titleSizer.Add (aboutTitle)
870
colSizer.Add (titleSizer)
872
detailSizer = wx.FlexGridSizer(cols = 2, vgap = 6)
874
if info.has_key('length'):
876
detailSizer.Add(StaticText('file name :'))
877
detailSizer.Add(StaticText(info['name']))
878
if info.has_key('md5sum'):
879
detailSizer.Add(StaticText('MD5 hash :'))
880
detailSizer.Add(StaticText(info['md5sum']))
881
file_length = info['length']
884
detail1Sizer = wx.FlexGridSizer(cols = 1, vgap = 6)
885
detail1Sizer.Add(StaticText('directory name : ' + info['name']))
886
colSizer.Add (detail1Sizer)
887
bgallocButton = wx.BitmapButton(panel, -1, self.allocbuttonBitmap, size = (52,20))
888
def bgalloc(self, frame = self):
889
if frame.dow.storagewrapper is not None:
890
frame.dow.storagewrapper.bgalloc()
891
wx.EVT_BUTTON(self.detailBox, bgallocButton.GetId(), bgalloc)
893
bgallocbuttonSizer = wx.FlexGridSizer(cols = 4, hgap = 4, vgap = 0)
894
bgallocbuttonSizer.Add(StaticText('(right-click to set priority)',self.FONT-1),0,wx.ALIGN_BOTTOM)
895
bgallocbuttonSizer.Add(StaticText('(finish allocation)'), -1, wx.ALIGN_CENTER_VERTICAL)
896
bgallocbuttonSizer.Add(bgallocButton, -1, wx.ALIGN_CENTER)
897
bgallocbuttonSizer.AddGrowableCol(0)
898
colSizer.Add(bgallocbuttonSizer, -1, wx.EXPAND)
902
fileListID = wx.NewId()
903
fileList = wx.ListCtrl(panel, fileListID,
904
wx.Point(-1,-1), (325,100), wx.LC_REPORT)
905
self.fileList = fileList
906
fileList.SetImageList(self.filestatusIcons, wx.IMAGE_LIST_SMALL)
908
fileList.SetAutoLayout (True)
909
fileList.InsertColumn(0, "file")
910
fileList.InsertColumn(1, "", format=wx.LIST_FORMAT_RIGHT, width=55)
911
fileList.InsertColumn(2, "")
913
for i in range(len(info['files'])):
915
fileList.InsertItem(x)
918
for file in info['files']:
920
for item in file['path']:
924
path += ' (' + str(file['length']) + ')'
925
fileList.SetStringItem(x, 0, path)
926
if file.has_key('md5sum'):
927
fileList.SetStringItem(x, 2, ' [' + str(file['md5sum']) + ']')
930
item = self.fileList.GetItem(x)
931
item.SetTextColour(self.prioritycolors[p+1])
932
fileList.SetItem(item)
934
file_length += file['length']
935
fileList.SetColumnWidth(0,wx.LIST_AUTOSIZE)
936
fileList.SetColumnWidth(2,wx.LIST_AUTOSIZE)
938
name = 'archive size'
939
colSizer.Add(fileList, 1, wx.EXPAND)
940
colSizer.AddGrowableRow(3)
942
detailSizer.Add(StaticText('info_hash :'),0,wx.ALIGN_CENTER_VERTICAL)
943
detailSizer.Add(wx.TextCtrl(panel, -1, tohex(info_hash), size = (325, -1), style = wx.TE_READONLY))
944
num_pieces = int((file_length+piece_length-1)/piece_length)
945
detailSizer.Add(StaticText(name + ' : '))
946
detailSizer.Add(StaticText('%s (%s bytes)' % (size_format(file_length), comma_format(file_length))))
947
detailSizer.Add(StaticText('pieces : '))
949
detailSizer.Add(StaticText('%i (%s bytes each)' % (num_pieces, comma_format(piece_length))))
951
detailSizer.Add(StaticText('1'))
953
if announce_list is None:
954
detailSizer.Add(StaticText('announce url : '),0,wx.ALIGN_CENTER_VERTICAL)
955
detailSizer.Add(wx.TextCtrl(panel, -1, announce, size = (325, -1), style = wx.TE_READONLY))
957
detailSizer.Add(StaticText(''))
958
trackerList = wx.ListCtrl(panel, -1, wx.Point(-1,-1), (325,75), wx.LC_REPORT)
959
trackerList.SetAutoLayout (True)
960
trackerList.InsertColumn(0, "")
961
trackerList.InsertColumn(1, "announce urls")
963
for tier in range(len(announce_list)):
964
for t in range(len(announce_list[tier])):
966
trackerList.InsertItem(i)
967
if announce is not None:
970
trackerList.InsertItem(i)
973
for tier in range(len(announce_list)):
974
for t in range(len(announce_list[tier])):
976
trackerList.SetStringItem(x, 0, 'tier '+str(tier)+':')
977
trackerList.SetStringItem(x, 1, announce_list[tier][t])
979
if announce is not None:
980
trackerList.SetStringItem(x+1, 0, 'single:')
981
trackerList.SetStringItem(x+1, 1, announce)
982
trackerList.SetColumnWidth(0,wx.LIST_AUTOSIZE)
983
trackerList.SetColumnWidth(1,wx.LIST_AUTOSIZE)
984
detailSizer.Add(trackerList)
986
if announce is None and announce_list is not None:
987
announce = announce_list[0][0]
988
if announce is not None:
989
detailSizer.Add(StaticText('likely tracker :'))
990
p = re.compile( '(.*/)[^/]+')
991
turl = p.sub (r'\1', announce)
992
trackerUrl = StaticText(turl, self.FONT, True, 'Blue')
993
detailSizer.Add(trackerUrl)
994
if metainfo.has_key('comment'):
995
detailSizer.Add(StaticText('comment :'))
996
detailSizer.Add(StaticText(metainfo['comment']))
997
if metainfo.has_key('creation date'):
998
detailSizer.Add(StaticText('creation date :'))
1000
detailSizer.Add(StaticText(
1001
strftime('%x %X',localtime(metainfo['creation date']))))
1004
detailSizer.Add(StaticText(metainfo['creation date']))
1006
detailSizer.Add(StaticText('<cannot read date>'))
1008
detailSizer.AddGrowableCol(1)
1009
colSizer.Add (detailSizer, 1, wx.EXPAND)
1011
okButton = wx.Button(panel, -1, 'Ok')
1012
colSizer.Add(okButton, 0, wx.ALIGN_RIGHT)
1013
colSizer.AddGrowableCol(0)
1015
if not self.configfileargs['gui_stretchwindow']:
1016
aboutTitle.SetSize((400,-1))
1018
panel.SetAutoLayout(True)
1020
border = wx.BoxSizer(wx.HORIZONTAL)
1021
border.Add(colSizer, 1, wx.EXPAND | wx.ALL, 4)
1022
panel.SetSizer(border)
1023
panel.SetAutoLayout(True)
1025
if fileselector and fileListID:
1026
def onRightClick(evt, self = self):
1030
i = self.fileList.GetNextItem(i,state=wx.LIST_STATE_SELECTED)
1034
if not s: # just in case
1036
oldstate = self.dow.fileselector[s[0]]
1039
if self.dow.fileselector[i] != oldstate:
1041
kind = wx.ITEM_NORMAL
1044
menu.Append(self.priorityIDs[1], "download first", kind=kind)
1045
menu.Append(self.priorityIDs[2], "download normally", kind=kind)
1046
menu.Append(self.priorityIDs[3], "download later", kind=kind)
1047
menu.Append(self.priorityIDs[0], "download never (deletes)", kind=kind)
1048
if oldstate is not None:
1049
menu.Check(self.priorityIDs[oldstate+1], True)
1051
def onSelection(evt, self = self, s = s):
1053
priorities = self.dow.fileselector.get_priorities()
1054
for i in xrange(len(self.priorityIDs)):
1055
if p == self.priorityIDs[i]:
1057
priorities[ss] = i-1
1058
item = self.fileList.GetItem(ss)
1059
item.SetTextColour(self.prioritycolors[i])
1060
self.fileList.SetItem(item)
1061
self.dow.fileselector.set_priorities(priorities)
1062
self.fileList.Refresh()
1063
self.refresh_details = True
1066
for id in self.priorityIDs:
1067
wx.EVT_MENU(self.detailBox, id, onSelection)
1069
self.detailBox.PopupMenu(menu, evt.GetPoint())
1071
wx.EVT_LIST_ITEM_RIGHT_CLICK(self.detailBox, fileListID, onRightClick)
1073
def closeDetail(evt, self = self):
1075
self.detailBox.Close()
1076
wx.EVT_BUTTON(self.detailBox, okButton.GetId(), closeDetail)
1077
def kill(evt, self = self):
1079
self.detailBox.RemoveIcon()
1082
self.detailBox.Destroy()
1083
self.detailBox = None
1084
self.fileList = None
1085
self.dow.filedatflag.clear()
1086
wx.EVT_CLOSE(self.detailBox, kill)
1088
def trackerurl(self, turl = turl):
1090
Thread(target = open_new(turl)).start()
1093
wx.EVT_LEFT_DOWN(trackerUrl, trackerurl)
1095
self.detailBox.Show ()
1097
self.detailBox.Fit()
1099
self.refresh_details = True
1100
self.dow.filedatflag.set()
1105
def credits(self, event):
1107
if (self.creditsBox is not None):
1109
self.creditsBox.Close()
1110
except wx.PyDeadObjectError, e:
1111
self.creditsBox = None
1113
self.creditsBox = wx.Frame(None, -1, 'Credits', size = (1,1),
1114
style = wx.DEFAULT_FRAME_STYLE|wx.FULL_REPAINT_ON_RESIZE)
1116
self.creditsBox.SetIcon(self.icon)
1120
panel = wx.Panel(self.creditsBox, -1)
1122
def StaticText(text, font = self.FONT, underline = False, color = None, panel = panel):
1123
x = wx.StaticText(panel, -1, text, style = wx.ALIGN_LEFT)
1124
x.SetFont(wx.Font(font, wx.DEFAULT, wx.NORMAL, wx.NORMAL, underline))
1125
if color is not None:
1126
x.SetForegroundColour(color)
1129
colSizer = wx.FlexGridSizer(cols = 1, vgap = 3)
1131
titleSizer = wx.BoxSizer(wx.HORIZONTAL)
1132
aboutTitle = StaticText('Credits', self.FONT+4)
1133
titleSizer.Add (aboutTitle)
1134
colSizer.Add (titleSizer)
1135
colSizer.Add (StaticText(
1136
'The following people have all helped with this\n' +
1137
'version of BitTorrent in some way (in no particular order) -\n'));
1138
creditSizer = wx.FlexGridSizer(cols = 3)
1139
creditSizer.Add(StaticText(
1140
'Bill Bumgarner\n' +
1141
'David Creswick\n' +
1142
'Andrew Loewenstern\n' +
1150
'Myers Carpenter\n' +
1155
'Xavier Bassery\n' +
1157
creditSizer.Add(StaticText(' '))
1158
creditSizer.Add(StaticText(
1162
'Eric Tiedemann\n' +
1163
'Henry "Pi" James\n' +
1166
'Michael Janssen\n' +
1174
'Christoph Hohmann\n' +
1176
colSizer.Add (creditSizer, flag = wx.ALIGN_CENTER_HORIZONTAL)
1177
okButton = wx.Button(panel, -1, 'Ok')
1178
colSizer.Add(okButton, 0, wx.ALIGN_RIGHT)
1179
colSizer.AddGrowableCol(0)
1181
border = wx.BoxSizer(wx.HORIZONTAL)
1182
border.Add(colSizer, 1, wx.EXPAND | wx.ALL, 4)
1183
panel.SetSizer(border)
1184
panel.SetAutoLayout(True)
1186
def closeCredits(e, self = self):
1188
self.creditsBox.Close()
1189
wx.EVT_BUTTON(self.creditsBox, okButton.GetId(), closeCredits)
1190
def kill(e, self = self):
1192
self.creditsBox.RemoveIcon()
1195
self.creditsBox.Destroy()
1196
self.creditsBox = None
1197
wx.EVT_CLOSE(self.creditsBox, kill)
1199
self.creditsBox.Show()
1201
self.creditsBox.Fit()
1206
def statusIconHelp(self, event):
1208
if (self.statusIconHelpBox is not None):
1210
self.statusIconHelpBox.Close()
1211
except wx.PyDeadObjectError, e:
1212
self.statusIconHelpBox = None
1214
self.statusIconHelpBox = wx.Frame(None, -1, 'Help with the BitTorrent Status Light', size = (1,1),
1215
style = wx.DEFAULT_FRAME_STYLE|wx.FULL_REPAINT_ON_RESIZE)
1217
self.statusIconHelpBox.SetIcon(self.icon)
1221
panel = wx.Panel(self.statusIconHelpBox, -1)
1223
def StaticText(text, font = self.FONT, underline = False, color = None, panel = panel):
1224
x = wx.StaticText(panel, -1, text, style = wx.ALIGN_LEFT)
1225
x.SetFont(wx.Font(font, wx.DEFAULT, wx.NORMAL, wx.NORMAL, underline))
1226
if color is not None:
1227
x.SetForegroundColour(color)
1230
fullsizer = wx.FlexGridSizer(cols = 1, vgap = 13)
1231
colsizer = wx.FlexGridSizer(cols = 2, hgap = 13, vgap = 13)
1233
disconnectedicon=self.createStatusIcon('disconnected')
1234
colsizer.Add(wx.StaticBitmap(panel, -1, disconnectedicon))
1235
colsizer.Add(StaticText(
1236
'Waiting to connect to the tracker.\n' +
1237
'If the status light stays black for a long time the tracker\n' +
1238
'you are trying to connect to may not be working. Unless you\n' +
1239
'are receiving a message telling you otherwise, please wait,\n' +
1240
'and BitTorrent will automatically try to reconnect for you.'), 1, wx.ALIGN_CENTER_VERTICAL)
1242
noconnectionsicon=self.createStatusIcon('noconnections')
1243
colsizer.Add(wx.StaticBitmap(panel, -1, noconnectionsicon))
1244
colsizer.Add(StaticText(
1245
'You have no connections with other clients.\n' +
1246
'Please be patient. If after several minutes the status\n' +
1247
'light remains red, this torrent may be old and abandoned.'), 1, wx.ALIGN_CENTER_VERTICAL)
1249
noincomingicon=self.createStatusIcon('noincoming')
1250
colsizer.Add(wx.StaticBitmap(panel, -1, noincomingicon))
1251
colsizer.Add(StaticText(
1252
'You have not received any incoming connections from others.\n' +
1253
'It may only be because no one has tried. If you never see\n' +
1254
'the status light turn green, it may indicate your system\n' +
1255
'is behind a firewall or proxy server. Please look into\n' +
1256
'routing BitTorrent through your firewall in order to receive\n' +
1257
'the best possible download rate.'), 1, wx.ALIGN_CENTER_VERTICAL)
1259
nocompletesicon=self.createStatusIcon('nocompletes')
1260
colsizer.Add(wx.StaticBitmap(panel, -1, nocompletesicon))
1261
colsizer.Add(StaticText(
1262
'There are no complete copies among the clients you are\n' +
1263
'connected to. Don\'t panic, other clients in the torrent\n' +
1264
"you can't see may have the missing data.\n" +
1265
'If the status light remains blue, you may have problems\n' +
1266
'completing your download.'), 1, wx.ALIGN_CENTER_VERTICAL)
1268
allgoodicon=self.createStatusIcon('allgood')
1269
colsizer.Add(wx.StaticBitmap(panel, -1, allgoodicon))
1270
colsizer.Add(StaticText(
1271
'The torrent is operating properly.'), 1, wx.ALIGN_CENTER_VERTICAL)
1273
fullsizer.Add(colsizer, 0, wx.ALIGN_CENTER)
1274
colsizer2 = wx.FlexGridSizer(cols = 1, hgap = 13)
1276
colsizer2.Add(StaticText(
1277
'Please note that the status light is not omniscient, and that it may\n' +
1278
'be wrong in many instances. A torrent with a blue light may complete\n' +
1279
"normally, and an occasional yellow light doesn't mean your computer\n" +
1280
'has suddenly become firewalled.'), 1, wx.ALIGN_CENTER_VERTICAL)
1282
colspacer = StaticText(' ')
1283
colsizer2.Add(colspacer)
1285
okButton = wx.Button(panel, -1, 'Ok')
1286
colsizer2.Add(okButton, 0, wx.ALIGN_CENTER)
1287
fullsizer.Add(colsizer2, 0, wx.ALIGN_CENTER)
1289
border = wx.BoxSizer(wx.HORIZONTAL)
1290
border.Add(fullsizer, 1, wx.EXPAND | wx.ALL, 4)
1292
panel.SetSizer(border)
1293
panel.SetAutoLayout(True)
1296
def closeHelp(self, frame = self):
1297
frame.statusIconHelpBox.Close()
1298
wx.EVT_BUTTON(self.statusIconHelpBox, okButton.GetId(), closeHelp)
1300
self.statusIconHelpBox.Show ()
1302
self.statusIconHelpBox.Fit()
1307
def openConfigMenu(self, event):
1309
self.configfile.configMenu(self)
1314
def advanced(self, event):
1316
if not self.dow or not self.filename:
1318
if (self.advBox is not None):
1320
self.advBox.Close ()
1321
except wx.PyDeadObjectError, e:
1324
self.advBox = wx.Frame(None, -1, 'BitTorrent Advanced', size = wx.Size(200,200),
1325
style = wx.DEFAULT_FRAME_STYLE|wx.FULL_REPAINT_ON_RESIZE)
1327
self.advBox.SetIcon(self.icon)
1331
panel = wx.Panel(self.advBox, -1, size = wx.Size (200,200))
1333
def StaticText(text, font = self.FONT-1, underline = False, color = None, panel = panel):
1334
x = wx.StaticText(panel, -1, text, style = wx.ALIGN_LEFT)
1335
x.SetFont(wx.Font(font, wx.DEFAULT, wx.NORMAL, wx.NORMAL, underline))
1336
if color is not None:
1337
x.SetForegroundColour(color)
1340
colSizer = wx.FlexGridSizer (cols = 1, vgap = 1)
1341
colSizer.Add (StaticText('Advanced Info for ' + self.filename, self.FONT+4))
1343
try: # get system font width
1344
fw = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT).GetPointSize()+1
1346
fw = wx.SystemSettings_GetFont(wx.SYS_SYSTEM_FONT).GetPointSize()+1
1348
spewList = wx.ListCtrl(panel, -1, wx.Point(-1,-1), (fw*66,350), wx.LC_REPORT|wx.LC_HRULES|wx.LC_VRULES)
1349
self.spewList = spewList
1350
spewList.SetAutoLayout (True)
1352
colSizer.Add(spewList, -1, wx.EXPAND)
1354
colSizer.Add(StaticText(''))
1355
self.storagestats1 = StaticText('')
1356
self.storagestats2 = StaticText('')
1357
colSizer.Add(self.storagestats1, -1, wx.EXPAND)
1358
colSizer.Add(self.storagestats2, -1, wx.EXPAND)
1359
spinnerSizer = wx.FlexGridSizer(cols=4,vgap=0,hgap=0)
1360
cstats = ' Listening on '
1361
if self.connection_stats['interfaces']:
1362
cstats += ', '.join(self.connection_stats['interfaces']) + ' on '
1363
cstats += 'port ' + str(self.connection_stats['port'])
1364
if self.connection_stats['upnp']:
1365
cstats += ', UPnP port forwarded'
1366
spinnerSizer.Add(StaticText(cstats), -1, wx.EXPAND)
1367
spinnerSizer.AddGrowableCol(0)
1368
spinnerSizer.Add(StaticText('Max download rate (kB/s) '),0,wx.ALIGN_CENTER_VERTICAL)
1369
self.downrateSpinner = wx.SpinCtrl (panel, -1, "", (-1,-1), (50, -1))
1370
self.downrateSpinner.SetFont(self.default_font)
1371
self.downrateSpinner.SetRange(0,5000)
1372
self.downrateSpinner.SetValue(self.config['max_download_rate'])
1373
spinnerSizer.Add (self.downrateSpinner, 0)
1374
wx.EVT_SPINCTRL(self.downrateSpinner, -1, self.onDownRateSpinner)
1375
spinnerSizer.Add(StaticText(' (0 = unlimited) '),0,wx.ALIGN_CENTER_VERTICAL)
1376
colSizer.Add(spinnerSizer,0,wx.EXPAND)
1378
colSizer.Add(StaticText(''))
1380
buttonSizer = wx.FlexGridSizer (cols = 5, hgap = 20)
1382
reannounceButton = wx.Button(panel, -1, 'Manual Announce')
1383
buttonSizer.Add (reannounceButton)
1385
extannounceButton = wx.Button(panel, -1, 'External Announce')
1386
buttonSizer.Add (extannounceButton)
1388
bgallocButton = wx.Button(panel, -1, 'Finish Allocation')
1389
buttonSizer.Add (bgallocButton)
1391
buttonSizer.Add(StaticText(''))
1393
okButton = wx.Button(panel, -1, 'Ok')
1394
buttonSizer.Add (okButton)
1396
colSizer.Add (buttonSizer, 0, wx.ALIGN_CENTER)
1397
colSizer.AddGrowableCol(0)
1398
colSizer.AddGrowableRow(1)
1400
panel.SetSizer(colSizer)
1401
panel.SetAutoLayout(True)
1403
spewList.InsertColumn(0, "Optimistic Unchoke", format=wx.LIST_FORMAT_CENTER, width=fw*2)
1404
spewList.InsertColumn(1, "Peer ID", width=0)
1405
spewList.InsertColumn(2, "IP", width=fw*11)
1406
spewList.InsertColumn(3, "Local/Remote", format=wx.LIST_FORMAT_CENTER, width=fw*3)
1407
spewList.InsertColumn(4, "Up", format=wx.LIST_FORMAT_RIGHT, width=fw*6)
1408
spewList.InsertColumn(5, "Interested", format=wx.LIST_FORMAT_CENTER, width=fw*2)
1409
spewList.InsertColumn(6, "Choking", format=wx.LIST_FORMAT_CENTER, width=fw*2)
1410
spewList.InsertColumn(7, "Down", format=wx.LIST_FORMAT_RIGHT, width=fw*6)
1411
spewList.InsertColumn(8, "Interesting", format=wx.LIST_FORMAT_CENTER, width=fw*2)
1412
spewList.InsertColumn(9, "Choked", format=wx.LIST_FORMAT_CENTER, width=fw*2)
1413
spewList.InsertColumn(10, "Snubbed", format=wx.LIST_FORMAT_CENTER, width=fw*2)
1414
spewList.InsertColumn(11, "Downloaded", format=wx.LIST_FORMAT_RIGHT, width=fw*7)
1415
spewList.InsertColumn(12, "Uploaded", format=wx.LIST_FORMAT_RIGHT, width=fw*7)
1416
spewList.InsertColumn(13, "Completed", format=wx.LIST_FORMAT_RIGHT, width=fw*6)
1417
spewList.InsertColumn(14, "Peer Download Speed", format=wx.LIST_FORMAT_RIGHT, width=fw*6)
1419
def reannounce(self, frame = self):
1420
if (clock() - frame.reannouncelast > 60):
1421
frame.reannouncelast = clock()
1422
frame.dow.reannounce()
1423
wx.EVT_BUTTON(self.advBox, reannounceButton.GetId(), reannounce)
1425
self.advextannouncebox = None
1426
def reannounce_external(self, frame = self):
1427
if (frame.advextannouncebox is not None):
1429
frame.advextannouncebox.Close ()
1430
except wx.PyDeadObjectError, e:
1431
frame.advextannouncebox = None
1433
frame.advextannouncebox = wx.Frame(None, -1, 'External Announce', size = (1,1),
1434
style = wx.DEFAULT_FRAME_STYLE|wx.FULL_REPAINT_ON_RESIZE)
1436
frame.advextannouncebox.SetIcon(frame.icon)
1440
panel = wx.Panel(frame.advextannouncebox, -1)
1442
fullsizer = wx.FlexGridSizer(cols = 1, vgap = 13)
1443
msg = wx.StaticText(panel, -1, "Enter tracker anounce URL:")
1444
msg.SetFont(frame.default_font)
1447
frame.advexturl = wx.TextCtrl(parent = panel, id = -1, value = '',
1448
size = (255, 20), style = wx.TE_PROCESS_TAB)
1449
frame.advexturl.SetFont(frame.default_font)
1450
frame.advexturl.SetValue(frame.lastexternalannounce)
1451
fullsizer.Add(frame.advexturl)
1453
buttonSizer = wx.FlexGridSizer (cols = 2, hgap = 10)
1455
okButton = wx.Button(panel, -1, 'OK')
1456
buttonSizer.Add (okButton)
1458
cancelButton = wx.Button(panel, -1, 'Cancel')
1459
buttonSizer.Add (cancelButton)
1461
fullsizer.Add (buttonSizer, 0, wx.ALIGN_CENTER)
1463
border = wx.BoxSizer(wx.HORIZONTAL)
1464
border.Add(fullsizer, 1, wx.EXPAND | wx.ALL, 4)
1466
panel.SetSizer(border)
1467
panel.SetAutoLayout(True)
1469
def ok(self, frame = frame):
1470
special = frame.advexturl.GetValue()
1472
frame.lastexternalannounce = special
1473
if (clock() - frame.reannouncelast > 60):
1474
frame.reannouncelast = clock()
1475
frame.dow.reannounce(special)
1476
frame.advextannouncebox.Close()
1477
wx.EVT_BUTTON(frame.advextannouncebox, okButton.GetId(), ok)
1479
def cancel(self, frame = frame):
1480
frame.advextannouncebox.Close()
1481
wx.EVT_BUTTON(frame.advextannouncebox, cancelButton.GetId(), cancel)
1483
frame.advextannouncebox.Show ()
1484
fullsizer.Fit(panel)
1485
frame.advextannouncebox.Fit()
1487
wx.EVT_BUTTON(self.advBox, extannounceButton.GetId(), reannounce_external)
1489
def bgalloc(self, frame = self):
1490
if frame.dow.storagewrapper is not None:
1491
frame.dow.storagewrapper.bgalloc()
1492
wx.EVT_BUTTON(self.advBox, bgallocButton.GetId(), bgalloc)
1494
def closeAdv(evt, self = self):
1496
def killAdv(evt, self = self):
1498
self.advBox.RemoveIcon()
1501
self.onDownRateSpinner()
1502
self.dow.spewflag.clear()
1503
self.advBox.Destroy()
1505
if (self.advextannouncebox is not None):
1507
self.advextannouncebox.Close()
1508
except wx.PyDeadObjectError, e:
1510
self.advextannouncebox = None
1511
wx.EVT_BUTTON(self.advBox, okButton.GetId(), closeAdv)
1512
wx.EVT_CLOSE(self.advBox, killAdv)
1518
self.dow.spewflag.set()
1523
def displayUsage(self, text):
1524
self.invokeLater(self.onDisplayUsage, [text])
1526
def onDisplayUsage(self, text):
1529
w = wx.Frame(None, -1, 'BITTORRENT USAGE',
1530
style = wx.DEFAULT_FRAME_STYLE|wx.FULL_REPAINT_ON_RESIZE)
1531
panel = wx.Panel(w, -1)
1532
sizer = wx.FlexGridSizer(cols = 1)
1533
sizer.Add(wx.TextCtrl(panel, -1, text,
1534
size = (500,300), style = wx.TE_READONLY|wx.TE_MULTILINE))
1535
okButton = wx.Button(panel, -1, 'Ok')
1537
def closeUsage(self, frame = self):
1538
frame.usageBox.Close()
1539
wx.EVT_BUTTON(w, okButton.GetId(), closeUsage)
1540
def kill(self, frame = self):
1541
frame.usageBox.Destroy()
1542
frame.usageBox = None
1543
wx.EVT_CLOSE(w, kill)
1545
sizer.Add(okButton, 0, wx.ALIGN_RIGHT)
1546
border = wx.BoxSizer(wx.HORIZONTAL)
1547
border.Add(sizer, 1, wx.EXPAND | wx.ALL, 4)
1549
panel.SetSizer(border)
1550
panel.SetAutoLayout(True)
1560
def updateStatus(self, dpflag = Event(), fractionDone = None,
1561
timeEst = None, downRate = None, upRate = None,
1562
activity = None, statistics = None, spew = None, sizeDone = None,
1564
if activity is not None:
1565
self.activity = activity
1566
self.gui_fractiondone = fractionDone
1567
self.invokeLater(self.onUpdateStatus,
1568
[dpflag, timeEst, downRate, upRate, statistics, spew, sizeDone])
1570
def onUpdateStatus(self, dpflag, timeEst, downRate, upRate,
1571
statistics, spew, sizeDone):
1572
if self.firstupdate:
1573
if not self.old_ratesettings:
1574
self.old_ratesettings = {}
1575
self.connChoice.SetStringSelection(
1576
self.old_ratesettings.get('rate setting',
1577
self.configfileargs['gui_ratesettingsdefault']))
1578
self.onConnChoice(0,
1579
self.old_ratesettings.get('uploads'),
1580
self.old_ratesettings.get('max upload rate'))
1581
if self.old_ratesettings.has_key('max download rate'):
1582
self.dow.setDownloadRate(self.old_ratesettings['max download rate'])
1584
self.downrateSpinner.SetValue(self.old_ratesettings['max download rate'])
1585
self.firstupdate = False
1587
self.dow.spewflag.set()
1588
if self.ispaused or statistics is None:
1589
self.setStatusIcon('startup')
1590
elif statistics.numPeers + statistics.numSeeds + statistics.numOldSeeds == 0:
1591
if statistics.last_failed:
1592
self.setStatusIcon('disconnected')
1594
self.setStatusIcon('noconnections')
1595
elif ( not statistics.external_connection_made
1596
and not self.configfileargs['gui_forcegreenonfirewall'] ):
1597
self.setStatusIcon('noincoming')
1598
elif ( (statistics.numSeeds + statistics.numOldSeeds == 0)
1599
and ( (self.fin and statistics.numCopies < 1)
1600
or (not self.fin and statistics.numCopies2 < 1) ) ):
1601
self.setStatusIcon('nocompletes')
1602
elif timeEst == 0 and sizeDone < self.torrentsize:
1603
self.setStatusIcon('nocompletes')
1605
self.setStatusIcon('allgood')
1606
if statistics is None:
1607
self.setgaugemode(-1)
1608
elif self.gui_fractiondone == None or self.gui_fractiondone == 1.0:
1609
self.setgaugemode(1)
1611
self.setgaugemode(0)
1613
if self.updateSliderFlag == 1:
1614
self.updateSliderFlag = 0
1615
newValue = (self.rateSpinner.GetValue()
1616
/ connChoices[self.connChoice.GetSelection()]['rate'].get('div',1))
1617
if self.rateslider.GetValue() != newValue:
1618
self.rateslider.SetValue(newValue)
1619
if self.updateSpinnerFlag == 1:
1620
self.updateSpinnerFlag = 0
1621
cc = connChoices[self.connChoice.GetSelection()]
1622
if cc.has_key('rate'):
1623
newValue = (self.rateslider.GetValue() * cc['rate'].get('div',1))
1624
if self.rateSpinner.GetValue() != newValue:
1625
self.rateSpinner.SetValue(newValue)
1628
if statistics is None or statistics.numOldSeeds > 0 or statistics.numCopies > 1:
1629
self.gauge.SetValue(1000)
1631
self.gauge.SetValue(int(1000*statistics.numCopies))
1632
elif self.gui_fractiondone is not None:
1633
gaugelevel = int(self.gui_fractiondone * 1000)
1634
self.gauge.SetValue(gaugelevel)
1635
if statistics is not None and statistics.downTotal is not None:
1636
if self.configfileargs['gui_displaymiscstats']:
1637
self.frame.SetTitle('%.1f%% (%.2f MiB) %s - BitTorrent %s' % (float(gaugelevel)/10, float(sizeDone) / (1<<20), self.filename, version))
1639
self.frame.SetTitle('%.1f%% %s - BitTorrent %s' % (float(gaugelevel)/10, self.filename, version))
1641
self.frame.SetTitle('%.0f%% %s - BitTorrent %s' % (float(gaugelevel)/10, self.filename, version))
1643
self.timeText.SetLabel(hours(clock() - self.starttime) + ' /')
1644
elif timeEst is None:
1645
self.timeText.SetLabel(hours(clock() - self.starttime) + ' / ' + self.activity)
1647
self.timeText.SetLabel(hours(clock() - self.starttime) + ' / ' + hours(timeEst))
1648
if not self.ispaused:
1649
if downRate is not None:
1650
self.downRateText.SetLabel('%.0f kB/s' % (float(downRate) / 1000))
1651
if upRate is not None:
1652
self.upRateText.SetLabel('%.0f kB/s' % (float(upRate) / 1000))
1653
if self.taskbaricon:
1654
icontext='BitTorrent '
1655
if self.gui_fractiondone is not None and not self.fin:
1656
if statistics is not None and statistics.downTotal is not None:
1657
icontext=icontext+' %.1f%% (%.2f MiB)' % (self.gui_fractiondone*100, float(sizeDone) / (1<<20))
1659
icontext=icontext+' %.0f%%' % (self.gui_fractiondone*100)
1660
if upRate is not None:
1661
icontext=icontext+' u:%.0f kB/s' % (float(upRate) / 1000)
1662
if downRate is not None:
1663
icontext=icontext+' d:%.0f kB/s' % (float(downRate) / 1000)
1664
icontext+=' %s' % self.filename
1666
if self.gui_fractiondone == None or self.gui_fractiondone == 1.0:
1667
self.frame.tbicon.SetIcon(self.finicon,icontext)
1669
self.frame.tbicon.SetIcon(self.icon,icontext)
1672
if statistics is not None:
1674
self.rateSpinner.SetValue(statistics.upRate)
1675
self.connSpinner.SetValue(statistics.upSlots)
1677
downtotal = statistics.downTotal + self.old_download
1678
uptotal = statistics.upTotal + self.old_upload
1679
if self.configfileargs['gui_displaymiscstats']:
1680
self.downText.SetLabel('%.2f MiB' % (float(downtotal) / (1 << 20)))
1681
self.upText.SetLabel('%.2f MiB' % (float(uptotal) / (1 << 20)))
1683
sharerating = float(uptotal)/downtotal
1684
if sharerating == 0:
1687
elif sharerating < 0.5:
1690
elif sharerating < 1.0:
1695
color = 'Forest Green'
1702
shareSmiley = '00 :-D'
1703
color = 'Forest Green'
1704
if sharerating is None:
1705
self.shareRatingText.SetLabel(shareSmiley)
1707
self.shareRatingText.SetLabel('%.3f %s' % (sharerating, shareSmiley))
1708
self.shareRatingText.SetForegroundColour(color)
1710
if self.configfileargs['gui_displaystats']:
1712
self.seedStatusText.SetLabel('connected to %d seeds; also seeing %.3f distributed copies' % (statistics.numSeeds,0.001*int(1000*statistics.numCopies2)))
1714
self.seedStatusText.SetLabel('%d seeds seen recently; also seeing %.3f distributed copies' % (statistics.numOldSeeds,0.001*int(1000*statistics.numCopies)))
1715
self.peerStatusText.SetLabel('connected to %d peers with an average of %.1f%% completed (total speed %.0f kB/s)' % (statistics.numPeers,statistics.percentDone,float(statistics.torrentRate) / (1000)))
1716
if ((clock() - self.lastError) > 300):
1717
self.errorText.SetLabel('')
1719
if ( self.configfileargs['gui_displaymiscstats']
1720
and statistics is not None and statistics.backgroundallocating ):
1721
self.bgalloc_periods += 1
1722
if self.bgalloc_periods > 3:
1723
self.bgalloc_periods = 0
1724
self.bgallocText.SetLabel('ALLOCATING'+(' .'*self.bgalloc_periods))
1725
elif self.dow.superseedflag.isSet():
1726
self.bgallocText.SetLabel('SUPER-SEED')
1728
self.bgallocText.SetLabel('')
1731
if spew is not None and (clock()-self.spewwait>1):
1732
if (self.advBox is not None):
1733
self.spewwait = clock()
1734
spewList = self.spewList
1735
spewlen = len(spew)+2
1736
if statistics is not None:
1737
kickbanlen = len(statistics.peers_kicked)+len(statistics.peers_banned)
1739
spewlen += kickbanlen+1
1742
for x in range(spewlen-spewList.GetItemCount()):
1744
spewList.InsertItem(i)
1745
for x in range(spewlen,spewList.GetItemCount()):
1746
spewList.DeleteItem(len(spew)+1)
1750
for x in range(len(spew)):
1751
if (spew[x]['optimistic'] == 1):
1755
spewList.SetStringItem(x, 0, a)
1756
spewList.SetStringItem(x, 1, spew[x]['id'])
1757
spewList.SetStringItem(x, 2, spew[x]['ip'])
1758
spewList.SetStringItem(x, 3, spew[x]['direction'])
1759
if spew[x]['uprate'] > 100:
1760
spewList.SetStringItem(x, 4, '%.0f kB/s' % (float(spew[x]['uprate']) / 1000))
1762
spewList.SetStringItem(x, 4, ' ')
1763
tot_uprate += spew[x]['uprate']
1764
if (spew[x]['uinterested'] == 1):
1768
spewList.SetStringItem(x, 5, a)
1769
if (spew[x]['uchoked'] == 1):
1773
spewList.SetStringItem(x, 6, a)
1775
if spew[x]['downrate'] > 100:
1776
spewList.SetStringItem(x, 7, '%.0f kB/s' % (float(spew[x]['downrate']) / 1000))
1778
spewList.SetStringItem(x, 7, ' ')
1779
tot_downrate += spew[x]['downrate']
1781
if (spew[x]['dinterested'] == 1):
1785
spewList.SetStringItem(x, 8, a)
1786
if (spew[x]['dchoked'] == 1):
1790
spewList.SetStringItem(x, 9, a)
1791
if (spew[x]['snubbed'] == 1):
1795
spewList.SetStringItem(x, 10, a)
1796
spewList.SetStringItem(x, 11, '%.2f MiB' % (float(spew[x]['dtotal']) / (1 << 20)))
1797
if spew[x]['utotal'] is not None:
1798
a = '%.2f MiB' % (float(spew[x]['utotal']) / (1 << 20))
1801
spewList.SetStringItem(x, 12, a)
1802
spewList.SetStringItem(x, 13, '%.1f%%' % (float(int(spew[x]['completed']*1000))/10))
1803
if spew[x]['speed'] is not None:
1804
a = '%.0f kB/s' % (float(spew[x]['speed']) / 1000)
1807
spewList.SetStringItem(x, 14, a)
1811
spewList.SetStringItem(x, i, '')
1814
spewList.SetStringItem(x, 2, ' TOTALS:')
1815
spewList.SetStringItem(x, 4, '%.0f kB/s' % (float(tot_uprate) / 1000))
1816
spewList.SetStringItem(x, 7, '%.0f kB/s' % (float(tot_downrate) / 1000))
1817
if statistics is not None:
1818
spewList.SetStringItem(x, 11, '%.2f MiB' % (float(statistics.downTotal) / (1 << 20)))
1819
spewList.SetStringItem(x, 12, '%.2f MiB' % (float(statistics.upTotal) / (1 << 20)))
1821
spewList.SetStringItem(x, 11, '')
1822
spewList.SetStringItem(x, 12, '')
1823
for i in [0,1,3,5,6,8,9,10,13,14]:
1824
spewList.SetStringItem(x, i, '')
1829
spewList.SetStringItem(x, i, '')
1831
for peer in statistics.peers_kicked:
1833
spewList.SetStringItem(x, 2, peer[0])
1834
spewList.SetStringItem(x, 1, peer[1])
1835
spewList.SetStringItem(x, 4, 'KICKED')
1836
for i in [0,3,5,6,7,8,9,10,11,12,13,14]:
1837
spewList.SetStringItem(x, i, '')
1839
for peer in statistics.peers_banned:
1841
spewList.SetStringItem(x, 2, peer[0])
1842
spewList.SetStringItem(x, 1, peer[1])
1843
spewList.SetStringItem(x, 4, 'BANNED')
1844
for i in [0,3,5,6,7,8,9,10,11,12,13,14]:
1845
spewList.SetStringItem(x, i, '')
1847
if statistics is not None:
1849
' currently downloading %d pieces (%d just started), %d pieces partially retrieved'
1850
% ( statistics.storage_active,
1851
statistics.storage_new,
1852
statistics.storage_dirty ) )
1853
if statistics.storage_isendgame:
1854
l1 += ', endgame mode'
1855
self.storagestats2.SetLabel(l1)
1856
self.storagestats1.SetLabel(
1857
' %d of %d pieces complete (%d just downloaded), %d failed hash check, %sKiB redundant data discarded'
1858
% ( statistics.storage_numcomplete,
1859
statistics.storage_totalpieces,
1860
statistics.storage_justdownloaded,
1861
statistics.storage_numflunked,
1862
comma_format(int(statistics.discarded/1024)) ) )
1864
if ( self.fileList is not None and statistics is not None
1865
and (statistics.filelistupdated.isSet() or self.refresh_details) ):
1866
for i in range(len(statistics.filecomplete)):
1867
if self.dow.fileselector[i] == -1:
1868
self.fileList.SetItemImage(i,0,0)
1869
self.fileList.SetStringItem(i,1,'')
1870
elif statistics.fileinplace[i]:
1871
self.fileList.SetItemImage(i,2,2)
1872
self.fileList.SetStringItem(i,1,"done")
1873
elif statistics.filecomplete[i]:
1874
self.fileList.SetItemImage(i,1,1)
1875
self.fileList.SetStringItem(i,1,"100%")
1877
self.fileList.SetItemImage(i,0,0)
1878
frac = statistics.fileamtdone[i]
1880
self.fileList.SetStringItem(i,1,'%d%%' % (frac*100))
1882
self.fileList.SetStringItem(i,1,'')
1884
statistics.filelistupdated.clear()
1885
self.refresh_details = False
1887
if self.configfile.configReset(): # whoopee! Set everything invisible! :-)
1889
self.dow.config['security'] = self.configfileargs['security']
1891
statsdisplayflag = self.configfileargs['gui_displaymiscstats']
1892
self.downTextLabel.Show(statsdisplayflag)
1893
self.upTextLabel.Show(statsdisplayflag)
1894
self.fileDestLabel.Show(statsdisplayflag)
1895
self.fileDestText.Show(statsdisplayflag)
1896
self.colSizer.Layout()
1898
self.downText.SetLabel('') # blank these to flush them
1899
self.upText.SetLabel('')
1900
self.seedStatusText.SetLabel('')
1901
self.peerStatusText.SetLabel('')
1903
ratesettingsmode = self.configfileargs['gui_ratesettingsmode']
1904
ratesettingsflag1 = True #\ settings
1905
ratesettingsflag2 = False #/ for 'basic'
1906
if ratesettingsmode == 'none':
1907
ratesettingsflag1 = False
1908
elif ratesettingsmode == 'full':
1909
ratesettingsflag2 = True
1910
self.connChoiceLabel.Show(ratesettingsflag1)
1911
self.connChoice.Show(ratesettingsflag1)
1912
self.rateSpinnerLabel.Show(ratesettingsflag2)
1913
self.rateSpinner.Show(ratesettingsflag2)
1914
self.rateLowerText.Show(ratesettingsflag2)
1915
self.rateUpperText.Show(ratesettingsflag2)
1916
self.rateslider.Show(ratesettingsflag2)
1917
self.connSpinnerLabel.Show(ratesettingsflag2)
1918
self.connSpinner.Show(ratesettingsflag2)
1919
self.connLowerText.Show(ratesettingsflag2)
1920
self.connUpperText.Show(ratesettingsflag2)
1921
self.connslider.Show(ratesettingsflag2)
1922
self.unlimitedLabel.Show(ratesettingsflag2)
1924
self.setgaugemode(None)
1927
self.frame.Refresh()
1929
self.gui_fractiondone = None
1935
self.invokeLater(self.onFinishEvent)
1939
self.invokeLater(self.onFailEvent)
1941
def error(self, errormsg):
1942
self.invokeLater(self.onErrorEvent, [errormsg])
1944
def onFinishEvent(self):
1945
self.activity = hours(clock() - self.starttime) + ' / ' +'Download Succeeded!'
1946
self.cancelButton.SetLabel('Finish')
1947
self.gauge.SetValue(0)
1948
self.frame.SetTitle('%s - Upload - BitTorrent %s' % (self.filename, version))
1950
self.frame.SetIcon(self.finicon)
1953
if self.taskbaricon:
1954
self.frame.tbicon.SetIcon(self.finicon, "BitTorrent - Finished")
1955
self.downRateText.SetLabel('')
1957
def onFailEvent(self):
1958
if not self.shuttingdown:
1959
self.timeText.SetLabel(hours(clock() - self.starttime) + ' / ' +'Failed!')
1960
self.activity = 'Failed!'
1961
self.cancelButton.SetLabel('Close')
1962
self.gauge.SetValue(0)
1963
self.downRateText.SetLabel('')
1964
self.setStatusIcon('startup')
1966
def onErrorEvent(self, errormsg):
1967
if errormsg[:2] == ' ': # indent at least 2 spaces means a warning message
1968
self.errorText.SetLabel(errormsg)
1969
self.lastError = clock()
1971
self.errorText.SetLabel(strftime('ERROR (%x %X) -\n') + errormsg)
1972
self.lastError = clock()
1975
def chooseFile(self, default, size, saveas, dir):
1978
self.invokeLater(self.onChooseFile, [default, bucket, f, size, dir, saveas])
1982
def onChooseFile(self, default, bucket, f, size, dir, saveas):
1984
if self.configfileargs['gui_default_savedir'] != '':
1985
start_dir = self.configfileargs['gui_default_savedir']
1987
start_dir = self.configfileargs['last_saved']
1988
if not isdir(start_dir): # if it's not set properly
1989
start_dir = '/' # yes, this hack does work in Windows
1991
start_dir1 = start_dir
1992
if isdir(join(start_dir,default)):
1993
start_dir = join(start_dir,default)
1994
dl = wx.DirDialog(self.frame,
1995
'Choose a directory to save to, pick a partial download to resume',
1996
defaultPath = start_dir, style = wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON)
1998
dl = wx.FileDialog(self.frame,
1999
'Choose file to save as, pick a partial download to resume',
2000
defaultDir = start_dir, defaultFile = default, wildcard = '*',
2003
if dl.ShowModal() != wx.ID_OK:
2015
self.configfile.WriteLastSaved(d)
2019
default = basename(saveas)
2021
self.onChooseFileDone(default, size)
2024
def ChooseFileDone(self, name, size):
2025
self.invokeLater(self.onChooseFileDone, [name, size])
2027
def onChooseFileDone(self, name, size):
2028
self.torrentsize = size
2029
lname = basename(name)
2030
self.filename = lname
2031
self.fileNameText.SetLabel('%s' % (lname))
2032
self.fileSizeText.SetLabel('(%.2f MiB)' % (float(size) / (1 << 20)))
2033
self.timeText.SetLabel(hours(clock() - self.starttime) + ' / ' + self.activity)
2034
self.fileDestText.SetLabel(name)
2035
self.frame.SetTitle(lname + '- BitTorrent ' + version)
2037
minsize = self.fileNameText.GetBestSize()
2038
if (not self.configfileargs['gui_stretchwindow'] or
2039
minsize.GetWidth() < self.addwidth):
2040
minsize.SetWidth(self.addwidth)
2041
self.fnsizer.SetMinSize (minsize)
2042
minsize.SetHeight(self.fileSizeText.GetBestSize().GetHeight())
2043
self.fnsizer2.SetMinSize (minsize)
2044
minsize.SetWidth(minsize.GetWidth()+(self.FONT*8))
2045
minsize.SetHeight(self.fileNameText.GetBestSize().GetHeight()+self.fileSizeText.GetBestSize().GetHeight())
2046
minsize.SetHeight(2*self.errorText.GetBestSize().GetHeight())
2047
self.errorTextSizer.SetMinSize(minsize)
2048
self.topboxsizer.SetMinSize(minsize)
2050
# Kludge to make details and about catch the event
2051
self.frame.SetSize ((self.frame.GetSizeTuple()[0]+1, self.frame.GetSizeTuple()[1]+1))
2052
self.frame.SetSize ((self.frame.GetSizeTuple()[0]-1, self.frame.GetSizeTuple()[1]-1))
2053
self.colSizer.Fit(self.frame)
2055
self.frame.Refresh()
2057
def newpath(self, path):
2058
self.invokeLater(self.onNewpath, [path])
2060
def onNewpath(self, path):
2061
self.fileDestText.SetLabel(path)
2063
def pause(self, event):
2064
self.invokeLater(self.onPause)
2070
self.ispaused = False
2071
self.pauseButton.SetLabel('Pause')
2074
if self.dow.Pause():
2075
self.ispaused = True
2076
self.pauseButton.SetLabel('Resume')
2077
self.downRateText.SetLabel(' ')
2078
self.upRateText.SetLabel(' ')
2079
self.setStatusIcon('startup')
2081
def done(self, event):
2084
self.shuttingdown = True
2087
self.frame.tbicon.RemoveIcon()
2091
self.frame.tbicon.Destroy()
2095
self.detailBox.Close()
2097
self.detailBox = None
2099
self.aboutBox.Close()
2101
self.aboutBox = None
2103
self.creditsBox.Close()
2105
self.creditsBox = None
2111
self.statusIconHelpBox.Close()
2113
self.statusIconHelpBox = None
2115
self.frame.RemoveIcon()
2119
self.frame.Destroy()
2122
def exception(self):
2124
print_exc(file = data)
2125
print data.getvalue() # report exception here too
2126
self.on_errorwindow(data.getvalue())
2128
def errorwindow(self, err):
2129
self.invokeLater(self.on_errorwindow,[err])
2131
def on_errorwindow(self, err):
2132
if self._errorwindow is None:
2133
w = wx.Frame(None, -1, 'BITTORRENT ERROR', size = (1,1),
2134
style = wx.DEFAULT_FRAME_STYLE|wx.FULL_REPAINT_ON_RESIZE)
2135
panel = wx.Panel(w, -1)
2137
sizer = wx.FlexGridSizer(cols = 1)
2138
t = ( 'BitTorrent ' + version + '\n' +
2139
'OS: ' + sys.platform + '\n' +
2140
'Python version: ' + sys.version + '\n' +
2141
'wx.Windows version: ' + wx.VERSION_STRING + '\n' )
2143
t += 'Psyco version: ' + hex(psyco.__version__)[2:] + '\n'
2147
t += 'Allocation method: ' + self.config['alloc_type']
2148
if self.dow.storagewrapper.bgalloc_active:
2153
sizer.Add(wx.TextCtrl(panel, -1, t + '\n' + err,
2154
size = (500,300), style = wx.TE_READONLY|wx.TE_MULTILINE))
2156
sizer.Add(wx.StaticText(panel, -1,
2157
'\nHelp us iron out the bugs in the engine!'))
2158
linkMail = wx.StaticText(panel, -1,
2159
'Please report this error to '+report_email)
2160
linkMail.SetFont(wx.Font(self.FONT, wx.DEFAULT, wx.NORMAL, wx.NORMAL, True))
2161
linkMail.SetForegroundColour('Blue')
2165
Thread(target = open_new("mailto:" + report_email
2166
+ "?subject=autobugreport")).start()
2167
wx.EVT_LEFT_DOWN(linkMail, maillink)
2169
border = wx.BoxSizer(wx.HORIZONTAL)
2170
border.Add(sizer, 1, wx.EXPAND | wx.ALL, 4)
2172
panel.SetSizer(border)
2173
panel.SetAutoLayout(True)
2178
self._errorwindow = w
2181
class btWxApp(wx.App):
2182
def __init__(self, x, params):
2183
self.params = params
2184
wx.App.__init__(self, x)
2188
self.configfile = configReader()
2189
d = DownloadInfoFrame(doneflag, self.configfile)
2190
self.SetTopWindow(d.frame)
2191
if len(self.params) == 0:
2192
b = wx.FileDialog (d.frame, 'Choose .torrent file to use',
2193
defaultDir = '', defaultFile = '', wildcard = '*.torrent',
2196
if b.ShowModal() == wx.ID_OK:
2197
self.params.append (b.GetPath())
2199
thread = Thread(target = next, args = [self.params, d, doneflag, self.configfile])
2200
thread.setDaemon(False)
2206
import profile, pstats
2207
p = profile.Profile()
2208
p.runcall(_run, params)
2209
log = open('profile_data_wx.'+strftime('%y%m%d%H%M%S')+'.txt','a')
2210
normalstdout = sys.stdout
2212
# pstats.Stats(p).strip_dirs().sort_stats('cumulative').print_stats()
2213
pstats.Stats(p).strip_dirs().sort_stats('time').print_stats()
2214
sys.stdout = normalstdout
2219
app = btWxApp(0, params)
2222
def next(params, d, doneflag, configfile):
2224
import profile, pstats
2225
p = profile.Profile()
2226
p.runcall(_next, params, d, doneflag, configfile)
2227
log = open('profile_data.'+strftime('%y%m%d%H%M%S')+'.txt','a')
2228
normalstdout = sys.stdout
2230
# pstats.Stats(p).strip_dirs().sort_stats('cumulative').print_stats()
2231
pstats.Stats(p).strip_dirs().sort_stats('time').print_stats()
2232
sys.stdout = normalstdout
2234
_next(params, d, doneflag, configfile)
2236
def _next(params, d, doneflag, configfile):
2241
config = parse_params(params, configfile.config)
2242
except ValueError, e:
2243
d.error('error: ' + str(e) + '\nrun with no args for parameter explanations')
2246
d.displayUsage(get_usage(presets = configfile.config))
2249
myid = createPeerID()
2252
rawserver = RawServer(doneflag, config['timeout_check_interval'],
2253
config['timeout'], ipv6_enable = config['ipv6_enabled'],
2254
failfunc = d.error, errorfunc = d.errorwindow)
2259
listen_port = rawserver.find_and_bind(config['minport'], config['maxport'],
2260
config['bind'], ipv6_socket_style = config['ipv6_binds_v4'],
2261
upnp = upnp_type, randomizer = config['random_port'])
2263
except socketerror, e:
2264
if upnp_type and e == UPnP_ERROR:
2265
d.error('WARNING: COULD NOT FORWARD VIA UPnP')
2268
d.error("Couldn't listen - " + str(e))
2271
d.connection_stats = rawserver.get_stats()
2273
response = get_response(config['responsefile'], config['url'], d.error)
2277
infohash = sha(bencode(response['info'])).digest()
2279
torrentdata = configfile.getTorrentData(infohash)
2281
oldsave = torrentdata.get('saved as')
2282
d.old_ratesettings = torrentdata.get('rate settings')
2283
s = torrentdata.get('stats')
2285
d.old_upload = s['uploaded']
2286
d.old_download = s['downloaded']
2290
dow = BT1Download(d.updateStatus, d.finished, d.error, d.errorwindow, doneflag,
2291
config, response, infohash, myid, rawserver, listen_port,
2292
configfile.getConfigDir())
2295
if config['gui_saveas_ask'] == 1:
2298
if not dow.checkSaveLocation(oldsave):
2301
def choosefile(default, size, saveas, dir, oldsave = oldsave):
2302
d.ChooseFileDone(oldsave, size)
2304
elif config['gui_saveas_ask'] == 0:
2305
def choosefile(default, size, saveas, dir,
2306
spot = config['gui_default_savedir']):
2307
spot = os.path.join(spot,default)
2308
d.ChooseFileDone(spot, size)
2311
choosefile = d.chooseFile
2312
savedas = dow.saveAs(choosefile, d.newpath)
2316
if not dow.initFiles(old_style = True):
2318
if not dow.startEngine():
2321
dow.startRerequester()
2324
if not dow.am_I_finished():
2325
d.updateStatus(activity = 'connecting to peers')
2326
rawserver.listen_forever(dow.getPortHandler())
2329
'rate setting': d.current_ratesetting,
2330
'max download rate': config['max_download_rate']
2332
if d.current_ratesetting != 'automatic':
2333
ratesettings['uploads'] = config['min_uploads']
2334
ratesettings['max upload rate'] = config['max_upload_rate']
2335
up, dn = dow.get_transfer_stats()
2337
'uploaded': up + d.old_upload,
2338
'downloaded': dn + d.old_download
2341
'saved as': savedas,
2342
'rate settings': ratesettings,
2345
dow.shutdown(torrentdata)
2350
print_exc(file = data)
2351
print data.getvalue() # report exception here too
2352
d.errorwindow(data.getvalue())
2354
rawserver.shutdown()
2360
sleep(3600*24*30) # this will make the app stick in the task manager,
2364
if __name__ == '__main__':
2365
if argv[1:] == ['--version']: