import gtk import math class PiecesTabManager(object): def __init__(self, manager): self.vbox = None self.manager = manager self.progress = [] self.tooltips = [] self.eventboxes = [] self.rows = 0 self.columns = 33 self.piece_width = 30 self.piece_height = 20 self.unique_id = -1 self.peer_speed = [] self.piece_info = [] self.all_files = None self.file_priorities = None self.index = 0 self.prev_file_index = -1 self.file_index = 0 self.next_file_index = 1 self.num_files = 0 self.current_first_index = None self.current_last_index = None self.handlers_connected = False def set_unique_id(self, unique_id): self.unique_id = unique_id def clear_pieces_store(self): self.unique_id = -1 self.rows = 0 self.peer_speed = [] self.eventboxes = [] self.progress = [] self.piece_info = [] self.tooltips = [] self.all_files = None self.file_priorities = None self.current_first_index = None self.current_last_index = None self.index = 0 self.num_files = 0 self.prev_file_index = -1 self.file_index = 0 self.next_file_index = 1 if not self.vbox is None: self.vbox.destroy() self.vbox = None def prepare_pieces_store(self): gtk.main_iteration_do(False) viewport = gtk.Viewport() scrolledWindow = gtk.ScrolledWindow() scrolledWindow.add(viewport) scrolledWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.vbox = gtk.VBox() viewport.add(self.vbox) self.all_files = self.manager.get_file_piece_range(self.unique_id) self.file_priorities = self.manager.get_priorities(self.unique_id) state = self.manager.get_torrent_state(self.unique_id) self.num_pieces = state["num_pieces"] for priority in self.file_priorities: self.current_first_index = self.all_files[self.file_index]['first_index'] self.current_last_index = self.all_files[self.file_index]['last_index'] if priority > 0: #if file is being downloaded build the file pieces information self.build_file_pieces() else: #if file is not being downloaded skip the file pieces self.skip_current_file() self.file_index += 1 self.next_file_index += 1 self.prev_file_index += 1 self.get_current_pieces_info() return scrolledWindow def build_file_pieces(self): gtk.main_iteration_do(False) label = gtk.Label() label.set_alignment(0,0) label.set_text(self.all_files[self.file_index]['path']) self.vbox.pack_start(label, expand=False) table = gtk.Table() self.rows = int(math.ceil((self.current_last_index-self.current_first_index)/self.columns)+1) self.vbox.pack_start(table, expand=False) table.resize(self.rows, self.columns) table.set_size_request((self.columns+1)*self.piece_width, (self.rows+1)*self.piece_height) if self.current_last_index != self.current_first_index: #if there is more than one piece self.build_pieces_table(table) only_one_piece = False else: #if file only has one piece self.index = 0 only_one_piece = True self.piece_info.append({'blocks_total':0, 'blocks_finished':0, 'blocks_requested':0}) self.progress.append(gtk.ProgressBar()) self.tooltips.append(gtk.Tooltips()) self.eventboxes.append(gtk.EventBox()) self.peer_speed.append("unknown") main_index = self.current_last_index if self.file_index > 0 and not self.piece_info[main_index] is None and only_one_piece: #if file has only one piece and it is shared destroy the table table.destroy() if self.file_index == 0 or self.piece_info[main_index] is None or not only_one_piece: # piece could be shared if file has only one piece and it's not the first file # only create it if it does not exist self.build_last_file_piece(table, main_index, only_one_piece) def build_pieces_table(self, table): temp_prev_priority = 1 if self.file_index == 0\ or self.current_first_index !=\ self.all_files[self.prev_file_index]['last_index']: #if first piece is not a shared piece temp_range = self.current_last_index-self.current_first_index diff = 0 else: #if first piece is shared temp_prev_priority = self.file_priorities[self.prev_file_index] if temp_prev_priority > 0: #if last file was not skipped, skip the first piece diff = 1 temp_range = self.current_last_index-(self.current_first_index+1) #otherwise keep the first piece else: diff = 0 temp_range = self.current_last_index-self.current_first_index #last piece handled outside of loop, skip it from range for index in xrange(temp_range): gtk.main_iteration_do(False) main_index = diff+self.current_first_index+index if temp_prev_priority > 0: #normal behavior self.piece_info.append({'blocks_total':0, 'blocks_finished':0, 'blocks_requested':0}) self.progress.append(gtk.ProgressBar()) self.tooltips.append(gtk.Tooltips()) self.eventboxes.append(gtk.EventBox()) self.peer_speed.append("unknown") else: #if first piece is shared with a skipped file self.share_skipped_piece(main_index) temp_prev_priority = 1 self.progress[main_index].set_size_request(self.piece_width, self.piece_height) row = index/self.columns column = index%self.columns table.attach(self.eventboxes[main_index], column, column+1, row, row+1, xoptions=0, yoptions=0, xpadding=0, ypadding=0) self.eventboxes[main_index].add(self.progress[main_index]) if self.manager.has_piece(self.unique_id, main_index): #if piece is already finished self.progress[main_index].set_fraction(1) self.tooltips[main_index].set_tip(self.eventboxes[main_index], _("Piece finished")) else: #if piece is not already finished self.tooltips[main_index].set_tip(self.eventboxes[main_index], _("Piece not started")) self.index = temp_range def build_last_file_piece(self, table, main_index, only_one_piece): gtk.main_iteration_do(False) if only_one_piece and self.file_index > 0: #if piece is shared with a skipped file self.share_skipped_piece(main_index) self.progress[main_index].set_size_request(self.piece_width, self.piece_height) if self.next_file_index < len(self.all_files): # if there is another file if self.file_priorities[self.next_file_index]==0\ or self.current_last_index != self.all_files[self.next_file_index]['first_index']: #if next file is skipped or there is no shared piece, keep last piece row=self.index/self.columns column=self.index%self.columns table.attach(self.eventboxes[main_index], column, column+1, row, row+1, xoptions=0, yoptions=0, xpadding=0, ypadding=0) self.eventboxes[main_index].add(self.progress[main_index]) if self.file_priorities[self.next_file_index]>0\ and self.current_last_index == self.all_files[self.next_file_index]['first_index']: #if next file is not skipped and there is a shared piece, do not keep last piece if only_one_piece: #only piece in file is shared, destroy table for file table.destroy() label = gtk.Label() label.set_alignment(0,0) label.set_text(_("Piece shared with next file(s)")) self.vbox.pack_start(label, expand=False) temp_table = gtk.Table() temp_table.resize(1,2) temp_table.set_size_request(self.piece_width, 2*self.piece_height) temp_table.attach(self.eventboxes[main_index], 0, 1, 0, 1, xoptions=0, yoptions=0, xpadding=0, ypadding=0) self.eventboxes[main_index].add(self.progress[main_index]) self.vbox.pack_start(temp_table, expand=False) else: #if there is no other file row=self.index/self.columns column=self.index%self.columns table.attach(self.eventboxes[main_index], column, column+1, row, row+1, xoptions=0, yoptions=0, xpadding=0, ypadding=0) self.eventboxes[main_index].add(self.progress[main_index]) if self.manager.has_piece(self.unique_id, main_index): #if the last piece is already finished self.progress[main_index].set_fraction(1) self.tooltips[main_index].set_tip(self.eventboxes[main_index], _("Piece finished")) else: #if the last piece is not already finished self.tooltips[main_index].set_tip(self.eventboxes[main_index], _("Piece not started")) def share_skipped_piece(self, main_index): self.piece_info[main_index] = {'blocks_total':0, 'blocks_finished':0, 'blocks_requested':0} self.progress[main_index] = gtk.ProgressBar() self.tooltips[main_index] = gtk.Tooltips() self.eventboxes[main_index] = gtk.EventBox() self.peer_speed[main_index] = "unknown" def skip_current_file(self): if self.file_index == 0\ or self.current_first_index !=\ self.all_files[self.prev_file_index]['last_index']: #if first piece is not shared temp_range = 1+self.current_last_index-self.current_first_index else: #if first piece is shared temp_range = self.current_last_index-self.current_first_index for index in xrange(temp_range): self.piece_info.append(None) self.progress.append(None) self.eventboxes.append(None) self.tooltips.append(None) self.peer_speed.append(None) def connect_handlers(self): self.handlers_connected = True self.manager.connect_event(self.manager.constants['EVENT_PIECE_FINISHED'], self.handle_event) self.manager.connect_event(self.manager.constants['EVENT_BLOCK_FINISHED'], self.handle_event) self.manager.connect_event(self.manager.constants['EVENT_BLOCK_DOWNLOADING'], self.handle_event) def disconnect_handlers(self): if self.handlers_connected: self.manager.disconnect_event(self.manager.constants['EVENT_PIECE_FINISHED'], self.handle_event) self.manager.disconnect_event(self.manager.constants['EVENT_BLOCK_FINISHED'], self.handle_event) self.manager.disconnect_event(self.manager.constants['EVENT_BLOCK_DOWNLOADING'], self.handle_event) self.handlers_connected = False def handle_event(self, event): #protect against pieces trying to display after file priority changed #or different torrent selected if event['unique_ID'] == self.unique_id\ and not self.piece_info[event['piece_index']] is None: if event['event_type'] is self.manager.constants['EVENT_PIECE_FINISHED']: self.update_pieces_store(event['piece_index'], piece_finished=True) elif event['event_type'] is self.manager.constants['EVENT_BLOCK_DOWNLOADING']: index = event['piece_index'] if self.piece_info[index]['blocks_total'] == 0: self.piece_info[index] = self.manager.get_piece_info(self.unique_id, index) temp_peer_speed = event['peer_speed'] if temp_peer_speed == "fast": peer_speed_msg = _("fast") elif temp_peer_speed == "slow": peer_speed_msg = _("slow") elif temp_peer_speed == "medium": peer_speed_msg = _("medium") else: peer_speed_msg = _("unknown") self.peer_speed[index] = peer_speed_msg self.update_pieces_store(index) else: index = event['piece_index'] if self.piece_info[index]['blocks_total'] == 0: self.piece_info[index] = self.manager.get_piece_info(self.unique_id, index) else: self.piece_info[index]['blocks_finished'] += 1 self.update_pieces_store(event['piece_index']) def get_current_pieces_info(self): all_piece_info = self.manager.get_all_piece_info(self.unique_id) for piece_index in all_piece_info: index = piece_index['piece_index'] if not self.piece_info[index] is None: temp_piece_info = {'blocks_total':piece_index['blocks_total'], \ 'blocks_finished':piece_index['blocks_finished']} self.piece_info[index] = temp_piece_info blocks_total = str(temp_piece_info['blocks_total']) info_string = str(temp_piece_info['blocks_finished']) + "/" + blocks_total + " " + _("blocks finished") + "\n" \ + _("peer speed: unknown") if self.progress[index].get_fraction() == 0: self.progress[index].set_fraction(0.5) self.tooltips[index].set_tip(self.eventboxes[index], info_string) def update_pieces_store(self, index, piece_finished=False): if piece_finished: self.progress[index].set_fraction(1) self.tooltips[index].set_tip(self.eventboxes[index], _("Piece finished")) else: temp_fraction = self.progress[index].get_fraction() if temp_fraction == 0: self.progress[index].set_fraction(0.5) if temp_fraction != 1: temp_piece_info = self.piece_info[index] blocks_total = str(temp_piece_info['blocks_total']) info_string = str(temp_piece_info['blocks_finished']) + "/" + blocks_total + " " + _("blocks finished") + "\n" \ + _("peer speed: ") + self.peer_speed[index] self.tooltips[index].set_tip(self.eventboxes[index], info_string)