Exemplo n.º 1
0
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)
Exemplo n.º 2
0
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))
Exemplo n.º 3
0
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
Exemplo n.º 4
0
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()
Exemplo n.º 5
0
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().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().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)

        self.window().log_location_chooser_button.clicked.connect(
            self.on_choose_log_dir_clicked)

    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 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

        if not is_dir_writable(log_dir):
            ConfirmationDialog.show_message(
                self.dialog_widget, "Insufficient Permissions",
                "<i>%s</i> is not writable. " % log_dir, "OK")
        else:
            self.window().log_location_input.setText(log_dir)

    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'])

        # Log directory
        self.window().log_location_input.setText(
            settings['general']['log_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(
                "%s" % 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['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['downloadconfig']['seeding_mode'] +
            "_radio").setChecked(True)
        self.window().seeding_time_input.setText(
            seconds_to_hhmm_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().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']['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()

        settings_data['libtorrent']['lt_proxyserver'] = None
        if self.window().lt_proxy_server_input.text() and len(
                self.window().lt_proxy_port_input.text()) > 0:
            settings_data['libtorrent']['lt_proxyserver'] = [
                self.window().lt_proxy_server_input.text(), None
            ]

            # The port should be a number
            try:
                lt_proxy_port = int(self.window().lt_proxy_port_input.text())
                settings_data['libtorrent']['lt_proxyserver'][
                    1] = lt_proxy_port
            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

        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. "
                "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

        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_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['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.window().settings_save_button.setEnabled(False)

        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.window().settings_save_button.setEnabled(True)
        self.saved_dialog.setParent(None)
        self.saved_dialog = None
Exemplo n.º 6
0
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)

        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)

        QCoreApplication.setOrganizationDomain("nl")
        QCoreApplication.setOrganizationName("TUDelft")
        QCoreApplication.setApplicationName("Tribler")
        QCoreApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)

        self.gui_settings = QSettings()
        api_port = get_gui_setting(self.gui_settings, "api_port",
                                   DEFAULT_API_PORT)
        TriblerRequestWorker.BASE_URL = "http://localhost:%d/" % api_port

        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(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.itemClicked.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.itemClicked.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()

        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()

    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.setParent(None)
        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 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):
        statistics = statistics["statistics"]
        if 'latest_block' in statistics:
            balance = (statistics["latest_block"]["transaction"]["total_up"] - \
                      statistics["latest_block"]["transaction"]["total_down"]) / 1024 / 1024
            self.token_balance_label.setText("%d" % balance)
        else:
            self.token_balance_label.setText("0")

    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):
            # Clear any previous dialog if exists
            if self.dialog:
                self.dialog.button_clicked.disconnect()
                self.dialog.setParent(None)
                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.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",
            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" % quote_plus(
                    (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):
        # Make sure that the window is visible (this action might be triggered from the tray icon)
        self.raise_window()

        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.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.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()

    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 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()
Exemplo n.º 7
0
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.setParent(None)
            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.setParent(None)
            self.dialog = None
        self.load_wallets()
Exemplo n.º 8
0
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, identifier):
        self.channel_identifier = identifier
        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",
                                                    "")

        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

        files_str = u""
        for ind in xrange(self.window().create_torrent_files_list.count()):
            files_str += u"files[]=%s&" % urllib.quote_plus(self.window(
            ).create_torrent_files_list.item(ind).text().encode('utf-8'))

        name = urllib.quote_plus(
            self.window().create_torrent_name_field.text().encode('utf-8'))
        description = urllib.quote_plus(self.window(
        ).create_torrent_description_field.toPlainText().encode('utf-8'))
        post_data = (u"%s&name=%s&description=%s" %
                     (files_str[:-1], name, description)).encode('utf-8')
        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.setParent(None)
        self.dialog = None

    def on_torrent_created(self, result):
        if 'torrent' in result:
            self.add_torrent_to_channel(result['torrent'])

    def add_torrent_to_channel(self, torrent):
        post_data = str("torrent=%s" % urllib.quote_plus(torrent))
        self.request_mgr = TriblerRequestManager()
        self.request_mgr.perform_request("channels/discovered/%s/torrents" %
                                         self.channel_identifier,
                                         self.on_torrent_to_channel_added,
                                         data=post_data,
                                         method='PUT')

    def on_torrent_to_channel_added(self, result):
        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_channel_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))
Exemplo n.º 9
0
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().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.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):
        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_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

        if not is_dir_writable(log_dir):
            ConfirmationDialog.show_message(
                self.window(), "Insufficient Permissions",
                "<i>%s</i> is not writable. " % log_dir, "OK")
        else:
            self.window().log_location_input.setText(log_dir)

    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().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'])

        # 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']:
            self.window().lt_proxy_server_input.setText(
                settings['libtorrent']['proxy_server'][0])
            self.window().lt_proxy_port_input.setText(
                "%s" % settings['libtorrent']['proxy_server'][1])
        if settings['libtorrent']['proxy_auth']:
            self.window().lt_proxy_username_input.setText(
                settings['libtorrent']['proxy_auth'][0])
            self.window().lt_proxy_password_input.setText(
                settings['libtorrent']['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(
            "%d" % 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']) - 1)
        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']))

    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)

        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': {}
        }
        settings_data['general']['family_filter'] = self.window(
        ).family_filter_checkbox.isChecked()
        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:
            settings_data['libtorrent']['proxy_server'] = [
                self.window().lt_proxy_server_input.text(), None
            ]
            settings_data['libtorrent']['proxy_server'][0] = self.window(
            ).lt_proxy_server_input.text()
            try:
                settings_data['libtorrent']['proxy_server'][1] = 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

        if len(self.window().lt_proxy_username_input.text()) > 0 and \
                        len(self.window().lt_proxy_password_input.text()) > 0:
            settings_data['libtorrent']['proxy_auth'] = [None, None]
            settings_data['libtorrent']['proxy_auth'][0] = self.window(
            ).lt_proxy_username_input.text()
            settings_data['libtorrent']['proxy_auth'][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. "
                "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()
        settings_data['credit_mining']['max_disk_space'] = int(
            self.window().max_disk_space_input.text())
        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() + 1
        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()

        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',
            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(
            "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.setParent(None)
        self.saved_dialog = None
Exemplo n.º 10
0
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

    def initialize_orders_page(self):
        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.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):
        for order in orders["orders"]:
            item = OrderWidgetItem(self.window().market_orders_list, order)
            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.setParent(None)
        self.dialog = None

    def on_order_cancelled(self, response):
        self.load_orders()
Exemplo n.º 11
0
class MyChannelPage(QWidget):
    """
    This class is responsible for managing lists and data on the your channel page, including torrents, playlists
    and rss feeds.
    """

    def initialize_my_channel_page(self):
        self.my_channel_stacked_widget = self.findChild(QStackedWidget, "my_channel_stacked_widget")
        self.my_channel_details_stacked_widget = self.findChild(QStackedWidget, "my_channel_details_stacked_widget")
        self.create_channel_form = self.findChild(QWidget, "create_channel_form")
        self.create_new_channel_intro_label = self.findChild(QLabel, "create_new_channel_intro_label")
        self.create_channel_intro_button = self.findChild(QPushButton, "create_channel_intro_button")
        self.create_channel_intro_button.clicked.connect(self.on_create_channel_intro_button_clicked)

        self.my_channel_name_label = self.findChild(QLabel, "my_channel_name_label")
        self.my_channel_description_label = self.findChild(QLabel, "my_channel_description_label")
        self.my_channel_identifier_label = self.findChild(QLabel, "my_channel_identifier_label")

        self.my_channel_name_input = self.findChild(QLineEdit, "my_channel_name_input")
        self.my_channel_description_input = self.findChild(QTextEdit, "my_channel_description_input")

        self.my_channel_torrents_list = self.findChild(QTreeWidget, "my_channel_torrents_list")
        self.my_channel_rss_feeds_list = self.findChild(QTreeWidget, "my_channel_rss_feeds_list")

        self.create_channel_intro_button_container = self.findChild(QWidget, "create_channel_intro_button_container")
        self.create_channel_form.hide()

        self.my_channel_stacked_widget.setCurrentIndex(1)
        self.my_channel_details_stacked_widget.setCurrentIndex(PAGE_MY_CHANNEL_OVERVIEW)

        self.channel_settings_page = self.findChild(QWidget, "channel_settings_page")

        self.my_channel_torrents_remove_selected_button = self.findChild(QToolButton,
                                                                         "my_channel_torrents_remove_selected_button")
        self.my_channel_torrents_remove_selected_button.clicked.connect(self.on_torrents_remove_selected_clicked)

        self.my_channel_torrents_remove_all_button = self.findChild(QToolButton,
                                                                    "my_channel_torrents_remove_all_button")
        self.my_channel_torrents_remove_all_button.clicked.connect(self.on_torrents_remove_all_clicked)

        self.my_channel_torrents_export_button = self.findChild(QToolButton, "my_channel_torrents_export_button")
        self.my_channel_torrents_export_button.clicked.connect(self.on_torrents_export_clicked)

        # Tab bar buttons
        self.channel_settings_tab = self.findChild(QWidget, "channel_settings_tab")
        self.channel_settings_tab.initialize()
        self.channel_settings_tab.clicked_tab_button.connect(self.clicked_tab_button)

        # add some dummy items to rss feeds list
        for i in range(0, 10):
            item = QTreeWidgetItem(self.my_channel_rss_feeds_list)
            item.setText(0, "http://fancyurl.com/rss_feed.xml")

            self.my_channel_rss_feeds_list.addTopLevelItem(item)

    def load_my_channel_overview(self):
        self.mychannel_request_mgr = TriblerRequestManager()
        self.mychannel_request_mgr.get_my_channel_overview(self.initialize_with_overview)

    def initialize_with_overview(self, overview):
        self.my_channel_overview = overview
        self.my_channel_name_label.setText(overview["name"])
        self.my_channel_description_label.setText(overview["description"])
        self.my_channel_identifier_label.setText(overview["identifier"])

        self.my_channel_name_input.setText(overview["name"])
        self.my_channel_description_input.setText(overview["description"])

    def load_my_channel_torrents(self):
        self.mychannel_request_mgr = TriblerRequestManager()
        self.mychannel_request_mgr.get_my_channel_torrents(self.initialize_with_torrents)

    def initialize_with_torrents(self, torrents):
        self.my_channel_torrents_list.clear()
        for torrent in torrents:
            item = QTreeWidgetItem(self.my_channel_torrents_list)
            item.setText(0, torrent["name"])
            item.setText(1, str(torrent["added"]))

            self.my_channel_torrents_list.addTopLevelItem(item)

    def load_my_channel_rss_feeds(self):
        self.mychannel_request_mgr = TriblerRequestManager()
        self.mychannel_request_mgr.get_my_channel_rss_feeds(self.initialize_with_rss_feeds)

    def initialize_with_rss_feeds(self, rss_feeds):
        self.my_channel_rss_feeds_list.clear()
        for feed in rss_feeds:
            item = QTreeWidgetItem(self.my_channel_rss_feeds_list)
            item.setText(0, feed["url"])

            self.my_channel_rss_feeds_list.addTopLevelItem(item)

    def on_torrents_remove_selected_clicked(self):
        num_selected = len(self.my_channel_torrents_list.selectedItems())
        if num_selected == 0:
            return

        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)
        self.dialog.button_clicked.connect(self.on_torrents_remove_selected_action)
        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.")
        self.dialog.button_clicked.connect(self.on_torrents_remove_all_action)
        self.dialog.show()

    def on_torrents_export_clicked(self):
        selected_dir = QFileDialog.getExistingDirectory(self, "Choose a directory to export the torrent files to")
        # TODO Martijn: actually export the .torrent files

    def on_torrents_remove_selected_action(self, result):
        self.dialog.setParent(None)
        self.dialog = None

    def on_torrents_remove_all_action(self, result):
        self.dialog.setParent(None)
        self.dialog = None

    def clicked_tab_button(self, tab_button_name):
        if tab_button_name == "my_channel_overview_button":
            self.my_channel_details_stacked_widget.setCurrentIndex(PAGE_MY_CHANNEL_OVERVIEW)
        elif tab_button_name == "my_channel_settings_button":
            self.my_channel_details_stacked_widget.setCurrentIndex(PAGE_MY_CHANNEL_SETTINGS)
        elif tab_button_name == "my_channel_torrents_button":
            self.my_channel_details_stacked_widget.setCurrentIndex(PAGE_MY_CHANNEL_TORRENTS)
            self.load_my_channel_torrents()
        elif tab_button_name == "my_channel_playlists_button":
            self.my_channel_details_stacked_widget.setCurrentIndex(PAGE_MY_CHANNEL_PLAYLISTS)
        elif tab_button_name == "my_channel_rss_feeds_button":
            self.my_channel_details_stacked_widget.setCurrentIndex(PAGE_MY_CHANNEL_RSS_FEEDS)
            self.load_my_channel_rss_feeds()

    def on_create_channel_intro_button_clicked(self):
        self.create_channel_form.show()
        self.create_channel_intro_button_container.hide()
        self.create_new_channel_intro_label.setText("Please enter your channel details below.")
Exemplo n.º 12
0
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.selected_item = None
        self.dialog = None
        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().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)

    def on_filter_text_changed(self, text):
        self.update_download_visibility()

    def start_loading_downloads(self):
        self.load_downloads()
        self.downloads_timer = QTimer()
        self.downloads_timer.timeout.connect(self.load_downloads)
        self.downloads_timer.start(1000)

    def stop_loading_downloads(self):
        self.downloads_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.request_mgr = TriblerRequestManager()
        self.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 QSystemTrayIcon.isSystemTrayAvailable():
            self.window().tray_icon.setToolTip(
                "Down: %s, Up: %s" %
                (format_speed(total_download), format_speed(total_upload)))
        self.update_download_visibility()

    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().download_details_widget.clear_data()
        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().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().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"]
            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)
            del self.download_widgets[infohash]
            if self.window().downloads_list.topLevelItemCount() == 0:
                self.window().download_details_widget.clear_data()

            # Reset video player if necessary
            if self.window().video_player_page.active_infohash == infohash:
                self.window().video_player_page.reset_player()

    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 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)

        self.request_mgr = TriblerRequestManager()
        self.request_mgr.download_file(
            "downloads/%s/torrent" %
            self.selected_item.download_info['infohash'],
            self.on_export_download_request_done)

    def on_export_download_request_done(self, filename, data):
        dest_path = os.path.join(self.export_dir, filename)
        with open(dest_path, "wb") as torrent_file:
            torrent_file.write(data)

        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)
        play_action = QAction('Play', 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)

        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))
        play_action.triggered.connect(self.on_play_download_clicked)
        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)

        menu.addAction(start_action)
        menu.addAction(stop_action)
        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.addAction(explore_files_action)

        menu.exec_(self.window().downloads_list.mapToGlobal(pos))
Exemplo n.º 13
0
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.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.load_transactions('BTC'))
            self.window().wallet_mc_overview_button.clicked.connect(
                lambda: self.load_transactions('MC'))
            self.window().wallet_paypal_overview_button.clicked.connect(
                lambda: self.load_transactions('PP'))
            self.window().wallet_abn_overview_button.clicked.connect(
                lambda: self.load_transactions('ABNA'))
            self.window().wallet_rabo_overview_button.clicked.connect(
                lambda: self.load_transactions('RABO'))
            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_paypal_overview_button.hide()
            self.window().wallet_abn_overview_button.hide()
            self.window().wallet_rabo_overview_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):
        self.wallets = wallets["wallets"]

        if 'MC' in self.wallets and self.wallets["MC"]["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 '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)

    def load_transactions(self, wallet_id):
        self.request_mgr = TriblerRequestManager()
        self.request_mgr.perform_request("wallets/%s/transactions" % wallet_id,
                                         self.on_transactions)

    def on_transactions(self, transactions):
        self.window().wallet_transactions_list.clear()
        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"])
            item.setText(6, transaction["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':
            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(
                self.on_create_btc_wallet_dialog_done)
            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):
        password = self.dialog.dialog_widget.dialog_input.text()

        if action == 1:  # Remove the dialog right now
            self.dialog.setParent(None)
            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/btc",
                                             self.on_wallet_created,
                                             method='PUT',
                                             data=post_data)

    def on_wallet_created(self, response):
        if self.dialog:
            self.dialog.setParent(None)
            self.dialog = None
        self.load_wallets()
Exemplo n.º 14
0
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):
        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.setParent(None)
        self.dialog = None

    def on_channel_subscribed(self, _):
        pass
Exemplo n.º 15
0
class TriblerRequestManager(QNetworkAccessManager):
    """
    This class is responsible for all the requests made to the Tribler REST API.
    """
    window = None

    received_json = pyqtSignal(object, int)
    received_file = pyqtSignal(str, object)

    def __init__(self):
        QNetworkAccessManager.__init__(self)
        self.request_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(10))
        self.base_url = "http://localhost:%d/" % API_PORT
        self.reply = None
        self.error_dialog = None

    def perform_request(self, endpoint, read_callback, data="", method='GET', capture_errors=True):
        """
        Perform a HTTP request.
        :param endpoint: the endpoint to call (i.e. "statistics")
        :param read_callback: the callback to be called with result info when we have the data
        :param data: optional POST data to be sent with the request
        :param method: the HTTP verb (GET/POST/PUT/PATCH)
        :param capture_errors: whether errors should be handled by this class (defaults to True)
        """
        performed_requests[self.request_id] = [endpoint, method, data, time(), -1]
        url = self.base_url + endpoint

        if method == 'GET':
            buf = QBuffer()
            buf.setData(data)
            buf.open(QIODevice.ReadOnly)
            get_request = QNetworkRequest(QUrl(url))
            self.reply = self.sendCustomRequest(get_request, "GET", buf)
            buf.setParent(self.reply)
        elif method == 'PATCH':
            buf = QBuffer()
            buf.setData(data)
            buf.open(QIODevice.ReadOnly)
            patch_request = QNetworkRequest(QUrl(url))
            self.reply = self.sendCustomRequest(patch_request, "PATCH", buf)
            buf.setParent(self.reply)
        elif method == 'PUT':
            request = QNetworkRequest(QUrl(url))
            request.setHeader(QNetworkRequest.ContentTypeHeader, "application/x-www-form-urlencoded")
            self.reply = self.put(request, data)
        elif method == 'DELETE':
            buf = QBuffer()
            buf.setData(data)
            buf.open(QIODevice.ReadOnly)
            delete_request = QNetworkRequest(QUrl(url))
            self.reply = self.sendCustomRequest(delete_request, "DELETE", buf)
            buf.setParent(self.reply)
        elif method == 'POST':
            request = QNetworkRequest(QUrl(url))
            request.setHeader(QNetworkRequest.ContentTypeHeader, "application/x-www-form-urlencoded")
            self.reply = self.post(request, data)

        if read_callback:
            self.received_json.connect(read_callback)

        self.finished.connect(lambda reply: self.on_finished(reply, capture_errors))

    def on_finished(self, reply, capture_errors):
        performed_requests[self.request_id][4] = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)

        data = reply.readAll()
        try:
            json_result = json.loads(str(data))

            if 'error' in json_result and capture_errors:
                if isinstance(json_result['error'], (str, unicode)):
                    self.show_error(json_result['error'])
                elif 'message' in json_result['error']:
                    self.show_error(json_result['error']['message'])
            else:
                self.received_json.emit(json_result, reply.error())
        except ValueError:
            self.received_json.emit(None, reply.error())
            logging.error("No json object could be decoded from data: %s" % data)

    def download_file(self, endpoint, read_callback):
        url = self.base_url + endpoint
        self.reply = self.get(QNetworkRequest(QUrl(url)))
        self.received_file.connect(read_callback)
        self.finished.connect(self.on_file_download_finished)

    def on_file_download_finished(self, reply):
        content_header = str(reply.rawHeader("Content-Disposition"))
        data = reply.readAll()
        self.received_file.emit(content_header.split("=")[1], data)

    def show_error(self, error_text):
        main_text = "An error occurred during the request:\n\n%s" % error_text
        self.error_dialog = ConfirmationDialog(TriblerRequestManager.window, "Request error",
                                               main_text, [('close', BUTTON_TYPE_NORMAL)])
        self.error_dialog.button_clicked.connect(self.on_error_dialog_cancel_clicked)
        self.error_dialog.show()

    def on_error_dialog_cancel_clicked(self, _):
        self.error_dialog.setParent(None)
        self.error_dialog = None

    def cancel_request(self):
        if self.reply:
            self.reply.abort()