def add_dir_to_channel(self, dirname, recursive=False): post_data = {"torrents_dir": dirname, "recursive": int(recursive)} request_mgr = TriblerRequestManager() request_mgr.perform_request("mychannel/torrents", self.on_torrent_to_channel_added, method='PUT', data=post_data)
def perform_start_download_request(self, uri, anon_download, safe_seeding, destination, selected_files, total_files=0, callback=None): selected_files_uri = "" if len(selected_files) != total_files: # Not all files included selected_files_uri = u'&' + u''.join(u"selected_files[]=%s&" % file for file in selected_files)[:-1] anon_hops = int(self.tribler_settings['download_defaults']['number_hops']) if anon_download else 0 safe_seeding = 1 if safe_seeding else 0 post_data = "uri=%s&anon_hops=%d&safe_seeding=%d&destination=%s%s" % (uri, anon_hops, safe_seeding, destination, selected_files_uri) post_data = post_data.encode('utf-8') # We need to send bytes in the request, not unicode request_mgr = TriblerRequestManager() self.pending_requests[request_mgr.request_id] = request_mgr request_mgr.perform_request("downloads", callback if callback else self.on_download_added, method='PUT', data=post_data) # Save the download location to the GUI settings current_settings = get_gui_setting(self.gui_settings, "recent_download_locations", "") recent_locations = current_settings.split(",") if len(current_settings) > 0 else [] encoded_destination = destination.encode('hex') if encoded_destination in recent_locations: recent_locations.remove(encoded_destination) recent_locations.insert(0, encoded_destination) if len(recent_locations) > 5: recent_locations = recent_locations[:5] self.gui_settings.setValue("recent_download_locations", ','.join(recent_locations))
def add_torrent_to_channel(self, filename): with open(filename, "rb") as torrent_file: torrent_content = b64encode(torrent_file.read()) request_mgr = TriblerRequestManager() request_mgr.perform_request("mychannel/torrents", self.on_torrent_to_channel_added, method='PUT', data={"torrent": torrent_content})
def on_torrents_remove_all_action(self, action): if action == 0: request_mgr = TriblerRequestManager() request_mgr.perform_request("mychannel/torrents", self.on_all_torrents_removed_response, method='DELETE') self.dialog.close_dialog() self.dialog = None
def add_torrent_url_to_channel(self, url): post_data = {"uri": url} request_mgr = TriblerRequestManager() request_mgr.perform_request("mychannel/torrents", self.on_torrent_to_channel_added, method='PUT', data=post_data)
def perform_start_download_request(self, uri, anon_download, safe_seeding, destination, selected_files, total_files=0): selected_files_uri = "" if len(selected_files) != total_files: # Not all files included selected_files_uri = '&' + ''.join( u"selected_files[]=%s&" % file for file in selected_files)[:-1].encode('utf-8') anon_hops = int(self.tribler_settings['Tribler'] ['default_number_hops']) if anon_download else 0 safe_seeding = 1 if safe_seeding else 0 post_data = str( "uri=%s&anon_hops=%d&safe_seeding=%d&destination=%s%s" % (uri, anon_hops, safe_seeding, destination, selected_files_uri)) request_mgr = TriblerRequestManager() self.pending_requests[request_mgr.request_id] = request_mgr request_mgr.perform_request("downloads", self.on_download_added, method='PUT', data=post_data)
def add_torrent_to_channel(self, filename): with open(filename, "rb") as torrent_file: torrent_content = urllib.quote_plus(base64.b64encode(torrent_file.read())) editchannel_request_mgr = TriblerRequestManager() editchannel_request_mgr.perform_request("channels/discovered/%s/torrents" % self.channel_overview['identifier'], self.on_torrent_to_channel_added, method='PUT', data='torrent=%s' % torrent_content)
def add_dir_to_channel(self, dirname, recursive=False): post_data = { "torrents_dir": dirname, "recursive": int(recursive) } request_mgr = TriblerRequestManager() request_mgr.perform_request("mychannel/torrents", self.on_torrent_to_channel_added, method='PUT', data=post_data)
def perform_start_download_request(self, uri, anon_download, safe_seeding, destination, selected_files, total_files=0, callback=None): # Check if destination directory is writable is_writable, error = is_dir_writable(destination) if not is_writable: gui_error_message = "Insufficient write permissions to <i>%s</i> directory. Please add proper " \ "write permissions on the directory and add the torrent again. %s" \ % (destination, error) ConfirmationDialog.show_message(self.window(), "Download error <i>%s</i>" % uri, gui_error_message, "OK") return selected_files_uri = "" if len(selected_files) != total_files: # Not all files included selected_files_uri = u'&' + u''.join( u"selected_files[]=%s&" % quote_plus_unicode(filename) for filename in selected_files)[:-1] anon_hops = int(self.tribler_settings['download_defaults'] ['number_hops']) if anon_download else 0 safe_seeding = 1 if safe_seeding else 0 post_data = "uri=%s&anon_hops=%d&safe_seeding=%d&destination=%s%s" % ( quote_plus_unicode(uri), anon_hops, safe_seeding, destination, selected_files_uri) post_data = post_data.encode( 'utf-8') # We need to send bytes in the request, not unicode request_mgr = TriblerRequestManager() request_mgr.perform_request( "downloads", callback if callback else self.on_download_added, method='PUT', data=post_data) # Save the download location to the GUI settings current_settings = get_gui_setting(self.gui_settings, "recent_download_locations", "") recent_locations = current_settings.split( ",") if len(current_settings) > 0 else [] if isinstance(destination, unicode): destination = destination.encode('utf-8') encoded_destination = destination.encode('hex') if encoded_destination in recent_locations: recent_locations.remove(encoded_destination) recent_locations.insert(0, encoded_destination) if len(recent_locations) > 5: recent_locations = recent_locations[:5] self.gui_settings.setValue("recent_download_locations", ','.join(recent_locations))
class SubscribedChannelsPage(QWidget): """ This page shows all the channels that the user has subscribed to. """ def __init__(self): QWidget.__init__(self) self.dialog = None self.request_mgr = None def initialize(self): self.window().add_subscription_button.clicked.connect(self.on_add_subscription_clicked) def load_subscribed_channels(self): self.window().subscribed_channels_list.set_data_items([(LoadingListItem, None)]) self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("channels/subscribed", self.received_subscribed_channels) def received_subscribed_channels(self, results): if not results: return self.window().subscribed_channels_list.set_data_items([]) items = [] if len(results['subscribed']) == 0: self.window().subscribed_channels_list.set_data_items( [(LoadingListItem, "You are not subscribed to any channel.")]) return for result in results['subscribed']: items.append((ChannelListItem, result)) self.window().subscribed_channels_list.set_data_items(items) def on_add_subscription_clicked(self): self.dialog = ConfirmationDialog(self, "Add subscribed channel", "Please enter the identifier of the channel you want to subscribe to below. " "It can take up to a minute before the channel is visible in your list of " "subscribed channels.", [('ADD', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)], show_input=True) self.dialog.dialog_widget.dialog_input.setPlaceholderText('Channel identifier') self.dialog.button_clicked.connect(self.on_subscription_added) self.dialog.show() def on_subscription_added(self, action): if action == 0: self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("channels/subscribed/%s" % self.dialog.dialog_widget.dialog_input.text(), self.on_channel_subscribed, method='PUT') self.dialog.close_dialog() self.dialog = None def on_channel_subscribed(self, _): pass
def perform_start_download_request(self, uri, anon_download, safe_seeding, destination, selected_files, total_files=0, callback=None): # Check if destination directory is writable is_writable, error = is_dir_writable(destination) if not is_writable: gui_error_message = "Insufficient write permissions to <i>%s</i> directory. Please add proper " \ "write permissions on the directory and add the torrent again. %s" \ % (destination, error) ConfirmationDialog.show_message(self.window(), "Download error <i>%s</i>" % uri, gui_error_message, "OK") return selected_files_list = [] if len(selected_files) != total_files: # Not all files included selected_files_list = [filename for filename in selected_files] anon_hops = int(self.tribler_settings['download_defaults'] ['number_hops']) if anon_download else 0 safe_seeding = 1 if safe_seeding else 0 post_data = { "uri": uri, "anon_hops": anon_hops, "safe_seeding": safe_seeding, "destination": destination, "selected_files": selected_files_list } request_mgr = TriblerRequestManager() request_mgr.perform_request( "downloads", callback if callback else self.on_download_added, method='PUT', data=post_data) # Save the download location to the GUI settings current_settings = get_gui_setting(self.gui_settings, "recent_download_locations", "") recent_locations = current_settings.split( ",") if len(current_settings) > 0 else [] if isinstance(destination, six.text_type): destination = destination.encode('utf-8') encoded_destination = hexlify(destination) if encoded_destination in recent_locations: recent_locations.remove(encoded_destination) recent_locations.insert(0, encoded_destination) if len(recent_locations) > 5: recent_locations = recent_locations[:5] self.gui_settings.setValue("recent_download_locations", ','.join(recent_locations))
def on_commit_control_clicked(self, index): infohash = index.model().data_items[index.row()][u'infohash'] status = index.model().data_items[index.row()][u'status'] new_status = COMMIT_STATUS_COMMITTED if status == COMMIT_STATUS_NEW or status == COMMIT_STATUS_COMMITTED: new_status = COMMIT_STATUS_TODELETE request_mgr = TriblerRequestManager() request_mgr.perform_request("mychannel/torrents/%s" % infohash, lambda response: self.on_torrent_status_updated(response, index), data={"status": new_status}, method='PATCH')
def on_torrents_remove_all_action(self, action): if action == 0: for torrent_ind in xrange(self.window().edit_channel_torrents_list.count()): torrent_data = self.window().edit_channel_torrents_list.item(torrent_ind).data(Qt.UserRole) request_mgr = TriblerRequestManager() request_mgr.perform_request("channels/discovered/%s/torrents/%s" % (self.channel_overview["identifier"], torrent_data['infohash']), None, method='DELETE') self.remove_torrent_requests.append(request_mgr) self.window().edit_channel_torrents_list.set_data_items([]) self.dialog.close_dialog() self.dialog = None
class DiscoveredPage(QWidget): """ The DiscoveredPage shows an overview of all discovered channels in Tribler. """ def __init__(self): QWidget.__init__(self) self.discovered_channels = [] self.request_mgr = None self.initialized = False def initialize_discovered_page(self): if not self.initialized: self.window( ).core_manager.events_manager.discovered_channel.connect( self.on_discovered_channel) self.initialized = True def load_discovered_channels(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("channels/discovered", self.received_discovered_channels) def received_discovered_channels(self, results): if not results or 'channels' not in results: return self.discovered_channels = [] self.window().discovered_channels_list.set_data_items([]) items = [] results['channels'].sort(key=lambda x: x['torrents'], reverse=True) for result in results['channels']: items.append((ChannelListItem, result)) self.discovered_channels.append(result) self.update_num_label() self.window().discovered_channels_list.set_data_items(items) def on_discovered_channel(self, channel_info): channel_info['torrents'] = 0 channel_info['subscribed'] = False channel_info['votes'] = 0 self.window().discovered_channels_list.append_item( (ChannelListItem, channel_info)) self.discovered_channels.append(channel_info) self.update_num_label() def update_num_label(self): self.window().num_discovered_channels_label.setText( "%d items" % len(self.discovered_channels))
def on_save_clicked(self): self.requests_done = 0 self.pending_requests = [] for torrent in self.torrents_to_create: request = TriblerRequestManager() request.perform_request("channels/discovered/%s/playlists/%s/%s" % (self.channel_info["identifier"], self.playlist_info['id'], torrent['infohash']), self.on_request_done, method="PUT") self.pending_requests.append(request) for torrent in self.torrents_to_remove: request = TriblerRequestManager() request.perform_request("channels/discovered/%s/playlists/%s/%s" % (self.channel_info["identifier"], self.playlist_info['id'], torrent['infohash']), self.on_request_done, method="DELETE") self.pending_requests.append(request)
def on_subscribe_control_clicked(self, index): item = index.model().data_items[index.row()] # skip LEGACY entries, regular torrents and personal channel if (u'subscribed' not in item or item[u'status'] == 1000 or item[u'my_channel']): return status = int(item[u'subscribed']) public_key = item[u'public_key'] request_mgr = TriblerRequestManager() request_mgr.perform_request("metadata/channels/%s" % public_key, (lambda _: self.on_unsubscribed_channel.emit(index)) if status else (lambda _: self.on_subscribed_channel.emit(index)), data={"subscribe": int(not status)}, method='POST') index.model().data_items[index.row()][u'subscribed'] = int(not status)
def on_torrents_remove_all_action(self, action): if action == 0: for torrent_ind in xrange(self.window().edit_channel_torrents_list.count()): torrent_data = self.window().edit_channel_torrents_list.item(torrent_ind).data(Qt.UserRole) request_mgr = TriblerRequestManager() request_mgr.perform_request("channels/discovered/%s/torrents/%s" % (self.channel_overview["identifier"], torrent_data['infohash']), None, method='DELETE') self.remove_torrent_requests.append(request_mgr) self.window().edit_channel_torrents_list.set_data_items([]) if "chant" in self.channel_overview: self.load_channel_torrents() self.dialog.close_dialog() self.dialog = None
def on_torrents_remove_selected_action(self, action, items): if action == 0: items = [str(item) for item in items] infohashes = ",".join(items) post_data = { "infohashes": infohashes, "status": COMMIT_STATUS_TODELETE } request_mgr = TriblerRequestManager() request_mgr.perform_request("mychannel/torrents", lambda response: self.on_torrents_removed_response(response, items), data=post_data, method='POST') if self.dialog: self.dialog.close_dialog() self.dialog = None
class DiscoveredPage(QWidget): """ The DiscoveredPage shows an overview of all discovered channels in Tribler. """ def __init__(self): QWidget.__init__(self) self.discovered_channels = [] self.request_mgr = None self.initialized = False def initialize_discovered_page(self): if not self.initialized: self.window().core_manager.events_manager.discovered_channel.connect(self.on_discovered_channel) self.initialized = True def load_discovered_channels(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("channels/discovered", self.received_discovered_channels) def received_discovered_channels(self, results): if not results or 'channels' not in results: return self.discovered_channels = [] self.window().discovered_channels_list.set_data_items([]) items = [] results['channels'].sort(key=lambda x: x['torrents'], reverse=True) for result in results['channels']: items.append((ChannelListItem, result)) self.discovered_channels.append(result) self.update_num_label() self.window().discovered_channels_list.set_data_items(items) def on_discovered_channel(self, channel_info): channel_info['torrents'] = 0 channel_info['subscribed'] = False channel_info['votes'] = 0 self.window().discovered_channels_list.append_item((ChannelListItem, channel_info)) self.discovered_channels.append(channel_info) self.update_num_label() def update_num_label(self): self.window().num_discovered_channels_label.setText("%d items" % len(self.discovered_channels))
def perform_start_download_request(self, uri, anon_download, safe_seeding, destination, selected_files, total_files=0, callback=None): # Check if destination directory is writable is_writable, error = is_dir_writable(destination) if not is_writable: gui_error_message = "Insufficient write permissions to <i>%s</i> directory. Please add proper " \ "write permissions on the directory and add the torrent again. %s" \ % (destination, error) ConfirmationDialog.show_message(self.window(), "Download error <i>%s</i>" % uri, gui_error_message, "OK") return selected_files_list = [] if len(selected_files) != total_files: # Not all files included selected_files_list = [filename for filename in selected_files] anon_hops = int(self.tribler_settings['download_defaults']['number_hops']) if anon_download else 0 safe_seeding = 1 if safe_seeding else 0 post_data = { "uri": uri, "anon_hops": anon_hops, "safe_seeding": safe_seeding, "destination": destination, "selected_files": selected_files_list } request_mgr = TriblerRequestManager() request_mgr.perform_request("downloads", callback if callback else self.on_download_added, method='PUT', data=post_data) # Save the download location to the GUI settings current_settings = get_gui_setting(self.gui_settings, "recent_download_locations", "") recent_locations = current_settings.split(",") if len(current_settings) > 0 else [] if isinstance(destination, six.text_type): destination = destination.encode('utf-8') encoded_destination = hexlify(destination) if encoded_destination in recent_locations: recent_locations.remove(encoded_destination) recent_locations.insert(0, encoded_destination) if len(recent_locations) > 5: recent_locations = recent_locations[:5] self.gui_settings.setValue("recent_download_locations", ','.join(recent_locations))
def on_torrents_remove_selected_action(self, action, items): if action == 0: items = [str(item) for item in items] infohashes = ",".join(items) post_data = { "infohashes": infohashes, "status": COMMIT_STATUS_TODELETE } request_mgr = TriblerRequestManager() request_mgr.perform_request( "mychannel/torrents", lambda response: self.on_torrents_removed_response( response, items), data=post_data, method='POST') if self.dialog: self.dialog.close_dialog() self.dialog = None
class MyTorrentsTableViewController(TorrentsTableViewController): """ This class manages the list with the torrents in your own channel. """ def __init__(self, *args, **kwargs): super(MyTorrentsTableViewController, self).__init__(*args, **kwargs) self.model.row_edited.connect(self._on_row_edited) def _on_row_edited(self, index, new_value): infohash = self.model.data_items[index.row()][u'infohash'] attribute_name = self.model.columns[index.column()] attribute_name = u'tags' if attribute_name == u'category' else attribute_name attribute_name = u'title' if attribute_name == u'name' else attribute_name self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("mychannel/torrents/%s" % infohash, self._on_row_update_results, method='PATCH', data={attribute_name: new_value}) def _on_row_update_results(self, response): if response: self.table_view.window( ).edit_channel_page.channel_dirty = response['dirty'] self.table_view.window( ).edit_channel_page.update_channel_commit_views() def perform_query(self, **kwargs): kwargs.update({ "rest_endpoint_url": "mychannel/torrents", "exclude_deleted": self.model.exclude_deleted }) super(MyTorrentsTableViewController, self).perform_query(**kwargs) def on_query_results(self, response): if super(MyTorrentsTableViewController, self).on_query_results(response): self.table_view.window( ).edit_channel_page.channel_dirty = response['dirty'] self.table_view.window( ).edit_channel_page.update_channel_commit_views()
def perform_start_download_request(self, uri, anon_download, safe_seeding, destination, selected_files, total_files=0, callback=None): # Check if destination directory is writable is_writable, error = is_dir_writable(destination) if not is_writable: gui_error_message = "Insufficient write permissions to <i>%s</i> directory. Please add proper " \ "write permissions on the directory and add the torrent again. %s" \ % (destination, error) ConfirmationDialog.show_message(self.window(), "Download error <i>%s</i>" % uri, gui_error_message, "OK") return selected_files_uri = "" if len(selected_files) != total_files: # Not all files included selected_files_uri = u'&' + u''.join(u"selected_files[]=%s&" % quote_plus_unicode(filename) for filename in selected_files)[:-1] anon_hops = int(self.tribler_settings['download_defaults']['number_hops']) if anon_download else 0 safe_seeding = 1 if safe_seeding else 0 post_data = "uri=%s&anon_hops=%d&safe_seeding=%d&destination=%s%s" % (quote_plus_unicode(uri), anon_hops, safe_seeding, destination, selected_files_uri) post_data = post_data.encode('utf-8') # We need to send bytes in the request, not unicode request_mgr = TriblerRequestManager() request_mgr.perform_request("downloads", callback if callback else self.on_download_added, method='PUT', data=post_data) # Save the download location to the GUI settings current_settings = get_gui_setting(self.gui_settings, "recent_download_locations", "") recent_locations = current_settings.split(",") if len(current_settings) > 0 else [] if isinstance(destination, unicode): destination = destination.encode('utf-8') encoded_destination = destination.encode('hex') if encoded_destination in recent_locations: recent_locations.remove(encoded_destination) recent_locations.insert(0, encoded_destination) if len(recent_locations) > 5: recent_locations = recent_locations[:5] self.gui_settings.setValue("recent_download_locations", ','.join(recent_locations))
class MyTorrentsTableViewController(TorrentsTableViewController): """ This class manages the list with the torrents in your own channel. """ def __init__(self, *args, **kwargs): super(MyTorrentsTableViewController, self).__init__(*args, **kwargs) self.model.row_edited.connect(self._on_row_edited) def _on_row_edited(self, index, new_value): infohash = self.model.data_items[index.row()][u'infohash'] attribute_name = self.model.columns[index.column()] attribute_name = u'tags' if attribute_name == u'category' else attribute_name attribute_name = u'title' if attribute_name == u'name' else attribute_name self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request( "mychannel/torrents/%s" % infohash, self._on_row_update_results, method='PATCH', data={attribute_name: new_value}) def _on_row_update_results(self, response): if response: self.table_view.window().edit_channel_page.channel_dirty = response['dirty'] self.table_view.window().edit_channel_page.update_channel_commit_views() def perform_query(self, **kwargs): kwargs.update({ "rest_endpoint_url": "mychannel/torrents", "exclude_deleted": self.model.exclude_deleted}) super(MyTorrentsTableViewController, self).perform_query(**kwargs) def on_query_results(self, response): if super(MyTorrentsTableViewController, self).on_query_results(response): self.table_view.window().edit_channel_page.channel_dirty = response['dirty'] self.table_view.window().edit_channel_page.update_channel_commit_views()
def on_delete_button_clicked(self, index): request_mgr = TriblerRequestManager() request_mgr.perform_request("mychannel/torrents/%s" % index.model().data_items[index.row()][u'infohash'], lambda response: self.on_torrent_status_updated(response, index), data={"status" : COMMIT_STATUS_TODELETE}, method='PATCH')
class TorrentDetailsTabWidget(QTabWidget): """ The TorrentDetailsTabWidget is the tab that provides details about a specific selected torrent. This information includes the generic info about the torrent, files and trackers. """ def __init__(self, parent): QTabWidget.__init__(self, parent) self.torrent_info = None self._logger = logging.getLogger("TriberGUI") self.torrent_detail_name_label = None self.torrent_detail_category_label = None self.torrent_detail_size_label = None self.torrent_detail_health_label = None self.torrent_detail_files_list = None self.torrent_detail_trackers_list = None self.check_health_button = None self.request_mgr = None self.health_request_mgr = None self.is_health_checking = False self.last_health_check_ts = -1 def initialize_details_widget(self): """ Initialize the details widget. We need to manually assign these attributes since we're dynamically loading this view (using uic.loadUI). """ self.torrent_detail_name_label = self.findChild(QLabel, "torrent_detail_name_label") self.torrent_detail_category_label = self.findChild(QLabel, "torrent_detail_category_label") self.torrent_detail_size_label = self.findChild(QLabel, "torrent_detail_size_label") self.torrent_detail_health_label = self.findChild(QLabel, "torrent_detail_health_label") self.torrent_detail_files_list = self.findChild(QTreeWidget, "torrent_detail_files_list") self.torrent_detail_trackers_list = self.findChild(QTreeWidget, "torrent_detail_trackers_list") self.setCurrentIndex(0) self.check_health_button = self.findChild(EllipseButton, "check_health_button") self.check_health_button.clicked.connect(lambda: self.on_check_health_clicked(timeout=15)) def on_torrent_info(self, torrent_info): if not torrent_info: return self.setTabEnabled(1, True) self.setTabEnabled(2, True) self.torrent_detail_files_list.clear() self.torrent_detail_trackers_list.clear() for file_info in torrent_info["files"]: item = QTreeWidgetItem(self.torrent_detail_files_list) item.setText(0, file_info["path"]) item.setText(1, format_size(float(file_info["size"]))) for tracker in torrent_info["trackers"]: if tracker == 'DHT': continue item = QTreeWidgetItem(self.torrent_detail_trackers_list) item.setText(0, tracker) if torrent_info["num_seeders"] > 0: self.torrent_detail_health_label.setText("good health (S%d L%d)" % (torrent_info["num_seeders"], torrent_info["num_leechers"])) elif torrent_info["num_leechers"] > 0: self.torrent_detail_health_label.setText("unknown health (found peers)") else: self.torrent_detail_health_label.setText("no peers found") def update_with_torrent(self, torrent_info): self.torrent_info = torrent_info self.is_health_checking = False self.torrent_detail_name_label.setText(self.torrent_info["name"]) if self.torrent_info["category"]: self.torrent_detail_category_label.setText(self.torrent_info["category"].lower()) else: self.torrent_detail_category_label.setText("unknown") if self.torrent_info["size"] is None: self.torrent_detail_size_label.setText("Size: -") else: self.torrent_detail_size_label.setText("%s" % format_size(float(self.torrent_info["size"]))) if self.torrent_info["num_seeders"] > 0: self.torrent_detail_health_label.setText("good health (S%d L%d)" % (self.torrent_info["num_seeders"], self.torrent_info["num_leechers"])) elif self.torrent_info["num_leechers"] > 0: self.torrent_detail_health_label.setText("unknown health (found peers)") else: self.torrent_detail_health_label.setText("no peers found") self.setCurrentIndex(0) self.setTabEnabled(1, False) self.setTabEnabled(2, False) self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("torrents/%s" % self.torrent_info["infohash"], self.on_torrent_info) def on_check_health_clicked(self, timeout=15): if self.is_health_checking and (time.time() - self.last_health_check_ts < timeout): return self.is_health_checking = True self.torrent_detail_health_label.setText("Checking...") self.last_health_check_ts = time.time() self.health_request_mgr = TriblerRequestManager() self.health_request_mgr.perform_request("torrents/%s/health?timeout=%s&refresh=%d" % (self.torrent_info["infohash"], timeout, 1), self.on_health_response, capture_errors=False, priority="LOW", on_cancel=self.on_cancel_health_check) def on_health_response(self, response): if not response: return total_seeders = 0 total_leechers = 0 if not response or 'error' in response: self.update_health(0, 0) # Just set the health to 0 seeders, 0 leechers return for _, status in response['health'].iteritems(): if 'error' in status: continue # Timeout or invalid status total_seeders += int(status['seeders']) total_leechers += int(status['leechers']) self.is_health_checking = False self.update_health(total_seeders, total_leechers) def update_health(self, seeders, leechers): try: if seeders > 0: self.torrent_detail_health_label.setText("good health (S%d L%d)" % (seeders, leechers)) elif leechers > 0: self.torrent_detail_health_label.setText("unknown health (found peers)") else: self.torrent_detail_health_label.setText("no peers found") except RuntimeError: self._logger.error("The underlying GUI widget has already been removed.") def on_cancel_health_check(self): self.is_health_checking = False
class EditChannelPage(QWidget): """ This class is responsible for managing lists and data on your channel page """ on_torrents_removed = pyqtSignal(list) on_all_torrents_removed = pyqtSignal() on_commit = pyqtSignal() def __init__(self): QWidget.__init__(self) self.channel_overview = None self.chosen_dir = None self.dialog = None self.editchannel_request_mgr = None self.model = None self.controller = None self.channel_dirty = False self.gui_settings = None self.commit_timer = None self.autocommit_enabled = None def initialize_edit_channel_page(self, gui_settings): self.gui_settings = gui_settings self.window().create_channel_intro_button.clicked.connect( self.on_create_channel_intro_button_clicked) self.window().create_channel_form.hide() self.update_channel_commit_views() self.window().edit_channel_stacked_widget.setCurrentIndex(1) self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_OVERVIEW) self.window().create_channel_button.clicked.connect( self.on_create_channel_button_pressed) self.window().edit_channel_save_button.clicked.connect( self.on_edit_channel_save_button_pressed) self.window().edit_channel_commit_button.clicked.connect( self.clicked_edit_channel_commit_button) # Tab bar buttons self.window().channel_settings_tab.initialize() self.window().channel_settings_tab.clicked_tab_button.connect( self.clicked_tab_button) self.window().export_channel_button.clicked.connect( self.on_export_mdblob) # TODO: re-enable remove_selected button self.window().remove_selected_button.setHidden(True) # Connect torrent addition/removal buttons self.window().remove_selected_button.clicked.connect( self.on_torrents_remove_selected_clicked) self.window().remove_all_button.clicked.connect( self.on_torrents_remove_all_clicked) self.window().add_button.clicked.connect(self.on_torrents_add_clicked) self.model = MyTorrentsContentModel() self.controller = MyTorrentsTableViewController( self.model, self.window().edit_channel_torrents_container.content_table, self.window().edit_channel_torrents_container.details_container, self.window().edit_channel_torrents_num_items_label, self.window().edit_channel_torrents_filter) self.window().edit_channel_torrents_container.details_container.hide() self.autocommit_enabled = get_gui_setting( self.gui_settings, "autocommit_enabled", True, is_bool=True) if self.gui_settings else True # Commit the channel just in case there are uncommitted changes left since the last time (e.g. Tribler crashed) # The timer thing here is a workaround for race condition with the core startup if self.autocommit_enabled: if not self.commit_timer: self.commit_timer = QTimer() self.commit_timer.setSingleShot(True) self.commit_timer.timeout.connect(self.autocommit_fired) self.controller.table_view.setColumnHidden(3, True) self.model.exclude_deleted = True self.commit_timer.stop() self.commit_timer.start(10000) else: self.controller.table_view.setColumnHidden(4, True) self.model.exclude_deleted = False def update_channel_commit_views(self, deleted_index=None): if self.channel_dirty and self.autocommit_enabled: self.commit_timer.stop() self.commit_timer.start(CHANNEL_COMMIT_DELAY) if deleted_index: # TODO: instead of reloading the whole table, just remove the deleted row and update start and end self.load_my_torrents() self.window().commit_control_bar.setHidden(not self.channel_dirty or self.autocommit_enabled) def load_my_channel_overview(self): if not self.channel_overview: self.window().edit_channel_stacked_widget.setCurrentIndex(2) self.editchannel_request_mgr = TriblerRequestManager() self.editchannel_request_mgr.perform_request( "mychannel", self.initialize_with_channel_overview, capture_errors=False) def initialize_with_channel_overview(self, overview): if not overview: return if 'error' in overview: self.window().edit_channel_stacked_widget.setCurrentIndex(0) return self.channel_overview = overview["mychannel"] self.channel_dirty = self.channel_overview['dirty'] self.update_channel_commit_views() self.window().export_channel_button.setHidden(False) self.window().edit_channel_name_label.setText("My channel") self.window().edit_channel_overview_name_label.setText( self.channel_overview["name"]) self.window().edit_channel_description_label.setText( self.channel_overview["description"]) self.window().edit_channel_identifier_label.setText( self.channel_overview["public_key"]) self.window().edit_channel_name_edit.setText( self.channel_overview["name"]) self.window().edit_channel_description_edit.setText( self.channel_overview["description"]) self.window().edit_channel_stacked_widget.setCurrentIndex(1) self.model.channel_pk = self.channel_overview["public_key"] def on_create_channel_button_pressed(self): channel_name = self.window().new_channel_name_edit.text() channel_description = self.window( ).new_channel_description_edit.toPlainText() if len(channel_name) == 0: self.window().new_channel_name_label.setStyleSheet("color: red;") return post_data = {"name": channel_name, "description": channel_description} self.editchannel_request_mgr = TriblerRequestManager() self.editchannel_request_mgr.perform_request("mychannel", self.on_channel_created, data=post_data, method='PUT') def on_channel_created(self, result): if not result: return if u'added' in result: self.window().create_channel_button.setEnabled(True) self.load_my_channel_overview() def on_edit_channel_save_button_pressed(self): channel_name = self.window().edit_channel_name_edit.text() channel_description = self.window( ).edit_channel_description_edit.toPlainText() post_data = {"name": channel_name, "description": channel_description} self.editchannel_request_mgr = TriblerRequestManager() self.editchannel_request_mgr.perform_request("mychannel", self.on_channel_edited, data=post_data, method='POST') def on_channel_edited(self, result): if not result: return if 'modified' in result: self.window().edit_channel_name_label.setText( self.window().edit_channel_name_edit.text()) self.window().edit_channel_description_label.setText( self.window().edit_channel_description_edit.toPlainText()) def clicked_tab_button(self, tab_button_name): if tab_button_name == "edit_channel_overview_button": self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_OVERVIEW) elif tab_button_name == "edit_channel_settings_button": self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_SETTINGS) elif tab_button_name == "edit_channel_torrents_button": self.load_my_torrents() self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_TORRENTS) def load_my_torrents(self): self.controller.model.reset() self.controller.perform_query(first=1, last=50) # Load the first 50 torrents def on_create_channel_intro_button_clicked(self): self.window().create_channel_form.show() self.window().create_channel_intro_button_container.hide() self.window().create_new_channel_intro_label.setText( "Please enter your channel details below.") def on_export_mdblob(self): export_dir = QFileDialog.getExistingDirectory( self, "Please select the destination directory", "", QFileDialog.ShowDirsOnly) if len(export_dir) == 0: return # Show confirmation dialog where we specify the name of the file mdblob_name = self.channel_overview["public_key"] dialog = ConfirmationDialog( self, "Export mdblob file", "Please enter the name of the channel metadata file:", [('SAVE', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)], show_input=True) def on_export_download_dialog_done(action): if action == 0: dest_path = os.path.join( export_dir, dialog.dialog_widget.dialog_input.text()) request_mgr = TriblerRequestManager() request_mgr.download_file( "channels/discovered/%s/mdblob" % mdblob_name, lambda data: on_export_download_request_done( dest_path, data)) dialog.close_dialog() def on_export_download_request_done(dest_path, data): try: torrent_file = open(dest_path, "wb") torrent_file.write(data) torrent_file.close() except IOError as exc: ConfirmationDialog.show_error( self.window(), "Error when exporting file", "An error occurred when exporting the torrent file: %s" % str(exc)) else: self.window().tray_show_message( "Torrent file exported", "Torrent file exported to %s" % dest_path) dialog.dialog_widget.dialog_input.setPlaceholderText( 'Channel file name') dialog.dialog_widget.dialog_input.setText("%s.mdblob" % mdblob_name) dialog.dialog_widget.dialog_input.setFocus() dialog.button_clicked.connect(on_export_download_dialog_done) dialog.show() # Torrent removal-related methods def on_torrents_remove_selected_clicked(self): selected_items = self.controller.table_view.selectedIndexes() num_selected = len(selected_items) if num_selected == 0: return selected_infohashes = [ self.model.data_items[row][u'infohash'] for row in set([index.row() for index in selected_items]) ] self.dialog = ConfirmationDialog( self, "Remove %s selected torrents" % len(selected_infohashes), "Are you sure that you want to remove %s selected torrents " "from your channel?" % len(selected_infohashes), [('CONFIRM', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)]) self.dialog.button_clicked.connect( lambda action: self.on_torrents_remove_selected_action( action, selected_infohashes)) self.dialog.show() def on_torrents_remove_selected_action(self, action, items): if action == 0: items = [str(item) for item in items] infohashes = ",".join(items) post_data = { "infohashes": infohashes, "status": COMMIT_STATUS_TODELETE } request_mgr = TriblerRequestManager() request_mgr.perform_request( "mychannel/torrents", lambda response: self.on_torrents_removed_response( response, items), data=post_data, method='POST') if self.dialog: self.dialog.close_dialog() self.dialog = None def on_torrents_removed_response(self, json_result, infohashes): if not json_result: return if 'success' in json_result and json_result['success']: self.on_torrents_removed.emit(infohashes) self.load_my_torrents() def on_torrents_remove_all_clicked(self): self.dialog = ConfirmationDialog( self.window(), "Remove all torrents", "Are you sure that you want to remove all torrents from your channel?", [('CONFIRM', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)]) self.dialog.button_clicked.connect(self.on_torrents_remove_all_action) self.dialog.show() def on_torrents_remove_all_action(self, action): if action == 0: request_mgr = TriblerRequestManager() request_mgr.perform_request("mychannel/torrents", self.on_all_torrents_removed_response, method='DELETE') self.dialog.close_dialog() self.dialog = None def on_all_torrents_removed_response(self, json_result): if not json_result: return if 'success' in json_result and json_result['success']: self.on_all_torrents_removed.emit() self.load_my_torrents() # Torrent addition-related methods def on_add_torrents_browse_dir(self): chosen_dir = QFileDialog.getExistingDirectory( self, "Please select the directory containing the .torrent files", QDir.homePath(), QFileDialog.ShowDirsOnly) if not chosen_dir: return self.chosen_dir = chosen_dir self.dialog = ConfirmationDialog( self, "Add torrents from directory", "Add all torrent files from the following directory " "to your Tribler channel:\n\n%s" % chosen_dir, [('ADD', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)], checkbox_text="Include subdirectories (recursive mode)") self.dialog.button_clicked.connect( self.on_confirm_add_directory_dialog) self.dialog.show() def on_confirm_add_directory_dialog(self, action): if action == 0: self.add_dir_to_channel(self.chosen_dir, recursive=self.dialog.checkbox.isChecked()) if self.dialog: self.dialog.close_dialog() self.dialog = None self.chosen_dir = None def on_torrents_add_clicked(self): menu = TriblerActionMenu(self) browse_files_action = QAction('Import torrent from file', self) browse_dir_action = QAction('Import torrent(s) from dir', self) add_url_action = QAction('Add torrent from URL/magnet link', self) create_torrent_action = QAction('Create torrent from file(s)', self) browse_files_action.triggered.connect(self.on_add_torrent_browse_file) browse_dir_action.triggered.connect(self.on_add_torrents_browse_dir) add_url_action.triggered.connect(self.on_add_torrent_from_url) create_torrent_action.triggered.connect( self.on_create_torrent_from_files) menu.addAction(browse_files_action) menu.addAction(browse_dir_action) menu.addAction(add_url_action) menu.addAction(create_torrent_action) menu.exec_(QCursor.pos()) def on_create_torrent_from_files(self): self.window().edit_channel_details_create_torrent.initialize() self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_CREATE_TORRENT) def on_add_torrent_browse_file(self): filenames = QFileDialog.getOpenFileNames( self, "Please select the .torrent file", "", "Torrent files (*.torrent)") if not filenames[0]: return for filename in filenames[0]: self.add_torrent_to_channel(filename) def on_add_torrent_from_url(self): self.dialog = ConfirmationDialog( self, "Add torrent from URL/magnet link", "Please enter the URL/magnet link in the field below:", [('ADD', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)], show_input=True) self.dialog.dialog_widget.dialog_input.setPlaceholderText( 'URL/magnet link') self.dialog.button_clicked.connect( self.on_torrent_from_url_dialog_done) self.dialog.show() def on_torrent_from_url_dialog_done(self, action): if action == 0: self.add_torrent_url_to_channel( self.dialog.dialog_widget.dialog_input.text()) self.dialog.close_dialog() self.dialog = None def autocommit_fired(self): def commit_channel(overview): try: if overview and overview['mychannel']['dirty']: self.editchannel_request_mgr = TriblerRequestManager() self.editchannel_request_mgr.perform_request( "mychannel/commit", lambda _: None, method='POST', capture_errors=False) except KeyError: return if self.channel_overview: self.clicked_edit_channel_commit_button() else: self.editchannel_request_mgr = TriblerRequestManager() self.editchannel_request_mgr.perform_request("mychannel", commit_channel, capture_errors=False) # Commit button-related methods def clicked_edit_channel_commit_button(self): request_mgr = TriblerRequestManager() request_mgr.perform_request("mychannel/commit", self.on_channel_committed, method='POST') def on_channel_committed(self, result): if not result: return if 'success' in result and result['success']: self.channel_dirty = False self.update_channel_commit_views() self.on_commit.emit() if not self.autocommit_enabled: self.load_my_torrents() def add_torrent_to_channel(self, filename): with open(filename, "rb") as torrent_file: torrent_content = b64encode(torrent_file.read()) request_mgr = TriblerRequestManager() request_mgr.perform_request("mychannel/torrents", self.on_torrent_to_channel_added, method='PUT', data={"torrent": torrent_content}) def add_dir_to_channel(self, dirname, recursive=False): post_data = {"torrents_dir": dirname, "recursive": int(recursive)} request_mgr = TriblerRequestManager() request_mgr.perform_request("mychannel/torrents", self.on_torrent_to_channel_added, method='PUT', data=post_data) def add_torrent_url_to_channel(self, url): post_data = {"uri": url} request_mgr = TriblerRequestManager() request_mgr.perform_request("mychannel/torrents", self.on_torrent_to_channel_added, method='PUT', data=post_data) def on_torrent_to_channel_added(self, result): if not result: return if 'added' in result: self.load_my_torrents()
class ManagePlaylistPage(QWidget): """ On this page, users can add or remove torrents from/to a playlist. """ playlist_saved = pyqtSignal() def __init__(self): QWidget.__init__(self) self.channel_info = None self.playlist_info = None self.request_mgr = None self.torrents_in_playlist = [] self.torrents_in_channel = [] self.torrents_to_create = [] self.torrents_to_remove = [] self.pending_requests = [] self.requests_done = 0 def initialize(self, channel_info, playlist_info): self.channel_info = channel_info self.playlist_info = playlist_info self.window().edit_channel_details_manage_playlist_header.setText( "Manage torrents in playlist '%s'" % playlist_info['name']) self.window().manage_channel_playlist_torrents_back.setIcon( QIcon(get_image_path('page_back.png'))) self.window().playlist_manage_add_to_playlist.clicked.connect( self.on_add_clicked) self.window().playlist_manage_remove_from_playlist.clicked.connect( self.on_remove_clicked) self.window().edit_channel_manage_playlist_save_button.clicked.connect( self.on_save_clicked) self.window().manage_channel_playlist_torrents_back.clicked.connect( self.on_playlist_manage_back_clicked) # Load torrents in your channel self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request( "channels/discovered/%s/torrents?disable_filter=1" % channel_info["identifier"], self.on_received_channel_torrents) self.torrents_in_playlist = [] self.torrents_in_channel = [] self.torrents_to_create = [] self.torrents_to_remove = [] self.pending_requests = [] self.requests_done = 0 def on_playlist_manage_back_clicked(self): self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_PLAYLIST_TORRENTS) def update_lists(self): self.window().playlist_manage_in_channel_list.clear() self.window().playlist_manage_in_playlist_list.clear() for torrent in self.torrents_in_channel: item = QListWidgetItem( torrent["name"], self.window().playlist_manage_in_channel_list) item.setData(Qt.UserRole, torrent) self.window().playlist_manage_in_channel_list.addItem(item) for torrent in self.torrents_in_playlist: item = QListWidgetItem( torrent["name"], self.window().playlist_manage_in_playlist_list) item.setData(Qt.UserRole, torrent) self.window().playlist_manage_in_playlist_list.addItem(item) @staticmethod def remove_torrent_from_list(torrent, remove_from_list): index = -1 for torrent_index in xrange(len(remove_from_list)): if remove_from_list[torrent_index]['infohash'] == torrent[ 'infohash']: index = torrent_index break if index != -1: del remove_from_list[index] def on_received_channel_torrents(self, result): self.torrents_in_playlist = self.playlist_info['torrents'] self.torrents_in_channel = [] for torrent in result['torrents']: if not ManagePlaylistPage.list_contains_torrent( self.torrents_in_playlist, torrent): self.torrents_in_channel.append(torrent) self.update_lists() @staticmethod def list_contains_torrent(torrent_list, torrent): for playlist_torrent in torrent_list: if torrent['infohash'] == playlist_torrent['infohash']: return True return False def on_add_clicked(self): for item in self.window( ).playlist_manage_in_channel_list.selectedItems(): torrent = item.data(Qt.UserRole) ManagePlaylistPage.remove_torrent_from_list( torrent, self.torrents_in_channel) self.torrents_in_playlist.append(torrent) if ManagePlaylistPage.list_contains_torrent( self.torrents_to_remove, torrent): ManagePlaylistPage.remove_torrent_from_list( torrent, self.torrents_to_remove) self.torrents_to_create.append(torrent) self.update_lists() def on_remove_clicked(self): for item in self.window( ).playlist_manage_in_playlist_list.selectedItems(): torrent = item.data(Qt.UserRole) ManagePlaylistPage.remove_torrent_from_list( torrent, self.torrents_in_playlist) self.torrents_in_channel.append(torrent) if ManagePlaylistPage.list_contains_torrent( self.torrents_to_create, torrent): ManagePlaylistPage.remove_torrent_from_list( torrent, self.torrents_to_create) self.torrents_to_remove.append(torrent) self.update_lists() def on_save_clicked(self): self.requests_done = 0 self.pending_requests = [] for torrent in self.torrents_to_create: request = TriblerRequestManager() request.perform_request( "channels/discovered/%s/playlists/%s/%s" % (self.channel_info["identifier"], self.playlist_info['id'], torrent['infohash']), self.on_request_done, method="PUT") self.pending_requests.append(request) for torrent in self.torrents_to_remove: request = TriblerRequestManager() request.perform_request( "channels/discovered/%s/playlists/%s/%s" % (self.channel_info["identifier"], self.playlist_info['id'], torrent['infohash']), self.on_request_done, method="DELETE") self.pending_requests.append(request) def on_request_done(self, _): self.requests_done += 1 if self.requests_done == len(self.pending_requests): self.on_requests_done() def on_requests_done(self): self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_PLAYLIST_TORRENTS) self.playlist_saved.emit()
class TriblerWindow(QMainWindow): resize_event = pyqtSignal() escape_pressed = pyqtSignal() received_search_completions = pyqtSignal(object) def on_exception(self, *exc_info): if self.exception_handler_called: # We only show one feedback dialog, even when there are two consecutive exceptions. return self.exception_handler_called = True if self.tray_icon: try: self.tray_icon.deleteLater() except RuntimeError: # The tray icon might have already been removed when unloading Qt. # This is due to the C code actually being asynchronous. logging.debug("Tray icon already removed, no further deletion necessary.") self.tray_icon = None # Stop the download loop self.downloads_page.stop_loading_downloads() # Add info about whether we are stopping Tribler or not os.environ['TRIBLER_SHUTTING_DOWN'] = str(self.core_manager.shutting_down) if not self.core_manager.shutting_down: self.core_manager.stop(stop_app_on_shutdown=False) self.setHidden(True) if self.debug_window: self.debug_window.setHidden(True) exception_text = "".join(traceback.format_exception(*exc_info)) logging.error(exception_text) dialog = FeedbackDialog(self, exception_text, self.core_manager.events_manager.tribler_version, self.start_time) dialog.show() def __init__(self, core_args=None, core_env=None, api_port=None): QMainWindow.__init__(self) QCoreApplication.setOrganizationDomain("nl") QCoreApplication.setOrganizationName("TUDelft") QCoreApplication.setApplicationName("Tribler") QCoreApplication.setAttribute(Qt.AA_UseHighDpiPixmaps) self.gui_settings = QSettings() api_port = api_port or int(get_gui_setting(self.gui_settings, "api_port", DEFAULT_API_PORT)) dispatcher.update_worker_settings(port=api_port) self.navigation_stack = [] self.tribler_started = False self.tribler_settings = None self.debug_window = None self.core_manager = CoreManager(api_port) self.pending_requests = {} self.pending_uri_requests = [] self.download_uri = None self.dialog = None self.new_version_dialog = None self.start_download_dialog_active = False self.request_mgr = None self.search_request_mgr = None self.search_suggestion_mgr = None self.selected_torrent_files = [] self.vlc_available = True self.has_search_results = False self.last_search_query = None self.last_search_time = None self.start_time = time.time() self.exception_handler_called = False self.token_refresh_timer = None sys.excepthook = self.on_exception uic.loadUi(get_ui_file_path('mainwindow.ui'), self) TriblerRequestManager.window = self self.tribler_status_bar.hide() # Load dynamic widgets uic.loadUi(get_ui_file_path('torrent_channel_list_container.ui'), self.channel_page_container) self.channel_torrents_list = self.channel_page_container.items_list self.channel_torrents_detail_widget = self.channel_page_container.details_tab_widget self.channel_torrents_detail_widget.initialize_details_widget() self.channel_torrents_list.itemSelectionChanged.connect(self.channel_page.clicked_item) uic.loadUi(get_ui_file_path('torrent_channel_list_container.ui'), self.search_page_container) self.search_results_list = self.search_page_container.items_list self.search_torrents_detail_widget = self.search_page_container.details_tab_widget self.search_torrents_detail_widget.initialize_details_widget() self.search_results_list.itemClicked.connect(self.on_channel_item_click) self.search_results_list.itemSelectionChanged.connect(self.search_results_page.clicked_item) self.token_balance_widget.mouseReleaseEvent = self.on_token_balance_click def on_state_update(new_state): self.loading_text_label.setText(new_state) self.core_manager.core_state_update.connect(on_state_update) self.magnet_handler = MagnetHandler(self.window) QDesktopServices.setUrlHandler("magnet", self.magnet_handler, "on_open_magnet_link") self.debug_pane_shortcut = QShortcut(QKeySequence("Ctrl+d"), self) self.debug_pane_shortcut.activated.connect(self.clicked_menu_button_debug) # Remove the focus rect on OS X for widget in self.findChildren(QLineEdit) + self.findChildren(QListWidget) + self.findChildren(QTreeWidget): widget.setAttribute(Qt.WA_MacShowFocusRect, 0) self.menu_buttons = [self.left_menu_button_home, self.left_menu_button_search, self.left_menu_button_my_channel, self.left_menu_button_subscriptions, self.left_menu_button_video_player, self.left_menu_button_downloads, self.left_menu_button_discovered] self.video_player_page.initialize_player() self.search_results_page.initialize_search_results_page() self.settings_page.initialize_settings_page() self.subscribed_channels_page.initialize() self.edit_channel_page.initialize_edit_channel_page() self.downloads_page.initialize_downloads_page() self.home_page.initialize_home_page() self.loading_page.initialize_loading_page() self.discovering_page.initialize_discovering_page() self.discovered_page.initialize_discovered_page() self.trust_page.initialize_trust_page() self.stackedWidget.setCurrentIndex(PAGE_LOADING) # Create the system tray icon if QSystemTrayIcon.isSystemTrayAvailable(): self.tray_icon = QSystemTrayIcon() use_monochrome_icon = get_gui_setting(self.gui_settings, "use_monochrome_icon", False, is_bool=True) self.update_tray_icon(use_monochrome_icon) # Create the tray icon menu menu = self.create_add_torrent_menu() show_downloads_action = QAction('Show downloads', self) show_downloads_action.triggered.connect(self.clicked_menu_button_downloads) token_balance_action = QAction('Show token balance', self) token_balance_action.triggered.connect(lambda: self.on_token_balance_click(None)) quit_action = QAction('Quit Tribler', self) quit_action.triggered.connect(self.close_tribler) menu.addSeparator() menu.addAction(show_downloads_action) menu.addAction(token_balance_action) menu.addSeparator() menu.addAction(quit_action) self.tray_icon.setContextMenu(menu) else: self.tray_icon = None self.hide_left_menu_playlist() self.left_menu_button_debug.setHidden(True) self.top_menu_button.setHidden(True) self.left_menu.setHidden(True) self.token_balance_widget.setHidden(True) self.settings_button.setHidden(True) self.add_torrent_button.setHidden(True) self.top_search_bar.setHidden(True) # Set various icons self.top_menu_button.setIcon(QIcon(get_image_path('menu.png'))) self.search_completion_model = QStringListModel() completer = QCompleter() completer.setModel(self.search_completion_model) completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.item_delegate = QStyledItemDelegate() completer.popup().setItemDelegate(self.item_delegate) completer.popup().setStyleSheet(""" QListView { background-color: #404040; } QListView::item { color: #D0D0D0; padding-top: 5px; padding-bottom: 5px; } QListView::item:hover { background-color: #707070; } """) self.top_search_bar.setCompleter(completer) # Toggle debug if developer mode is enabled self.window().left_menu_button_debug.setHidden( not get_gui_setting(self.gui_settings, "debug", False, is_bool=True)) # Start Tribler self.core_manager.start(core_args=core_args, core_env=core_env) self.core_manager.events_manager.received_search_result_channel.connect( self.search_results_page.received_search_result_channel) self.core_manager.events_manager.received_search_result_torrent.connect( self.search_results_page.received_search_result_torrent) self.core_manager.events_manager.torrent_finished.connect(self.on_torrent_finished) self.core_manager.events_manager.new_version_available.connect(self.on_new_version_available) self.core_manager.events_manager.tribler_started.connect(self.on_tribler_started) self.core_manager.events_manager.events_started.connect(self.on_events_started) self.core_manager.events_manager.low_storage_signal.connect(self.on_low_storage) # Install signal handler for ctrl+c events def sigint_handler(*_): self.close_tribler() signal.signal(signal.SIGINT, sigint_handler) self.installEventFilter(self.video_player_page) # Resize the window according to the settings center = QApplication.desktop().availableGeometry(self).center() pos = self.gui_settings.value("pos", QPoint(center.x() - self.width() * 0.5, center.y() - self.height() * 0.5)) size = self.gui_settings.value("size", self.size()) self.move(pos) self.resize(size) self.show() def update_tray_icon(self, use_monochrome_icon): if not QSystemTrayIcon.isSystemTrayAvailable(): return if use_monochrome_icon: self.tray_icon.setIcon(QIcon(QPixmap(get_image_path('monochrome_tribler.png')))) else: self.tray_icon.setIcon(QIcon(QPixmap(get_image_path('tribler.png')))) self.tray_icon.show() def on_low_storage(self): """ Dealing with low storage space available. First stop the downloads and the core manager and ask user to user to make free space. :return: """ self.downloads_page.stop_loading_downloads() self.core_manager.stop(False) close_dialog = ConfirmationDialog(self.window(), "<b>CRITICAL ERROR</b>", "You are running low on disk space (<100MB). Please make sure to have " "sufficient free space available and restart Tribler again.", [("Close Tribler", BUTTON_TYPE_NORMAL)]) close_dialog.button_clicked.connect(lambda _: self.close_tribler()) close_dialog.show() def on_torrent_finished(self, torrent_info): if self.tray_icon: self.window().tray_icon.showMessage("Download finished", "Download of %s has finished." % torrent_info["name"]) def show_loading_screen(self): self.top_menu_button.setHidden(True) self.left_menu.setHidden(True) self.token_balance_widget.setHidden(True) self.settings_button.setHidden(True) self.add_torrent_button.setHidden(True) self.top_search_bar.setHidden(True) self.stackedWidget.setCurrentIndex(PAGE_LOADING) def on_tribler_started(self): self.tribler_started = True self.top_menu_button.setHidden(False) self.left_menu.setHidden(False) self.token_balance_widget.setHidden(False) self.settings_button.setHidden(False) self.add_torrent_button.setHidden(False) self.top_search_bar.setHidden(False) # fetch the settings, needed for the video player port self.request_mgr = TriblerRequestManager() self.fetch_settings() self.downloads_page.start_loading_downloads() self.home_page.load_popular_torrents() if not self.gui_settings.value("first_discover", False) and not self.core_manager.use_existing_core: self.window().gui_settings.setValue("first_discover", True) self.discovering_page.is_discovering = True self.stackedWidget.setCurrentIndex(PAGE_DISCOVERING) else: self.clicked_menu_button_home() self.setAcceptDrops(True) def on_events_started(self, json_dict): self.setWindowTitle("Tribler %s" % json_dict["version"]) def show_status_bar(self, message): self.tribler_status_bar_label.setText(message) self.tribler_status_bar.show() def hide_status_bar(self): self.tribler_status_bar.hide() def process_uri_request(self): """ Process a URI request if we have one in the queue. """ if len(self.pending_uri_requests) == 0: return uri = self.pending_uri_requests.pop() if uri.startswith('file') or uri.startswith('magnet'): self.start_download_from_uri(uri) def perform_start_download_request(self, uri, anon_download, safe_seeding, destination, selected_files, total_files=0, callback=None): # Check if destination directory is writable if not is_dir_writable(destination): ConfirmationDialog.show_message(self.window(), "Download error <i>%s</i>" % uri, "Insufficient write permissions to <i>%s</i> directory. " "Please add proper write permissions on the directory and " "add the torrent again." % destination, "OK") return selected_files_uri = "" if len(selected_files) != total_files: # Not all files included selected_files_uri = u'&' + u''.join(u"selected_files[]=%s&" % quote_plus_unicode(filename) for filename in selected_files)[:-1] anon_hops = int(self.tribler_settings['download_defaults']['number_hops']) if anon_download else 0 safe_seeding = 1 if safe_seeding else 0 post_data = "uri=%s&anon_hops=%d&safe_seeding=%d&destination=%s%s" % (quote_plus_unicode(uri), anon_hops, safe_seeding, destination, selected_files_uri) post_data = post_data.encode('utf-8') # We need to send bytes in the request, not unicode request_mgr = TriblerRequestManager() request_mgr.perform_request("downloads", callback if callback else self.on_download_added, method='PUT', data=post_data) # Save the download location to the GUI settings current_settings = get_gui_setting(self.gui_settings, "recent_download_locations", "") recent_locations = current_settings.split(",") if len(current_settings) > 0 else [] if isinstance(destination, unicode): destination = destination.encode('utf-8') encoded_destination = destination.encode('hex') if encoded_destination in recent_locations: recent_locations.remove(encoded_destination) recent_locations.insert(0, encoded_destination) if len(recent_locations) > 5: recent_locations = recent_locations[:5] self.gui_settings.setValue("recent_download_locations", ','.join(recent_locations)) def on_new_version_available(self, version): if version == str(self.gui_settings.value('last_reported_version')): return self.new_version_dialog = ConfirmationDialog(self, "New version available", "Version %s of Tribler is available.Do you want to visit the " "website to download the newest version?" % version, [('IGNORE', BUTTON_TYPE_NORMAL), ('LATER', BUTTON_TYPE_NORMAL), ('OK', BUTTON_TYPE_NORMAL)]) self.new_version_dialog.button_clicked.connect(lambda action: self.on_new_version_dialog_done(version, action)) self.new_version_dialog.show() def on_new_version_dialog_done(self, version, action): if action == 0: # ignore self.gui_settings.setValue("last_reported_version", version) elif action == 2: # ok import webbrowser webbrowser.open("https://tribler.org") self.new_version_dialog.close_dialog() self.new_version_dialog = None def on_search_text_change(self, text): self.search_suggestion_mgr = TriblerRequestManager() self.search_suggestion_mgr.perform_request( "search/completions?q=%s" % text, self.on_received_search_completions) def on_received_search_completions(self, completions): if completions is None: return self.received_search_completions.emit(completions) self.search_completion_model.setStringList(completions["completions"]) def fetch_settings(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("settings", self.received_settings, capture_errors=False) def received_settings(self, settings): if not settings: return # If we cannot receive the settings, stop Tribler with an option to send the crash report. if 'error' in settings: raise RuntimeError(TriblerRequestManager.get_message_from_error(settings)) self.tribler_settings = settings['settings'] # Set the video server port self.video_player_page.video_player_port = settings["ports"]["video_server~port"] # Disable various components based on the settings if not self.tribler_settings['search_community']['enabled']: self.window().top_search_bar.setHidden(True) if not self.tribler_settings['video_server']['enabled']: self.left_menu_button_video_player.setHidden(True) self.downloads_creditmining_button.setHidden(not self.tribler_settings["credit_mining"]["enabled"]) self.downloads_all_button.click() # process pending file requests (i.e. someone clicked a torrent file when Tribler was closed) # We do this after receiving the settings so we have the default download location. self.process_uri_request() # Set token balance refresh timer and load the token balance self.token_refresh_timer = QTimer() self.token_refresh_timer.timeout.connect(self.load_token_balance) self.token_refresh_timer.start(60000) self.load_token_balance() def on_top_search_button_click(self): current_ts = time.time() current_search_query = self.top_search_bar.text() if self.last_search_query and self.last_search_time \ and self.last_search_query == self.top_search_bar.text() \ and current_ts - self.last_search_time < 1: logging.info("Same search query already sent within 500ms so dropping this one") return self.left_menu_button_search.setChecked(True) self.has_search_results = True self.clicked_menu_button_search() self.search_results_page.perform_search(current_search_query) self.search_request_mgr = TriblerRequestManager() self.search_request_mgr.perform_request("search?q=%s" % current_search_query, None) self.last_search_query = current_search_query self.last_search_time = current_ts def on_settings_button_click(self): self.deselect_all_menu_buttons() self.stackedWidget.setCurrentIndex(PAGE_SETTINGS) self.settings_page.load_settings() self.navigation_stack = [] self.hide_left_menu_playlist() def on_token_balance_click(self, _): self.raise_window() self.deselect_all_menu_buttons() self.stackedWidget.setCurrentIndex(PAGE_TRUST) self.trust_page.load_trust_statistics() self.navigation_stack = [] self.hide_left_menu_playlist() def load_token_balance(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("trustchain/statistics", self.received_token_balance, capture_errors=False) def received_token_balance(self, statistics): if not statistics or "statistics" not in statistics: return statistics = statistics["statistics"] if 'latest_block' in statistics: balance = (statistics["latest_block"]["transaction"]["total_up"] - statistics["latest_block"]["transaction"]["total_down"]) self.set_token_balance(balance) else: self.token_balance_label.setText("0 MB") def set_token_balance(self, balance): if abs(balance) > 1024 ** 4: # Balance is over a TB balance /= 1024.0 ** 4 self.token_balance_label.setText("%.1f TB" % balance) elif abs(balance) > 1024 ** 3: # Balance is over a GB balance /= 1024.0 ** 3 self.token_balance_label.setText("%.1f GB" % balance) else: balance /= 1024.0 ** 2 self.token_balance_label.setText("%d MB" % balance) def raise_window(self): self.setWindowState(self.windowState() & ~Qt.WindowMinimized | Qt.WindowActive) self.raise_() self.activateWindow() def create_add_torrent_menu(self): """ Create a menu to add new torrents. Shows when users click on the tray icon or the big plus button. """ menu = TriblerActionMenu(self) browse_files_action = QAction('Import torrent from file', self) browse_directory_action = QAction('Import torrent(s) from directory', self) add_url_action = QAction('Import torrent from magnet/URL', self) browse_files_action.triggered.connect(self.on_add_torrent_browse_file) browse_directory_action.triggered.connect(self.on_add_torrent_browse_dir) add_url_action.triggered.connect(self.on_add_torrent_from_url) menu.addAction(browse_files_action) menu.addAction(browse_directory_action) menu.addAction(add_url_action) return menu def on_add_torrent_button_click(self, pos): self.create_add_torrent_menu().exec_(self.mapToGlobal(self.add_torrent_button.pos())) def on_add_torrent_browse_file(self): filenames = QFileDialog.getOpenFileNames(self, "Please select the .torrent file", QDir.homePath(), "Torrent files (*.torrent)") if len(filenames[0]) > 0: [self.pending_uri_requests.append(u"file:%s" % filename) for filename in filenames[0]] self.process_uri_request() def start_download_from_uri(self, uri): self.download_uri = uri if get_gui_setting(self.gui_settings, "ask_download_settings", True, is_bool=True): # If tribler settings is not available, fetch the settings and inform the user to try again. if not self.tribler_settings: self.fetch_settings() ConfirmationDialog.show_error(self, "Download Error", "Tribler settings is not available yet. " "Fetching it now. Please try again later.") return # Clear any previous dialog if exists if self.dialog: self.dialog.close_dialog() self.dialog = None self.dialog = StartDownloadDialog(self, self.download_uri) self.dialog.button_clicked.connect(self.on_start_download_action) self.dialog.show() self.start_download_dialog_active = True else: # In the unlikely scenario that tribler settings are not available yet, try to fetch settings again and # add the download uri back to self.pending_uri_requests to process again. if not self.tribler_settings: self.fetch_settings() if self.download_uri not in self.pending_uri_requests: self.pending_uri_requests.append(self.download_uri) return self.window().perform_start_download_request(self.download_uri, self.window().tribler_settings['download_defaults'][ 'anonymity_enabled'], self.window().tribler_settings['download_defaults'][ 'safeseeding_enabled'], self.tribler_settings['download_defaults']['saveas'], [], 0) self.process_uri_request() def on_start_download_action(self, action): if action == 1: if self.dialog and self.dialog.dialog_widget: self.window().perform_start_download_request( self.download_uri, self.dialog.dialog_widget.anon_download_checkbox.isChecked(), self.dialog.dialog_widget.safe_seed_checkbox.isChecked(), self.dialog.dialog_widget.destination_input.currentText(), self.dialog.get_selected_files(), self.dialog.dialog_widget.files_list_view.topLevelItemCount()) else: ConfirmationDialog.show_error(self, "Tribler UI Error", "Something went wrong. Please try again.") logging.exception("Error while trying to download. Either dialog or dialog.dialog_widget is None") self.dialog.request_mgr.cancel_request() # To abort the torrent info request self.dialog.close_dialog() self.dialog = None self.start_download_dialog_active = False if action == 0: # We do this after removing the dialog since process_uri_request is blocking self.process_uri_request() def on_add_torrent_browse_dir(self): chosen_dir = QFileDialog.getExistingDirectory(self, "Please select the directory containing the .torrent files", QDir.homePath(), QFileDialog.ShowDirsOnly) if len(chosen_dir) != 0: self.selected_torrent_files = [torrent_file for torrent_file in glob.glob(chosen_dir + "/*.torrent")] self.dialog = ConfirmationDialog(self, "Add torrents from directory", "Are you sure you want to add %d torrents to Tribler?" % len(self.selected_torrent_files), [('ADD', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)]) self.dialog.button_clicked.connect(self.on_confirm_add_directory_dialog) self.dialog.show() def on_confirm_add_directory_dialog(self, action): if action == 0: for torrent_file in self.selected_torrent_files: escaped_uri = u"file:%s" % pathname2url(torrent_file) self.perform_start_download_request(escaped_uri, self.window().tribler_settings['download_defaults'][ 'anonymity_enabled'], self.window().tribler_settings['download_defaults'][ 'safeseeding_enabled'], self.tribler_settings['download_defaults']['saveas'], [], 0) if self.dialog: self.dialog.close_dialog() self.dialog = None def on_add_torrent_from_url(self): # Make sure that the window is visible (this action might be triggered from the tray icon) self.raise_window() if self.video_player_page.isVisible(): # If we're adding a torrent from the video player page, go to the home page. # This is necessary since VLC takes the screen and the popup becomes invisible. self.clicked_menu_button_home() self.dialog = ConfirmationDialog(self, "Add torrent from URL/magnet link", "Please enter the URL/magnet link in the field below:", [('ADD', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)], show_input=True) self.dialog.dialog_widget.dialog_input.setPlaceholderText('URL/magnet link') self.dialog.dialog_widget.dialog_input.setFocus() self.dialog.button_clicked.connect(self.on_torrent_from_url_dialog_done) self.dialog.show() def on_torrent_from_url_dialog_done(self, action): if self.dialog and self.dialog.dialog_widget: uri = self.dialog.dialog_widget.dialog_input.text() # Remove first dialog self.dialog.close_dialog() self.dialog = None if action == 0: self.start_download_from_uri(uri) def on_download_added(self, result): if not result: return if len(self.pending_uri_requests) == 0: # Otherwise, we first process the remaining requests. self.window().left_menu_button_downloads.click() else: self.process_uri_request() def on_top_menu_button_click(self): if self.left_menu.isHidden(): self.left_menu.show() else: self.left_menu.hide() def deselect_all_menu_buttons(self, except_select=None): for button in self.menu_buttons: if button == except_select: button.setEnabled(False) continue button.setEnabled(True) if button == self.left_menu_button_search and not self.has_search_results: button.setEnabled(False) button.setChecked(False) def clicked_menu_button_home(self): self.deselect_all_menu_buttons(self.left_menu_button_home) self.stackedWidget.setCurrentIndex(PAGE_HOME) self.navigation_stack = [] self.hide_left_menu_playlist() def clicked_menu_button_search(self): self.deselect_all_menu_buttons(self.left_menu_button_search) self.stackedWidget.setCurrentIndex(PAGE_SEARCH_RESULTS) self.navigation_stack = [] self.hide_left_menu_playlist() def clicked_menu_button_discovered(self): self.deselect_all_menu_buttons(self.left_menu_button_discovered) self.stackedWidget.setCurrentIndex(PAGE_DISCOVERED) self.discovered_page.load_discovered_channels() self.navigation_stack = [] self.hide_left_menu_playlist() def clicked_menu_button_my_channel(self): self.deselect_all_menu_buttons(self.left_menu_button_my_channel) self.stackedWidget.setCurrentIndex(PAGE_EDIT_CHANNEL) self.edit_channel_page.load_my_channel_overview() self.navigation_stack = [] self.hide_left_menu_playlist() def clicked_menu_button_video_player(self): self.deselect_all_menu_buttons(self.left_menu_button_video_player) self.stackedWidget.setCurrentIndex(PAGE_VIDEO_PLAYER) self.navigation_stack = [] self.show_left_menu_playlist() def clicked_menu_button_downloads(self): self.raise_window() self.left_menu_button_downloads.setChecked(True) self.deselect_all_menu_buttons(self.left_menu_button_downloads) self.stackedWidget.setCurrentIndex(PAGE_DOWNLOADS) self.navigation_stack = [] self.hide_left_menu_playlist() def clicked_menu_button_debug(self): if not self.debug_window: self.debug_window = DebugWindow(self.tribler_settings, self.core_manager.events_manager.tribler_version) self.debug_window.show() def clicked_menu_button_subscriptions(self): self.deselect_all_menu_buttons(self.left_menu_button_subscriptions) self.subscribed_channels_page.load_subscribed_channels() self.stackedWidget.setCurrentIndex(PAGE_SUBSCRIBED_CHANNELS) self.navigation_stack = [] self.hide_left_menu_playlist() def hide_left_menu_playlist(self): self.left_menu_seperator.setHidden(True) self.left_menu_playlist_label.setHidden(True) self.left_menu_playlist.setHidden(True) def show_left_menu_playlist(self): self.left_menu_seperator.setHidden(False) self.left_menu_playlist_label.setHidden(False) self.left_menu_playlist.setHidden(False) def on_channel_item_click(self, channel_list_item): list_widget = channel_list_item.listWidget() from TriblerGUI.widgets.channel_list_item import ChannelListItem if isinstance(list_widget.itemWidget(channel_list_item), ChannelListItem): channel_info = channel_list_item.data(Qt.UserRole) self.channel_page.initialize_with_channel(channel_info) self.navigation_stack.append(self.stackedWidget.currentIndex()) self.stackedWidget.setCurrentIndex(PAGE_CHANNEL_DETAILS) def on_playlist_item_click(self, playlist_list_item): list_widget = playlist_list_item.listWidget() from TriblerGUI.widgets.playlist_list_item import PlaylistListItem if isinstance(list_widget.itemWidget(playlist_list_item), PlaylistListItem): playlist_info = playlist_list_item.data(Qt.UserRole) self.playlist_page.initialize_with_playlist(playlist_info) self.navigation_stack.append(self.stackedWidget.currentIndex()) self.stackedWidget.setCurrentIndex(PAGE_PLAYLIST_DETAILS) def on_page_back_clicked(self): try: prev_page = self.navigation_stack.pop() self.stackedWidget.setCurrentIndex(prev_page) if prev_page == PAGE_SEARCH_RESULTS: self.stackedWidget.widget(prev_page).load_search_results_in_list() if prev_page == PAGE_SUBSCRIBED_CHANNELS: self.stackedWidget.widget(prev_page).load_subscribed_channels() if prev_page == PAGE_DISCOVERED: self.stackedWidget.widget(prev_page).load_discovered_channels() except IndexError: logging.exception("Unknown page found in stack") def on_edit_channel_clicked(self): self.stackedWidget.setCurrentIndex(PAGE_EDIT_CHANNEL) self.navigation_stack = [] self.channel_page.on_edit_channel_clicked() def resizeEvent(self, _): # Resize home page cells cell_width = self.home_page_table_view.width() / 3 - 3 # We have some padding to the right cell_height = cell_width / 2 + 60 for i in range(0, 3): self.home_page_table_view.setColumnWidth(i, cell_width) self.home_page_table_view.setRowHeight(i, cell_height) self.resize_event.emit() def exit_full_screen(self): self.top_bar.show() self.left_menu.show() self.video_player_page.is_full_screen = False self.showNormal() def close_tribler(self): if not self.core_manager.shutting_down: def show_force_shutdown(): self.loading_text_label.setText("Tribler is taking longer than expected to shut down. You can force " "Tribler to shutdown by pressing the button below. This might lead " "to data loss.") self.window().force_shutdown_btn.show() if self.tray_icon: self.tray_icon.deleteLater() self.show_loading_screen() self.hide_status_bar() self.loading_text_label.setText("Shutting down...") self.shutdown_timer = QTimer() self.shutdown_timer.timeout.connect(show_force_shutdown) self.shutdown_timer.start(SHUTDOWN_WAITING_PERIOD) self.gui_settings.setValue("pos", self.pos()) self.gui_settings.setValue("size", self.size()) if self.core_manager.use_existing_core: # Don't close the core that we are using QApplication.quit() self.core_manager.stop() self.core_manager.shutting_down = True self.downloads_page.stop_loading_downloads() request_queue.clear() # Stop the token balance timer if self.token_refresh_timer: self.token_refresh_timer.stop() def closeEvent(self, close_event): self.close_tribler() close_event.ignore() def keyReleaseEvent(self, event): if event.key() == Qt.Key_Escape: self.escape_pressed.emit() if self.isFullScreen(): self.exit_full_screen() def dragEnterEvent(self, e): file_urls = [_qurl_to_path(url) for url in e.mimeData().urls()] if e.mimeData().hasUrls() else [] if any(os.path.isfile(filename) for filename in file_urls): e.accept() else: e.ignore() def dropEvent(self, e): file_urls = ([(_qurl_to_path(url), url.toString()) for url in e.mimeData().urls()] if e.mimeData().hasUrls() else []) for filename, fileurl in file_urls: if os.path.isfile(filename): self.start_download_from_uri(fileurl) e.accept() def clicked_force_shutdown(self): process_checker = ProcessChecker() if process_checker.already_running: core_pid = process_checker.get_pid_from_lock_file() os.kill(int(core_pid), 9) # Stop the Qt application QApplication.quit()
class ChannelPage(QWidget): """ The ChannelPage is the page with an overview of each channel and displays the list of torrents/playlist available. """ def __init__(self): QWidget.__init__(self) self.playlists = [] self.torrents = [] self.loaded_channels = False self.loaded_playlists = False self.channel_info = None self.get_torents_in_channel_manager = None self.get_playlists_in_channel_manager = None def initialize_with_channel(self, channel_info): self.playlists = [] self.torrents = [] self.loaded_channels = False self.loaded_playlists = False self.get_torents_in_channel_manager = None self.get_playlists_in_channel_manager = None self.channel_info = channel_info self.window().channel_torrents_list.set_data_items([(LoadingListItem, None)]) self.window().channel_torrents_detail_widget.hide() self.window().channel_preview_label.setHidden(channel_info['subscribed']) self.window().channel_back_button.setIcon(QIcon(get_image_path('page_back.png'))) self.get_torents_in_channel_manager = TriblerRequestManager() self.get_torents_in_channel_manager.perform_request("channels/discovered/%s/torrents" % channel_info['dispersy_cid'], self.received_torrents_in_channel) if len(channel_info['dispersy_cid']) == 148: # Check-hack for Channel2.0 style address self.loaded_playlists = True else: self.get_playlists_in_channel_manager = TriblerRequestManager() self.get_playlists_in_channel_manager.perform_request("channels/discovered/%s/playlists" % channel_info['dispersy_cid'], self.received_playlists_in_channel) # initialize the page about a channel self.window().channel_name_label.setText(channel_info['name']) self.window().num_subs_label.setText(str(channel_info['votes'])) self.window().subscription_widget.initialize_with_channel(channel_info) def clicked_item(self): if len(self.window().channel_torrents_list.selectedItems()) != 1: self.window().channel_torrents_detail_widget.hide() else: item = self.window().channel_torrents_list.selectedItems()[0] list_widget = item.listWidget() list_item = list_widget.itemWidget(item) if isinstance(list_item, ChannelTorrentListItem): self.window().channel_torrents_detail_widget.update_with_torrent(list_item.torrent_info) self.window().channel_torrents_detail_widget.show() else: self.window().channel_torrents_detail_widget.hide() def update_result_list(self): if self.loaded_channels and self.loaded_playlists: self.window().channel_torrents_list.set_data_items(self.playlists + self.torrents) def received_torrents_in_channel(self, results): if not results: return def sort_key(torrent): """ Scoring algorithm for sorting the torrent to show liveness. The score is basically the sum of number of seeders and leechers. If swarm info is unknown, we give unknown seeder and leecher as 0.5 & 0.4 so that the sum is less than 1 and higher than zero. This means unknown torrents will have higher score than dead torrent with no seeders and leechers and lower score than any barely alive torrent with a single seeder or leecher. """ seeder_score = torrent['num_seeders'] if torrent['num_seeders'] or torrent['last_tracker_check'] > 0\ else 0.5 leecher_score = torrent['num_leechers'] if torrent['num_leechers'] or torrent['last_tracker_check'] > 0\ else 0.5 return seeder_score + .5 * leecher_score for result in sorted(results['torrents'], key=sort_key, reverse=True): self.torrents.append((ChannelTorrentListItem, result)) if not self.channel_info['subscribed']: self.torrents.append((TextListItem, "You're looking at a preview of this channel.\n" "Subscribe to this channel to see the full content.")) self.loaded_channels = True self.update_result_list() def received_playlists_in_channel(self, results): if not results: return for result in results['playlists']: self.playlists.append((PlaylistListItem, result)) self.loaded_playlists = True self.update_result_list()
class DownloadsPage(QWidget): """ This class is responsible for managing all items on the downloads page. The downloads page shows all downloads and specific details about a download. """ received_downloads = pyqtSignal(object) def __init__(self): QWidget.__init__(self) self.export_dir = None self.filter = DOWNLOADS_FILTER_ALL self.download_widgets = {} # key: infohash, value: QTreeWidgetItem self.downloads = None self.downloads_timer = QTimer() self.downloads_timeout_timer = QTimer() self.downloads_last_update = 0 self.selected_items = None self.dialog = None self.downloads_request_mgr = TriblerRequestManager() self.request_mgr = None self.loading_message_widget = None def showEvent(self, QShowEvent): """ When the downloads tab is clicked, we want to update the downloads list immediately. """ super(DownloadsPage, self).showEvent(QShowEvent) self.stop_loading_downloads() self.schedule_downloads_timer(True) def initialize_downloads_page(self): self.window().downloads_tab.initialize() self.window().downloads_tab.clicked_tab_button.connect(self.on_downloads_tab_button_clicked) self.window().start_download_button.clicked.connect(self.on_start_download_clicked) self.window().stop_download_button.clicked.connect(self.on_stop_download_clicked) self.window().remove_download_button.clicked.connect(self.on_remove_download_clicked) self.window().play_download_button.clicked.connect(self.on_play_download_clicked) self.window().downloads_list.itemSelectionChanged.connect(self.on_download_item_clicked) self.window().downloads_list.customContextMenuRequested.connect(self.on_right_click_item) self.window().download_details_widget.initialize_details_widget() self.window().download_details_widget.hide() self.window().downloads_filter_input.textChanged.connect(self.on_filter_text_changed) self.window().downloads_list.header().resizeSection(12, 146) if not self.window().vlc_available: self.window().play_download_button.setHidden(True) def on_filter_text_changed(self, text): self.window().downloads_list.clearSelection() self.window().download_details_widget.hide() self.update_download_visibility() def start_loading_downloads(self): self.window().downloads_list.setSelectionMode(QAbstractItemView.NoSelection) self.loading_message_widget = QTreeWidgetItem() self.window().downloads_list.addTopLevelItem(self.loading_message_widget) self.window().downloads_list.setItemWidget(self.loading_message_widget, 2, LoadingListItem(self.window().downloads_list)) self.schedule_downloads_timer(now=True) def schedule_downloads_timer(self, now=False): self.downloads_timer = QTimer() self.downloads_timer.setSingleShot(True) self.downloads_timer.timeout.connect(self.load_downloads) self.downloads_timer.start(0 if now else 1000) self.downloads_timeout_timer = QTimer() self.downloads_timeout_timer.setSingleShot(True) self.downloads_timeout_timer.timeout.connect(self.on_downloads_request_timeout) self.downloads_timeout_timer.start(16000) def on_downloads_request_timeout(self): self.downloads_request_mgr.cancel_request() self.schedule_downloads_timer() def stop_loading_downloads(self): self.downloads_timer.stop() self.downloads_timeout_timer.stop() def load_downloads(self): url = "downloads?get_pieces=1" if self.window().download_details_widget.currentIndex() == 3: url += "&get_peers=1" elif self.window().download_details_widget.currentIndex() == 1: url += "&get_files=1" if not self.isHidden() or (time.time() - self.downloads_last_update > 30): # Update if the downloads page is visible or if we haven't updated for longer than 30 seconds self.downloads_last_update = time.time() priority = "LOW" if self.isHidden() else "HIGH" self.downloads_request_mgr.cancel_request() self.downloads_request_mgr = TriblerRequestManager() self.downloads_request_mgr.perform_request(url, self.on_received_downloads, priority=priority) def on_received_downloads(self, downloads): if not downloads: return # This might happen when closing Tribler loading_widget_index = self.window().downloads_list.indexOfTopLevelItem(self.loading_message_widget) if loading_widget_index > -1: self.window().downloads_list.takeTopLevelItem(loading_widget_index) self.window().downloads_list.setSelectionMode(QAbstractItemView.ExtendedSelection) self.downloads = downloads self.total_download = 0 self.total_upload = 0 download_infohashes = set() items = [] for download in downloads["downloads"]: if download["infohash"] in self.download_widgets: item = self.download_widgets[download["infohash"]] else: item = DownloadWidgetItem() self.download_widgets[download["infohash"]] = item items.append(item) item.update_with_download(download) # Update video player with download info video_infohash = self.window().video_player_page.active_infohash if video_infohash != "" and download["infohash"] == video_infohash: self.window().video_player_page.update_with_download_info(download) self.total_download += download["speed_down"] self.total_upload += download["speed_up"] download_infohashes.add(download["infohash"]) if self.window().download_details_widget.current_download is not None and \ self.window().download_details_widget.current_download["infohash"] == download["infohash"]: self.window().download_details_widget.current_download = download self.window().download_details_widget.update_pages() self.window().downloads_list.addTopLevelItems(items) for item in items: self.window().downloads_list.setItemWidget(item, 2, item.bar_container) # Check whether there are download that should be removed for infohash, item in self.download_widgets.items(): if infohash not in download_infohashes: index = self.window().downloads_list.indexOfTopLevelItem(item) self.window().downloads_list.takeTopLevelItem(index) del self.download_widgets[infohash] self.window().tray_set_tooltip("Down: %s, Up: %s" % (format_speed(self.total_download), format_speed(self.total_upload))) self.update_download_visibility() self.schedule_downloads_timer() # Update the top download management button if we have a row selected if len(self.window().downloads_list.selectedItems()) > 0: self.on_download_item_clicked() self.update_credit_mining_disk_usage() self.received_downloads.emit(downloads) def update_download_visibility(self): for i in range(self.window().downloads_list.topLevelItemCount()): item = self.window().downloads_list.topLevelItem(i) if not isinstance(item, DownloadWidgetItem): continue filter_match = self.window().downloads_filter_input.text().lower() in item.download_info["name"].lower() is_creditmining = item.download_info["credit_mining"] if self.filter == DOWNLOADS_FILTER_CREDITMINING: item.setHidden(not is_creditmining or not filter_match) else: item.setHidden(not item.get_raw_download_status() in DOWNLOADS_FILTER_DEFINITION[self.filter] or \ not filter_match or is_creditmining) def on_downloads_tab_button_clicked(self, button_name): if button_name == "downloads_all_button": self.filter = DOWNLOADS_FILTER_ALL elif button_name == "downloads_downloading_button": self.filter = DOWNLOADS_FILTER_DOWNLOADING elif button_name == "downloads_completed_button": self.filter = DOWNLOADS_FILTER_COMPLETED elif button_name == "downloads_active_button": self.filter = DOWNLOADS_FILTER_ACTIVE elif button_name == "downloads_inactive_button": self.filter = DOWNLOADS_FILTER_INACTIVE elif button_name == "downloads_creditmining_button": self.filter = DOWNLOADS_FILTER_CREDITMINING self.window().downloads_list.clearSelection() self.window().download_details_widget.hide() self.update_download_visibility() self.update_credit_mining_disk_usage() def update_credit_mining_disk_usage(self): on_credit_mining_tab = self.filter == DOWNLOADS_FILTER_CREDITMINING self.window().diskspace_usage.setVisible(on_credit_mining_tab) if not on_credit_mining_tab or not self.window().tribler_settings or not self.downloads: return bytes_max = self.window().tribler_settings["credit_mining"]["max_disk_space"] bytes_used = 0 for download in self.downloads["downloads"]: if download["credit_mining"] and \ download["status"] in ("DLSTATUS_DOWNLOADING", "DLSTATUS_SEEDING", "DLSTATUS_STOPPED", "DLSTATUS_STOPPED_ON_ERROR"): bytes_used += download["progress"] * download["size"] self.window().diskspace_usage.setText("Current disk space usage %s / %s" % (format_size(float(bytes_used)), format_size(float(bytes_max)))) @staticmethod def start_download_enabled(download_widgets): return any([download_widget.get_raw_download_status() == DLSTATUS_STOPPED for download_widget in download_widgets]) @staticmethod def stop_download_enabled(download_widgets): return any([download_widget.get_raw_download_status() not in [DLSTATUS_STOPPED, DLSTATUS_STOPPED_ON_ERROR] for download_widget in download_widgets]) @staticmethod def force_recheck_download_enabled(download_widgets): return any([download_widget.get_raw_download_status() not in [DLSTATUS_METADATA, DLSTATUS_HASHCHECKING, DLSTATUS_WAITING4HASHCHECK] for download_widget in download_widgets]) def on_download_item_clicked(self): selected_count = len(self.window().downloads_list.selectedItems()) if selected_count == 0: self.window().play_download_button.setEnabled(False) self.window().remove_download_button.setEnabled(False) self.window().start_download_button.setEnabled(False) self.window().stop_download_button.setEnabled(False) self.window().download_details_widget.hide() elif selected_count == 1: self.selected_items = self.window().downloads_list.selectedItems() self.window().play_download_button.setEnabled(True) self.window().remove_download_button.setEnabled(True) self.window().start_download_button.setEnabled(DownloadsPage.start_download_enabled(self.selected_items)) self.window().stop_download_button.setEnabled(DownloadsPage.stop_download_enabled(self.selected_items)) self.window().download_details_widget.update_with_download(self.selected_items[0].download_info) self.window().download_details_widget.show() else: self.selected_items = self.window().downloads_list.selectedItems() self.window().play_download_button.setEnabled(False) self.window().remove_download_button.setEnabled(True) self.window().start_download_button.setEnabled(DownloadsPage.start_download_enabled(self.selected_items)) self.window().stop_download_button.setEnabled(DownloadsPage.stop_download_enabled(self.selected_items)) self.window().download_details_widget.hide() def on_start_download_clicked(self): for selected_item in self.selected_items: infohash = selected_item.download_info["infohash"] self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("downloads/%s" % infohash, self.on_download_resumed, method='PATCH', data="state=resume") def on_download_resumed(self, json_result): if json_result and 'modified' in json_result: for selected_item in self.selected_items: if selected_item.download_info["infohash"] == json_result["infohash"]: selected_item.download_info['status'] = "DLSTATUS_DOWNLOADING" selected_item.update_item() self.on_download_item_clicked() def on_stop_download_clicked(self): for selected_item in self.selected_items: infohash = selected_item.download_info["infohash"] self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("downloads/%s" % infohash, self.on_download_stopped, method='PATCH', data="state=stop") def on_play_download_clicked(self): self.window().left_menu_button_video_player.click() selected_item = self.selected_items[:1] if selected_item and \ self.window().video_player_page.active_infohash != selected_item[0].download_info["infohash"]: self.window().video_player_page.play_media_item(selected_item[0].download_info["infohash"], -1) def on_download_stopped(self, json_result): if json_result and "modified" in json_result: for selected_item in self.selected_items: if selected_item.download_info["infohash"] == json_result["infohash"]: selected_item.download_info['status'] = "DLSTATUS_STOPPED" selected_item.update_item() self.on_download_item_clicked() def on_remove_download_clicked(self): self.dialog = ConfirmationDialog(self, "Remove download", "Are you sure you want to remove this download?", [('remove download', BUTTON_TYPE_NORMAL), ('remove download + data', BUTTON_TYPE_NORMAL), ('cancel', BUTTON_TYPE_CONFIRM)]) self.dialog.button_clicked.connect(self.on_remove_download_dialog) self.dialog.show() def on_remove_download_dialog(self, action): if action != 2: for selected_item in self.selected_items: infohash = selected_item.download_info["infohash"] # Reset video player if necessary before doing the actual request if self.window().video_player_page.active_infohash == infohash: self.window().video_player_page.reset_player() self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("downloads/%s" % infohash, self.on_download_removed, method='DELETE', data="remove_data=%d" % action) self.dialog.close_dialog() self.dialog = None def on_download_removed(self, json_result): if json_result and "removed" in json_result: self.load_downloads() self.window().download_details_widget.hide() def on_force_recheck_download(self): for selected_item in self.selected_items: infohash = selected_item.download_info["infohash"] self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("downloads/%s" % infohash, self.on_forced_recheck, method='PATCH', data='state=recheck') def on_forced_recheck(self, result): if result and "modified" in result: for selected_item in self.selected_items: if selected_item.download_info["infohash"] == result["infohash"]: selected_item.download_info['status'] = "DLSTATUS_HASHCHECKING" selected_item.update_item() self.on_download_item_clicked() def change_anonymity(self, hops): for selected_item in self.selected_items: infohash = selected_item.download_info["infohash"] self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("downloads/%s" % infohash, lambda _: None, method='PATCH', data='anon_hops=%d' % hops) def on_explore_files(self): for selected_item in self.selected_items: path = os.path.normpath(os.path.join(self.window().tribler_settings['download_defaults']['saveas'], selected_item.download_info["destination"])) QDesktopServices.openUrl(QUrl.fromLocalFile(path)) def on_export_download(self): self.export_dir = QFileDialog.getExistingDirectory(self, "Please select the destination directory", "", QFileDialog.ShowDirsOnly) selected_item = self.selected_items[:1] if len(self.export_dir) > 0 and selected_item: # Show confirmation dialog where we specify the name of the file torrent_name = selected_item[0].download_info['name'] self.dialog = ConfirmationDialog(self, "Export torrent file", "Please enter the name of the torrent file:", [('SAVE', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)], show_input=True) self.dialog.dialog_widget.dialog_input.setPlaceholderText('Torrent file name') self.dialog.dialog_widget.dialog_input.setText("%s.torrent" % torrent_name) self.dialog.dialog_widget.dialog_input.setFocus() self.dialog.button_clicked.connect(self.on_export_download_dialog_done) self.dialog.show() def on_export_download_dialog_done(self, action): selected_item = self.selected_items[:1] if action == 0 and selected_item: filename = self.dialog.dialog_widget.dialog_input.text() self.request_mgr = TriblerRequestManager() self.request_mgr.download_file("downloads/%s/torrent" % selected_item[0].download_info['infohash'], lambda data: self.on_export_download_request_done(filename, data)) self.dialog.close_dialog() self.dialog = None def on_export_download_request_done(self, filename, data): dest_path = os.path.join(self.export_dir, filename) try: torrent_file = open(dest_path, "wb") torrent_file.write(data) torrent_file.close() except IOError as exc: ConfirmationDialog.show_error(self.window(), "Error when exporting file", "An error occurred when exporting the torrent file: %s" % str(exc)) else: self.window().tray_show_message("Torrent file exported", "Torrent file exported to %s" % dest_path) def on_right_click_item(self, pos): item_clicked = self.window().downloads_list.itemAt(pos) if not item_clicked or self.selected_items is None: return if item_clicked not in self.selected_items: self.selected_items.append(item_clicked) menu = TriblerActionMenu(self) start_action = QAction('Start', self) stop_action = QAction('Stop', self) remove_download_action = QAction('Remove download', self) force_recheck_action = QAction('Force recheck', self) export_download_action = QAction('Export .torrent file', self) explore_files_action = QAction('Explore files', self) no_anon_action = QAction('No anonymity', self) one_hop_anon_action = QAction('One hop', self) two_hop_anon_action = QAction('Two hops', self) three_hop_anon_action = QAction('Three hops', self) start_action.triggered.connect(self.on_start_download_clicked) start_action.setEnabled(DownloadsPage.start_download_enabled(self.selected_items)) stop_action.triggered.connect(self.on_stop_download_clicked) stop_action.setEnabled(DownloadsPage.stop_download_enabled(self.selected_items)) remove_download_action.triggered.connect(self.on_remove_download_clicked) force_recheck_action.triggered.connect(self.on_force_recheck_download) force_recheck_action.setEnabled(DownloadsPage.force_recheck_download_enabled(self.selected_items)) export_download_action.triggered.connect(self.on_export_download) explore_files_action.triggered.connect(self.on_explore_files) no_anon_action.triggered.connect(lambda: self.change_anonymity(0)) one_hop_anon_action.triggered.connect(lambda: self.change_anonymity(1)) two_hop_anon_action.triggered.connect(lambda: self.change_anonymity(2)) three_hop_anon_action.triggered.connect(lambda: self.change_anonymity(3)) menu.addAction(start_action) menu.addAction(stop_action) if self.window().vlc_available and len(self.selected_items) == 1: play_action = QAction('Play', self) play_action.triggered.connect(self.on_play_download_clicked) menu.addAction(play_action) menu.addSeparator() menu.addAction(remove_download_action) menu.addSeparator() menu.addAction(force_recheck_action) menu.addSeparator() exclude_states = [DLSTATUS_METADATA, DLSTATUS_CIRCUITS, DLSTATUS_HASHCHECKING, DLSTATUS_WAITING4HASHCHECK] if len(self.selected_items) == 1 and self.selected_items[0].get_raw_download_status() not in exclude_states: menu.addAction(export_download_action) menu.addSeparator() menu_anon_level = menu.addMenu("Change anonymity") menu_anon_level.addAction(no_anon_action) menu_anon_level.addAction(one_hop_anon_action) menu_anon_level.addAction(two_hop_anon_action) menu_anon_level.addAction(three_hop_anon_action) menu.addAction(explore_files_action) menu.exec_(self.window().downloads_list.mapToGlobal(pos))
class MarketOrdersPage(QWidget): """ This page displays orders in the decentralized market in Tribler. """ def __init__(self): QWidget.__init__(self) self.request_mgr = None self.initialized = False self.selected_item = None self.dialog = None self.wallets = {} def initialize_orders_page(self, wallets): if not self.initialized: self.window().orders_back_button.setIcon(QIcon(get_image_path('page_back.png'))) self.window().market_orders_list.sortItems(0, Qt.AscendingOrder) self.window().market_orders_list.customContextMenuRequested.connect(self.on_right_click_order) self.initialized = True self.wallets = wallets self.load_orders() def load_orders(self): self.window().market_orders_list.clear() self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("market/orders", self.on_received_orders) def on_received_orders(self, orders): if not orders: return for order in orders["orders"]: if self.wallets: asset1_prec = self.wallets[order["assets"]["first"]["type"]]["precision"] asset2_prec = self.wallets[order["assets"]["second"]["type"]]["precision"] item = OrderWidgetItem(self.window().market_orders_list, order, asset1_prec, asset2_prec) self.window().market_orders_list.addTopLevelItem(item) def on_right_click_order(self, pos): item_clicked = self.window().market_orders_list.itemAt(pos) if not item_clicked: return self.selected_item = item_clicked if self.selected_item.order['status'] == 'open': # We can only cancel an open order menu = TriblerActionMenu(self) cancel_action = QAction('Cancel order', self) cancel_action.triggered.connect(self.on_cancel_order_clicked) menu.addAction(cancel_action) menu.exec_(self.window().market_orders_list.mapToGlobal(pos)) def on_cancel_order_clicked(self): self.dialog = ConfirmationDialog(self, "Cancel order", "Are you sure you want to cancel the order with id %s?" % self.selected_item.order['order_number'], [('NO', BUTTON_TYPE_NORMAL), ('YES', BUTTON_TYPE_CONFIRM)]) self.dialog.button_clicked.connect(self.on_confirm_cancel_order) self.dialog.show() def on_confirm_cancel_order(self, action): if action == 1: self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("market/orders/%s/cancel" % self.selected_item.order['order_number'], self.on_order_cancelled, method='POST') self.dialog.close_dialog() self.dialog = None def on_order_cancelled(self, response): if not response: return self.load_orders()
class StartDownloadDialog(DialogContainer): button_clicked = pyqtSignal(int) received_metainfo = pyqtSignal(dict) def __init__(self, parent, download_uri): DialogContainer.__init__(self, parent) torrent_name = download_uri if torrent_name.startswith('file:'): torrent_name = torrent_name[5:] elif torrent_name.startswith('magnet:'): torrent_name = unquote_plus(torrent_name) self.download_uri = download_uri self.has_metainfo = False uic.loadUi(get_ui_file_path('startdownloaddialog.ui'), self.dialog_widget) self.dialog_widget.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) self.dialog_widget.browse_dir_button.clicked.connect(self.on_browse_dir_clicked) self.dialog_widget.cancel_button.clicked.connect(lambda: self.button_clicked.emit(0)) self.dialog_widget.download_button.clicked.connect(self.on_download_clicked) self.dialog_widget.select_all_files_button.clicked.connect(self.on_all_files_selected_clicked) self.dialog_widget.deselect_all_files_button.clicked.connect(self.on_all_files_deselected_clicked) self.dialog_widget.destination_input.setStyleSheet(""" QComboBox { background-color: #444; border: none; color: #C0C0C0; padding: 4px; } QComboBox::drop-down { width: 20px; border: 1px solid #999; border-radius: 2px; } QComboBox QAbstractItemView { selection-background-color: #707070; color: #C0C0C0; } QComboBox::down-arrow { width: 12px; height: 12px; image: url('%s'); } """ % get_image_path('down_arrow_input.png')) if self.window().tribler_settings: # Set the most recent download locations in the QComboBox current_settings = get_gui_setting(self.window().gui_settings, "recent_download_locations", "") if len(current_settings) > 0: recent_locations = [url.decode('hex').decode('utf-8') for url in current_settings.split(",")] self.dialog_widget.destination_input.addItems(recent_locations) else: self.dialog_widget.destination_input.setCurrentText( self.window().tribler_settings['download_defaults']['saveas']) self.dialog_widget.torrent_name_label.setText(torrent_name) self.dialog_widget.anon_download_checkbox.stateChanged.connect(self.on_anon_download_state_changed) self.dialog_widget.anon_download_checkbox\ .setChecked(self.window().tribler_settings['download_defaults']['anonymity_enabled']) self.dialog_widget.safe_seed_checkbox\ .setChecked(self.window().tribler_settings['download_defaults']['safeseeding_enabled']) self.dialog_widget.safe_seed_checkbox.setEnabled(self.dialog_widget.anon_download_checkbox.isChecked()) self.perform_files_request() self.dialog_widget.files_list_view.setHidden(True) self.dialog_widget.download_files_container.setHidden(True) self.dialog_widget.adjustSize() self.on_anon_download_state_changed(None) self.on_main_window_resize() def get_selected_files(self): included_files = [] for ind in xrange(self.dialog_widget.files_list_view.topLevelItemCount()): item = self.dialog_widget.files_list_view.topLevelItem(ind) if item.checkState(2) == Qt.Checked: included_files.append(u'/'.join(item.data(0, Qt.UserRole)['path'])) return included_files def perform_files_request(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("torrentinfo?uri=%s" % quote_plus_unicode(self.download_uri), self.on_received_metainfo, capture_errors=False) def on_received_metainfo(self, metainfo): if not metainfo: return if 'error' in metainfo: if metainfo['error'] == 'timeout': self.dialog_widget.loading_files_label.setText("Timeout when trying to fetch files.") elif 'code' in metainfo['error'] and metainfo['error']['code'] == 'IOError': self.dialog_widget.loading_files_label.setText("Unable to read torrent file data") else: self.dialog_widget.loading_files_label.setText("Error: %s" % metainfo['error']) return metainfo = metainfo['metainfo'] if 'files' in metainfo['info']: # Multi-file torrent files = metainfo['info']['files'] else: files = [{'path': [metainfo['info']['name']], 'length': metainfo['info']['length']}] # Show if the torrent already exists in the downloads if 'download_exists' in metainfo and metainfo['download_exists']: self.dialog_widget.existing_download_info_label.setText("Note: this torrent already exists in the Downloads") else: self.dialog_widget.existing_download_info_label.setText("") for filename in files: item = DownloadFileTreeWidgetItem(self.dialog_widget.files_list_view) item.setText(0, '/'.join(filename['path'])) item.setText(1, format_size(float(filename['length']))) item.setData(0, Qt.UserRole, filename) item.setCheckState(2, Qt.Checked) self.dialog_widget.files_list_view.addTopLevelItem(item) self.has_metainfo = True self.dialog_widget.loading_files_label.setHidden(True) self.dialog_widget.download_files_container.setHidden(False) self.dialog_widget.files_list_view.setHidden(False) self.dialog_widget.adjustSize() self.on_main_window_resize() self.received_metainfo.emit(metainfo) def on_browse_dir_clicked(self): chosen_dir = QFileDialog.getExistingDirectory(self.window(), "Please select the destination directory of your " "download", "", QFileDialog.ShowDirsOnly) if len(chosen_dir) != 0: self.dialog_widget.destination_input.setCurrentText(chosen_dir) is_writable, error = is_dir_writable(chosen_dir) if not is_writable: gui_error_message = "Tribler cannot download to <i>%s</i> directory. Please add proper write " \ "permissions to the directory or choose another download directory. [%s]" \ % (chosen_dir, error) ConfirmationDialog.show_message(self.dialog_widget, "Insufficient Permissions", gui_error_message, "OK") def on_anon_download_state_changed(self, _): if self.dialog_widget.anon_download_checkbox.isChecked(): self.dialog_widget.safe_seed_checkbox.setChecked(True) self.dialog_widget.safe_seed_checkbox.setEnabled(not self.dialog_widget.anon_download_checkbox.isChecked()) def on_download_clicked(self): if self.has_metainfo and len(self.get_selected_files()) == 0: # User deselected all torrents ConfirmationDialog.show_error(self.window(), "No files selected", "Please select at least one file to download.") else: download_dir = self.dialog_widget.destination_input.currentText() is_writable, error = is_dir_writable(download_dir) if not is_writable: gui_error_message = "Tribler cannot download to <i>%s</i> directory. Please add proper write " \ "permissions to the directory or choose another download directory and try " \ "to download again. [%s]" % (download_dir, error) ConfirmationDialog.show_message(self.dialog_widget, "Insufficient Permissions", gui_error_message, "OK") else: self.button_clicked.emit(1) def on_all_files_selected_clicked(self): for ind in xrange(self.dialog_widget.files_list_view.topLevelItemCount()): item = self.dialog_widget.files_list_view.topLevelItem(ind) item.setCheckState(2, Qt.Checked) def on_all_files_deselected_clicked(self): for ind in xrange(self.dialog_widget.files_list_view.topLevelItemCount()): item = self.dialog_widget.files_list_view.topLevelItem(ind) item.setCheckState(2, Qt.Unchecked)
class CreateTorrentPage(QWidget): """ The CreateTorrentPage is the page where users can create torrent files so they can be added to their channel. """ def __init__(self): QWidget.__init__(self) self.channel_identifier = None self.request_mgr = None self.dialog = None self.selected_item_index = -1 self.initialized = False def initialize(self): self.window().create_torrent_name_field.setText('') self.window().create_torrent_description_field.setText('') self.window().create_torrent_files_list.clear() self.window().seed_after_adding_checkbox.setChecked(True) self.window().edit_channel_create_torrent_progress_label.hide() if not self.initialized: self.window().manage_channel_create_torrent_back.setIcon(QIcon(get_image_path('page_back.png'))) self.window().create_torrent_files_list.customContextMenuRequested.connect(self.on_right_click_file_item) self.window().manage_channel_create_torrent_back.clicked.connect(self.on_create_torrent_manage_back_clicked) self.window().create_torrent_choose_files_button.clicked.connect(self.on_choose_files_clicked) self.window().create_torrent_choose_dir_button.clicked.connect(self.on_choose_dir_clicked) self.window().edit_channel_create_torrent_button.clicked.connect(self.on_create_clicked) self.initialized = True def on_create_torrent_manage_back_clicked(self): self.window().edit_channel_details_stacked_widget.setCurrentIndex(PAGE_EDIT_CHANNEL_TORRENTS) def on_choose_files_clicked(self): filenames, _ = QFileDialog.getOpenFileNames(self.window(), "Please select the files", QDir.homePath()) for filename in filenames: self.window().create_torrent_files_list.addItem(filename) def on_choose_dir_clicked(self): chosen_dir = QFileDialog.getExistingDirectory(self.window(), "Please select the directory containing the files", "", QFileDialog.ShowDirsOnly) if len(chosen_dir) == 0: return files = [] for path, _, dir_files in os.walk(chosen_dir): for filename in dir_files: files.append(os.path.join(path, filename)) self.window().create_torrent_files_list.clear() for filename in files: self.window().create_torrent_files_list.addItem(filename) def on_create_clicked(self): if self.window().create_torrent_files_list.count() == 0: self.dialog = ConfirmationDialog(self, "Notice", "You should add at least one file to your torrent.", [('CLOSE', BUTTON_TYPE_NORMAL)]) self.dialog.button_clicked.connect(self.on_dialog_ok_clicked) self.dialog.show() return self.window().edit_channel_create_torrent_button.setEnabled(False) files_list = [] for ind in xrange(self.window().create_torrent_files_list.count()): file_str = self.window().create_torrent_files_list.item(ind).text() files_list.append(file_str) name = self.window().create_torrent_name_field.text() description = self.window().create_torrent_description_field.toPlainText() post_data = { "name": name, "description": description, "files": files_list } url = "createtorrent?download=1" if self.window().seed_after_adding_checkbox.isChecked() else "createtorrent" self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request(url, self.on_torrent_created, data=post_data, method='POST') # Show creating torrent text self.window().edit_channel_create_torrent_progress_label.show() def on_dialog_ok_clicked(self, _): self.dialog.close_dialog() self.dialog = None def on_torrent_created(self, result): if not result: return self.window().edit_channel_create_torrent_button.setEnabled(True) if 'torrent' in result: self.add_torrent_to_channel(result['torrent']) def add_torrent_to_channel(self, torrent): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("mychannel/torrents", self.on_torrent_to_channel_added, data={"torrent": torrent}, method='PUT') def on_torrent_to_channel_added(self, result): if not result: return self.window().edit_channel_create_torrent_progress_label.hide() if 'added' in result: self.window().edit_channel_details_stacked_widget.setCurrentIndex(PAGE_EDIT_CHANNEL_TORRENTS) self.window().edit_channel_page.load_my_torrents() def on_remove_entry(self): self.window().create_torrent_files_list.takeItem(self.selected_item_index) def on_right_click_file_item(self, pos): item_clicked = self.window().create_torrent_files_list.itemAt(pos) if not item_clicked: return self.selected_item_index = self.window().create_torrent_files_list.row(item_clicked) menu = TriblerActionMenu(self) remove_action = QAction('Remove file', self) remove_action.triggered.connect(self.on_remove_entry) menu.addAction(remove_action) menu.exec_(self.window().create_torrent_files_list.mapToGlobal(pos))
class ManagePlaylistPage(QWidget): """ On this page, users can add or remove torrents from/to a playlist. """ playlist_saved = pyqtSignal() def __init__(self): QWidget.__init__(self) self.channel_info = None self.playlist_info = None self.request_mgr = None self.torrents_in_playlist = [] self.torrents_in_channel = [] self.torrents_to_create = [] self.torrents_to_remove = [] self.pending_requests = [] self.requests_done = 0 def initialize(self, channel_info, playlist_info): self.channel_info = channel_info self.playlist_info = playlist_info self.window().edit_channel_details_manage_playlist_header.setText("Manage torrents in playlist '%s'" % playlist_info['name']) self.window().manage_channel_playlist_torrents_back.setIcon(QIcon(get_image_path('page_back.png'))) self.window().playlist_manage_add_to_playlist.clicked.connect(self.on_add_clicked) self.window().playlist_manage_remove_from_playlist.clicked.connect(self.on_remove_clicked) self.window().edit_channel_manage_playlist_save_button.clicked.connect(self.on_save_clicked) self.window().manage_channel_playlist_torrents_back.clicked.connect(self.on_playlist_manage_back_clicked) # Load torrents in your channel self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("channels/discovered/%s/torrents?disable_filter=1" % channel_info["identifier"], self.on_received_channel_torrents) self.torrents_in_playlist = [] self.torrents_in_channel = [] self.torrents_to_create = [] self.torrents_to_remove = [] self.pending_requests = [] self.requests_done = 0 def on_playlist_manage_back_clicked(self): self.window().edit_channel_details_stacked_widget.setCurrentIndex(PAGE_EDIT_CHANNEL_PLAYLIST_TORRENTS) def update_lists(self): self.window().playlist_manage_in_channel_list.clear() self.window().playlist_manage_in_playlist_list.clear() for torrent in self.torrents_in_channel: item = QListWidgetItem(torrent["name"], self.window().playlist_manage_in_channel_list) item.setData(Qt.UserRole, torrent) self.window().playlist_manage_in_channel_list.addItem(item) for torrent in self.torrents_in_playlist: item = QListWidgetItem(torrent["name"], self.window().playlist_manage_in_playlist_list) item.setData(Qt.UserRole, torrent) self.window().playlist_manage_in_playlist_list.addItem(item) @staticmethod def remove_torrent_from_list(torrent, remove_from_list): index = -1 for torrent_index in xrange(len(remove_from_list)): if remove_from_list[torrent_index]['infohash'] == torrent['infohash']: index = torrent_index break if index != -1: del remove_from_list[index] def on_received_channel_torrents(self, result): if not result: return self.torrents_in_playlist = self.playlist_info['torrents'] self.torrents_in_channel = [] for torrent in result['torrents']: if not ManagePlaylistPage.list_contains_torrent(self.torrents_in_playlist, torrent): self.torrents_in_channel.append(torrent) self.update_lists() @staticmethod def list_contains_torrent(torrent_list, torrent): for playlist_torrent in torrent_list: if torrent['infohash'] == playlist_torrent['infohash']: return True return False def on_add_clicked(self): for item in self.window().playlist_manage_in_channel_list.selectedItems(): torrent = item.data(Qt.UserRole) ManagePlaylistPage.remove_torrent_from_list(torrent, self.torrents_in_channel) self.torrents_in_playlist.append(torrent) if ManagePlaylistPage.list_contains_torrent(self.torrents_to_remove, torrent): ManagePlaylistPage.remove_torrent_from_list(torrent, self.torrents_to_remove) self.torrents_to_create.append(torrent) self.update_lists() def on_remove_clicked(self): for item in self.window().playlist_manage_in_playlist_list.selectedItems(): torrent = item.data(Qt.UserRole) ManagePlaylistPage.remove_torrent_from_list(torrent, self.torrents_in_playlist) self.torrents_in_channel.append(torrent) if ManagePlaylistPage.list_contains_torrent(self.torrents_to_create, torrent): ManagePlaylistPage.remove_torrent_from_list(torrent, self.torrents_to_create) self.torrents_to_remove.append(torrent) self.update_lists() def on_save_clicked(self): self.requests_done = 0 self.pending_requests = [] for torrent in self.torrents_to_create: request = TriblerRequestManager() request.perform_request("channels/discovered/%s/playlists/%s/%s" % (self.channel_info["identifier"], self.playlist_info['id'], torrent['infohash']), self.on_request_done, method="PUT") self.pending_requests.append(request) for torrent in self.torrents_to_remove: request = TriblerRequestManager() request.perform_request("channels/discovered/%s/playlists/%s/%s" % (self.channel_info["identifier"], self.playlist_info['id'], torrent['infohash']), self.on_request_done, method="DELETE") self.pending_requests.append(request) def on_request_done(self, result): if not result: return self.requests_done += 1 if self.requests_done == len(self.pending_requests): self.on_requests_done() def on_requests_done(self): self.window().edit_channel_details_stacked_widget.setCurrentIndex(PAGE_EDIT_CHANNEL_PLAYLIST_TORRENTS) self.playlist_saved.emit()
class DebugWindow(QMainWindow): """ The debug window shows various statistics about Tribler such as performed requests, Dispersy statistics and community information. """ def __init__(self, settings): QMainWindow.__init__(self) self.request_mgr = None uic.loadUi(get_ui_file_path('debugwindow.ui'), self) self.setWindowTitle("Tribler debug pane") self.window().debug_tab_widget.setCurrentIndex(0) self.window().dispersy_tab_widget.setCurrentIndex(0) self.window().debug_tab_widget.currentChanged.connect(self.tab_changed) self.window().dispersy_tab_widget.currentChanged.connect(self.dispersy_tab_changed) self.window().events_tree_widget.itemClicked.connect(self.on_event_clicked) self.load_general_tab() if not settings['trustchain']['enabled']: self.window().debug_tab_widget.setTabEnabled(2, False) def tab_changed(self, index): if index == 0: self.load_general_tab() elif index == 1: self.load_requests_tab() elif index == 2: self.load_trustchain_tab() elif index == 3: self.dispersy_tab_changed(self.window().dispersy_tab_widget.currentIndex()) elif index == 4: self.load_events_tab() def dispersy_tab_changed(self, index): if index == 0: self.load_dispersy_general_tab() elif index == 1: self.load_dispersy_communities_tab() def create_and_add_widget_item(self, key, value, widget): item = QTreeWidgetItem(widget) item.setText(0, key) item.setText(1, "%s" % value) widget.addTopLevelItem(item) def load_general_tab(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("statistics/tribler", self.on_tribler_statistics) def on_tribler_statistics(self, data): data = data["tribler_statistics"] self.window().general_tree_widget.clear() self.create_and_add_widget_item("Number of channels", data["num_channels"], self.window().general_tree_widget) self.create_and_add_widget_item("Database size", format_size(data["database_size"]), self.window().general_tree_widget) self.create_and_add_widget_item("Number of collected torrents", data["torrents"]["num_collected"], self.window().general_tree_widget) self.create_and_add_widget_item("Number of torrent files", data["torrents"]["num_files"], self.window().general_tree_widget) self.create_and_add_widget_item("Total size of torrent files", format_size(data["torrents"]["total_size"]), self.window().general_tree_widget) def load_requests_tab(self): self.window().requests_tree_widget.clear() for endpoint, method, data, timestamp, status_code in sorted(tribler_performed_requests.values(), key=lambda x: x[3]): item = QTreeWidgetItem(self.window().requests_tree_widget) item.setText(0, "%s %s %s" % (method, endpoint, data)) item.setText(1, ("%d" % status_code) if status_code else "unknown") item.setText(2, "%s" % strftime("%H:%M:%S", localtime(timestamp))) self.window().requests_tree_widget.addTopLevelItem(item) def load_trustchain_tab(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("trustchain/statistics", self.on_trustchain_statistics) def on_trustchain_statistics(self, data): self.window().trustchain_tree_widget.clear() for key, value in data["statistics"].iteritems(): self.create_and_add_widget_item(key, value, self.window().trustchain_tree_widget) def load_dispersy_general_tab(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("statistics/dispersy", self.on_dispersy_general_stats) def on_dispersy_general_stats(self, data): self.window().dispersy_general_tree_widget.clear() for key, value in data["dispersy_statistics"].iteritems(): self.create_and_add_widget_item(key, value, self.window().dispersy_general_tree_widget) def load_dispersy_communities_tab(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("statistics/communities", self.on_dispersy_community_stats) def on_dispersy_community_stats(self, data): self.window().communities_tree_widget.clear() for community in data["community_statistics"]: item = QTreeWidgetItem(self.window().communities_tree_widget) item.setText(0, community["classification"]) item.setText(1, community["identifier"][:6]) item.setText(2, community["member"][:6]) item.setText(3, "%s" % community["candidates"]) self.window().communities_tree_widget.addTopLevelItem(item) def on_event_clicked(self, item): event_dict = item.data(0, Qt.UserRole) self.window().event_text_box.setPlainText(json.dumps(event_dict)) def load_events_tab(self): self.window().events_tree_widget.clear() for event_dict, timestamp in tribler_received_events: item = QTreeWidgetItem(self.window().events_tree_widget) item.setData(0, Qt.UserRole, event_dict) item.setText(0, "%s" % event_dict['type']) item.setText(1, "%s" % strftime("%H:%M:%S", localtime(timestamp))) self.window().events_tree_widget.addTopLevelItem(item)
class EditChannelPage(QWidget): """ This class is responsible for managing lists and data on the your channel page, including torrents, playlists and rss feeds. """ playlists_loaded = pyqtSignal(object) def __init__(self): QWidget.__init__(self) self.remove_torrent_requests = [] self.channel_overview = None self.playlists = None self.editing_playlist = None self.viewing_playlist = None self.editing_own_channel = False self.dialog = None self.editchannel_request_mgr = None def initialize_edit_channel_page(self): self.window().create_channel_intro_button.clicked.connect( self.on_create_channel_intro_button_clicked) self.window().create_channel_form.hide() self.window().edit_channel_stacked_widget.setCurrentIndex(1) self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_OVERVIEW) self.window().create_channel_button.clicked.connect( self.on_create_channel_button_pressed) self.window().edit_channel_save_button.clicked.connect( self.on_edit_channel_save_button_pressed) self.window( ).edit_channel_torrents_remove_selected_button.clicked.connect( self.on_torrents_remove_selected_clicked) self.window().edit_channel_torrents_remove_all_button.clicked.connect( self.on_torrents_remove_all_clicked) self.window().edit_channel_torrents_add_button.clicked.connect( self.on_torrents_add_clicked) self.window( ).edit_channel_details_playlist_manage.playlist_saved.connect( self.load_channel_playlists) self.window().edit_channel_playlist_torrents_back.clicked.connect( self.on_playlist_torrents_back_clicked) self.window().edit_channel_playlists_list.itemClicked.connect( self.on_playlist_item_clicked) self.window( ).edit_channel_playlist_manage_torrents_button.clicked.connect( self.on_playlist_manage_clicked) self.window().edit_channel_create_playlist_button.clicked.connect( self.on_playlist_created_clicked) self.window().playlist_edit_save_button.clicked.connect( self.on_playlist_edit_save_clicked) self.window().playlist_edit_cancel_button.clicked.connect( self.on_playlist_edit_cancel_clicked) self.window( ).edit_channel_details_rss_feeds_remove_selected_button.clicked.connect( self.on_rss_feeds_remove_selected_clicked) self.window().edit_channel_details_rss_add_button.clicked.connect( self.on_rss_feed_add_clicked) self.window().edit_channel_details_rss_refresh_button.clicked.connect( self.on_rss_feeds_refresh_clicked) # Tab bar buttons self.window().channel_settings_tab.initialize() self.window().channel_settings_tab.clicked_tab_button.connect( self.clicked_tab_button) def load_my_channel_overview(self): if not self.channel_overview: self.window().edit_channel_stacked_widget.setCurrentIndex(2) self.editchannel_request_mgr = TriblerRequestManager() self.editchannel_request_mgr.perform_request( "mychannel", self.initialize_with_channel_overview, capture_errors=False) def initialize_with_channel_overview(self, overview): if 'error' in overview: self.window().edit_channel_stacked_widget.setCurrentIndex(0) else: if "mychannel" in overview: self.channel_overview = overview["mychannel"] self.set_editing_own_channel(True) self.window().edit_channel_name_label.setText("My channel") else: self.channel_overview = overview["channel"] self.set_editing_own_channel(False) self.window().edit_channel_name_label.setText( self.channel_overview["name"]) self.window().edit_channel_overview_name_label.setText( self.channel_overview["name"]) self.window().edit_channel_description_label.setText( self.channel_overview["description"]) self.window().edit_channel_identifier_label.setText( self.channel_overview["identifier"]) self.window().edit_channel_name_edit.setText( self.channel_overview["name"]) self.window().edit_channel_description_edit.setText( self.channel_overview["description"]) self.window().edit_channel_stacked_widget.setCurrentIndex(1) def set_editing_own_channel(self, edit_own): self.editing_own_channel = edit_own self.window().edit_channel_settings_button.setHidden(not edit_own) self.window().edit_channel_rss_feeds_button.setHidden(not edit_own) self.window().edit_channel_playlists_button.setHidden(not edit_own) self.window().edit_channel_torrents_remove_all_button.setHidden( not edit_own) self.window().edit_channel_torrents_remove_selected_button.setHidden( not edit_own) def load_channel_torrents(self): self.window().edit_channel_torrents_list.set_data_items([ (LoadingListItem, None) ]) self.editchannel_request_mgr = TriblerRequestManager() self.editchannel_request_mgr.perform_request( "channels/discovered/%s/torrents?disable_filter=1" % self.channel_overview["identifier"], self.initialize_with_torrents) def initialize_with_torrents(self, torrents): self.window().edit_channel_torrents_list.set_data_items([]) items = [] for result in torrents['torrents']: items.append((ChannelTorrentListItem, result, { "show_controls": True, "on_remove_clicked": self.on_torrent_remove_clicked })) self.window().edit_channel_torrents_list.set_data_items(items) def load_channel_playlists(self): self.window().edit_channel_playlists_list.set_data_items([ (LoadingListItem, None) ]) self.editchannel_request_mgr = TriblerRequestManager() self.editchannel_request_mgr.perform_request( "channels/discovered/%s/playlists?disable_filter=1" % self.channel_overview["identifier"], self.initialize_with_playlists) def initialize_with_playlists(self, playlists): self.playlists_loaded.emit(playlists) self.playlists = playlists self.window().edit_channel_playlists_list.set_data_items([]) self.update_playlist_list() viewing_playlist_index = self.get_index_of_viewing_playlist() if viewing_playlist_index != -1: self.viewing_playlist = self.playlists['playlists'][ viewing_playlist_index] self.update_playlist_torrent_list() def load_channel_rss_feeds(self): self.editchannel_request_mgr = TriblerRequestManager() self.editchannel_request_mgr.perform_request( "channels/discovered/%s/rssfeeds" % self.channel_overview["identifier"], self.initialize_with_rss_feeds) def initialize_with_rss_feeds(self, rss_feeds): self.window().edit_channel_rss_feeds_list.clear() for feed in rss_feeds["rssfeeds"]: item = QTreeWidgetItem(self.window().edit_channel_rss_feeds_list) item.setText(0, feed["url"]) self.window().edit_channel_rss_feeds_list.addTopLevelItem(item) def on_torrent_remove_clicked(self, item): self.dialog = ConfirmationDialog( self, "Remove selected torrent", "Are you sure that you want to remove the selected torrent from this channel?", [('CONFIRM', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)]) self.dialog.button_clicked.connect( lambda action: self.on_torrents_remove_selected_action( action, item)) self.dialog.show() def on_create_channel_button_pressed(self): channel_name = self.window().new_channel_name_edit.text() channel_description = self.window( ).new_channel_description_edit.toPlainText() if len(channel_name) == 0: self.window().new_channel_name_label.setStyleSheet("color: red;") return self.window().create_channel_button.setEnabled(False) self.editchannel_request_mgr = TriblerRequestManager() self.editchannel_request_mgr.perform_request( "channels/discovered", self.on_channel_created, data=unicode('name=%s&description=%s' % (channel_name, channel_description)).encode('utf-8'), method='PUT') def on_channel_created(self, result): if u'added' in result: self.window().create_channel_button.setEnabled(True) self.load_my_channel_overview() def on_edit_channel_save_button_pressed(self): channel_name = self.window().edit_channel_name_edit.text() channel_description = self.window( ).edit_channel_description_edit.toPlainText() self.editchannel_request_mgr = TriblerRequestManager() self.editchannel_request_mgr.perform_request( "mychannel", self.on_channel_edited, data=unicode('name=%s&description=%s' % (channel_name, channel_description)).encode('utf-8'), method='POST') def on_channel_edited(self, result): if 'modified' in result: self.window().edit_channel_name_label.setText( self.window().edit_channel_name_edit.text()) self.window().edit_channel_description_label.setText( self.window().edit_channel_description_edit.toPlainText()) def on_torrents_remove_selected_clicked(self): num_selected = len( self.window().edit_channel_torrents_list.selectedItems()) if num_selected == 0: return selected_torrent_items = [ self.window().edit_channel_torrents_list.itemWidget( list_widget_item) for list_widget_item in self.window().edit_channel_torrents_list.selectedItems() ] self.dialog = ConfirmationDialog( self, "Remove %s selected torrents" % num_selected, "Are you sure that you want to remove %s selected torrents " "from your channel?" % num_selected, [('CONFIRM', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)]) self.dialog.button_clicked.connect( lambda action: self.on_torrents_remove_selected_action( action, selected_torrent_items)) self.dialog.show() def on_torrents_remove_all_clicked(self): self.dialog = ConfirmationDialog( self.window(), "Remove all torrents", "Are you sure that you want to remove all torrents from your channel? " "You cannot undo this action.", [('CONFIRM', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)]) self.dialog.button_clicked.connect(self.on_torrents_remove_all_action) self.dialog.show() def on_torrents_add_clicked(self): menu = TriblerActionMenu(self) browse_files_action = QAction('Import torrent from file', self) add_url_action = QAction('Add URL', self) create_torrent_action = QAction('Create torrent from file(s)', self) browse_files_action.triggered.connect(self.on_add_torrent_browse_file) add_url_action.triggered.connect(self.on_add_torrent_from_url) create_torrent_action.triggered.connect( self.on_create_torrent_from_files) menu.addAction(browse_files_action) menu.addAction(add_url_action) menu.addAction(create_torrent_action) menu.exec_(QCursor.pos()) def on_add_torrent_browse_file(self): filename = QFileDialog.getOpenFileName( self, "Please select the .torrent file", "", "Torrent files (*.torrent)") if len(filename[0]) == 0: return with open(filename[0], "rb") as torrent_file: torrent_content = urllib.quote_plus( base64.b64encode(torrent_file.read())) self.editchannel_request_mgr = TriblerRequestManager() self.editchannel_request_mgr.perform_request( "channels/discovered/%s/torrents" % self.channel_overview['identifier'], self.on_torrent_to_channel_added, method='PUT', data='torrent=%s' % torrent_content) def on_add_torrent_from_url(self): self.dialog = ConfirmationDialog( self, "Add torrent from URL/magnet link", "Please enter the URL/magnet link in the field below:", [('ADD', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)], show_input=True) self.dialog.dialog_widget.dialog_input.setPlaceholderText( 'URL/magnet link') self.dialog.button_clicked.connect( self.on_torrent_from_url_dialog_done) self.dialog.show() def on_torrent_from_url_dialog_done(self, action): if action == 0: url = urllib.quote_plus( self.dialog.dialog_widget.dialog_input.text()) self.editchannel_request_mgr = TriblerRequestManager() self.editchannel_request_mgr.perform_request( "channels/discovered/%s/torrents/%s" % (self.channel_overview['identifier'], url), self.on_torrent_to_channel_added, method='PUT') self.dialog.setParent(None) self.dialog = None def on_torrent_to_channel_added(self, result): if 'added' in result: self.load_channel_torrents() def on_create_torrent_from_files(self): self.window().edit_channel_details_create_torrent.initialize( self.channel_overview['identifier']) self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_CREATE_TORRENT) def on_playlist_torrents_back_clicked(self): self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_PLAYLISTS) def on_playlist_item_clicked(self, item): playlist_info = item.data(Qt.UserRole) self.window().edit_channel_playlist_torrents_list.set_data_items([]) self.window().edit_channel_details_playlist_torrents_header.setText( "Torrents in '%s'" % playlist_info['name']) self.window().edit_channel_playlist_torrents_back.setIcon( QIcon(get_image_path('page_back.png'))) self.viewing_playlist = playlist_info self.update_playlist_torrent_list() self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_PLAYLIST_TORRENTS) def update_playlist_list(self): self.playlists['playlists'].sort( key=lambda torrent: len(torrent['torrents']), reverse=True) items = [] for result in self.playlists['playlists']: items.append((PlaylistListItem, result, { "show_controls": True, "on_remove_clicked": self.on_playlist_remove_clicked, "on_edit_clicked": self.on_playlist_edit_clicked })) self.window().edit_channel_playlists_list.set_data_items(items) def update_playlist_torrent_list(self): items = [] for torrent in self.viewing_playlist["torrents"]: items.append((ChannelTorrentListItem, torrent, { "show_controls": True, "on_remove_clicked": self.on_playlist_torrent_remove_clicked })) self.window().edit_channel_playlist_torrents_list.set_data_items(items) def on_playlist_manage_clicked(self): self.window().edit_channel_details_playlist_manage.initialize( self.channel_overview, self.viewing_playlist) self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_PLAYLIST_MANAGE) def on_playlist_torrent_remove_clicked(self, item): self.dialog = ConfirmationDialog( self, "Remove selected torrent from playlist", "Are you sure that you want to remove the selected torrent " "from this playlist?", [('CONFIRM', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)]) self.dialog.button_clicked.connect( lambda action: self.on_playlist_torrent_remove_selected_action( item, action)) self.dialog.show() def on_playlist_torrent_remove_selected_action(self, item, action): if action == 0: self.editchannel_request_mgr = TriblerRequestManager() self.editchannel_request_mgr.perform_request( "channels/discovered/%s/playlists/%s/%s" % (self.channel_overview["identifier"], self.viewing_playlist['id'], item.torrent_info['infohash']), lambda result: self.on_playlist_torrent_removed( result, item.torrent_info), method='DELETE') self.dialog.setParent(None) self.dialog = None def on_playlist_torrent_removed(self, result, torrent): self.remove_torrent_from_playlist(torrent) def get_index_of_viewing_playlist(self): if self.viewing_playlist is None: return -1 for index in xrange(len(self.playlists['playlists'])): if self.playlists['playlists'][index][ 'id'] == self.viewing_playlist['id']: return index return -1 def remove_torrent_from_playlist(self, torrent): playlist_index = self.get_index_of_viewing_playlist() torrent_index = -1 for index in xrange(len(self.viewing_playlist['torrents'])): if self.viewing_playlist['torrents'][index]['infohash'] == torrent[ 'infohash']: torrent_index = index break if torrent_index != -1: del self.playlists['playlists'][playlist_index]['torrents'][ torrent_index] self.viewing_playlist = self.playlists['playlists'][playlist_index] self.update_playlist_list() self.update_playlist_torrent_list() def on_playlist_edit_save_clicked(self): if len(self.window().playlist_edit_name.text()) == 0: return name = self.window().playlist_edit_name.text() description = self.window().playlist_edit_description.toPlainText() self.editchannel_request_mgr = TriblerRequestManager() if self.editing_playlist is None: self.editchannel_request_mgr.perform_request( "channels/discovered/%s/playlists" % self.channel_overview["identifier"], self.on_playlist_created, data=unicode('name=%s&description=%s' % (name, description)).encode('utf-8'), method='PUT') else: self.editchannel_request_mgr.perform_request( "channels/discovered/%s/playlists/%s" % (self.channel_overview["identifier"], self.editing_playlist["id"]), self.on_playlist_edited, data=unicode('name=%s&description=%s' % (name, description)).encode('utf-8'), method='POST') def on_playlist_created(self, json_result): if 'created' in json_result and json_result['created']: self.on_playlist_edited_done() def on_playlist_edited(self, json_result): if 'modified' in json_result and json_result['modified']: self.on_playlist_edited_done() def on_playlist_edited_done(self): self.window().playlist_edit_name.setText('') self.window().playlist_edit_description.setText('') self.load_channel_playlists() self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_PLAYLISTS) def on_playlist_edit_cancel_clicked(self): self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_PLAYLISTS) def on_playlist_created_clicked(self): self.editing_playlist = None self.window().playlist_edit_save_button.setText("CREATE") self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_PLAYLIST_EDIT) def on_playlist_remove_clicked(self, item): self.dialog = ConfirmationDialog( self, "Remove selected playlist", "Are you sure that you want to remove the selected playlist " "from your channel?", [('CONFIRM', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)]) self.dialog.button_clicked.connect( lambda action: self.on_playlist_remove_selected_action( item, action)) self.dialog.show() def on_playlist_remove_selected_action(self, item, action): if action == 0: self.editchannel_request_mgr = TriblerRequestManager() self.editchannel_request_mgr.perform_request( "channels/discovered/%s/playlists/%s" % (self.channel_overview["identifier"], item.playlist_info['id']), self.on_playlist_removed, method='DELETE') self.dialog.setParent(None) self.dialog = None def on_playlist_removed(self, json_result): if 'removed' in json_result and json_result['removed']: self.load_channel_playlists() def on_playlist_edit_clicked(self, item): self.editing_playlist = item.playlist_info self.window().playlist_edit_save_button.setText("CREATE") self.window().playlist_edit_name.setText(item.playlist_info["name"]) self.window().playlist_edit_description.setText( item.playlist_info["description"]) self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_PLAYLIST_EDIT) def on_torrents_remove_selected_action(self, action, items): if action == 0: if isinstance(items, list): infohash = ",".join([ torrent_item.torrent_info['infohash'] for torrent_item in items ]) else: infohash = items.torrent_info['infohash'] self.editchannel_request_mgr = TriblerRequestManager() self.editchannel_request_mgr.perform_request( "channels/discovered/%s/torrents/%s" % (self.channel_overview["identifier"], infohash), self.on_torrent_removed, method='DELETE') self.dialog.setParent(None) self.dialog = None def on_torrent_removed(self, json_result): if 'removed' in json_result and json_result['removed']: self.load_channel_torrents() def on_torrents_remove_all_action(self, action): if action == 0: for torrent_ind in xrange( self.window().edit_channel_torrents_list.count()): torrent_data = self.window().edit_channel_torrents_list.item( torrent_ind).data(Qt.UserRole) request_mgr = TriblerRequestManager() request_mgr.perform_request( "channels/discovered/%s/torrents/%s" % (self.channel_overview["identifier"], torrent_data['infohash']), None, method='DELETE') self.remove_torrent_requests.append(request_mgr) self.window().edit_channel_torrents_list.set_data_items([]) self.dialog.setParent(None) self.dialog = None def clicked_tab_button(self, tab_button_name): if tab_button_name == "edit_channel_overview_button": self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_OVERVIEW) elif tab_button_name == "edit_channel_settings_button": self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_SETTINGS) elif tab_button_name == "edit_channel_torrents_button": self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_TORRENTS) self.load_channel_torrents() elif tab_button_name == "edit_channel_playlists_button": self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_PLAYLISTS) self.load_channel_playlists() elif tab_button_name == "edit_channel_rss_feeds_button": self.window().edit_channel_details_stacked_widget.setCurrentIndex( PAGE_EDIT_CHANNEL_RSS_FEEDS) self.load_channel_rss_feeds() def on_create_channel_intro_button_clicked(self): self.window().create_channel_form.show() self.window().create_channel_intro_button_container.hide() self.window().create_new_channel_intro_label.setText( "Please enter your channel details below.") def on_rss_feed_add_clicked(self): self.dialog = ConfirmationDialog( self, "Add RSS feed", "Please enter the RSS feed URL in the field below:", [('ADD', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)], show_input=True) self.dialog.dialog_widget.dialog_input.setPlaceholderText( 'RSS feed URL') self.dialog.button_clicked.connect(self.on_rss_feed_dialog_added) self.dialog.show() def on_rss_feed_dialog_added(self, action): if action == 0: url = urllib.quote_plus( self.dialog.dialog_widget.dialog_input.text()) self.editchannel_request_mgr = TriblerRequestManager() self.editchannel_request_mgr.perform_request( "channels/discovered/%s/rssfeeds/%s" % (self.channel_overview["identifier"], url), self.on_rss_feed_added, method='PUT') self.dialog.setParent(None) self.dialog = None def on_rss_feed_added(self, json_result): if json_result['added']: self.load_channel_rss_feeds() def on_rss_feeds_remove_selected_clicked(self): if len(self.window().edit_channel_rss_feeds_list.selectedItems()) == 0: ConfirmationDialog.show_message( self, "Remove RSS Feeds", "Selection is empty. Please select the feeds to remove.", "OK") return self.dialog = ConfirmationDialog( self, "Remove RSS feed", "Are you sure you want to remove the selected RSS feed?", [('REMOVE', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)]) self.dialog.button_clicked.connect(self.on_rss_feed_dialog_removed) self.dialog.show() def on_rss_feed_dialog_removed(self, action): if action == 0: url = urllib.quote_plus( self.window().edit_channel_rss_feeds_list.selectedItems() [0].text(0)) self.editchannel_request_mgr = TriblerRequestManager() self.editchannel_request_mgr.perform_request( "channels/discovered/%s/rssfeeds/%s" % (self.channel_overview["identifier"], url), self.on_rss_feed_removed, method='DELETE') self.dialog.setParent(None) self.dialog = None def on_rss_feed_removed(self, json_result): if json_result['removed']: self.load_channel_rss_feeds() def on_rss_feeds_refresh_clicked(self): self.window().edit_channel_details_rss_refresh_button.setEnabled(False) self.editchannel_request_mgr = TriblerRequestManager() self.editchannel_request_mgr.perform_request('channels/discovered/%s/recheckfeeds' % self.channel_overview["identifier"], self.on_rss_feeds_refreshed,\ method='POST') def on_rss_feeds_refreshed(self, json_result): if json_result["rechecked"]: self.window().edit_channel_details_rss_refresh_button.setEnabled( True)
class SettingsPage(QWidget): """ This class is responsible for displaying and adjusting the settings present in Tribler. """ def __init__(self): QWidget.__init__(self) self.settings = None self.settings_request_mgr = None self.trustchain_request_mgr = None self.saved_dialog = None self.empty_tokens_barcode_dialog = None self.empty_partial_tokens_dialog = None self.confirm_empty_tokens_dialog = None def initialize_settings_page(self): self.window().settings_tab.initialize() self.window().settings_tab.clicked_tab_button.connect(self.clicked_tab_button) self.window().settings_save_button.clicked.connect(self.save_settings) self.window().download_location_chooser_button.clicked.connect(self.on_choose_download_dir_clicked) self.window().watch_folder_chooser_button.clicked.connect(self.on_choose_watch_dir_clicked) self.window().channel_autocommit_checkbox.stateChanged.connect(self.on_channel_autocommit_checkbox_changed) self.window().family_filter_checkbox.stateChanged.connect(self.on_family_filter_checkbox_changed) self.window().developer_mode_enabled_checkbox.stateChanged.connect(self.on_developer_mode_checkbox_changed) self.window().use_monochrome_icon_checkbox.stateChanged.connect(self.on_use_monochrome_icon_checkbox_changed) self.window().download_settings_anon_checkbox.stateChanged.connect(self.on_anon_download_state_changed) self.window().fully_empty_tokens_button.clicked.connect(self.confirm_fully_empty_tokens) self.window().partially_empty_tokens_button.clicked.connect(self.partially_empty_tokens) self.window().log_location_chooser_button.clicked.connect(self.on_choose_log_dir_clicked) self.update_stacked_widget_height() def confirm_fully_empty_tokens(self): self.confirm_empty_tokens_dialog = ConfirmationDialog(self, "Empty tokens into another account", "Are you sure you want to empty ALL bandwidth tokens " "into another account? " "Warning: one-way action that cannot be revered", [ ('EMPTY', BUTTON_TYPE_CONFIRM), ('CANCEL', BUTTON_TYPE_NORMAL) ]) self.confirm_empty_tokens_dialog.button_clicked.connect(self.on_confirm_fully_empty_tokens) self.confirm_empty_tokens_dialog.show() def on_confirm_fully_empty_tokens(self, action): self.confirm_empty_tokens_dialog.close_dialog() self.confirm_empty_tokens_dialog = None if action == 0: self.trustchain_request_mgr = TriblerRequestManager() self.trustchain_request_mgr.perform_request("trustchain/bootstrap", self.on_emptying_tokens) def partially_empty_tokens(self): self.empty_partial_tokens_dialog = ConfirmationDialog(self, "Empty tokens into another account", "Specify the amount of bandwidth tokens to empty into " "another account below:", [ ('EMPTY', BUTTON_TYPE_CONFIRM), ('CANCEL', BUTTON_TYPE_NORMAL) ], show_input=True) self.empty_partial_tokens_dialog.dialog_widget.dialog_input.setPlaceholderText( 'Please enter the amount of tokens in MB') self.empty_partial_tokens_dialog.dialog_widget.dialog_input.setFocus() self.empty_partial_tokens_dialog.button_clicked.connect(self.confirm_partially_empty_tokens) self.empty_partial_tokens_dialog.show() def confirm_partially_empty_tokens(self, action): tokens = self.empty_partial_tokens_dialog.dialog_widget.dialog_input.text() self.empty_partial_tokens_dialog.close_dialog() self.empty_partial_tokens_dialog = None if action == 0: try: tokens = int(float(tokens)) except ValueError: ConfirmationDialog.show_error(self.window(), "Wrong input", "The provided amount is not a number") return self.confirm_empty_tokens_dialog = ConfirmationDialog(self, "Empty tokens into another account", "Are you sure you want to empty %d bandwidth tokens " "into another account? " "Warning: one-way action that cannot be revered" % tokens, [ ('EMPTY', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM) ]) self.confirm_empty_tokens_dialog.button_clicked.connect( lambda action2: self.on_confirm_partially_empty_tokens(action2, tokens)) self.confirm_empty_tokens_dialog.show() def on_confirm_partially_empty_tokens(self, action, tokens): self.confirm_empty_tokens_dialog.close_dialog() self.confirm_empty_tokens_dialog = None if action == 0: self.trustchain_request_mgr = TriblerRequestManager() self.trustchain_request_mgr.perform_request("trustchain/bootstrap?amount=%d" % (tokens * MEBIBYTE), self.on_emptying_tokens) def on_emptying_tokens(self, data): if not data: return json_data = json.dumps(data) if has_qr: self.empty_tokens_barcode_dialog = QWidget() self.empty_tokens_barcode_dialog.setWindowTitle("Please scan the following QR code") self.empty_tokens_barcode_dialog.setGeometry(10, 10, 500, 500) qr = qrcode.QRCode( version=1, error_correction=qrcode.constants.ERROR_CORRECT_M, box_size=10, border=5, ) qr.add_data(json_data) qr.make(fit=True) img = qr.make_image() # PIL format qim = ImageQt(img) pixmap = QtGui.QPixmap.fromImage(qim).scaled(600, 600, QtCore.Qt.KeepAspectRatio) label = QLabel(self.empty_tokens_barcode_dialog) label.setPixmap(pixmap) self.empty_tokens_barcode_dialog.resize(pixmap.width(), pixmap.height()) self.empty_tokens_barcode_dialog.show() else: ConfirmationDialog.show_error(self.window(), DEPENDENCY_ERROR_TITLE, DEPENDENCY_ERROR_MESSAGE) def on_channel_autocommit_checkbox_changed(self, _): self.window().gui_settings.setValue("autocommit_enabled", self.window().channel_autocommit_checkbox.isChecked()) def on_family_filter_checkbox_changed(self, _): self.window().gui_settings.setValue("family_filter", self.window().family_filter_checkbox.isChecked()) def on_developer_mode_checkbox_changed(self, _): self.window().gui_settings.setValue("debug", self.window().developer_mode_enabled_checkbox.isChecked()) self.window().left_menu_button_debug.setHidden(not self.window().developer_mode_enabled_checkbox.isChecked()) def on_use_monochrome_icon_checkbox_changed(self, _): use_monochrome_icon = self.window().use_monochrome_icon_checkbox.isChecked() self.window().gui_settings.setValue("use_monochrome_icon", use_monochrome_icon) self.window().update_tray_icon(use_monochrome_icon) def on_anon_download_state_changed(self, _): if self.window().download_settings_anon_checkbox.isChecked(): self.window().download_settings_anon_seeding_checkbox.setChecked(True) self.window().download_settings_anon_seeding_checkbox.setEnabled( not self.window().download_settings_anon_checkbox.isChecked()) def on_choose_download_dir_clicked(self): previous_download_path = self.window().download_location_input.text() or "" download_dir = QFileDialog.getExistingDirectory(self.window(), "Please select the download location", previous_download_path, QFileDialog.ShowDirsOnly) if not download_dir: return self.window().download_location_input.setText(download_dir) def on_choose_watch_dir_clicked(self): if self.window().watchfolder_enabled_checkbox.isChecked(): previous_watch_dir = self.window().watchfolder_location_input.text() or "" watch_dir = QFileDialog.getExistingDirectory(self.window(), "Please select the watch folder", previous_watch_dir, QFileDialog.ShowDirsOnly) if not watch_dir: return self.window().watchfolder_location_input.setText(watch_dir) def on_choose_log_dir_clicked(self): previous_log_dir = self.window().log_location_input.text() or "" log_dir = QFileDialog.getExistingDirectory(self.window(), "Please select the log directory", previous_log_dir, QFileDialog.ShowDirsOnly) if not log_dir or log_dir == previous_log_dir: return is_writable, error = is_dir_writable(log_dir) if not is_writable: gui_error_message = "<i>%s</i> is not writable. [%s]" % (log_dir, error) ConfirmationDialog.show_message(self.window(), "Insufficient Permissions", gui_error_message, "OK") else: self.window().log_location_input.setText(log_dir) def initialize_with_settings(self, settings): if not settings: return self.settings = settings settings = settings["settings"] gui_settings = self.window().gui_settings # General settings self.window().family_filter_checkbox.setChecked(get_gui_setting(gui_settings, 'family_filter', True, is_bool=True)) self.window().use_monochrome_icon_checkbox.setChecked(get_gui_setting(gui_settings, "use_monochrome_icon", False, is_bool=True)) self.window().download_location_input.setText(settings['download_defaults']['saveas']) self.window().always_ask_location_checkbox.setChecked( get_gui_setting(gui_settings, "ask_download_settings", True, is_bool=True)) self.window().download_settings_anon_checkbox.setChecked(settings['download_defaults']['anonymity_enabled']) self.window().download_settings_anon_seeding_checkbox.setChecked(settings['download_defaults'][ 'safeseeding_enabled']) self.window().watchfolder_enabled_checkbox.setChecked(settings['watch_folder']['enabled']) self.window().watchfolder_location_input.setText(settings['watch_folder']['directory']) # Channel settings self.window().channel_autocommit_checkbox.setChecked( get_gui_setting(gui_settings, "autocommit_enabled", True, is_bool=True)) # Log directory self.window().log_location_input.setText(settings['general']['log_dir']) # Connection settings self.window().lt_proxy_type_combobox.setCurrentIndex(settings['libtorrent']['proxy_type']) if settings['libtorrent']['proxy_server']: proxy_server = settings['libtorrent']['proxy_server'].split(":") self.window().lt_proxy_server_input.setText(proxy_server[0]) self.window().lt_proxy_port_input.setText(proxy_server[1]) if settings['libtorrent']['proxy_auth']: proxy_auth = settings['libtorrent']['proxy_auth'].split(":") self.window().lt_proxy_username_input.setText(proxy_auth[0]) self.window().lt_proxy_password_input.setText(proxy_auth[1]) self.window().lt_utp_checkbox.setChecked(settings['libtorrent']['utp']) max_conn_download = settings['libtorrent']['max_connections_download'] if max_conn_download == -1: max_conn_download = 0 self.window().max_connections_download_input.setText(str(max_conn_download)) self.window().api_port_input.setText("%s" % get_gui_setting(gui_settings, "api_port", DEFAULT_API_PORT)) # Bandwidth settings self.window().upload_rate_limit_input.setText(str(settings['libtorrent']['max_upload_rate'] // 1024)) self.window().download_rate_limit_input.setText(str(settings['libtorrent']['max_download_rate'] // 1024)) # Seeding settings getattr(self.window(), "seeding_" + settings['download_defaults']['seeding_mode'] + "_radio").setChecked(True) self.window().seeding_time_input.setText(seconds_to_hhmm_string(settings['download_defaults']['seeding_time'])) ind = self.window().seeding_ratio_combobox.findText(str(settings['download_defaults']['seeding_ratio'])) if ind != -1: self.window().seeding_ratio_combobox.setCurrentIndex(ind) # Anonymity settings self.window().allow_exit_node_checkbox.setChecked(settings['tunnel_community']['exitnode_enabled']) self.window().number_hops_slider.setValue(int(settings['download_defaults']['number_hops'])) self.window().number_hops_slider.valueChanged.connect(self.update_anonymity_cost_label) self.update_anonymity_cost_label(int(settings['download_defaults']['number_hops'])) self.window().credit_mining_enabled_checkbox.setChecked(settings['credit_mining']['enabled']) self.window().max_disk_space_input.setText(str(settings['credit_mining']['max_disk_space'])) # Debug self.window().developer_mode_enabled_checkbox.setChecked(get_gui_setting(gui_settings, "debug", False, is_bool=True)) self.window().checkbox_enable_resource_log.setChecked(settings['resource_monitor']['enabled']) cpu_priority = 1 if 'cpu_priority' in settings['resource_monitor']: cpu_priority = int(settings['resource_monitor']['cpu_priority']) self.window().slider_cpu_level.setValue(cpu_priority) self.window().cpu_priority_value.setText("Current Priority = %s" % cpu_priority) self.window().slider_cpu_level.valueChanged.connect(self.show_updated_cpu_priority) self.window().checkbox_enable_network_statistics.setChecked(settings['ipv8']['statistics']) def update_anonymity_cost_label(self, value): html_text = """<html><head/><body><p>Download with <b>%d</b> hop(s) of anonymity. When you download a file of 200 Megabyte, you will pay roughly <b>%d</b> Megabyte of bandwidth tokens.</p></body></html> """ % (value, 400 * (value - 1) + 200) self.window().anonymity_costs_label.setText(html_text) def show_updated_cpu_priority(self, value): self.window().cpu_priority_value.setText("Current Priority = %s" % value) def load_settings(self): self.settings_request_mgr = TriblerRequestManager() self.settings_request_mgr.perform_request("settings", self.initialize_with_settings) def clicked_tab_button(self, tab_button_name): if tab_button_name == "settings_general_button": self.window().settings_stacked_widget.setCurrentIndex(PAGE_SETTINGS_GENERAL) elif tab_button_name == "settings_connection_button": self.window().settings_stacked_widget.setCurrentIndex(PAGE_SETTINGS_CONNECTION) elif tab_button_name == "settings_bandwidth_button": self.window().settings_stacked_widget.setCurrentIndex(PAGE_SETTINGS_BANDWIDTH) elif tab_button_name == "settings_seeding_button": self.window().settings_stacked_widget.setCurrentIndex(PAGE_SETTINGS_SEEDING) elif tab_button_name == "settings_anonymity_button": self.window().settings_stacked_widget.setCurrentIndex(PAGE_SETTINGS_ANONYMITY) elif tab_button_name == "settings_debug_button": self.window().settings_stacked_widget.setCurrentIndex(PAGE_SETTINGS_DEBUG) self.update_stacked_widget_height() def update_stacked_widget_height(self): """ Update the height of the settings tab. This is required since the height of a QStackedWidget is by default the height of the largest page. This messes up the scroll bar. """ for index in range(self.window().settings_stacked_widget.count()): if index == self.window().settings_stacked_widget.currentIndex(): self.window().settings_stacked_widget.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) else: self.window().settings_stacked_widget.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.window().settings_stacked_widget.adjustSize() def save_settings(self): # Create a dictionary with all available settings settings_data = {'general': {}, 'Tribler': {}, 'download_defaults': {}, 'libtorrent': {}, 'watch_folder': {}, 'tunnel_community': {}, 'trustchain': {}, 'credit_mining': {}, 'resource_monitor': {}, 'ipv8': {}, 'chant': {}} settings_data['download_defaults']['saveas'] = self.window().download_location_input.text().encode('utf-8') settings_data['general']['log_dir'] = self.window().log_location_input.text() settings_data['watch_folder']['enabled'] = self.window().watchfolder_enabled_checkbox.isChecked() if settings_data['watch_folder']['enabled']: settings_data['watch_folder']['directory'] = self.window().watchfolder_location_input.text() settings_data['libtorrent']['proxy_type'] = self.window().lt_proxy_type_combobox.currentIndex() if self.window().lt_proxy_server_input.text() and len(self.window().lt_proxy_server_input.text()) > 0 and len( self.window().lt_proxy_port_input.text()) > 0: try: settings_data['libtorrent']['proxy_server'] = "%s:%s" % (self.window().lt_proxy_server_input.text(), int(self.window().lt_proxy_port_input.text())) except ValueError: ConfirmationDialog.show_error(self.window(), "Invalid proxy port number", "You've entered an invalid format for the proxy port number. " "Please enter a whole number.") return else: settings_data['libtorrent']['proxy_server'] = ":" if self.window().lt_proxy_username_input.text() and self.window().lt_proxy_password_input.text(): settings_data['libtorrent']['proxy_auth'] = "%s:%s" % (self.window().lt_proxy_username_input.text(), self.window().lt_proxy_password_input.text()) else: settings_data['libtorrent']['proxy_auth'] = ":" settings_data['libtorrent']['utp'] = self.window().lt_utp_checkbox.isChecked() try: max_conn_download = int(self.window().max_connections_download_input.text()) except ValueError: ConfirmationDialog.show_error(self.window(), "Invalid number of connections", "You've entered an invalid format for the maximum number of connections. " "Please enter a whole number.") return if max_conn_download == 0: max_conn_download = -1 settings_data['libtorrent']['max_connections_download'] = max_conn_download try: if self.window().upload_rate_limit_input.text(): user_upload_rate_limit = int(self.window().upload_rate_limit_input.text()) * 1024 if user_upload_rate_limit < sys.maxsize: settings_data['libtorrent']['max_upload_rate'] = user_upload_rate_limit else: raise ValueError if self.window().download_rate_limit_input.text(): user_download_rate_limit = int(self.window().download_rate_limit_input.text()) * 1024 if user_download_rate_limit < sys.maxsize: settings_data['libtorrent']['max_download_rate'] = user_download_rate_limit else: raise ValueError except ValueError: ConfirmationDialog.show_error(self.window(), "Invalid value for bandwidth limit", "You've entered an invalid value for the maximum upload/download rate. " "Please enter a whole number (max: %d)" % (sys.maxsize / 1000)) return try: if self.window().api_port_input.text(): api_port = int(self.window().api_port_input.text()) if api_port <= 0 or api_port >= 65536: raise ValueError() self.window().gui_settings.setValue("api_port", api_port) except ValueError: ConfirmationDialog.show_error(self.window(), "Invalid value for api port", "Please enter a valid port for the api (between 0 and 65536)") return seeding_modes = ['forever', 'time', 'never', 'ratio'] selected_mode = 'forever' for seeding_mode in seeding_modes: if getattr(self.window(), "seeding_" + seeding_mode + "_radio").isChecked(): selected_mode = seeding_mode break settings_data['download_defaults']['seeding_mode'] = selected_mode settings_data['download_defaults']['seeding_ratio'] = self.window().seeding_ratio_combobox.currentText() try: settings_data['download_defaults']['seeding_time'] = string_to_seconds( self.window().seeding_time_input.text()) except ValueError: ConfirmationDialog.show_error(self.window(), "Invalid seeding time", "You've entered an invalid format for the seeding time (expected HH:MM)") return settings_data['credit_mining']['enabled'] = self.window().credit_mining_enabled_checkbox.isChecked() try: settings_data['credit_mining']['max_disk_space'] = int(self.window().max_disk_space_input.text()) except ValueError: ConfirmationDialog.show_error(self.window(), "Invalid number", "You've entered an invalid number for max disk space value") return settings_data['tunnel_community']['exitnode_enabled'] = self.window().allow_exit_node_checkbox.isChecked() settings_data['download_defaults']['number_hops'] = self.window().number_hops_slider.value() settings_data['download_defaults']['anonymity_enabled'] = \ self.window().download_settings_anon_checkbox.isChecked() settings_data['download_defaults']['safeseeding_enabled'] = \ self.window().download_settings_anon_seeding_checkbox.isChecked() settings_data['resource_monitor']['enabled'] = self.window().checkbox_enable_resource_log.isChecked() settings_data['resource_monitor']['cpu_priority'] = int(self.window().slider_cpu_level.value()) # network statistics settings_data['ipv8']['statistics'] = self.window().checkbox_enable_network_statistics.isChecked() self.window().settings_save_button.setEnabled(False) self.settings_request_mgr = TriblerRequestManager() self.settings_request_mgr.perform_request("settings", self.on_settings_saved, method='POST', raw_data=json.dumps(settings_data)) def on_settings_saved(self, data): if not data: return # Now save the GUI settings self.window().gui_settings.setValue("family_filter", self.window().family_filter_checkbox.isChecked()) self.window().gui_settings.setValue("autocommit_enabled", self.window().channel_autocommit_checkbox.isChecked()) self.window().gui_settings.setValue("ask_download_settings", self.window().always_ask_location_checkbox.isChecked()) self.window().gui_settings.setValue("use_monochrome_icon", self.window().use_monochrome_icon_checkbox.isChecked()) self.saved_dialog = ConfirmationDialog(TriblerRequestManager.window, "Settings saved", "Your settings have been saved.", [('CLOSE', BUTTON_TYPE_NORMAL)]) self.saved_dialog.button_clicked.connect(self.on_dialog_cancel_clicked) self.saved_dialog.show() self.window().fetch_settings() def on_dialog_cancel_clicked(self, _): self.window().settings_save_button.setEnabled(True) self.saved_dialog.close_dialog() self.saved_dialog = None
class LeftMenuPlaylist(QListWidget): """ This class represents the menu with video files that is visible in the left menu. Only shows when a video is playing. """ playing_item_change = pyqtSignal(int) # file index list_loaded = pyqtSignal() item_should_play = pyqtSignal() # no info required, a double click always follow a click event def __init__(self, parent): QListWidget.__init__(self, parent) self.files_data = [] self.loaded_list = False self.loading_list = False self.active_index = -1 self.infohash = None self.itemClicked.connect(self.on_item_clicked) self.itemDoubleClicked.connect(self.on_item_double_clicked) self.files_request_mgr = None self.files_request_timer = None def set_loading(self): self.clear() self.addItem("Loading...") self.loaded_list = False self.loading_list = True def load_list(self, infohash): self.infohash = infohash self.set_loading() if self.files_request_timer: self.files_request_timer.stop() self.files_request_timer = QTimer() self.files_request_timer.timeout.connect(self.perform_get_files_request) self.files_request_timer.start(1000) def perform_get_files_request(self): self.files_request_mgr = TriblerRequestManager() self.files_request_mgr.perform_request("downloads/%s/files" % self.infohash, self.on_received_files, capture_errors=False) def on_received_files(self, files): if not files: return if "files" not in files or not files["files"]: return if self.files_request_timer: self.files_request_timer.stop() self.files_request_timer = None self.set_files(files["files"]) self.loaded_list = True self.loading_list = False self.list_loaded.emit() def get_largest_file(self): largest_file = None largest_index = None for index, file_info in enumerate(self.files_data): if is_video_file(file_info["name"]) and \ (largest_file is None or file_info["size"] > largest_file["size"]): largest_file = file_info largest_index = index return largest_index, largest_file def set_files(self, files): self.clear() self.files_data = [] for file_info in files: if is_video_file(file_info['name']): self.addItem(file_info['name']) self.files_data.append(file_info) def set_active_index(self, file_index): cur_ind = 0 for ind, file_info in enumerate(self.files_data): if ind == file_index: self.item(cur_ind).setSelected(True) self.setFocus() break cur_ind += 1 def get_file_info(self, menu_index): """ Get the file info, based on the menu index """ return self.files_data[menu_index] if menu_index < len(self.files_data) else None def on_item_clicked(self, item): item_ind = self.row(item) if self.loaded_list: self.playing_item_change.emit(item_ind) def on_item_double_clicked(self, item): self.item_should_play.emit()
class DownloadsDetailsTabWidget(QTabWidget): """ The DownloadDetailsTab is the tab that provides details about a specific selected download. This information includes the connected peers, tracker status and file information. """ def __init__(self, parent): QTabWidget.__init__(self, parent) self.current_download = None self.request_mgr = None self.files_widgets = {} # dict of file name -> widget def initialize_details_widget(self): self.window().download_files_list.customContextMenuRequested.connect(self.on_right_click_file_item) self.window().download_files_list.header().resizeSection(0, 220) self.setCurrentIndex(0) def update_with_download(self, download): did_change = self.current_download != download self.current_download = download self.update_pages(new_download=did_change) @staticmethod def update_file_row(item, file_info): item.file_info = file_info item.update_item() @staticmethod def update_tracker_row(item, tracker): item.setText(0, tracker["url"]) item.setText(1, tracker["status"]) item.setText(2, str(tracker["peers"])) @staticmethod def update_peer_row(item, peer): peer_name = "%s:%s" % (peer["ip"], peer["port"]) if peer['connection_type'] == 1: peer_name += ' [WebSeed]' elif peer['connection_type'] == 2: peer_name += ' [HTTP Seed]' elif peer['connection_type'] == 3: peer_name += ' [uTP]' state = "" if peer['optimistic']: state += "O," if peer['uinterested']: state += "UI," if peer['uchoked']: state += "UC," if peer['uhasqueries']: state += "UQ," if not peer['uflushed']: state += "UBL," if peer['dinterested']: state += "DI," if peer['dchoked']: state += "DC," if peer['snubbed']: state += "S," state += peer['direction'] item.setText(0, peer_name) item.setText(1, '%d%%' % (peer['completed'] * 100.0)) item.setText(2, format_speed(peer['downrate'])) item.setText(3, format_speed(peer['uprate'])) item.setText(4, state) item.setText(5, peer['extended_version']) def update_pages(self, new_download=False): if self.current_download is None: return if "files" not in self.current_download: self.current_download["files"] = [] self.window().download_progress_bar.update_with_download(self.current_download) self.window().download_detail_name_label.setText(self.current_download['name']) if self.current_download["vod_mode"]: self.window().download_detail_status_label.setText('Streaming') else: status_string = DLSTATUS_STRINGS[eval(self.current_download["status"])] if eval(self.current_download["status"]) == DLSTATUS_STOPPED_ON_ERROR: status_string += " (error: %s)" % self.current_download["error"] self.window().download_detail_status_label.setText(status_string) self.window().download_detail_filesize_label.setText("%s in %d files" % (format_size(float(self.current_download["size"])), len(self.current_download["files"]))) self.window().download_detail_health_label.setText("%d seeders, %d leechers" % (self.current_download["num_seeds"], self.current_download["num_peers"])) self.window().download_detail_infohash_label.setText(self.current_download['infohash']) self.window().download_detail_destination_label.setText(self.current_download["destination"]) self.window().download_detail_ratio_label.setText( "%.3f, up: %s, down: %s" % ( self.current_download["ratio"], format_size(self.current_download["total_up"]), format_size(self.current_download["total_down"]))) self.window().download_detail_availability_label.setText("%.2f" % self.current_download['availability']) if new_download or len(self.current_download["files"]) != len(self.files_widgets.keys()): # (re)populate the files list self.window().download_files_list.clear() self.files_widgets = {} for dfile in self.current_download["files"]: item = DownloadFileWidgetItem(self.window().download_files_list, dfile) DownloadsDetailsTabWidget.update_file_row(item, dfile) self.files_widgets[dfile["name"]] = item else: # No new download, just update data in the lists for dfile in self.current_download["files"]: DownloadsDetailsTabWidget.update_file_row(self.files_widgets[dfile["name"]], dfile) # Populate the trackers list self.window().download_trackers_list.clear() for tracker in self.current_download["trackers"]: item = QTreeWidgetItem(self.window().download_trackers_list) DownloadsDetailsTabWidget.update_tracker_row(item, tracker) # Populate the peers list if the peer information is available self.window().download_peers_list.clear() if "peers" in self.current_download: for peer in self.current_download["peers"]: item = QTreeWidgetItem(self.window().download_peers_list) DownloadsDetailsTabWidget.update_peer_row(item, peer) def on_right_click_file_item(self, pos): num_selected = len(self.window().download_files_list.selectedItems()) if num_selected == 0: return item_infos = [] # Array of (item, included, is_selected) selected_files_info = [] for i in range(self.window().download_files_list.topLevelItemCount()): item = self.window().download_files_list.topLevelItem(i) is_selected = item in self.window().download_files_list.selectedItems() item_infos.append((item, item.file_info["included"], is_selected)) if is_selected: selected_files_info.append(item.file_info) item_clicked = self.window().download_files_list.itemAt(pos) if not item_clicked or not item_clicked in self.window().download_files_list.selectedItems(): return # Check whether we should enable the 'exclude' button num_excludes = 0 num_includes_selected = 0 for item_info in item_infos: if item_info[1] and item_info[0] in self.window().download_files_list.selectedItems(): num_includes_selected += 1 if not item_info[1]: num_excludes += 1 menu = TriblerActionMenu(self) include_action = QAction('Include file' + ('(s)' if num_selected > 1 else ''), self) exclude_action = QAction('Exclude file' + ('(s)' if num_selected > 1 else ''), self) include_action.triggered.connect(lambda: self.on_files_included(selected_files_info)) include_action.setEnabled(True) exclude_action.triggered.connect(lambda: self.on_files_excluded(selected_files_info)) exclude_action.setEnabled(not (num_excludes + num_includes_selected == len(item_infos))) menu.addAction(include_action) menu.addAction(exclude_action) if len(selected_files_info) == 1 and is_video_file(selected_files_info[0]['name'])\ and self.window().vlc_available: play_action = QAction('Play', self) play_action.triggered.connect(lambda: self.on_play_file(selected_files_info[0])) menu.addAction(play_action) menu.exec_(self.window().download_files_list.mapToGlobal(pos)) def get_video_file_index(self, file_index): video_index = 0 for index in xrange(file_index): if is_video_file(self.current_download["files"][index]['name']): video_index += 1 return video_index def get_included_file_list(self): return [file_info["index"] for file_info in self.current_download["files"] if file_info["included"]] def on_files_included(self, files_data): included_list = self.get_included_file_list() for file_data in files_data: if not file_data["index"] in included_list: included_list.append(file_data["index"]) self.set_included_files(included_list) def on_files_excluded(self, files_data): included_list = self.get_included_file_list() for file_data in files_data: if file_data["index"] in included_list: included_list.remove(file_data["index"]) self.set_included_files(included_list) def on_play_file(self, file_info): self.window().left_menu_button_video_player.click() self.window().video_player_page.play_media_item(self.current_download["infohash"], self.get_video_file_index(file_info["index"])) def set_included_files(self, files): data_str = ''.join("selected_files[]=%s&" % ind for ind in files)[:-1] self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("downloads/%s" % self.current_download['infohash'], lambda _: None, method='PATCH', data=data_str)
class DownloadsPage(QWidget): """ This class is responsible for managing all items on the downloads page. The downloads page shows all downloads and specific details about a download. """ received_downloads = pyqtSignal(object) def __init__(self): QWidget.__init__(self) self.export_dir = None self.filter = DOWNLOADS_FILTER_ALL self.download_widgets = {} # key: infohash, value: QTreeWidgetItem self.downloads = None self.downloads_timer = QTimer() self.downloads_timeout_timer = QTimer() self.selected_item = None self.dialog = None self.downloads_request_mgr = TriblerRequestManager() self.request_mgr = None def initialize_downloads_page(self): self.window().downloads_tab.initialize() self.window().downloads_tab.clicked_tab_button.connect(self.on_downloads_tab_button_clicked) self.window().start_download_button.clicked.connect(self.on_start_download_clicked) self.window().stop_download_button.clicked.connect(self.on_stop_download_clicked) self.window().remove_download_button.clicked.connect(self.on_remove_download_clicked) self.window().play_download_button.clicked.connect(self.on_play_download_clicked) self.window().downloads_list.itemSelectionChanged.connect(self.on_download_item_clicked) self.window().downloads_list.customContextMenuRequested.connect(self.on_right_click_item) self.window().download_details_widget.initialize_details_widget() self.window().download_details_widget.hide() self.window().downloads_filter_input.textChanged.connect(self.on_filter_text_changed) self.window().downloads_list.header().resizeSection(12, 146) if not self.window().vlc_available: self.window().play_download_button.setHidden(True) def on_filter_text_changed(self, text): self.window().downloads_list.clearSelection() self.window().download_details_widget.hide() self.update_download_visibility() def start_loading_downloads(self): self.schedule_downloads_timer(now=True) def schedule_downloads_timer(self, now=False): self.downloads_timer = QTimer() self.downloads_timer.setSingleShot(True) self.downloads_timer.timeout.connect(self.load_downloads) self.downloads_timer.start(0 if now else 1000) self.downloads_timeout_timer = QTimer() self.downloads_timeout_timer.setSingleShot(True) self.downloads_timeout_timer.timeout.connect(self.on_downloads_request_timeout) self.downloads_timeout_timer.start(16000) def on_downloads_request_timeout(self): self.downloads_request_mgr.cancel_request() self.schedule_downloads_timer() def stop_loading_downloads(self): self.downloads_timer.stop() self.downloads_timeout_timer.stop() def load_downloads(self): url = "downloads?get_pieces=1" if self.window().download_details_widget.currentIndex() == 3: url = "downloads?get_peers=1&get_pieces=1" self.downloads_request_mgr.generate_request_id() self.downloads_request_mgr.perform_request(url, self.on_received_downloads) def on_received_downloads(self, downloads): if not downloads: return # This might happen when closing Tribler total_download = 0 total_upload = 0 self.received_downloads.emit(downloads) self.downloads = downloads download_infohashes = set() for download in downloads["downloads"]: if download["infohash"] in self.download_widgets: item = self.download_widgets[download["infohash"]] else: item = DownloadWidgetItem(self.window().downloads_list) self.download_widgets[download["infohash"]] = item item.update_with_download(download) # Update video player with download info video_infohash = self.window().video_player_page.active_infohash if video_infohash != "" and download["infohash"] == video_infohash: self.window().video_player_page.update_with_download_info(download) total_download += download["speed_down"] total_upload += download["speed_up"] download_infohashes.add(download["infohash"]) if self.window().download_details_widget.current_download is not None and \ self.window().download_details_widget.current_download["infohash"] == download["infohash"]: self.window().download_details_widget.current_download = download self.window().download_details_widget.update_pages() # Check whether there are download that should be removed toremove = set() for infohash, item in self.download_widgets.iteritems(): if infohash not in download_infohashes: index = self.window().downloads_list.indexOfTopLevelItem(item) toremove.add((infohash, index)) for infohash, index in toremove: self.window().downloads_list.takeTopLevelItem(index) del self.download_widgets[infohash] if self.window().tray_icon: self.window().tray_icon.setToolTip( "Down: %s, Up: %s" % (format_speed(total_download), format_speed(total_upload))) self.update_download_visibility() self.schedule_downloads_timer() # Update the top download management button if we have a row selected if len(self.window().downloads_list.selectedItems()) > 0: self.on_download_item_clicked() def update_download_visibility(self): for i in range(self.window().downloads_list.topLevelItemCount()): item = self.window().downloads_list.topLevelItem(i) filter_match = self.window().downloads_filter_input.text().lower() in item.download_info["name"].lower() item.setHidden( not item.get_raw_download_status() in DOWNLOADS_FILTER_DEFINITION[self.filter] or not filter_match) def on_downloads_tab_button_clicked(self, button_name): if button_name == "downloads_all_button": self.filter = DOWNLOADS_FILTER_ALL elif button_name == "downloads_downloading_button": self.filter = DOWNLOADS_FILTER_DOWNLOADING elif button_name == "downloads_completed_button": self.filter = DOWNLOADS_FILTER_COMPLETED elif button_name == "downloads_active_button": self.filter = DOWNLOADS_FILTER_ACTIVE elif button_name == "downloads_inactive_button": self.filter = DOWNLOADS_FILTER_INACTIVE self.window().downloads_list.clearSelection() self.window().download_details_widget.hide() self.update_download_visibility() @staticmethod def start_download_enabled(download_widget): return download_widget.get_raw_download_status() == DLSTATUS_STOPPED @staticmethod def stop_download_enabled(download_widget): status = download_widget.get_raw_download_status() return status != DLSTATUS_STOPPED and status != DLSTATUS_STOPPED_ON_ERROR @staticmethod def force_recheck_download_enabled(download_widget): status = download_widget.get_raw_download_status() return status != DLSTATUS_METADATA and status != DLSTATUS_HASHCHECKING and status != DLSTATUS_WAITING4HASHCHECK def on_download_item_clicked(self): self.window().download_details_widget.show() if len(self.window().downloads_list.selectedItems()) == 0: self.window().play_download_button.setEnabled(False) self.window().remove_download_button.setEnabled(False) self.window().start_download_button.setEnabled(False) self.window().stop_download_button.setEnabled(False) return self.selected_item = self.window().downloads_list.selectedItems()[0] self.window().play_download_button.setEnabled(True) self.window().remove_download_button.setEnabled(True) self.window().start_download_button.setEnabled(DownloadsPage.start_download_enabled(self.selected_item)) self.window().stop_download_button.setEnabled(DownloadsPage.stop_download_enabled(self.selected_item)) self.window().download_details_widget.update_with_download(self.selected_item.download_info) def on_start_download_clicked(self): infohash = self.selected_item.download_info["infohash"] self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("downloads/%s" % infohash, self.on_download_resumed, method='PATCH', data="state=resume") def on_download_resumed(self, json_result): if json_result["modified"]: self.selected_item.download_info['status'] = "DLSTATUS_DOWNLOADING" self.selected_item.update_item() self.on_download_item_clicked() def on_stop_download_clicked(self): infohash = self.selected_item.download_info["infohash"] self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("downloads/%s" % infohash, self.on_download_stopped, method='PATCH', data="state=stop") def on_play_download_clicked(self): self.window().left_menu_button_video_player.click() self.window().video_player_page.set_torrent_infohash(self.selected_item.download_info["infohash"]) self.window().left_menu_playlist.set_loading() def on_download_stopped(self, json_result): if json_result["modified"]: self.selected_item.download_info['status'] = "DLSTATUS_STOPPED" self.selected_item.update_item() self.on_download_item_clicked() def on_remove_download_clicked(self): self.dialog = ConfirmationDialog(self, "Remove download", "Are you sure you want to remove this download?", [('remove download', BUTTON_TYPE_NORMAL), ('remove download + data', BUTTON_TYPE_NORMAL), ('cancel', BUTTON_TYPE_CONFIRM)]) self.dialog.button_clicked.connect(self.on_remove_download_dialog) self.dialog.show() def on_remove_download_dialog(self, action): if action != 2: infohash = self.selected_item.download_info["infohash"] # Reset video player if necessary before doing the actual request if self.window().video_player_page.active_infohash == infohash: self.window().video_player_page.reset_player() self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("downloads/%s" % infohash, self.on_download_removed, method='DELETE', data="remove_data=%d" % action) self.dialog.setParent(None) self.dialog = None def on_download_removed(self, json_result): if json_result["removed"]: infohash = self.selected_item.download_info["infohash"] index = self.window().downloads_list.indexOfTopLevelItem(self.selected_item) self.window().downloads_list.takeTopLevelItem(index) if infohash in self.download_widgets: # Could have been removed already through API del self.download_widgets[infohash] self.window().download_details_widget.hide() def on_force_recheck_download(self): infohash = self.selected_item.download_info["infohash"] self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("downloads/%s" % infohash, self.on_forced_recheck, method='PATCH', data='state=recheck') def on_forced_recheck(self, result): if result['modified']: self.selected_item.download_info['status'] = "DLSTATUS_HASHCHECKING" self.selected_item.update_item() self.on_download_item_clicked() def change_anonymity(self, hops): infohash = self.selected_item.download_info["infohash"] self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("downloads/%s" % infohash, lambda _: None, method='PATCH', data='anon_hops=%d' % hops) def on_explore_files(self): QDesktopServices.openUrl(QUrl.fromLocalFile(self.selected_item.download_info["destination"])) def on_export_download(self): self.export_dir = QFileDialog.getExistingDirectory(self, "Please select the destination directory", "", QFileDialog.ShowDirsOnly) if len(self.export_dir) > 0: # Show confirmation dialog where we specify the name of the file torrent_name = self.selected_item.download_info['name'] self.dialog = ConfirmationDialog(self, "Export torrent file", "Please enter the name of the torrent file:", [('SAVE', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)], show_input=True) self.dialog.dialog_widget.dialog_input.setPlaceholderText('Torrent file name') self.dialog.dialog_widget.dialog_input.setText("%s.torrent" % torrent_name) self.dialog.dialog_widget.dialog_input.setFocus() self.dialog.button_clicked.connect(self.on_export_download_dialog_done) self.dialog.show() def on_export_download_dialog_done(self, action): if action == 0: filename = self.dialog.dialog_widget.dialog_input.text() self.request_mgr = TriblerRequestManager() self.request_mgr.download_file("downloads/%s/torrent" % self.selected_item.download_info['infohash'], lambda data: self.on_export_download_request_done(filename, data)) self.dialog.setParent(None) self.dialog = None def on_export_download_request_done(self, filename, data): dest_path = os.path.join(self.export_dir, filename) try: torrent_file = open(dest_path, "wb") torrent_file.write(data) torrent_file.close() except IOError as exc: ConfirmationDialog.show_error(self.window(), "Error when exporting file", "An error occurred when exporting the torrent file: %s" % str(exc)) else: if self.window().tray_icon: self.window().tray_icon.showMessage("Torrent file exported", "Torrent file exported to %s" % dest_path) def on_right_click_item(self, pos): item_clicked = self.window().downloads_list.itemAt(pos) if not item_clicked: return self.selected_item = item_clicked menu = TriblerActionMenu(self) start_action = QAction('Start', self) stop_action = QAction('Stop', self) remove_download_action = QAction('Remove download', self) force_recheck_action = QAction('Force recheck', self) export_download_action = QAction('Export .torrent file', self) explore_files_action = QAction('Explore files', self) no_anon_action = QAction('No anonymity', self) one_hop_anon_action = QAction('One hop', self) two_hop_anon_action = QAction('Two hops', self) three_hop_anon_action = QAction('Three hops', self) start_action.triggered.connect(self.on_start_download_clicked) start_action.setEnabled(DownloadsPage.start_download_enabled(self.selected_item)) stop_action.triggered.connect(self.on_stop_download_clicked) stop_action.setEnabled(DownloadsPage.stop_download_enabled(self.selected_item)) remove_download_action.triggered.connect(self.on_remove_download_clicked) force_recheck_action.triggered.connect(self.on_force_recheck_download) force_recheck_action.setEnabled(DownloadsPage.force_recheck_download_enabled(self.selected_item)) export_download_action.triggered.connect(self.on_export_download) explore_files_action.triggered.connect(self.on_explore_files) no_anon_action.triggered.connect(lambda: self.change_anonymity(0)) one_hop_anon_action.triggered.connect(lambda: self.change_anonymity(1)) two_hop_anon_action.triggered.connect(lambda: self.change_anonymity(2)) three_hop_anon_action.triggered.connect(lambda: self.change_anonymity(3)) menu.addAction(start_action) menu.addAction(stop_action) if self.window().vlc_available: play_action = QAction('Play', self) play_action.triggered.connect(self.on_play_download_clicked) menu.addAction(play_action) menu.addSeparator() menu.addAction(remove_download_action) menu.addSeparator() menu.addAction(force_recheck_action) menu.addSeparator() menu.addAction(export_download_action) menu.addSeparator() menu_anon_level = menu.addMenu("Change anonymity") menu_anon_level.addAction(no_anon_action) menu_anon_level.addAction(one_hop_anon_action) menu_anon_level.addAction(two_hop_anon_action) menu_anon_level.addAction(three_hop_anon_action) menu.addAction(explore_files_action) menu.exec_(self.window().downloads_list.mapToGlobal(pos))
class DebugWindow(QMainWindow): """ The debug window shows various statistics about Tribler such as performed requests, Dispersy statistics and community information. """ def __init__(self, settings, tribler_version): QMainWindow.__init__(self) self.request_mgr = None self.cpu_plot = None self.memory_plot = None self.initialized_cpu_plot = False self.initialized_memory_plot = False self.cpu_plot_timer = None self.memory_plot_timer = None self.tribler_version = tribler_version self.profiler_enabled = False self.toggling_profiler = False uic.loadUi(get_ui_file_path('debugwindow.ui'), self) self.setWindowTitle("Tribler debug pane") self.window().dump_memory_core_button.clicked.connect( lambda: self.on_memory_dump_button_clicked(True)) self.window().dump_memory_gui_button.clicked.connect( lambda: self.on_memory_dump_button_clicked(False)) self.window().toggle_profiler_button.clicked.connect( self.on_toggle_profiler_button_clicked) self.window().debug_tab_widget.setCurrentIndex(0) self.window().dispersy_tab_widget.setCurrentIndex(0) self.window().system_tab_widget.setCurrentIndex(0) self.window().debug_tab_widget.currentChanged.connect(self.tab_changed) self.window().dispersy_tab_widget.currentChanged.connect( self.dispersy_tab_changed) self.window().events_tree_widget.itemClicked.connect( self.on_event_clicked) self.window().system_tab_widget.currentChanged.connect( self.system_tab_changed) self.load_general_tab() self.window().open_files_tree_widget.header().setSectionResizeMode( 0, QHeaderView.Stretch) # Enable/disable tabs, based on settings self.window().debug_tab_widget.setTabEnabled( 2, settings and settings['trustchain']['enabled']) self.window().debug_tab_widget.setTabEnabled( 3, settings and settings['dispersy']['enabled']) self.window().system_tab_widget.setTabEnabled( 3, settings and settings['resource_monitor']['enabled']) self.window().system_tab_widget.setTabEnabled( 4, settings and settings['resource_monitor']['enabled']) # Refresh logs self.window().log_refresh_button.clicked.connect( lambda: self.load_logs_tab()) self.window().log_tab_widget.currentChanged.connect( lambda index: self.load_logs_tab()) # Position to center frame_geometry = self.frameGeometry() screen = QDesktopWidget().screenNumber(QDesktopWidget().cursor().pos()) center_point = QDesktopWidget().screenGeometry(screen).center() frame_geometry.moveCenter(center_point) self.move(frame_geometry.topLeft()) def tab_changed(self, index): if index == 0: self.load_general_tab() elif index == 1: self.load_requests_tab() elif index == 2: self.load_trustchain_tab() elif index == 3: self.dispersy_tab_changed( self.window().dispersy_tab_widget.currentIndex()) elif index == 4: self.load_events_tab() elif index == 5: self.system_tab_changed( self.window().system_tab_widget.currentIndex()) elif index == 6: self.load_logs_tab() def dispersy_tab_changed(self, index): if index == 0: self.load_dispersy_general_tab() elif index == 1: self.load_dispersy_communities_tab() def system_tab_changed(self, index): if index == 0: self.load_open_files_tab() elif index == 1: self.load_open_sockets_tab() elif index == 2: self.load_threads_tab() elif index == 3: self.load_cpu_tab() elif index == 4: self.load_memory_tab() elif index == 5: self.load_profiler_tab() def create_and_add_widget_item(self, key, value, widget): item = QTreeWidgetItem(widget) item.setText(0, key) item.setText(1, "%s" % value) widget.addTopLevelItem(item) def load_general_tab(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("statistics/tribler", self.on_tribler_statistics) def on_tribler_statistics(self, data): if not data: return data = data["tribler_statistics"] self.window().general_tree_widget.clear() self.create_and_add_widget_item("Tribler version", self.tribler_version, self.window().general_tree_widget) self.create_and_add_widget_item("Number of channels", data["num_channels"], self.window().general_tree_widget) self.create_and_add_widget_item("Database size", format_size(data["database_size"]), self.window().general_tree_widget) self.create_and_add_widget_item("Number of collected torrents", data["torrents"]["num_collected"], self.window().general_tree_widget) self.create_and_add_widget_item("Number of torrent files", data["torrents"]["num_files"], self.window().general_tree_widget) self.create_and_add_widget_item( "Total size of torrent files", format_size(data["torrents"]["total_size"]), self.window().general_tree_widget) self.create_and_add_widget_item("", "", self.window().general_tree_widget) disk_usage = psutil.disk_usage('/') self.create_and_add_widget_item("Total disk space", format_size(disk_usage.total), self.window().general_tree_widget) self.create_and_add_widget_item("Used disk space", format_size(disk_usage.used), self.window().general_tree_widget) self.create_and_add_widget_item("Free disk space", format_size(disk_usage.free), self.window().general_tree_widget) def load_requests_tab(self): self.window().requests_tree_widget.clear() for endpoint, method, data, timestamp, status_code in sorted( tribler_performed_requests, key=lambda x: x[3]): item = QTreeWidgetItem(self.window().requests_tree_widget) item.setText(0, "%s %s %s" % (method, endpoint, data)) item.setText(1, ("%d" % status_code) if status_code else "unknown") item.setText(2, "%s" % strftime("%H:%M:%S", localtime(timestamp))) self.window().requests_tree_widget.addTopLevelItem(item) def load_trustchain_tab(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("trustchain/statistics", self.on_trustchain_statistics) def on_trustchain_statistics(self, data): if not data: return self.window().trustchain_tree_widget.clear() for key, value in data["statistics"].iteritems(): self.create_and_add_widget_item( key, value, self.window().trustchain_tree_widget) def load_dispersy_general_tab(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("statistics/dispersy", self.on_dispersy_general_stats) def on_dispersy_general_stats(self, data): if not data: return self.window().dispersy_general_tree_widget.clear() for key, value in data["dispersy_statistics"].iteritems(): self.create_and_add_widget_item( key, value, self.window().dispersy_general_tree_widget) def load_dispersy_communities_tab(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("statistics/communities", self.on_dispersy_community_stats) def on_dispersy_community_stats(self, data): if not data: return self.window().communities_tree_widget.clear() for community in data["dispersy_community_statistics"]: item = QTreeWidgetItem(self.window().communities_tree_widget) item.setText(0, community["classification"]) item.setText(1, community["identifier"][:6]) item.setText(2, community["member"][:6]) item.setText(3, "%s" % community["candidates"]) self.window().communities_tree_widget.addTopLevelItem(item) def on_event_clicked(self, item): event_dict = item.data(0, Qt.UserRole) self.window().event_text_box.setPlainText(json.dumps(event_dict)) def load_events_tab(self): self.window().events_tree_widget.clear() for event_dict, timestamp in tribler_received_events: item = QTreeWidgetItem(self.window().events_tree_widget) item.setData(0, Qt.UserRole, event_dict) item.setText(0, "%s" % event_dict['type']) item.setText(1, "%s" % strftime("%H:%M:%S", localtime(timestamp))) self.window().events_tree_widget.addTopLevelItem(item) def load_open_files_tab(self): # Fill the open files (GUI) tree widget my_process = psutil.Process() self.window().open_files_tree_widget.clear() gui_item = QTreeWidgetItem(self.window().open_files_tree_widget) try: open_files = my_process.open_files() gui_item.setText(0, "GUI (%d)" % len(open_files)) self.window().open_files_tree_widget.addTopLevelItem(gui_item) for open_file in open_files: item = QTreeWidgetItem() item.setText(0, open_file.path) item.setText(1, "%d" % open_file.fd) gui_item.addChild(item) except psutil.AccessDenied as exc: gui_item.setText(0, "Unable to get open files for GUI (%s)" % exc) self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("debug/open_files", self.on_core_open_files) def on_core_open_files(self, data): if not data: return core_item = QTreeWidgetItem(self.window().open_files_tree_widget) core_item.setText(0, "Core (%d)" % len(data["open_files"])) self.window().open_files_tree_widget.addTopLevelItem(core_item) for open_file in data["open_files"]: item = QTreeWidgetItem() item.setText(0, open_file["path"]) item.setText(1, "%d" % open_file["fd"]) core_item.addChild(item) def load_open_sockets_tab(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("debug/open_sockets", self.on_core_open_sockets) def on_core_open_sockets(self, data): if not data: return self.window().open_sockets_tree_widget.clear() self.window().open_sockets_label.setText( "Sockets opened by core (%d):" % len(data["open_sockets"])) for open_socket in data["open_sockets"]: if open_socket["family"] == socket.AF_INET: family = "AF_INET" elif open_socket["family"] == socket.AF_INET6: family = "AF_INET6" elif open_socket["family"] == socket.AF_UNIX: family = "AF_UNIX" else: family = "-" item = QTreeWidgetItem(self.window().open_sockets_tree_widget) item.setText(0, open_socket["laddr"]) item.setText(1, open_socket["raddr"]) item.setText(2, family) item.setText( 3, "SOCK_STREAM" if open_socket["type"] == socket.SOCK_STREAM else "SOCK_DGRAM") item.setText(4, open_socket["status"]) self.window().open_sockets_tree_widget.addTopLevelItem(item) def load_threads_tab(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("debug/threads", self.on_core_threads) def on_core_threads(self, data): if not data: return self.window().threads_tree_widget.clear() for thread_info in data["threads"]: thread_item = QTreeWidgetItem(self.window().threads_tree_widget) thread_item.setText(0, "%d" % thread_info["thread_id"]) thread_item.setText(1, thread_info["thread_name"]) self.window().threads_tree_widget.addTopLevelItem(thread_item) for frame in thread_info["frames"]: frame_item = QTreeWidgetItem() frame_item.setText(2, frame) thread_item.addChild(frame_item) def load_cpu_tab(self): if not self.initialized_cpu_plot: vlayout = self.window().cpu_plot_widget.layout() self.cpu_plot = CPUPlotMplCanvas(self.window().cpu_plot_widget, dpi=100) vlayout.addWidget(self.cpu_plot) self.initialized_cpu_plot = True self.refresh_cpu_plot() # Start timer self.cpu_plot_timer = QTimer() self.cpu_plot_timer.timeout.connect(self.load_cpu_tab) self.cpu_plot_timer.start(5000) def refresh_cpu_plot(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("debug/cpu/history", self.on_core_cpu_history) def on_core_cpu_history(self, data): if not data: return plot_data = [[], []] for cpu_info in data["cpu_history"]: if cpu_info["cpu"] == 0.0: continue # Ignore the initial measurement, is always zero plot_data[0].append( datetime.datetime.fromtimestamp(cpu_info["time"])) plot_data[1].append(cpu_info["cpu"]) if len(plot_data[0]) == 0: plot_data = [[datetime.datetime.now()], [0]] self.cpu_plot.plot_data = plot_data self.cpu_plot.compute_initial_figure() def load_memory_tab(self): if not self.initialized_memory_plot: vlayout = self.window().memory_plot_widget.layout() self.memory_plot = MemoryPlotMplCanvas( self.window().memory_plot_widget, dpi=100) vlayout.addWidget(self.memory_plot) self.initialized_memory_plot = True self.refresh_memory_plot() # Start timer self.memory_plot_timer = QTimer() self.memory_plot_timer.timeout.connect(self.load_memory_tab) self.memory_plot_timer.start(5000) def load_profiler_tab(self): self.window().toggle_profiler_button.setEnabled(False) self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("debug/profiler", self.on_profiler_info) def on_profiler_info(self, data): self.profiler_enabled = (data["state"] == "STARTED") self.window().toggle_profiler_button.setEnabled(True) self.window().toggle_profiler_button.setText( "%s profiler" % ("Stop" if self.profiler_enabled else "Start")) def on_toggle_profiler_button_clicked(self): if self.toggling_profiler: return self.toggling_profiler = True self.window().toggle_profiler_button.setEnabled(False) self.request_mgr = TriblerRequestManager() method = "DELETE" if self.profiler_enabled else "PUT" self.request_mgr.perform_request("debug/profiler", self.on_profiler_state_changed, method=method) def on_profiler_state_changed(self, data): self.toggling_profiler = False self.window().toggle_profiler_button.setEnabled(True) self.load_profiler_tab() if 'profiler_file' in data: QMessageBox.about( self, "Profiler statistics saved", "The profiler data has been saved to %s." % data['profiler_file']) def refresh_memory_plot(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("debug/memory/history", self.on_core_memory_history) def on_core_memory_history(self, data): if not data: return plot_data = [[], []] for mem_info in data["memory_history"]: plot_data[0].append( datetime.datetime.fromtimestamp(mem_info["time"])) plot_data[1].append(mem_info["mem"] / 1024 / 1024) if len(plot_data[0]) == 0: plot_data = [[datetime.datetime.now()], [0]] self.memory_plot.plot_data = plot_data self.memory_plot.compute_initial_figure() def on_memory_dump_button_clicked(self, dump_core): self.export_dir = QFileDialog.getExistingDirectory( self, "Please select the destination directory", "", QFileDialog.ShowDirsOnly) if len(self.export_dir) > 0: filename = "tribler_mem_dump_%s_%s.json" % \ ('core' if dump_core else 'gui', datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S")) if dump_core: self.request_mgr = TriblerRequestManager() self.request_mgr.download_file( "debug/memory/dump", lambda data: self.on_memory_dump_data_available( filename, data)) else: scanner.dump_all_objects( os.path.join(self.export_dir, filename)) def on_memory_dump_data_available(self, filename, data): if not data: return dest_path = os.path.join(self.export_dir, filename) try: memory_dump_file = open(dest_path, "wb") memory_dump_file.write(data) memory_dump_file.close() except IOError as exc: ConfirmationDialog.show_error( self.window(), "Error when exporting file", "An error occurred when exporting the torrent file: %s" % str(exc)) def closeEvent(self, close_event): if self.cpu_plot_timer: self.cpu_plot_timer.stop() if self.memory_plot_timer: self.memory_plot_timer.stop() def load_logs_tab(self): # Max lines from GUI max_log_lines = self.window().max_lines_value.text() tab_index = self.window().log_tab_widget.currentIndex() tab_name = "core" if tab_index == 0 else "gui" self.request_mgr = TriblerRequestManager() request_query = "process=%s&max_lines=%s" % (tab_name, max_log_lines) self.request_mgr.perform_request("debug/log?%s" % request_query, self.display_logs) def display_logs(self, data): tab_index = self.window().log_tab_widget.currentIndex() log_display_widget = self.window().core_log_display_area if tab_index == 0 \ else self.window().gui_log_display_area log_display_widget.moveCursor(QTextCursor.End) key_content = u'content' key_max_lines = u'max_lines' if not key_content in data or not data[key_content]: log_display_widget.setPlainText('No logs found') else: log_display_widget.setPlainText(data[key_content]) if not key_max_lines in data or not data[key_max_lines]: self.window().max_lines_value.setText('') else: self.window().max_lines_value.setText(str(data[key_max_lines])) sb = log_display_widget.verticalScrollBar() sb.setValue(sb.maximum()) def show(self): super(DebugWindow, self).show() # this will remove minimized status # and restore window with keeping maximized/normal state self.window().setWindowState(self.window().windowState() & ~Qt.WindowMinimized | Qt.WindowActive) self.window().activateWindow()
class DebugWindow(QMainWindow): """ The debug window shows various statistics about Tribler such as performed requests, IPv8 statistics and community information. """ resize_event = pyqtSignal() def __init__(self, settings, tribler_version): self._logger = logging.getLogger(self.__class__.__name__) QMainWindow.__init__(self) self.request_mgr = None self.cpu_plot = None self.memory_plot = None self.initialized_cpu_plot = False self.initialized_memory_plot = False self.cpu_plot_timer = None self.memory_plot_timer = None self.tribler_version = tribler_version self.profiler_enabled = False self.toggling_profiler = False uic.loadUi(get_ui_file_path('debugwindow.ui'), self) self.setWindowTitle("Tribler debug pane") self.window().dump_memory_core_button.clicked.connect(lambda: self.on_memory_dump_button_clicked(True)) self.window().dump_memory_gui_button.clicked.connect(lambda: self.on_memory_dump_button_clicked(False)) self.window().toggle_profiler_button.clicked.connect(self.on_toggle_profiler_button_clicked) self.window().debug_tab_widget.setCurrentIndex(0) self.window().ipv8_tab_widget.setCurrentIndex(0) self.window().tunnel_tab_widget.setCurrentIndex(0) self.window().system_tab_widget.setCurrentIndex(0) self.window().debug_tab_widget.currentChanged.connect(self.tab_changed) self.window().ipv8_tab_widget.currentChanged.connect(self.ipv8_tab_changed) self.window().tunnel_tab_widget.currentChanged.connect(self.tunnel_tab_changed) self.window().events_tree_widget.itemClicked.connect(self.on_event_clicked) self.window().system_tab_widget.currentChanged.connect(self.system_tab_changed) self.load_general_tab() self.window().open_files_tree_widget.header().setSectionResizeMode(0, QHeaderView.Stretch) # Enable/disable tabs, based on settings self.window().debug_tab_widget.setTabEnabled(2, settings and settings['trustchain']['enabled']) self.window().debug_tab_widget.setTabEnabled(3, settings and settings['ipv8']['enabled']) self.window().system_tab_widget.setTabEnabled(3, settings and settings['resource_monitor']['enabled']) self.window().system_tab_widget.setTabEnabled(4, settings and settings['resource_monitor']['enabled']) # Refresh logs self.window().log_refresh_button.clicked.connect(lambda: self.load_logs_tab()) self.window().log_tab_widget.currentChanged.connect(lambda index: self.load_logs_tab()) # IPv8 statistics enabled? self.ipv8_statistics_enabled = settings['ipv8']['statistics'] # Libtorrent tab self.init_libtorrent_tab() # Position to center frame_geometry = self.frameGeometry() screen = QDesktopWidget().screenNumber(QDesktopWidget().cursor().pos()) center_point = QDesktopWidget().screenGeometry(screen).center() frame_geometry.moveCenter(center_point) self.move(frame_geometry.topLeft()) # Refresh timer self.refresh_timer = None def hideEvent(self, hide_event): self.stop_timer() def run_with_timer(self, call_fn, timeout=DEBUG_PANE_REFRESH_TIMEOUT): call_fn() self.stop_timer() self.refresh_timer = QTimer() self.refresh_timer.setSingleShot(True) self.refresh_timer.timeout.connect(lambda _call_fn=call_fn, _timeout=timeout: self.run_with_timer(_call_fn, timeout=_timeout)) self.refresh_timer.start(timeout) def stop_timer(self): if self.refresh_timer: try: self.refresh_timer.stop() self.refresh_timer.deleteLater() except RuntimeError: self._logger.error("Failed to stop refresh timer in Debug pane") def init_libtorrent_tab(self): self.window().libtorrent_tab_widget.setCurrentIndex(0) self.window().libtorrent_tab_widget.currentChanged.connect(lambda _: self.load_libtorrent_data(export=False)) self.window().lt_zero_hop_btn.clicked.connect(lambda _: self.load_libtorrent_data(export=False)) self.window().lt_one_hop_btn.clicked.connect(lambda _: self.load_libtorrent_data(export=False)) self.window().lt_two_hop_btn.clicked.connect(lambda _: self.load_libtorrent_data(export=False)) self.window().lt_three_hop_btn.clicked.connect(lambda _: self.load_libtorrent_data(export=False)) self.window().lt_export_btn.clicked.connect(lambda _: self.load_libtorrent_data(export=True)) self.window().lt_zero_hop_btn.setChecked(True) def tab_changed(self, index): if index == 0: self.load_general_tab() elif index == 1: self.load_requests_tab() elif index == 2: self.run_with_timer(self.load_trustchain_tab) elif index == 3: self.ipv8_tab_changed(self.window().ipv8_tab_widget.currentIndex()) elif index == 4: self.tunnel_tab_changed(self.window().tunnel_tab_widget.currentIndex()) elif index == 5: self.run_with_timer(self.load_dht_tab) elif index == 6: self.run_with_timer(self.load_events_tab) elif index == 7: self.system_tab_changed(self.window().system_tab_widget.currentIndex()) elif index == 8: self.load_libtorrent_data() elif index == 9: self.load_logs_tab() def ipv8_tab_changed(self, index): if index == 0: self.run_with_timer(self.load_ipv8_general_tab) elif index == 1: self.run_with_timer(self.load_ipv8_communities_tab) elif index == 2: self.run_with_timer(self.load_ipv8_community_details_tab) def tunnel_tab_changed(self, index): if index == 0: self.run_with_timer(self.load_tunnel_circuits_tab) elif index == 1: self.run_with_timer(self.load_tunnel_relays_tab) elif index == 2: self.run_with_timer(self.load_tunnel_exits_tab) def system_tab_changed(self, index): if index == 0: self.load_open_files_tab() elif index == 1: self.load_open_sockets_tab() elif index == 2: self.load_threads_tab() elif index == 3: self.load_cpu_tab() elif index == 4: self.load_memory_tab() elif index == 5: self.load_profiler_tab() def create_and_add_widget_item(self, key, value, widget): item = QTreeWidgetItem(widget) item.setText(0, key) item.setText(1, "%s" % value) widget.addTopLevelItem(item) def load_general_tab(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("statistics/tribler", self.on_tribler_statistics) def on_tribler_statistics(self, data): if not data: return data = data["tribler_statistics"] self.window().general_tree_widget.clear() self.create_and_add_widget_item("Tribler version", self.tribler_version, self.window().general_tree_widget) self.create_and_add_widget_item("Number of channels", data["num_channels"], self.window().general_tree_widget) self.create_and_add_widget_item("Database size", format_size(data["db_size"]), self.window().general_tree_widget) self.create_and_add_widget_item("Number of known torrents", data["num_torrents"], self.window().general_tree_widget) self.create_and_add_widget_item("", "", self.window().general_tree_widget) disk_usage = psutil.disk_usage('/') self.create_and_add_widget_item("Total disk space", format_size(disk_usage.total), self.window().general_tree_widget) self.create_and_add_widget_item("Used disk space", format_size(disk_usage.used), self.window().general_tree_widget) self.create_and_add_widget_item("Free disk space", format_size(disk_usage.free), self.window().general_tree_widget) def load_requests_tab(self): self.window().requests_tree_widget.clear() for endpoint, method, data, timestamp, status_code in sorted(tribler_performed_requests, key=lambda x: x[3]): item = QTreeWidgetItem(self.window().requests_tree_widget) item.setText(0, "%s %s %s" % (method, endpoint, data)) item.setText(1, ("%d" % status_code) if status_code else "unknown") item.setText(2, "%s" % strftime("%H:%M:%S", localtime(timestamp))) self.window().requests_tree_widget.addTopLevelItem(item) def load_trustchain_tab(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("trustchain/statistics", self.on_trustchain_statistics) def on_trustchain_statistics(self, data): if not data: return self.window().trustchain_tree_widget.clear() for key, value in data["statistics"].items(): self.create_and_add_widget_item(key, value, self.window().trustchain_tree_widget) def load_ipv8_general_tab(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("statistics/ipv8", self.on_ipv8_general_stats) def on_ipv8_general_stats(self, data): if not data: return self.window().ipv8_general_tree_widget.clear() for key, value in data["ipv8_statistics"].items(): if key in ('total_up', 'total_down'): value = "%.2f MB" % (value / (1024.0 * 1024.0)) elif key == 'session_uptime': value = "%s" % str(datetime.timedelta(seconds=int(value))) self.create_and_add_widget_item(key, value, self.window().ipv8_general_tree_widget) def load_ipv8_communities_tab(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("ipv8/overlays", self.on_ipv8_community_stats) def _colored_peer_count(self, peer_count, overlay_count, master_peer): is_discovery = (master_peer == "3081a7301006072a8648ce3d020106052b81040027038192000403b3ab059ced9b20646ab5e01" "762b3595c5e8855227ae1e424cff38a1e4edee73734ff2e2e829eb4f39bab20d7578284fcba72" "51acd74e7daf96f21d01ea17077faf4d27a655837d072baeb671287a88554e1191d8904b0dc57" "2d09ff95f10ff092c8a5e2a01cd500624376aec875a6e3028aab784cfaf0bac6527245db8d939" "00d904ac2a922a02716ccef5a22f7968") limits = [20, overlay_count * 30 + 1] if is_discovery else [20, 31] color = 0xF4D03F if peer_count < limits[0] else (0x56F129 if peer_count < limits[1] else 0xF12929) return QBrush(QColor(color)) def on_ipv8_community_stats(self, data): if not data: return self.window().communities_tree_widget.clear() for overlay in data["overlays"]: item = QTreeWidgetItem(self.window().communities_tree_widget) item.setText(0, overlay["overlay_name"]) item.setText(1, overlay["master_peer"][-12:]) item.setText(2, overlay["my_peer"][-12:]) peer_count = len(overlay["peers"]) item.setText(3, "%s" % peer_count) item.setForeground(3, self._colored_peer_count(peer_count, len(data["overlays"]), overlay["master_peer"])) if "statistics" in overlay and overlay["statistics"]: statistics = overlay["statistics"] item.setText(4, "%.3f" % (statistics["bytes_up"]/(1024.0 * 1024.0))) item.setText(5, "%.3f" % (statistics["bytes_down"]/(1024.0 * 1024.0))) item.setText(6, "%s" % statistics["num_up"]) item.setText(7, "%s" % statistics["num_down"]) item.setText(8, "%.3f" % statistics["diff_time"]) else: item.setText(4, "N/A") item.setText(5, "N/A") item.setText(6, "N/A") item.setText(7, "N/A") item.setText(8, "N/A") self.window().communities_tree_widget.addTopLevelItem(item) map(self.window().communities_tree_widget.resizeColumnToContents, xrange(10)) def load_ipv8_community_details_tab(self): if self.ipv8_statistics_enabled: self.window().ipv8_statistics_error_label.setHidden(True) self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("ipv8/overlays/statistics", self.on_ipv8_community_detail_stats) else: self.window().ipv8_statistics_error_label.setHidden(False) self.window().ipv8_communities_details_widget.setHidden(True) def on_ipv8_community_detail_stats(self, data): if not data: return self.window().ipv8_communities_details_widget.setHidden(False) self.window().ipv8_communities_details_widget.clear() for overlay in data["statistics"]: self.window().ipv8_communities_details_widget.setColumnWidth(0, 250) for key, stats in overlay.items(): header_item = QTreeWidgetItem(self.window().ipv8_communities_details_widget) header_item.setFirstColumnSpanned(True) header_item.setBackground(0, QtGui.QColor('#CCCCCC')) header_item.setText(0, key) self.window().ipv8_communities_details_widget.addTopLevelItem(header_item) for request_id, stat in stats.items(): stat_item = QTreeWidgetItem(self.window().ipv8_communities_details_widget) stat_item.setText(0, request_id) stat_item.setText(1, "%.3f" % (stat["bytes_up"] / (1024.0 * 1024.0))) stat_item.setText(2, "%.3f" % (stat["bytes_down"] / (1024.0 * 1024.0))) stat_item.setText(3, "%s" % stat["num_up"]) stat_item.setText(4, "%s" % stat["num_down"]) self.window().ipv8_communities_details_widget.addTopLevelItem(stat_item) def add_items_to_tree(self, tree, items, keys): tree.clear() for item in items: widget_item = QTreeWidgetItem(tree) for index, key in enumerate(keys): value = format_size(item[key]) if key in ["bytes_up", "bytes_down"] else str(item[key]) widget_item.setText(index, value) tree.addTopLevelItem(widget_item) def load_tunnel_circuits_tab(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("ipv8/tunnel/circuits", self.on_tunnel_circuits) def on_tunnel_circuits(self, data): if data: self.add_items_to_tree(self.window().circuits_tree_widget, data.get("circuits"), ["circuit_id", "goal_hops", "actual_hops", "unverified_hop", "type", "state", "bytes_up", "bytes_down"]) def load_tunnel_relays_tab(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("ipv8/tunnel/relays", self.on_tunnel_relays) def on_tunnel_relays(self, data): if data: self.add_items_to_tree(self.window().relays_tree_widget, data["relays"], ["circuit_from", "circuit_to", "is_rendezvous", "bytes_up", "bytes_down"]) def load_tunnel_exits_tab(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("ipv8/tunnel/exits", self.on_tunnel_exits) def on_tunnel_exits(self, data): if data: self.add_items_to_tree(self.window().exits_tree_widget, data["exits"], ["circuit_from", "enabled", "bytes_up", "bytes_down"]) def load_dht_tab(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("ipv8/dht/statistics", self.on_dht_statistics) def on_dht_statistics(self, data): if not data: return self.window().dht_tree_widget.clear() for key, value in data["statistics"].items(): self.create_and_add_widget_item(key, value, self.window().dht_tree_widget) def on_event_clicked(self, item): event_dict = item.data(0, Qt.UserRole) self.window().event_text_box.setPlainText(json.dumps(event_dict)) def load_events_tab(self): self.window().events_tree_widget.clear() for event_dict, timestamp in tribler_received_events: item = QTreeWidgetItem(self.window().events_tree_widget) item.setData(0, Qt.UserRole, event_dict) item.setText(0, "%s" % event_dict['type']) item.setText(1, "%s" % strftime("%H:%M:%S", localtime(timestamp))) self.window().events_tree_widget.addTopLevelItem(item) def load_open_files_tab(self): # Fill the open files (GUI) tree widget my_process = psutil.Process() self.window().open_files_tree_widget.clear() gui_item = QTreeWidgetItem(self.window().open_files_tree_widget) try: open_files = my_process.open_files() gui_item.setText(0, "GUI (%d)" % len(open_files)) self.window().open_files_tree_widget.addTopLevelItem(gui_item) for open_file in open_files: item = QTreeWidgetItem() item.setText(0, open_file.path) item.setText(1, "%d" % open_file.fd) gui_item.addChild(item) except psutil.AccessDenied as exc: gui_item.setText(0, "Unable to get open files for GUI (%s)" % exc) self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("debug/open_files", self.on_core_open_files) def on_core_open_files(self, data): if not data: return core_item = QTreeWidgetItem(self.window().open_files_tree_widget) core_item.setText(0, "Core (%d)" % len(data["open_files"])) self.window().open_files_tree_widget.addTopLevelItem(core_item) for open_file in data["open_files"]: item = QTreeWidgetItem() item.setText(0, open_file["path"]) item.setText(1, "%d" % open_file["fd"]) core_item.addChild(item) def load_open_sockets_tab(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("debug/open_sockets", self.on_core_open_sockets) def on_core_open_sockets(self, data): if not data: return self.window().open_sockets_tree_widget.clear() self.window().open_sockets_label.setText("Sockets opened by core (%d):" % len(data["open_sockets"])) for open_socket in data["open_sockets"]: if open_socket["family"] == socket.AF_INET: family = "AF_INET" elif open_socket["family"] == socket.AF_INET6: family = "AF_INET6" elif open_socket["family"] == socket.AF_UNIX: family = "AF_UNIX" else: family = "-" item = QTreeWidgetItem(self.window().open_sockets_tree_widget) item.setText(0, open_socket["laddr"]) item.setText(1, open_socket["raddr"]) item.setText(2, family) item.setText(3, "SOCK_STREAM" if open_socket["type"] == socket.SOCK_STREAM else "SOCK_DGRAM") item.setText(4, open_socket["status"]) self.window().open_sockets_tree_widget.addTopLevelItem(item) def load_threads_tab(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("debug/threads", self.on_core_threads) def on_core_threads(self, data): if not data: return self.window().threads_tree_widget.clear() for thread_info in data["threads"]: thread_item = QTreeWidgetItem(self.window().threads_tree_widget) thread_item.setText(0, "%d" % thread_info["thread_id"]) thread_item.setText(1, thread_info["thread_name"]) self.window().threads_tree_widget.addTopLevelItem(thread_item) for frame in thread_info["frames"]: frame_item = QTreeWidgetItem() frame_item.setText(2, frame) thread_item.addChild(frame_item) def load_cpu_tab(self): if not self.initialized_cpu_plot: vlayout = self.window().cpu_plot_widget.layout() self.cpu_plot = CPUPlotMplCanvas(self.window().cpu_plot_widget, dpi=100) vlayout.addWidget(self.cpu_plot) self.initialized_cpu_plot = True self.refresh_cpu_plot() # Start timer self.cpu_plot_timer = QTimer() self.cpu_plot_timer.timeout.connect(self.load_cpu_tab) self.cpu_plot_timer.start(5000) def refresh_cpu_plot(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("debug/cpu/history", self.on_core_cpu_history) def on_core_cpu_history(self, data): if not data: return plot_data = [[], []] for cpu_info in data["cpu_history"]: if cpu_info["cpu"] == 0.0: continue # Ignore the initial measurement, is always zero plot_data[0].append(datetime.datetime.fromtimestamp(cpu_info["time"])) plot_data[1].append(cpu_info["cpu"]) if len(plot_data[0]) == 0: plot_data = [[datetime.datetime.now()], [0]] self.cpu_plot.plot_data = plot_data self.cpu_plot.compute_initial_figure() def load_memory_tab(self): if not self.initialized_memory_plot: vlayout = self.window().memory_plot_widget.layout() self.memory_plot = MemoryPlotMplCanvas(self.window().memory_plot_widget, dpi=100) vlayout.addWidget(self.memory_plot) self.initialized_memory_plot = True self.refresh_memory_plot() # Start timer self.memory_plot_timer = QTimer() self.memory_plot_timer.timeout.connect(self.load_memory_tab) self.memory_plot_timer.start(5000) def load_profiler_tab(self): self.window().toggle_profiler_button.setEnabled(False) self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("debug/profiler", self.on_profiler_info) def on_profiler_info(self, data): if not data: return self.profiler_enabled = (data["state"] == "STARTED") self.window().toggle_profiler_button.setEnabled(True) self.window().toggle_profiler_button.setText("%s profiler" % ("Stop" if self.profiler_enabled else "Start")) def on_toggle_profiler_button_clicked(self): if self.toggling_profiler: return self.toggling_profiler = True self.window().toggle_profiler_button.setEnabled(False) self.request_mgr = TriblerRequestManager() method = "DELETE" if self.profiler_enabled else "PUT" self.request_mgr.perform_request("debug/profiler", self.on_profiler_state_changed, method=method) def on_profiler_state_changed(self, data): if not data: return self.toggling_profiler = False self.window().toggle_profiler_button.setEnabled(True) self.load_profiler_tab() if 'profiler_file' in data: QMessageBox.about(self, "Profiler statistics saved", "The profiler data has been saved to %s." % data['profiler_file']) def refresh_memory_plot(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("debug/memory/history", self.on_core_memory_history) def on_core_memory_history(self, data): if not data: return plot_data = [[], []] for mem_info in data["memory_history"]: plot_data[0].append(datetime.datetime.fromtimestamp(mem_info["time"])) plot_data[1].append(mem_info["mem"] / 1024 / 1024) if len(plot_data[0]) == 0: plot_data = [[datetime.datetime.now()], [0]] self.memory_plot.plot_data = plot_data self.memory_plot.compute_initial_figure() def on_memory_dump_button_clicked(self, dump_core): self.export_dir = QFileDialog.getExistingDirectory(self, "Please select the destination directory", "", QFileDialog.ShowDirsOnly) if len(self.export_dir) > 0: filename = "tribler_mem_dump_%s_%s.json" % \ ('core' if dump_core else 'gui', datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S")) if dump_core: self.request_mgr = TriblerRequestManager() self.request_mgr.download_file("debug/memory/dump", lambda data: self.on_memory_dump_data_available(filename, data)) elif scanner: scanner.dump_all_objects(os.path.join(self.export_dir, filename)) else: ConfirmationDialog.show_error(self.window(), "Error when performing a memory dump", "meliae memory dumper is not compatible with Python 3") def on_memory_dump_data_available(self, filename, data): if not data: return dest_path = os.path.join(self.export_dir, filename) try: memory_dump_file = open(dest_path, "wb") memory_dump_file.write(data) memory_dump_file.close() except IOError as exc: ConfirmationDialog.show_error(self.window(), "Error when exporting file", "An error occurred when exporting the torrent file: %s" % str(exc)) def closeEvent(self, close_event): self.request_mgr.cancel_request() if self.cpu_plot_timer: self.cpu_plot_timer.stop() if self.memory_plot_timer: self.memory_plot_timer.stop() def load_logs_tab(self): # Max lines from GUI max_log_lines = self.window().max_lines_value.text() tab_index = self.window().log_tab_widget.currentIndex() tab_name = "core" if tab_index == 0 else "gui" self.request_mgr = TriblerRequestManager() request_query = "process=%s&max_lines=%s" % (tab_name, max_log_lines) self.request_mgr.perform_request("debug/log?%s" % request_query, self.display_logs) def display_logs(self, data): if not data: return tab_index = self.window().log_tab_widget.currentIndex() log_display_widget = self.window().core_log_display_area if tab_index == 0 \ else self.window().gui_log_display_area log_display_widget.moveCursor(QTextCursor.End) key_content = u'content' key_max_lines = u'max_lines' if not key_content in data or not data[key_content]: log_display_widget.setPlainText('No logs found') else: log_display_widget.setPlainText(data[key_content]) if not key_max_lines in data or not data[key_max_lines]: self.window().max_lines_value.setText('') else: self.window().max_lines_value.setText(str(data[key_max_lines])) sb = log_display_widget.verticalScrollBar() sb.setValue(sb.maximum()) def show(self): super(DebugWindow, self).show() # this will remove minimized status # and restore window with keeping maximized/normal state self.window().setWindowState(self.window().windowState() & ~Qt.WindowMinimized | Qt.WindowActive) self.window().activateWindow() def load_libtorrent_data(self, export=False): tab = self.window().libtorrent_tab_widget.currentIndex() hop = 0 if self.window().lt_zero_hop_btn.isChecked() \ else 1 if self.window().lt_one_hop_btn.isChecked() \ else 2 if self.window().lt_two_hop_btn.isChecked() \ else 3 if tab == 0: self.load_libtorrent_settings_tab(hop, export=export) elif tab == 1: self.load_libtorrent_sessions_tab(hop, export=export) def load_libtorrent_settings_tab(self, hop, export=False): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("libtorrent/settings?hop=%d" % hop, lambda data: self.on_libtorrent_settings_received(data, export=export)) self.window().libtorrent_settings_tree_widget.clear() def on_libtorrent_settings_received(self, data, export=False): if not data: return for key, value in data["settings"].items(): item = QTreeWidgetItem(self.window().libtorrent_settings_tree_widget) item.setText(0, key) item.setText(1, str(value)) self.window().libtorrent_settings_tree_widget.addTopLevelItem(item) if export: self.save_to_file("libtorrent_settings.json", data) def load_libtorrent_sessions_tab(self, hop, export=False): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("libtorrent/session?hop=%d" % hop, lambda data: self.on_libtorrent_session_received(data, export=export)) self.window().libtorrent_session_tree_widget.clear() def on_libtorrent_session_received(self, data, export=False): if not data: return for key, value in data["session"].items(): item = QTreeWidgetItem(self.window().libtorrent_session_tree_widget) item.setText(0, key) item.setText(1, str(value)) self.window().libtorrent_session_tree_widget.addTopLevelItem(item) if export: self.save_to_file("libtorrent_session.json", data) def save_to_file(self, filename, data): base_dir = QFileDialog.getExistingDirectory(self, "Select an export directory", "", QFileDialog.ShowDirsOnly) if len(base_dir) > 0: dest_path = os.path.join(base_dir, filename) try: torrent_file = open(dest_path, "wb") torrent_file.write(json.dumps(data)) torrent_file.close() except IOError as exc: ConfirmationDialog.show_error(self.window(), "Error exporting file", str(exc))
class TrustPage(QWidget): """ This page shows various trust statistics. """ def __init__(self): QWidget.__init__(self) self.trust_plot = None self.public_key = None self.request_mgr = None self.blocks = None self.byte_scale = 1024 * 1024 self.dialog = None # Timer for garbage collection self.gc_timer = 0 def initialize_trust_page(self): vlayout = self.window().plot_widget.layout() self.trust_plot = TrustPlotMplCanvas(self.window().plot_widget, dpi=100) vlayout.addWidget(self.trust_plot) self.window().trade_button.clicked.connect(self.on_trade_button_clicked) self.window().trust_explain_button.clicked.connect(self.on_info_button_clicked) def on_trade_button_clicked(self): self.window().market_page.initialize_market_page() self.window().navigation_stack.append(self.window().stackedWidget.currentIndex()) self.window().stackedWidget.setCurrentIndex(PAGE_MARKET) def on_info_button_clicked(self): self.dialog = TrustExplanationDialog(self.window()) self.dialog.show() def load_blocks(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("ipv8/trustchain/users/%s/blocks" % self.public_key, self.received_trustchain_blocks) def received_trustchain_statistics(self, statistics): if not statistics: return statistics = statistics["statistics"] self.public_key = statistics["id"] total_up = 0 total_down = 0 if 'latest_block' in statistics: total_up = statistics["latest_block"]["transaction"]["total_up"] total_down = statistics["latest_block"]["transaction"]["total_down"] self.window().trust_contribution_amount_label.setText("%s MBytes" % (total_up / self.byte_scale)) self.window().trust_consumption_amount_label.setText("%s MBytes" % (total_down / self.byte_scale)) self.window().trust_people_helped_label.setText("%d" % statistics["peers_that_pk_helped"]) self.window().trust_people_helped_you_label.setText("%d" % statistics["peers_that_helped_pk"]) def received_trustchain_blocks(self, blocks): if blocks: self.blocks = blocks["blocks"] self.plot_absolute_values() # Matplotlib is leaking memory on re-plotting. Refer: https://github.com/matplotlib/matplotlib/issues/8528 # Note that gc is called every 10 minutes. if self.gc_timer == GC_TIMEOUT: gc.collect() self.gc_timer = 0 else: self.gc_timer += 1 def plot_absolute_values(self): """ Plot two lines of the absolute amounts of contributed and consumed bytes. """ plot_data = [[[], []], []] # Convert all dates to a datetime object num_bandwidth_blocks = 0 for block in self.blocks: if block["type"] != "tribler_bandwidth": continue plot_data[1].append(datetime.datetime.strptime(block["insert_time"], "%Y-%m-%d %H:%M:%S")) plot_data[0][0].append(block["transaction"]["total_up"] / self.byte_scale) plot_data[0][1].append(block["transaction"]["total_down"] / self.byte_scale) num_bandwidth_blocks += 1 if num_bandwidth_blocks == 0: # Create on single data point with 0mb up and 0mb down plot_data = [[[0], [0]], [datetime.datetime.now()]] self.trust_plot.plot_data = plot_data self.trust_plot.compute_initial_figure()
def clicked_edit_channel_commit_button(self): request_mgr = TriblerRequestManager() request_mgr.perform_request("mychannel/commit", self.on_channel_committed, method='POST')
class FeedbackDialog(QDialog): def __init__(self, parent, exception_text, tribler_version, start_time): QDialog.__init__(self, parent) uic.loadUi(get_ui_file_path('feedback_dialog.ui'), self) self.setWindowTitle("Unexpected error") self.selected_item_index = 0 self.tribler_version = tribler_version self.request_mgr = None # Qt 5.2 does not have the setPlaceholderText property if hasattr(self.comments_text_edit, "setPlaceholderText"): self.comments_text_edit.setPlaceholderText("Comments (optional)") def add_item_to_info_widget(key, value): item = QTreeWidgetItem(self.env_variables_list) item.setText(0, key) item.setText(1, value) self.error_text_edit.setPlainText(exception_text.rstrip()) self.cancel_button.clicked.connect(self.on_cancel_clicked) self.send_report_button.clicked.connect(self.on_send_clicked) # Add machine information to the tree widget add_item_to_info_widget('os.getcwd', '%s' % os.getcwd()) add_item_to_info_widget('sys.executable', '%s' % sys.executable) add_item_to_info_widget('os', os.name) add_item_to_info_widget('platform', sys.platform) add_item_to_info_widget('platform.details', platform.platform()) add_item_to_info_widget('platform.machine', platform.machine()) add_item_to_info_widget('python.version', sys.version) add_item_to_info_widget('indebug', str(__debug__)) add_item_to_info_widget('tribler_uptime', "%s" % (time.time() - start_time)) for argv in sys.argv: add_item_to_info_widget('sys.argv', '%s' % argv) for path in sys.path: add_item_to_info_widget('sys.path', '%s' % path) for key in os.environ.keys(): add_item_to_info_widget('os.environ', '%s: %s' % (key, os.environ[key])) # Add recent requests to feedback dialog request_ind = 1 for endpoint, method, data, timestamp, status_code in sorted(tribler_performed_requests, key=lambda x: x[3])[-30:]: add_item_to_info_widget('request_%d' % request_ind, '%s %s %s (time: %s, code: %s)' % (endpoint, method, data, timestamp, status_code)) request_ind += 1 # Add recent events to feedback dialog events_ind = 1 for event, event_time in received_events[:30][::-1]: add_item_to_info_widget('event_%d' % events_ind, '%s (time: %s)' % (json.dumps(event), event_time)) events_ind += 1 # Users can remove specific lines in the report self.env_variables_list.customContextMenuRequested.connect(self.on_right_click_item) def on_remove_entry(self): self.env_variables_list.takeTopLevelItem(self.selected_item_index) def on_right_click_item(self, pos): item_clicked = self.env_variables_list.itemAt(pos) if not item_clicked: return self.selected_item_index = self.env_variables_list.indexOfTopLevelItem(item_clicked) menu = TriblerActionMenu(self) remove_action = QAction('Remove entry', self) remove_action.triggered.connect(self.on_remove_entry) menu.addAction(remove_action) menu.exec_(self.env_variables_list.mapToGlobal(pos)) def on_cancel_clicked(self): QApplication.quit() def on_report_sent(self, response): if not response: return sent = response[u'sent'] success_text = "Successfully sent the report! Thanks for your contribution." error_text = "Could not send the report! Please post this issue on GitHub." box = QMessageBox(self.window()) box.setWindowTitle("Report Sent" if sent else "ERROR: Report Sending Failed") box.setText(success_text if sent else error_text) box.setStyleSheet("QPushButton { color: white; }") box.exec_() QApplication.quit() def on_send_clicked(self): self.send_report_button.setEnabled(False) self.send_report_button.setText("SENDING...") self.request_mgr = TriblerRequestManager() endpoint = 'http://reporter.tribler.org/report' sys_info = "" for ind in xrange(self.env_variables_list.topLevelItemCount()): item = self.env_variables_list.topLevelItem(ind) sys_info += "%s\t%s\n" % (quote_plus(item.text(0)), quote_plus(item.text(1))) comments = self.comments_text_edit.toPlainText() if len(comments) == 0: comments = "Not provided" comments = quote_plus(comments) stack = quote_plus(self.error_text_edit.toPlainText()) post_data = "version=%s&machine=%s&os=%s×tamp=%s&sysinfo=%s&comments=%s&stack=%s" % \ (self.tribler_version, platform.machine(), platform.platform(), int(time.time()), sys_info, comments, stack) self.request_mgr.perform_request(endpoint, self.on_report_sent, data=str(post_data), method='POST') def closeEvent(self, close_event): QApplication.quit() close_event.ignore()
class SettingsPage(QWidget): """ This class is responsible for displaying and adjusting the settings present in Tribler. """ def __init__(self): QWidget.__init__(self) self.settings = None self.settings_request_mgr = None self.saved_dialog = None def initialize_settings_page(self): self.window().settings_tab.initialize() self.window().settings_tab.clicked_tab_button.connect( self.clicked_tab_button) self.window().settings_save_button.clicked.connect(self.save_settings) self.window().developer_mode_enabled_checkbox.stateChanged.connect( self.on_developer_mode_checkbox_changed) self.window().download_settings_anon_checkbox.stateChanged.connect( self.on_anon_download_state_changed) def on_developer_mode_checkbox_changed(self, _): self.window().gui_settings.setValue( "debug", self.window().developer_mode_enabled_checkbox.isChecked()) self.window().left_menu_button_debug.setHidden( not self.window().developer_mode_enabled_checkbox.isChecked()) def on_anon_download_state_changed(self, _): if self.window().download_settings_anon_checkbox.isChecked(): self.window().download_settings_anon_seeding_checkbox.setChecked( True) self.window().download_settings_anon_seeding_checkbox.setEnabled( not self.window().download_settings_anon_checkbox.isChecked()) def initialize_with_settings(self, settings): self.settings = settings settings = settings["settings"] gui_settings = self.window().gui_settings # General settings self.window().developer_mode_enabled_checkbox.setChecked( get_gui_setting(gui_settings, "debug", False, is_bool=True)) self.window().family_filter_checkbox.setChecked( settings['general']['family_filter']) self.window().download_location_input.setText( settings['downloadconfig']['saveas']) self.window().always_ask_location_checkbox.setChecked( get_gui_setting(gui_settings, "ask_download_settings", True, is_bool=True)) self.window().download_settings_anon_checkbox.setChecked( get_gui_setting(gui_settings, "default_anonymity_enabled", True, is_bool=True)) self.window().download_settings_anon_seeding_checkbox.setChecked( get_gui_setting(gui_settings, "default_safeseeding_enabled", True, is_bool=True)) self.window().watchfolder_enabled_checkbox.setChecked( settings['watch_folder']['enabled']) self.window().watchfolder_location_input.setText( settings['watch_folder']['watch_folder_dir']) # Connection settings self.window().firewall_current_port_input.setText( str(settings['general']['minport'])) self.window().lt_proxy_type_combobox.setCurrentIndex( settings['libtorrent']['lt_proxytype']) if settings['libtorrent']['lt_proxyserver']: self.window().lt_proxy_server_input.setText( settings['libtorrent']['lt_proxyserver'][0]) self.window().lt_proxy_port_input.setText( settings['libtorrent']['lt_proxyserver'][1]) if settings['libtorrent']['lt_proxyauth']: self.window().lt_proxy_username_input.setText( settings['libtorrent']['lt_proxyauth'][0]) self.window().lt_proxy_password_input.setText( settings['libtorrent']['lt_proxyauth'][1]) self.window().lt_utp_checkbox.setChecked(settings['libtorrent']['utp']) max_conn_download = settings['libtorrent']['max_connections_download'] if max_conn_download == -1: max_conn_download = 0 self.window().max_connections_download_input.setText( str(max_conn_download)) # Bandwidth settings self.window().upload_rate_limit_input.setText( str(settings['Tribler']['maxuploadrate'])) self.window().download_rate_limit_input.setText( str(settings['Tribler']['maxdownloadrate'])) # Seeding settings getattr( self.window(), "seeding_" + settings['downloadconfig']['seeding_mode'] + "_radio").setChecked(True) self.window().seeding_time_input.setText( seconds_to_string(settings['downloadconfig']['seeding_time'])) ind = self.window().seeding_ratio_combobox.findText( str(settings['downloadconfig']['seeding_ratio'])) if ind != -1: self.window().seeding_ratio_combobox.setCurrentIndex(ind) # Anonymity settings self.window().allow_exit_node_checkbox.setChecked( settings['tunnel_community']['exitnode_enabled']) self.window().number_hops_slider.setValue( int(settings['Tribler']['default_number_hops']) - 1) self.window().multichain_enabled_checkbox.setChecked( settings['multichain']['enabled']) def load_settings(self): self.settings_request_mgr = TriblerRequestManager() self.settings_request_mgr.perform_request( "settings", self.initialize_with_settings) def clicked_tab_button(self, tab_button_name): if tab_button_name == "settings_general_button": self.window().settings_stacked_widget.setCurrentIndex( PAGE_SETTINGS_GENERAL) elif tab_button_name == "settings_connection_button": self.window().settings_stacked_widget.setCurrentIndex( PAGE_SETTINGS_CONNECTION) elif tab_button_name == "settings_bandwidth_button": self.window().settings_stacked_widget.setCurrentIndex( PAGE_SETTINGS_BANDWIDTH) elif tab_button_name == "settings_seeding_button": self.window().settings_stacked_widget.setCurrentIndex( PAGE_SETTINGS_SEEDING) elif tab_button_name == "settings_anonymity_button": self.window().settings_stacked_widget.setCurrentIndex( PAGE_SETTINGS_ANONYMITY) def save_settings(self): # Create a dictionary with all available settings settings_data = { 'general': {}, 'Tribler': {}, 'downloadconfig': {}, 'libtorrent': {}, 'watch_folder': {}, 'tunnel_community': {}, 'multichain': {} } settings_data['general']['family_filter'] = self.window( ).family_filter_checkbox.isChecked() settings_data['downloadconfig']['saveas'] = self.window( ).download_location_input.text() settings_data['watch_folder']['enabled'] = self.window( ).watchfolder_enabled_checkbox.isChecked() if settings_data['watch_folder']['enabled']: settings_data['watch_folder']['watch_folder_dir'] = self.window( ).watchfolder_location_input.text() settings_data['general']['minport'] = self.window( ).firewall_current_port_input.text() settings_data['libtorrent']['lt_proxytype'] = self.window( ).lt_proxy_type_combobox.currentIndex() if len(self.window().lt_proxy_server_input.text()) > 0 and len( self.window().lt_proxy_port_input.text()) > 0: settings_data['libtorrent']['lt_proxyserver'] = [None, None] settings_data['libtorrent']['lt_proxyserver'][0] = self.window( ).lt_proxy_server_input.text() settings_data['libtorrent']['lt_proxyserver'][1] = self.window( ).lt_proxy_port_input.text() if len(self.window().lt_proxy_username_input.text()) > 0 and \ len(self.window().lt_proxy_password_input.text()) > 0: settings_data['libtorrent']['lt_proxyauth'] = [None, None] settings_data['libtorrent']['lt_proxyauth'][0] = self.window( ).lt_proxy_username_input.text() settings_data['libtorrent']['lt_proxyauth'][1] = self.window( ).lt_proxy_password_input.text() settings_data['libtorrent']['utp'] = self.window( ).lt_utp_checkbox.isChecked() try: max_conn_download = int( self.window().max_connections_download_input.text()) except ValueError: ConfirmationDialog.show_error( self.window(), "Invalid number of connections", "You've entered an invalid format for the maximum number of connections." ) return if max_conn_download == 0: max_conn_download = -1 settings_data['libtorrent'][ 'max_connections_download'] = max_conn_download if self.window().upload_rate_limit_input.text(): settings_data['Tribler']['maxuploadrate'] = self.window( ).upload_rate_limit_input.text() if self.window().download_rate_limit_input.text(): settings_data['Tribler']['maxdownloadrate'] = self.window( ).download_rate_limit_input.text() seeding_modes = ['forever', 'time', 'never', 'ratio'] selected_mode = 'forever' for seeding_mode in seeding_modes: if getattr(self.window(), "seeding_" + seeding_mode + "_radio").isChecked(): selected_mode = seeding_mode break settings_data['downloadconfig']['seeding_mode'] = selected_mode settings_data['downloadconfig']['seeding_ratio'] = self.window( ).seeding_ratio_combobox.currentText() try: settings_data['downloadconfig'][ 'seeding_time'] = string_to_minutes( self.window().seeding_time_input.text()) except ValueError: ConfirmationDialog.show_error( self.window(), "Invalid seeding time", "You've entered an invalid format for the seeding time (expected HH:MM)" ) return settings_data['tunnel_community']['exitnode_enabled'] = self.window( ).allow_exit_node_checkbox.isChecked() settings_data['Tribler']['default_number_hops'] = self.window( ).number_hops_slider.value() + 1 settings_data['multichain']['enabled'] = self.window( ).multichain_enabled_checkbox.isChecked() self.settings_request_mgr = TriblerRequestManager() self.settings_request_mgr.perform_request( "settings", self.on_settings_saved, method='POST', data=json.dumps(settings_data)) def on_settings_saved(self, _): # Now save the GUI settings self.window().gui_settings.setValue( "ask_download_settings", self.window().always_ask_location_checkbox.isChecked()) self.window().gui_settings.setValue( "default_anonymity_enabled", self.window().download_settings_anon_checkbox.isChecked()) self.window().gui_settings.setValue( "default_safeseeding_enabled", self.window().download_settings_anon_seeding_checkbox.isChecked()) self.saved_dialog = ConfirmationDialog( TriblerRequestManager.window, "Settings saved", "Your settings have been saved.", [('close', BUTTON_TYPE_NORMAL)]) self.saved_dialog.button_clicked.connect(self.on_dialog_cancel_clicked) self.saved_dialog.show() self.window().fetch_settings() def on_dialog_cancel_clicked(self, _): self.saved_dialog.setParent(None) self.saved_dialog = None
class SubscriptionsWidget(QWidget): """ This widget shows a favorite button and the number of subscriptions that a specific channel has. """ unsubscribed_channel = pyqtSignal(object) subscribed_channel = pyqtSignal(object) def __init__(self, parent): QWidget.__init__(self, parent) self.subscribe_button = None self.channel_info = None self.num_subs_label = None self.credit_mining_button = None self.request_mgr = None self.initialized = False def initialize_with_channel(self, channel): self.channel_info = channel if not self.initialized: self.subscribe_button = self.findChild(QWidget, "subscribe_button") self.num_subs_label = self.findChild(QWidget, "num_subs_label") self.credit_mining_button = self.findChild(QWidget, "credit_mining_button") self.subscribe_button.clicked.connect(self.on_subscribe_button_click) self.credit_mining_button.clicked.connect(self.on_credit_mining_button_click) self.initialized = True self.update_subscribe_button() def update_subscribe_button(self, remote_response=None): if remote_response and 'subscribed' in remote_response: self.channel_info["subscribed"] = remote_response['subscribed'] if remote_response and 'votes' in remote_response: self.channel_info["votes"] = remote_response['votes'] if self.channel_info["subscribed"]: self.subscribe_button.setIcon(QIcon(QPixmap(get_image_path('subscribed_yes.png')))) else: self.subscribe_button.setIcon(QIcon(QPixmap(get_image_path('subscribed_not.png')))) self.num_subs_label.setText(str(self.channel_info["votes"])) if self.window().tribler_settings: # It could be that the settings are not loaded yet self.credit_mining_button.setHidden(not self.window().tribler_settings["credit_mining"]["enabled"]) if self.channel_info["dispersy_cid"] in self.window().tribler_settings["credit_mining"]["sources"]: self.credit_mining_button.setIcon(QIcon(QPixmap(get_image_path('credit_mining_yes.png')))) else: self.credit_mining_button.setIcon(QIcon(QPixmap(get_image_path('credit_mining_not.png')))) else: self.credit_mining_button.hide() def on_subscribe_button_click(self): self.request_mgr = TriblerRequestManager() if self.channel_info["subscribed"]: self.request_mgr.perform_request("channels/subscribed/%s" % self.channel_info['dispersy_cid'], self.on_channel_unsubscribed, method='DELETE') else: self.request_mgr.perform_request("channels/subscribed/%s" % self.channel_info['dispersy_cid'], self.on_channel_subscribed, method='PUT') def on_channel_unsubscribed(self, json_result): if not json_result: return if json_result["unsubscribed"]: self.unsubscribed_channel.emit(self.channel_info) self.channel_info["subscribed"] = False self.channel_info["votes"] -= 1 self.update_subscribe_button() def on_channel_subscribed(self, json_result): if not json_result: return if json_result["subscribed"]: self.subscribed_channel.emit(self.channel_info) self.channel_info["subscribed"] = True self.channel_info["votes"] += 1 self.update_subscribe_button() def on_credit_mining_button_click(self): old_sources = self.window().tribler_settings["credit_mining"]["sources"] new_sources = [] if self.channel_info["dispersy_cid"] in old_sources else [self.channel_info["dispersy_cid"]] settings = {"credit_mining": {"sources": new_sources}} self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("settings", self.on_credit_mining_sources, method='POST', data=json.dumps(settings)) def on_credit_mining_sources(self, json_result): if not json_result: return if json_result["modified"]: old_source = next(iter(self.window().tribler_settings["credit_mining"]["sources"]), None) new_sources = [self.channel_info["dispersy_cid"]] if self.channel_info["dispersy_cid"] != old_source else [] self.window().tribler_settings["credit_mining"]["sources"] = new_sources self.update_subscribe_button() channels_list = self.window().discovered_channels_list for index, data_item in enumerate(channels_list.data_items): if data_item[1]['dispersy_cid'] == old_source: channel_item = channels_list.itemWidget(channels_list.item(index)) if channel_item: channel_item.subscriptions_widget.update_subscribe_button() break
class StartDownloadDialog(DialogContainer): button_clicked = pyqtSignal(int) received_metainfo = pyqtSignal(dict) def __init__(self, parent, download_uri): DialogContainer.__init__(self, parent) torrent_name = download_uri if torrent_name.startswith('file:'): torrent_name = torrent_name[5:] elif torrent_name.startswith('magnet:'): torrent_name = unquote_plus(torrent_name) self.download_uri = download_uri self.has_metainfo = False gui_settings = self.window().gui_settings uic.loadUi(get_ui_file_path('startdownloaddialog.ui'), self.dialog_widget) self.dialog_widget.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) self.dialog_widget.browse_dir_button.clicked.connect( self.on_browse_dir_clicked) self.dialog_widget.cancel_button.clicked.connect( lambda: self.button_clicked.emit(0)) self.dialog_widget.download_button.clicked.connect( self.on_download_clicked) self.dialog_widget.select_all_files_button.clicked.connect( self.on_all_files_selected_clicked) self.dialog_widget.deselect_all_files_button.clicked.connect( self.on_all_files_deselected_clicked) self.dialog_widget.destination_input.setStyleSheet(""" QComboBox { background-color: #444; border: none; color: #C0C0C0; padding: 4px; } QComboBox::drop-down { width: 20px; border: 1px solid #999; border-radius: 2px; } QComboBox QAbstractItemView { selection-background-color: #707070; color: #C0C0C0; } QComboBox::down-arrow { width: 12px; height: 12px; image: url('%s'); } """ % get_image_path('down_arrow_input.png')) if self.window().tribler_settings: # Set the most recent download locations in the QComboBox current_settings = get_gui_setting(self.window().gui_settings, "recent_download_locations", "") if len(current_settings) > 0: recent_locations = [ url.decode('hex') for url in current_settings.split(",") ] self.dialog_widget.destination_input.addItems(recent_locations) else: self.dialog_widget.destination_input.setCurrentText( self.window().tribler_settings['download_defaults'] ['saveas']) self.dialog_widget.torrent_name_label.setText(torrent_name) self.dialog_widget.anon_download_checkbox.stateChanged.connect( self.on_anon_download_state_changed) self.dialog_widget.anon_download_checkbox\ .setChecked(self.window().tribler_settings['download_defaults']['anonymity_enabled']) self.dialog_widget.safe_seed_checkbox\ .setChecked(self.window().tribler_settings['download_defaults']['safeseeding_enabled']) self.dialog_widget.safe_seed_checkbox.setEnabled( self.dialog_widget.anon_download_checkbox.isChecked()) self.perform_files_request() self.dialog_widget.files_list_view.setHidden(True) self.dialog_widget.download_files_container.setHidden(True) self.dialog_widget.adjustSize() self.on_anon_download_state_changed(None) self.on_main_window_resize() def get_selected_files(self): included_files = [] for ind in xrange( self.dialog_widget.files_list_view.topLevelItemCount()): item = self.dialog_widget.files_list_view.topLevelItem(ind) if item.checkState(2) == Qt.Checked: included_files.append(u'/'.join( item.data(0, Qt.UserRole)['path'])) return included_files def perform_files_request(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("torrentinfo?uri=%s" % quote_plus_unicode(self.download_uri), self.on_received_metainfo, capture_errors=False) def on_received_metainfo(self, metainfo): if not metainfo: return if 'error' in metainfo: if metainfo['error'] == 'timeout': self.dialog_widget.loading_files_label.setText( "Timeout when trying to fetch files.") elif 'code' in metainfo['error'] and metainfo['error'][ 'code'] == 'IOError': self.dialog_widget.loading_files_label.setText( "Unable to read torrent file data") else: self.dialog_widget.loading_files_label.setText( "Error: %s" % metainfo['error']) return metainfo = metainfo['metainfo'] if 'files' in metainfo['info']: # Multi-file torrent files = metainfo['info']['files'] else: files = [{ 'path': [metainfo['info']['name']], 'length': metainfo['info']['length'] }] for filename in files: item = DownloadFileTreeWidgetItem( self.dialog_widget.files_list_view) item.setText(0, '/'.join(filename['path'])) item.setText(1, format_size(float(filename['length']))) item.setData(0, Qt.UserRole, filename) item.setCheckState(2, Qt.Checked) self.dialog_widget.files_list_view.addTopLevelItem(item) self.has_metainfo = True self.dialog_widget.loading_files_label.setHidden(True) self.dialog_widget.download_files_container.setHidden(False) self.dialog_widget.files_list_view.setHidden(False) self.dialog_widget.adjustSize() self.on_main_window_resize() self.received_metainfo.emit(metainfo) def on_browse_dir_clicked(self): chosen_dir = QFileDialog.getExistingDirectory( self.window(), "Please select the destination directory of your " "download", "", QFileDialog.ShowDirsOnly) if len(chosen_dir) != 0: self.dialog_widget.destination_input.setCurrentText(chosen_dir) if not is_dir_writable(chosen_dir): ConfirmationDialog.show_message( self.dialog_widget, "Insufficient Permissions", "Tribler cannot download to <i>%s</i> directory. " "Please add proper write permissions to the directory " "or choose another download directory." % chosen_dir, "OK") def on_anon_download_state_changed(self, _): if self.dialog_widget.anon_download_checkbox.isChecked(): self.dialog_widget.safe_seed_checkbox.setChecked(True) self.dialog_widget.safe_seed_checkbox.setEnabled( not self.dialog_widget.anon_download_checkbox.isChecked()) def on_download_clicked(self): if self.has_metainfo and len(self.get_selected_files() ) == 0: # User deselected all torrents ConfirmationDialog.show_error( self.window(), "No files selected", "Please select at least one file to download.") else: download_dir = self.dialog_widget.destination_input.currentText() if not is_dir_writable(download_dir): ConfirmationDialog.show_message( self.dialog_widget, "Insufficient Permissions", "Tribler cannot download to <i>%s</i> directory. " "Please add proper write permissions to the directory " "or choose another download directory and try to download again." % download_dir, "OK") else: self.button_clicked.emit(1) def on_all_files_selected_clicked(self): for ind in xrange( self.dialog_widget.files_list_view.topLevelItemCount()): item = self.dialog_widget.files_list_view.topLevelItem(ind) item.setCheckState(2, Qt.Checked) def on_all_files_deselected_clicked(self): for ind in xrange( self.dialog_widget.files_list_view.topLevelItemCount()): item = self.dialog_widget.files_list_view.topLevelItem(ind) item.setCheckState(2, Qt.Unchecked)
class MarketPage(QWidget): """ This page displays the decentralized market in Tribler. """ def __init__(self): QWidget.__init__(self) self.request_mgr = None self.asks_request_mgr = None self.bids_request_mgr = None self.dialog = None self.initialized = False self.wallets = [] self.chosen_wallets = None self.wallet_widgets = {} self.bids = [] self.asks = [] def initialize_market_page(self): if not self.initialized: self.window().market_back_button.setIcon( QIcon(get_image_path('page_back.png'))) self.window( ).core_manager.events_manager.received_market_ask.connect( self.on_ask) self.window( ).core_manager.events_manager.received_market_bid.connect( self.on_bid) self.window( ).core_manager.events_manager.expired_market_ask.connect( self.on_ask_timeout) self.window( ).core_manager.events_manager.expired_market_bid.connect( self.on_bid_timeout) self.window( ).core_manager.events_manager.market_payment_received.connect( self.on_payment) self.window( ).core_manager.events_manager.market_payment_sent.connect( self.on_payment) self.window( ).core_manager.events_manager.market_transaction_complete.connect( self.on_transaction_complete) self.window( ).core_manager.events_manager.market_iom_input_required.connect( self.on_iom_input_required) self.window().create_ask_button.clicked.connect( self.on_create_ask_clicked) self.window().create_bid_button.clicked.connect( self.on_create_bid_clicked) self.window().market_currency_type_button.clicked.connect( self.on_currency_type_clicked) self.window().market_transactions_button.clicked.connect( self.on_transactions_button_clicked) self.window().market_wallets_button.clicked.connect( self.on_wallets_button_clicked) self.window().market_orders_button.clicked.connect( self.on_orders_button_clicked) self.window().market_create_wallet_button.clicked.connect( self.on_wallets_button_clicked) # Sort asks ascending and bids descending self.window().asks_list.sortItems(2, Qt.AscendingOrder) self.window().bids_list.sortItems(2, Qt.DescendingOrder) self.window().asks_list.itemSelectionChanged.connect( lambda: self.on_tick_item_clicked(self.window().asks_list)) self.window().bids_list.itemSelectionChanged.connect( lambda: self.on_tick_item_clicked(self.window().bids_list)) self.window().tick_detail_container.hide() self.window().market_create_wallet_button.hide() self.initialized = True self.load_wallets() def load_wallets(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("wallets", self.on_wallets) def on_wallets(self, wallets): wallets = wallets["wallets"] currency_wallets = ['BTC'] total_currency_wallets = 0 for wallet_id in wallets.keys(): if wallet_id in currency_wallets: total_currency_wallets += 1 if currency_wallets == 0: self.window().market_create_wallet_button.show() self.window().create_ask_button.hide() self.window().create_bid_button.hide() self.wallets = [] for wallet_id in wallets.keys(): self.wallets.append(wallet_id) if self.chosen_wallets is None and len(self.wallets) >= 2: self.chosen_wallets = (self.wallets[0], self.wallets[1]) self.update_button_texts() for wallet_id, wallet in wallets.iteritems(): if not wallet['created'] or not wallet['unlocked']: continue if wallet_id not in self.wallet_widgets: wallet_widget = MarketCurrencyBox( self.window().market_header_widget, wallets[wallet_id]['name']) self.window().market_header_widget.layout().insertWidget( 4, wallet_widget) wallet_widget.setFixedWidth(100) wallet_widget.setFixedHeight(34) wallet_widget.show() self.wallet_widgets[wallet_id] = wallet_widget spacer = QSpacerItem(10, 20, QSizePolicy.Fixed, QSizePolicy.Fixed) self.window().market_header_widget.layout().insertSpacerItem( 5, spacer) # The total balance keys might be different between wallet balance_amount = wallet['balance']['available'] balance_currency = None if wallet_id == 'PP' or wallet_id == 'ABNA' or wallet_id == 'RABO': balance_currency = wallet['balance']['currency'] self.wallet_widgets[wallet_id].update_with_amount( balance_amount, balance_currency) self.load_asks() self.load_bids() def update_button_texts(self): self.window().market_currency_type_button.setText( "%s / %s" % (self.chosen_wallets[0], self.chosen_wallets[1])) self.window().create_ask_button.setText( "Sell %s for %s" % (self.chosen_wallets[0], self.chosen_wallets[1])) self.window().create_bid_button.setText( "Buy %s for %s" % (self.chosen_wallets[0], self.chosen_wallets[1])) def create_widget_item_from_tick(self, tick_list, tick, is_ask=True): tick["type"] = "ask" if is_ask else "bid" item = TickWidgetItem(tick_list, tick) item.setText(0, "%s %s" % (tick["quantity"], tick["quantity_type"])) item.setText(1, "%s %s" % (tick["price"], tick["price_type"])) return item def load_asks(self): self.asks_request_mgr = TriblerRequestManager() self.asks_request_mgr.perform_request("market/asks", self.on_received_asks) def on_received_asks(self, asks): self.asks = asks["asks"] self.update_filter_asks_list() def update_filter_asks_list(self): self.window().asks_list.clear() ticks = None for price_level_info in self.asks: if (price_level_info['quantity_type'], price_level_info['price_type']) == self.chosen_wallets: ticks = price_level_info["ticks"] break if ticks: for ask in ticks: self.window().asks_list.addTopLevelItem( self.create_widget_item_from_tick(self.window().asks_list, ask, is_ask=True)) def load_bids(self): self.bids_request_mgr = TriblerRequestManager() self.bids_request_mgr.perform_request("market/bids", self.on_received_bids) def on_received_bids(self, bids): self.bids = bids["bids"] self.update_filter_bids_list() def update_filter_bids_list(self): self.window().bids_list.clear() ticks = None for price_level_info in self.bids: if (price_level_info['quantity_type'], price_level_info['price_type']) == self.chosen_wallets: ticks = price_level_info["ticks"] break if ticks: for bid in ticks: self.window().bids_list.addTopLevelItem( self.create_widget_item_from_tick(self.window().bids_list, bid, is_ask=False)) def on_ask(self, ask): has_level = False for price_level_info in self.asks: if price_level_info['quantity_type'] == ask['quantity_type'] \ and price_level_info['price_type'] == ask['price_type']: price_level_info['ticks'].append(ask) has_level = True if not has_level: self.asks.append({ 'price_type': ask['price_type'], 'quantity_type': ask['quantity_type'], 'ticks': [ask] }) self.update_filter_asks_list() def on_bid(self, bid): has_level = False for price_level_info in self.bids: if price_level_info['quantity_type'] == bid['quantity_type'] \ and price_level_info['price_type'] == bid['price_type']: price_level_info['ticks'].append(bid) if not has_level: self.bids.append({ 'price_type': bid['price_type'], 'quantity_type': bid['quantity_type'], 'ticks': [bid] }) self.update_filter_bids_list() def on_transaction_complete(self, transaction): if transaction["mine"]: transaction = transaction["tx"] main_text = "Transaction with price %f %s and quantity %f %s completed." \ % (transaction["price"], transaction["price_type"], transaction["quantity"], transaction["quantity_type"]) self.window().tray_icon.showMessage("Transaction completed", main_text) self.window().hide_status_bar() # Reload wallets self.load_wallets() # Reload transactions self.window().market_transactions_page.load_transactions() else: self.load_asks() self.load_bids() def on_iom_input_required(self, event_dict): self.dialog = IomInputDialog(self.window().stackedWidget, event_dict['bank_name'], event_dict['input']) self.dialog.button_clicked.connect(self.on_iom_input) self.dialog.show() def on_iom_input(self, action): if action == 1: post_data = {'input_name': self.dialog.required_input['name']} for input_name, input_widget in self.dialog.input_widgets.iteritems( ): post_data[input_name] = input_widget.text() self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("iominput", None, data=urlencode(post_data), method='POST') self.dialog.setParent(None) self.dialog = None def create_order(self, is_ask, price, price_type, quantity, quantity_type): post_data = str("price=%f&price_type=%s&quantity=%f&quantity_type=%s" % (price, price_type, quantity, quantity_type)) self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request( "market/%s" % ('asks' if is_ask else 'bids'), lambda response: self.on_order_created(response, is_ask), data=post_data, method='PUT') def on_transactions_button_clicked(self): self.window().market_transactions_page.initialize_transactions_page() self.window().navigation_stack.append( self.window().stackedWidget.currentIndex()) self.window().stackedWidget.setCurrentIndex(PAGE_MARKET_TRANSACTIONS) def on_wallets_button_clicked(self): self.window().market_wallets_page.initialize_wallets_page() self.window().navigation_stack.append( self.window().stackedWidget.currentIndex()) self.window().stackedWidget.setCurrentIndex(PAGE_MARKET_WALLETS) def on_orders_button_clicked(self): self.window().market_orders_page.initialize_orders_page() self.window().navigation_stack.append( self.window().stackedWidget.currentIndex()) self.window().stackedWidget.setCurrentIndex(PAGE_MARKET_ORDERS) def on_order_created(self, response, is_ask): if is_ask: self.load_asks() else: self.load_bids() def on_tick_item_clicked(self, tick_list): if len(tick_list.selectedItems()) == 0: return tick = tick_list.selectedItems()[0].tick if tick_list == self.window().asks_list: self.window().bids_list.clearSelection() else: self.window().asks_list.clearSelection() tick_time = datetime.datetime.fromtimestamp(int( tick["timestamp"])).strftime('%Y-%m-%d %H:%M:%S') self.window().market_detail_trader_id_label.setText(tick["trader_id"]) self.window().market_detail_order_number_label.setText( "%s" % tick["order_number"]) self.window().market_detail_quantity_label.setText( "%s %s" % (tick["quantity"], tick["quantity_type"])) self.window().market_detail_price_label.setText( "%s %s" % (tick["price"], tick["price_type"])) self.window().market_detail_time_created_label.setText(tick_time) self.window().tick_detail_container.show() def on_create_ask_clicked(self): self.show_new_order_dialog(True) def on_create_bid_clicked(self): self.show_new_order_dialog(False) def on_currency_type_clicked(self): menu = TriblerActionMenu(self) for first_wallet_id in self.wallets: sub_menu = menu.addMenu(first_wallet_id) for second_wallet_id in self.wallets: if first_wallet_id == second_wallet_id: continue wallet_action = QAction( '%s / %s' % (first_wallet_id, second_wallet_id), self) wallet_action.triggered.connect( lambda _, id1=first_wallet_id, id2=second_wallet_id: self. on_currency_type_changed(id1, id2)) sub_menu.addAction(wallet_action) menu.exec_(QCursor.pos()) def on_currency_type_changed(self, currency1, currency2): self.chosen_wallets = (currency1, currency2) self.update_button_texts() self.update_filter_asks_list() self.update_filter_bids_list() def show_new_order_dialog(self, is_ask): self.dialog = NewMarketOrderDialog(self.window().stackedWidget, is_ask, self.chosen_wallets[1], self.chosen_wallets[0]) self.dialog.button_clicked.connect(self.on_new_order_action) self.dialog.show() def on_new_order_action(self, action): if action == 1: self.create_order(self.dialog.is_ask, self.dialog.price, self.dialog.price_type, self.dialog.quantity, self.dialog.quantity_type) self.dialog.setParent(None) self.dialog = None def on_ask_timeout(self, ask): self.remove_tick_with_msg_id(self.window().asks_list, ask["message_id"]) def on_bid_timeout(self, bid): self.remove_tick_with_msg_id(self.window().bids_list, bid["message_id"]) def remove_tick_with_msg_id(self, tick_list, msg_id): index_to_remove = -1 for ind in xrange(tick_list.topLevelItemCount()): item = tick_list.topLevelItem(ind) if item.tick["message_id"] == msg_id: index_to_remove = ind break if index_to_remove != -1: tick_list.takeTopLevelItem(index_to_remove) def on_payment(self, payment): if not payment["success"]: # Error occurred during payment main_text = "Transaction with id %s failed." % payment[ "transaction_number"] self.window().tray_icon.showMessage("Transaction failed", main_text) ConfirmationDialog.show_error(self.window(), "Transaction failed", main_text) self.window().hide_status_bar() else: self.window().show_status_bar( "Transaction in process, please don't close Tribler.")
class TrustPage(QWidget): """ This page shows various trust statistics. """ def __init__(self): QWidget.__init__(self) self.trust_plot = None self.public_key = None self.request_mgr = None self.blocks = None self.byte_scale = 1024 * 1024 self.dialog = None # Timer for garbage collection self.gc_timer = 0 def initialize_trust_page(self): vlayout = self.window().plot_widget.layout() if vlayout.isEmpty(): self.trust_plot = TrustPlotMplCanvas(self.window().plot_widget, dpi=100) vlayout.addWidget(self.trust_plot) self.window().trade_button.clicked.connect( self.on_trade_button_clicked) self.window().mine_button.clicked.connect(self.on_mine_button_clicked) self.window().trust_explain_button.clicked.connect( self.on_info_button_clicked) def on_trade_button_clicked(self): self.window().market_page.initialize_market_page() self.window().navigation_stack.append( self.window().stackedWidget.currentIndex()) self.window().stackedWidget.setCurrentIndex(PAGE_MARKET) def on_info_button_clicked(self): self.dialog = TrustExplanationDialog(self.window()) self.dialog.show() def on_mine_button_clicked(self): self.window().token_mining_page.initialize_token_mining_page() self.window().navigation_stack.append( self.window().stackedWidget.currentIndex()) self.window().stackedWidget.setCurrentIndex(PAGE_TOKEN_MINING_PAGE) def load_blocks(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request( "ipv8/trustchain/users/%s/blocks" % self.public_key, self.received_trustchain_blocks) def received_trustchain_statistics(self, statistics): if not statistics: return statistics = statistics["statistics"] self.public_key = statistics["id"] total_up = 0 total_down = 0 if 'latest_block' in statistics: total_up = statistics["latest_block"]["transaction"]["total_up"] total_down = statistics["latest_block"]["transaction"][ "total_down"] self.window().trust_contribution_amount_label.setText( "%s MBytes" % (total_up / self.byte_scale)) self.window().trust_consumption_amount_label.setText( "%s MBytes" % (total_down / self.byte_scale)) self.window().trust_people_helped_label.setText( "%d" % statistics["peers_that_pk_helped"]) self.window().trust_people_helped_you_label.setText( "%d" % statistics["peers_that_helped_pk"]) def received_trustchain_blocks(self, blocks): if blocks: self.blocks = blocks["blocks"] self.plot_absolute_values() # Matplotlib is leaking memory on re-plotting. Refer: https://github.com/matplotlib/matplotlib/issues/8528 # Note that gc is called every 10 minutes. if self.gc_timer == GC_TIMEOUT: gc.collect() self.gc_timer = 0 else: self.gc_timer += 1 def plot_absolute_values(self): """ Plot two lines of the absolute amounts of contributed and consumed bytes. """ plot_data = [[[], []], []] # Convert all dates to a datetime object num_bandwidth_blocks = 0 for block in self.blocks: if block["type"] != "tribler_bandwidth": continue plot_data[1].append( datetime.datetime.strptime(block["insert_time"], "%Y-%m-%d %H:%M:%S")) plot_data[0][0].append(block["transaction"]["total_up"] / self.byte_scale) plot_data[0][1].append(block["transaction"]["total_down"] / self.byte_scale) num_bandwidth_blocks += 1 if num_bandwidth_blocks == 0: # Create on single data point with 0mb up and 0mb down plot_data = [[[0], [0]], [datetime.datetime.now()]] self.trust_plot.plot_data = plot_data self.trust_plot.compute_initial_figure()
class MarketWalletsPage(QWidget): """ This page displays information about wallets. """ def __init__(self): QWidget.__init__(self) self.request_mgr = None self.initialized = False self.wallets_to_create = [] self.wallets = None self.active_wallet = None self.dialog = None def initialize_wallets_page(self): if not self.initialized: self.window().wallets_back_button.setIcon( QIcon(get_image_path('page_back.png'))) self.window().wallet_btc_overview_button.clicked.connect( lambda: self.initialize_wallet_info( 'BTC', self.window().wallet_btc_overview_button)) self.window().wallet_tbtc_overview_button.clicked.connect( lambda: self.initialize_wallet_info( 'TBTC', self.window().wallet_tbtc_overview_button)) self.window().wallet_mc_overview_button.clicked.connect( lambda: self.initialize_wallet_info( 'MB', self.window().wallet_mc_overview_button)) self.window().wallet_paypal_overview_button.clicked.connect( lambda: self.initialize_wallet_info( 'PP', self.window().wallet_paypal_overview_button)) self.window().wallet_abn_overview_button.clicked.connect( lambda: self.initialize_wallet_info( 'ABNA', self.window().wallet_abn_overview_button)) self.window().wallet_rabo_overview_button.clicked.connect( lambda: self.initialize_wallet_info( 'RABO', self.window().wallet_rabo_overview_button)) self.window().add_wallet_button.clicked.connect( self.on_add_wallet_clicked) self.window().wallet_mc_overview_button.hide() self.window().wallet_btc_overview_button.hide() self.window().wallet_tbtc_overview_button.hide() self.window().wallet_paypal_overview_button.hide() self.window().wallet_abn_overview_button.hide() self.window().wallet_rabo_overview_button.hide() self.window().wallet_info_tabs.hide() self.window().wallet_info_tabs.currentChanged.connect( self.tab_changed) self.initialized = True self.load_wallets() def load_wallets(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("wallets", self.on_wallets) def on_wallets(self, wallets): self.wallets = wallets["wallets"] if 'MB' in self.wallets and self.wallets["MB"]["created"]: self.window().wallet_mc_overview_button.show() if 'BTC' in self.wallets and self.wallets["BTC"]["created"]: self.window().wallet_btc_overview_button.show() if 'TBTC' in self.wallets and self.wallets["TBTC"]["created"]: self.window().wallet_tbtc_overview_button.show() if 'PP' in self.wallets and self.wallets["PP"]["created"]: self.window().wallet_paypal_overview_button.show() if 'ABNA' in self.wallets and self.wallets["ABNA"]["created"]: self.window().wallet_abn_overview_button.show() if 'RABO' in self.wallets and self.wallets["RABO"]["created"]: self.window().wallet_rabo_overview_button.show() # Find out which wallets we still can create self.wallets_to_create = [] for identifier, wallet in self.wallets.iteritems(): if not wallet["created"]: self.wallets_to_create.append(identifier) if len(self.wallets_to_create) > 0: self.window().add_wallet_button.setEnabled(True) else: self.window().add_wallet_button.hide() def tab_changed(self, index): if index == 1 and self.active_wallet: self.load_transactions(self.active_wallet) def initialize_wallet_info(self, wallet_id, pressed_button): # Show the tab again self.window().wallet_info_tabs.show() self.window().wallet_management_placeholder_widget.hide() # Clear the selection of all other buttons, except the pressed button for button in self.window().wallet_buttons_container.findChildren( QPushButton): if button != pressed_button: button.setChecked(False) self.active_wallet = wallet_id self.window().wallet_info_tabs.setCurrentIndex(0) self.window().wallet_address_label.setText( self.wallets[wallet_id]['address']) # Create a QR code of the wallet address try: import qrcode qr = qrcode.QRCode( version=1, error_correction=qrcode.constants.ERROR_CORRECT_M, box_size=10, border=5, ) qr.add_data(self.wallets[wallet_id]['address']) qr.make(fit=True) img = qr.make_image() # PIL format qim = ImageQt(img) pixmap = QtGui.QPixmap.fromImage(qim).scaled( 300, 300, QtCore.Qt.KeepAspectRatio) self.window().wallet_address_qr_label.setPixmap(pixmap) except ImportError: self.window().wallet_address_qr_label.setText( "QR Code functionality not available!") def load_transactions(self, wallet_id): self.window().wallet_transactions_list.clear() self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("wallets/%s/transactions" % wallet_id, self.on_transactions) def on_transactions(self, transactions): for transaction in transactions["transactions"]: item = QTreeWidgetItem(self.window().wallet_transactions_list) item.setText(0, "Sent" if transaction["outgoing"] else "Received") item.setText(1, transaction["from"]) item.setText(2, transaction["to"]) item.setText( 3, "%g %s" % (transaction["amount"], transaction["currency"])) item.setText( 4, "%g %s" % (transaction["fee_amount"], transaction["currency"])) item.setText(5, transaction["id"]) timestamp = timestamp_to_time( float(transaction["timestamp"] )) if transaction["timestamp"] != "False" else "-" item.setText(6, timestamp) self.window().wallet_transactions_list.addTopLevelItem(item) def on_add_wallet_clicked(self): menu = TriblerActionMenu(self) for wallet_id in self.wallets_to_create: wallet_action = QAction(self.wallets[wallet_id]['name'], self) wallet_action.triggered.connect( lambda _, wid=wallet_id: self.should_create_wallet(wid)) menu.addAction(wallet_action) menu.exec_(QCursor.pos()) def should_create_wallet(self, wallet_id): if wallet_id == 'BTC' or wallet_id == 'TBTC': self.dialog = ConfirmationDialog( self, "Create Bitcoin wallet", "Please enter the password of your Bitcoin wallet below:", [('CREATE', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)], show_input=True) self.dialog.dialog_widget.dialog_input.setPlaceholderText( 'Wallet password') self.dialog.button_clicked.connect( lambda action: self.on_create_btc_wallet_dialog_done( action, wallet_id)) self.dialog.show() else: self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("wallets/%s" % wallet_id, self.on_wallet_created, method='PUT', data='') def on_create_btc_wallet_dialog_done(self, action, wallet_id): password = self.dialog.dialog_widget.dialog_input.text() if action == 1: # Remove the dialog right now self.dialog.close_dialog() self.dialog = None elif action == 0: self.dialog.buttons[0].setEnabled(False) self.dialog.buttons[1].setEnabled(False) self.dialog.buttons[0].setText("CREATING...") self.request_mgr = TriblerRequestManager() post_data = str("password=%s" % password) self.request_mgr.perform_request("wallets/%s" % wallet_id, self.on_wallet_created, method='PUT', data=post_data) def on_wallet_created(self, response): if self.dialog: self.dialog.close_dialog() self.dialog = None self.load_wallets()
class TriblerWindow(QMainWindow): resize_event = pyqtSignal() escape_pressed = pyqtSignal() received_search_completions = pyqtSignal(object) def on_exception(self, *exc_info): # Stop the download loop self.downloads_page.stop_loading_downloads() # Add info about whether we are stopping Tribler or not os.environ['TRIBLER_SHUTTING_DOWN'] = str( self.core_manager.shutting_down) if not self.core_manager.shutting_down: self.core_manager.stop(stop_app_on_shutdown=False) self.setHidden(True) if self.debug_window: self.debug_window.setHidden(True) exception_text = "".join(traceback.format_exception(*exc_info)) logging.error(exception_text) if not self.feedback_dialog_is_open: dialog = FeedbackDialog( self, exception_text, self.core_manager.events_manager.tribler_version, self.start_time) self.feedback_dialog_is_open = True _ = dialog.exec_() def __init__(self): QMainWindow.__init__(self) self.navigation_stack = [] self.feedback_dialog_is_open = False self.tribler_started = False self.tribler_settings = None self.debug_window = None self.core_manager = CoreManager() self.pending_requests = {} self.pending_uri_requests = [] self.download_uri = None self.dialog = None self.start_download_dialog_active = False self.request_mgr = None self.search_request_mgr = None self.search_suggestion_mgr = None self.selected_torrent_files = [] self.vlc_available = True self.has_search_results = False self.start_time = time.time() sys.excepthook = self.on_exception uic.loadUi(get_ui_file_path('mainwindow.ui'), self) TriblerRequestManager.window = self self.tribler_status_bar.hide() self.magnet_handler = MagnetHandler(self.window) QDesktopServices.setUrlHandler("magnet", self.magnet_handler, "on_open_magnet_link") QCoreApplication.setOrganizationDomain("nl") QCoreApplication.setOrganizationName("TUDelft") QCoreApplication.setApplicationName("Tribler") QCoreApplication.setAttribute(Qt.AA_UseHighDpiPixmaps) self.read_settings() # Remove the focus rect on OS X for widget in self.findChildren(QLineEdit) + self.findChildren( QListWidget) + self.findChildren(QTreeWidget): widget.setAttribute(Qt.WA_MacShowFocusRect, 0) self.menu_buttons = [ self.left_menu_button_home, self.left_menu_button_search, self.left_menu_button_my_channel, self.left_menu_button_subscriptions, self.left_menu_button_video_player, self.left_menu_button_downloads, self.left_menu_button_discovered ] self.video_player_page.initialize_player() self.search_results_page.initialize_search_results_page() self.settings_page.initialize_settings_page() self.subscribed_channels_page.initialize() self.edit_channel_page.initialize_edit_channel_page() self.downloads_page.initialize_downloads_page() self.home_page.initialize_home_page() self.loading_page.initialize_loading_page() self.discovering_page.initialize_discovering_page() self.discovered_page.initialize_discovered_page() self.trust_page.initialize_trust_page() self.stackedWidget.setCurrentIndex(PAGE_LOADING) # Create the system tray icon if QSystemTrayIcon.isSystemTrayAvailable(): self.tray_icon = QSystemTrayIcon() use_monochrome_icon = get_gui_setting(self.gui_settings, "use_monochrome_icon", False, is_bool=True) self.update_tray_icon(use_monochrome_icon) self.hide_left_menu_playlist() self.left_menu_button_debug.setHidden(True) self.top_menu_button.setHidden(True) self.left_menu.setHidden(True) self.trust_button.setHidden(True) self.settings_button.setHidden(True) self.add_torrent_button.setHidden(True) self.top_search_bar.setHidden(True) # Set various icons self.top_menu_button.setIcon(QIcon(get_image_path('menu.png'))) self.search_completion_model = QStringListModel() completer = QCompleter() completer.setModel(self.search_completion_model) completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.item_delegate = QStyledItemDelegate() completer.popup().setItemDelegate(self.item_delegate) completer.popup().setStyleSheet(""" QListView { background-color: #404040; } QListView::item { color: #D0D0D0; padding-top: 5px; padding-bottom: 5px; } QListView::item:hover { background-color: #707070; } """) self.top_search_bar.setCompleter(completer) # Toggle debug if developer mode is enabled self.window().left_menu_button_debug.setHidden(not get_gui_setting( self.gui_settings, "debug", False, is_bool=True)) self.core_manager.start() self.core_manager.events_manager.received_search_result_channel.connect( self.search_results_page.received_search_result_channel) self.core_manager.events_manager.received_search_result_torrent.connect( self.search_results_page.received_search_result_torrent) self.core_manager.events_manager.torrent_finished.connect( self.on_torrent_finished) self.core_manager.events_manager.new_version_available.connect( self.on_new_version_available) self.core_manager.events_manager.tribler_started.connect( self.on_tribler_started) # Install signal handler for ctrl+c events def sigint_handler(*_): self.close_tribler() signal.signal(signal.SIGINT, sigint_handler) self.installEventFilter(self.video_player_page) self.show() def update_tray_icon(self, use_monochrome_icon): if not QSystemTrayIcon.isSystemTrayAvailable(): return if use_monochrome_icon: self.tray_icon.setIcon( QIcon(QPixmap(get_image_path('monochrome_tribler.png')))) else: self.tray_icon.setIcon( QIcon(QPixmap(get_image_path('tribler.png')))) self.tray_icon.show() def on_torrent_finished(self, torrent_info): if QSystemTrayIcon.isSystemTrayAvailable(): self.window().tray_icon.showMessage( "Download finished", "Download of %s has finished." % torrent_info["name"]) def show_loading_screen(self): self.top_menu_button.setHidden(True) self.left_menu.setHidden(True) self.trust_button.setHidden(True) self.settings_button.setHidden(True) self.add_torrent_button.setHidden(True) self.top_search_bar.setHidden(True) self.stackedWidget.setCurrentIndex(PAGE_LOADING) def on_tribler_started(self): self.tribler_started = True self.top_menu_button.setHidden(False) self.left_menu.setHidden(False) self.trust_button.setHidden(False) self.settings_button.setHidden(False) self.add_torrent_button.setHidden(False) self.top_search_bar.setHidden(False) # fetch the settings, needed for the video player port self.request_mgr = TriblerRequestManager() self.fetch_settings() self.downloads_page.start_loading_downloads() self.home_page.load_popular_torrents() if not self.gui_settings.value( "first_discover", False) and not self.core_manager.use_existing_core: self.window().gui_settings.setValue("first_discover", True) self.discovering_page.is_discovering = True self.stackedWidget.setCurrentIndex(PAGE_DISCOVERING) else: self.clicked_menu_button_home() def show_status_bar(self, message): self.tribler_status_bar_label.setText(message) self.tribler_status_bar.show() def hide_status_bar(self): self.tribler_status_bar.hide() def process_uri_request(self): """ Process a URI request if we have one in the queue. """ if len(self.pending_uri_requests) == 0: return uri = self.pending_uri_requests.pop() if uri.startswith('file') or uri.startswith('magnet'): self.start_download_from_uri(uri) def perform_start_download_request(self, uri, anon_download, safe_seeding, destination, selected_files, total_files=0, callback=None): selected_files_uri = "" if len(selected_files) != total_files: # Not all files included selected_files_uri = u'&' + u''.join( u"selected_files[]=%s&" % file for file in selected_files)[:-1] anon_hops = int(self.tribler_settings['download_defaults'] ['number_hops']) if anon_download else 0 safe_seeding = 1 if safe_seeding else 0 post_data = "uri=%s&anon_hops=%d&safe_seeding=%d&destination=%s%s" % ( uri, anon_hops, safe_seeding, destination, selected_files_uri) post_data = post_data.encode( 'utf-8') # We need to send bytes in the request, not unicode request_mgr = TriblerRequestManager() self.pending_requests[request_mgr.request_id] = request_mgr request_mgr.perform_request( "downloads", callback if callback else self.on_download_added, method='PUT', data=post_data) # Save the download location to the GUI settings current_settings = get_gui_setting(self.gui_settings, "recent_download_locations", "") recent_locations = current_settings.split( ",") if len(current_settings) > 0 else [] encoded_destination = destination.encode('hex') if encoded_destination in recent_locations: recent_locations.remove(encoded_destination) recent_locations.insert(0, encoded_destination) if len(recent_locations) > 5: recent_locations = recent_locations[:5] self.gui_settings.setValue("recent_download_locations", ','.join(recent_locations)) def on_new_version_available(self, version): if version == str(self.gui_settings.value('last_reported_version')): return self.dialog = ConfirmationDialog( self, "New version available", "Version %s of Tribler is available.Do you want to visit the website to " "download the newest version?" % version, [('IGNORE', BUTTON_TYPE_NORMAL), ('LATER', BUTTON_TYPE_NORMAL), ('OK', BUTTON_TYPE_NORMAL)]) self.dialog.button_clicked.connect( lambda action: self.on_new_version_dialog_done(version, action)) self.dialog.show() def on_new_version_dialog_done(self, version, action): if action == 0: # ignore self.gui_settings.setValue("last_reported_version", version) elif action == 2: # ok import webbrowser webbrowser.open("https://tribler.org") self.dialog.setParent(None) self.dialog = None def read_settings(self): self.gui_settings = QSettings() center = QApplication.desktop().availableGeometry(self).center() pos = self.gui_settings.value( "pos", QPoint(center.x() - self.width() * 0.5, center.y() - self.height() * 0.5)) size = self.gui_settings.value("size", self.size()) self.move(pos) self.resize(size) def on_search_text_change(self, text): self.search_suggestion_mgr = TriblerRequestManager() self.search_suggestion_mgr.perform_request( "search/completions?q=%s" % text, self.on_received_search_completions) def on_received_search_completions(self, completions): self.received_search_completions.emit(completions) self.search_completion_model.setStringList(completions["completions"]) def fetch_settings(self): self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("settings", self.received_settings, capture_errors=False) def received_settings(self, settings): # If we cannot receive the settings, stop Tribler with an option to send the crash report. if 'error' in settings: raise RuntimeError( TriblerRequestManager.get_message_from_error(settings)) else: self.tribler_settings = settings['settings'] # Disable various components based on the settings if not self.tribler_settings['search_community']['enabled']: self.window().top_search_bar.setHidden(True) if not self.tribler_settings['video_server']['enabled']: self.left_menu_button_video_player.setHidden(True) # Set the video server port self.video_player_page.video_player_port = self.tribler_settings[ "video_server"]["port"] # process pending file requests (i.e. someone clicked a torrent file when Tribler was closed) # We do this after receiving the settings so we have the default download location. self.process_uri_request() def on_top_search_button_click(self): self.left_menu_button_search.setChecked(True) self.has_search_results = True self.clicked_menu_button_search() self.search_results_page.perform_search(self.top_search_bar.text()) self.search_request_mgr = TriblerRequestManager() self.search_request_mgr.perform_request( "search?q=%s" % self.top_search_bar.text(), None) def on_settings_button_click(self): self.deselect_all_menu_buttons() self.stackedWidget.setCurrentIndex(PAGE_SETTINGS) self.settings_page.load_settings() self.navigation_stack = [] self.hide_left_menu_playlist() def on_trust_button_click(self): self.deselect_all_menu_buttons() self.stackedWidget.setCurrentIndex(PAGE_TRUST) self.trust_page.load_trust_statistics() self.navigation_stack = [] self.hide_left_menu_playlist() def on_add_torrent_button_click(self, pos): menu = TriblerActionMenu(self) browse_files_action = QAction('Import torrent from file', self) browse_directory_action = QAction('Import torrents from directory', self) add_url_action = QAction('Import torrent from magnet/URL', self) browse_files_action.triggered.connect(self.on_add_torrent_browse_file) browse_directory_action.triggered.connect( self.on_add_torrent_browse_dir) add_url_action.triggered.connect(self.on_add_torrent_from_url) menu.addAction(browse_files_action) menu.addAction(browse_directory_action) menu.addAction(add_url_action) menu.exec_(self.mapToGlobal(self.add_torrent_button.pos())) def on_add_torrent_browse_file(self): filenames = QFileDialog.getOpenFileNames( self, "Please select the .torrent file", "", "Torrent files (*.torrent)") if len(filenames[0]) > 0: [ self.pending_uri_requests.append(u"file:%s" % filename) for filename in filenames[0] ] self.process_uri_request() def start_download_from_uri(self, uri): self.download_uri = uri if get_gui_setting(self.gui_settings, "ask_download_settings", True, is_bool=True): self.dialog = StartDownloadDialog(self.window().stackedWidget, self.download_uri) self.dialog.button_clicked.connect(self.on_start_download_action) self.dialog.show() self.start_download_dialog_active = True else: self.window().perform_start_download_request( self.download_uri, self.window().tribler_settings['download_defaults'] ['anonymity_enabled'], self.window().tribler_settings['download_defaults'] ['safeseeding_enabled'], self.tribler_settings['download_defaults']['saveas'], [], 0) self.process_uri_request() def on_start_download_action(self, action): if action == 1: self.window().perform_start_download_request( self.download_uri, self.dialog.dialog_widget.anon_download_checkbox.isChecked(), self.dialog.dialog_widget.safe_seed_checkbox.isChecked(), self.dialog.dialog_widget.destination_input.currentText(), self.dialog.get_selected_files(), self.dialog.dialog_widget.files_list_view.topLevelItemCount()) self.dialog.request_mgr.cancel_request( ) # To abort the torrent info request self.dialog.setParent(None) self.dialog = None self.start_download_dialog_active = False if action == 0: # We do this after removing the dialog since process_uri_request is blocking self.process_uri_request() def on_add_torrent_browse_dir(self): chosen_dir = QFileDialog.getExistingDirectory( self, "Please select the directory containing the .torrent files", "", QFileDialog.ShowDirsOnly) if len(chosen_dir) != 0: self.selected_torrent_files = [ torrent_file for torrent_file in glob.glob(chosen_dir + "/*.torrent") ] self.dialog = ConfirmationDialog( self, "Add torrents from directory", "Are you sure you want to add %d torrents to Tribler?" % len(self.selected_torrent_files), [('ADD', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)]) self.dialog.button_clicked.connect( self.on_confirm_add_directory_dialog) self.dialog.show() def on_confirm_add_directory_dialog(self, action): if action == 0: for torrent_file in self.selected_torrent_files: escaped_uri = quote_plus( (u"file:%s" % torrent_file).encode('utf-8')) self.perform_start_download_request( escaped_uri, self.window().tribler_settings['download_defaults'] ['anonymity_enabled'], self.window().tribler_settings['download_defaults'] ['safeseeding_enabled'], self.tribler_settings['download_defaults']['saveas'], [], 0) self.dialog.setParent(None) self.dialog = None def on_add_torrent_from_url(self): self.dialog = ConfirmationDialog( self, "Add torrent from URL/magnet link", "Please enter the URL/magnet link in the field below:", [('ADD', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)], show_input=True) self.dialog.dialog_widget.dialog_input.setPlaceholderText( 'URL/magnet link') self.dialog.dialog_widget.dialog_input.setFocus() self.dialog.button_clicked.connect( self.on_torrent_from_url_dialog_done) self.dialog.show() def on_torrent_from_url_dialog_done(self, action): uri = self.dialog.dialog_widget.dialog_input.text() # Remove first dialog self.dialog.setParent(None) self.dialog = None if action == 0: self.start_download_from_uri(uri) def on_download_added(self, result): if len(self.pending_uri_requests ) == 0: # Otherwise, we first process the remaining requests. self.window().left_menu_button_downloads.click() else: self.process_uri_request() def on_top_menu_button_click(self): if self.left_menu.isHidden(): self.left_menu.show() else: self.left_menu.hide() def deselect_all_menu_buttons(self, except_select=None): for button in self.menu_buttons: if button == except_select: button.setEnabled(False) continue button.setEnabled(True) if button == self.left_menu_button_search and not self.has_search_results: button.setEnabled(False) button.setChecked(False) def clicked_menu_button_home(self): self.deselect_all_menu_buttons(self.left_menu_button_home) self.stackedWidget.setCurrentIndex(PAGE_HOME) self.navigation_stack = [] self.hide_left_menu_playlist() def clicked_menu_button_search(self): self.deselect_all_menu_buttons(self.left_menu_button_search) self.stackedWidget.setCurrentIndex(PAGE_SEARCH_RESULTS) self.navigation_stack = [] self.hide_left_menu_playlist() def clicked_menu_button_discovered(self): self.deselect_all_menu_buttons(self.left_menu_button_discovered) self.stackedWidget.setCurrentIndex(PAGE_DISCOVERED) self.discovered_page.load_discovered_channels() self.navigation_stack = [] self.hide_left_menu_playlist() def clicked_menu_button_my_channel(self): self.deselect_all_menu_buttons(self.left_menu_button_my_channel) self.stackedWidget.setCurrentIndex(PAGE_EDIT_CHANNEL) self.edit_channel_page.load_my_channel_overview() self.navigation_stack = [] self.hide_left_menu_playlist() def clicked_menu_button_video_player(self): self.deselect_all_menu_buttons(self.left_menu_button_video_player) self.stackedWidget.setCurrentIndex(PAGE_VIDEO_PLAYER) self.navigation_stack = [] self.show_left_menu_playlist() def clicked_menu_button_downloads(self): self.deselect_all_menu_buttons(self.left_menu_button_downloads) self.stackedWidget.setCurrentIndex(PAGE_DOWNLOADS) self.navigation_stack = [] self.hide_left_menu_playlist() def clicked_menu_button_debug(self): self.debug_window = DebugWindow(self.tribler_settings) self.debug_window.show() def clicked_menu_button_subscriptions(self): self.deselect_all_menu_buttons(self.left_menu_button_subscriptions) self.subscribed_channels_page.load_subscribed_channels() self.stackedWidget.setCurrentIndex(PAGE_SUBSCRIBED_CHANNELS) self.navigation_stack = [] self.hide_left_menu_playlist() def hide_left_menu_playlist(self): self.left_menu_seperator.setHidden(True) self.left_menu_playlist_label.setHidden(True) self.left_menu_playlist.setHidden(True) def show_left_menu_playlist(self): self.left_menu_seperator.setHidden(False) self.left_menu_playlist_label.setHidden(False) self.left_menu_playlist.setHidden(False) def on_channel_item_click(self, channel_list_item): list_widget = channel_list_item.listWidget() from TriblerGUI.widgets.channel_list_item import ChannelListItem if isinstance(list_widget.itemWidget(channel_list_item), ChannelListItem): channel_info = channel_list_item.data(Qt.UserRole) self.channel_page.initialize_with_channel(channel_info) self.navigation_stack.append(self.stackedWidget.currentIndex()) self.stackedWidget.setCurrentIndex(PAGE_CHANNEL_DETAILS) def on_playlist_item_click(self, playlist_list_item): list_widget = playlist_list_item.listWidget() from TriblerGUI.widgets.playlist_list_item import PlaylistListItem if isinstance(list_widget.itemWidget(playlist_list_item), PlaylistListItem): playlist_info = playlist_list_item.data(Qt.UserRole) self.playlist_page.initialize_with_playlist(playlist_info) self.navigation_stack.append(self.stackedWidget.currentIndex()) self.stackedWidget.setCurrentIndex(PAGE_PLAYLIST_DETAILS) def on_page_back_clicked(self): prev_page = self.navigation_stack.pop() self.stackedWidget.setCurrentIndex(prev_page) def on_edit_channel_clicked(self): self.stackedWidget.setCurrentIndex(PAGE_EDIT_CHANNEL) self.navigation_stack = [] self.channel_page.on_edit_channel_clicked() def resizeEvent(self, _): # Resize home page cells cell_width = self.home_page_table_view.width( ) / 3 - 3 # We have some padding to the right cell_height = cell_width / 2 + 60 for i in range(0, 3): self.home_page_table_view.setColumnWidth(i, cell_width) self.home_page_table_view.setRowHeight(i, cell_height) self.resize_event.emit() def exit_full_screen(self): self.top_bar.show() self.left_menu.show() self.video_player_page.is_full_screen = False self.showNormal() def close_tribler(self): if not self.core_manager.shutting_down: self.show_loading_screen() self.gui_settings.setValue("pos", self.pos()) self.gui_settings.setValue("size", self.size()) if self.core_manager.use_existing_core: # Don't close the core that we are using QApplication.quit() self.core_manager.stop() self.core_manager.shutting_down = True self.downloads_page.stop_loading_downloads() def closeEvent(self, close_event): self.close_tribler() close_event.ignore() def keyReleaseEvent(self, event): if event.key() == Qt.Key_Escape: self.escape_pressed.emit() if self.isFullScreen(): self.exit_full_screen()
class TriblerTableViewController(object): """ Base controller for a table view that displays some data. """ def __init__(self, model, table_view): self.model = model self.model.on_sort.connect(self._on_view_sort) self.table_view = table_view self.table_view.setModel(self.model) self.table_view.verticalScrollBar().valueChanged.connect(self._on_list_scroll) self.query_text = '' self.num_results_label = None self.request_mgr = None self.query_uuid = None def _on_view_sort(self, column, ascending): self.model.reset() self.perform_query(first=1, last=50) def _on_list_scroll(self, event): if self.table_view.verticalScrollBar().value() == self.table_view.verticalScrollBar().maximum() and \ self.model.data_items: # workaround for duplicate calls to _on_list_scroll on view creation self.perform_query() def _get_sort_parameters(self): """ Return a tuple (column_name, sort_asc) that indicates the sorting column/order of the table view. """ sort_by = self.model.columns[self.table_view.horizontalHeader().sortIndicatorSection()] sort_asc = self.table_view.horizontalHeader().sortIndicatorOrder() return sort_by, sort_asc def perform_query(self, **kwargs): """ Fetch results for a given query. """ if 'first' not in kwargs or 'last' not in kwargs: kwargs["first"], kwargs[ 'last'] = self.model.rowCount() + 1, self.model.rowCount() + self.model.item_load_batch # Create a new uuid for each new search if kwargs['first'] == 1 or not self.query_uuid: self.query_uuid = uuid.uuid4().hex sort_by, sort_asc = self._get_sort_parameters() kwargs.update({ "uuid": self.query_uuid, "filter": to_fts_query(self.query_text), "sort_by": sort_by, "sort_asc": sort_asc, "hide_xxx": self.model.hide_xxx}) rest_endpoint_url = kwargs.pop("rest_endpoint_url") self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request(rest_endpoint_url, self.on_query_results, url_params=kwargs) def on_query_results(self, response, remote=False): """ Updates the table with the response. :param response: List of the items to be added to the model :param remote: True if response is from a remote peer. Default: False :return: None """ if not response: return False if self.is_new_result(response): self.model.add_items(response['results'], remote=remote) self.model.total_items = len(self.model.data_items) if self.num_results_label: self.num_results_label.setText("%d results" % self.model.total_items) return True def is_new_result(self, response): """ Returns True if the response is a new fresh response else False. - If UUID of the response and the last query does not match, then it is a stale response. - If the response has info about pagination, here 'first' field, if the value of the first field is less than the number of results already present, then the response is stale. :param response: List of items :return: True for fresh response else False """ if 'uuid' in response and response['uuid'] != self.query_uuid: return False if 'first' in response and response['first'] < self.model.rowCount(): return False return True