def on_read_data(self): if self.receivers(self.finished) == 0: connect(self.finished, lambda reply: self.on_finished()) self.connect_timer.stop() data = self.reply.readAll() self.current_event_string += bytes(data).decode('utf8') if len(self.current_event_string ) > 0 and self.current_event_string[-2:] == '\n\n': for event in self.current_event_string.split('\n\n'): if len(event) == 0: continue event = event[5:] if event.startswith('data:') else event json_dict = json.loads(event) received_events.insert(0, (json_dict, time.time())) if len(received_events ) > 100: # Only buffer the last 100 events received_events.pop() topic_name = json_dict.get("topic", "noname") args = json_dict.get("args", []) kwargs = json_dict.get("kwargs", {}) self.notifier.notify_by_topic_name(topic_name, *args, **kwargs) self.current_event_string = ""
def on_export_download(self, checked): self.export_dir = QFileDialog.getExistingDirectory( self, tr("Please select the destination directory"), "", QFileDialog.ShowDirsOnly) selected_item = self.selected_items[:1] if len(self.export_dir) > 0 and selected_item: # Show confirmation dialog where we specify the name of the file torrent_name = selected_item[0].download_info['name'] self.dialog = ConfirmationDialog( self, tr("Export torrent file"), tr("Please enter the name of the torrent file:"), [(tr("SAVE"), BUTTON_TYPE_NORMAL), (tr("CANCEL"), BUTTON_TYPE_CONFIRM)], show_input=True, ) self.dialog.dialog_widget.dialog_input.setPlaceholderText( tr("Torrent file name")) self.dialog.dialog_widget.dialog_input.setText( f"{torrent_name}.torrent") self.dialog.dialog_widget.dialog_input.setFocus() connect(self.dialog.button_clicked, self.on_export_download_dialog_done) self.dialog.show()
def __init__(self, parent, title, main_text, buttons, show_input=False, checkbox_text=None): DialogContainer.__init__(self, parent) uic.loadUi(get_ui_file_path('buttonsdialog.ui'), self.dialog_widget) self.dialog_widget.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) self.dialog_widget.dialog_title_label.setText(title) self.dialog_widget.dialog_main_text_label.setText(main_text) self.dialog_widget.dialog_main_text_label.adjustSize() self.checkbox = self.dialog_widget.checkbox if not show_input: self.dialog_widget.dialog_input.setHidden(True) else: connect(self.dialog_widget.dialog_input.returnPressed, lambda: self.button_clicked.emit(0)) if not checkbox_text: self.dialog_widget.checkbox.setHidden(True) else: self.dialog_widget.checkbox.setText(checkbox_text) hspacer_left = QSpacerItem(1, 1, QSizePolicy.Expanding, QSizePolicy.Fixed) self.dialog_widget.dialog_button_container.layout().addSpacerItem(hspacer_left) self.buttons = [] for index in range(len(buttons)): self.create_button(index, *buttons[index]) hspacer_right = QSpacerItem(1, 1, QSizePolicy.Expanding, QSizePolicy.Fixed) self.dialog_widget.dialog_button_container.layout().addSpacerItem(hspacer_right) if hasattr(self.window(), 'escape_pressed'): connect(self.window().escape_pressed, self.close_dialog) self.on_main_window_resize()
def create_button(self, index, button_text, _): button = EllipseButton(self.dialog_widget) button.setText(button_text) button.setFixedHeight(26) button.setCursor(QCursor(Qt.PointingHandCursor)) self.buttons.append(button) button.setStyleSheet( """ EllipseButton { border: 1px solid #B5B5B5; border-radius: 13px; color: white; padding-left: 4px; padding-right: 4px; } EllipseButton::hover { border: 1px solid white; color: white; } """ ) self.dialog_widget.dialog_button_container.layout().addWidget(button) connect(button.clicked, lambda _: self.button_clicked.emit(index))
def on_add_torrent_browse_dir(self, checked): self.raise_window( ) # For the case when the action is triggered by tray icon chosen_dir = QFileDialog.getExistingDirectory( self, tr("Please select the directory containing the .torrent files"), QDir.homePath(), QFileDialog.ShowDirsOnly, ) self.chosen_dir = chosen_dir if len(chosen_dir) != 0: self.selected_torrent_files = [ torrent_file for torrent_file in glob.glob(chosen_dir + "/*.torrent") ] self.dialog = ConfirmationDialog( self, tr("Add torrents from directory"), tr("Add %s torrent files from the following directory to your Tribler channel: \n\n%s" ) % (len(self.selected_torrent_files), chosen_dir), [(tr("ADD"), BUTTON_TYPE_NORMAL), (tr("CANCEL"), BUTTON_TYPE_CONFIRM)], checkbox_text=tr("Add torrents to My Channel"), ) connect(self.dialog.button_clicked, self.on_confirm_add_directory_dialog) self.dialog.show()
def on_settings_saved(self, data): if not data: return # Now save the GUI settings self.window().gui_settings.setValue( "family_filter", self.window().family_filter_checkbox.isChecked()) self.window().gui_settings.setValue( "autocommit_enabled", self.window().channel_autocommit_checkbox.isChecked()) self.window().gui_settings.setValue( "ask_download_settings", self.window().always_ask_location_checkbox.isChecked()) self.window().gui_settings.setValue( "use_monochrome_icon", self.window().use_monochrome_icon_checkbox.isChecked()) self.saved_dialog = ConfirmationDialog( TriblerRequestManager.window, tr("Settings saved"), tr("Your settings have been saved."), [(tr("CLOSE"), BUTTON_TYPE_NORMAL)], ) connect(self.saved_dialog.button_clicked, self.on_dialog_cancel_clicked) self.saved_dialog.show() self.window().fetch_settings()
def __init__(self, parent): QWidget.__init__(self, parent) self.setStyleSheet("background-color: rgba(30, 30, 30, 0.75);") self.dialog_widget = QWidget(self) self.closed = False connect(self.window().resize_event, self.on_main_window_resize)
def received_settings(self, settings): if not settings: return # If we cannot receive the settings, stop Tribler with an option to send the crash report. if 'error' in settings: raise RuntimeError(TriblerRequestManager.get_message_from_error(settings)) # If there is any pending dialog (likely download dialog or error dialog of setting not available), # close the dialog if self.dialog: self.dialog.close_dialog() self.dialog = None self.tribler_settings = settings['settings'] 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() connect(self.token_refresh_timer.timeout, self.load_token_balance) self.token_refresh_timer.start(60000) self.load_token_balance()
def on_channel_delete(self, channel_info): def _on_delete_action(action): if action == 0: delete_data = [{"public_key": channel_info['public_key'], "id": channel_info['id']}] TriblerNetworkRequest( "metadata", lambda data: self.core_manager.events_manager.node_info_updated.emit(data[0]), raw_data=json.dumps(delete_data), method='DELETE', ) if self.dialog: self.dialog.close_dialog() self.dialog = None self.dialog = ConfirmationDialog( self, "Delete channel", "Are you sure you want to <b>delete</b> your personal channel<br/>" + '\"' + f"<b>{channel_info['name']}</b>" + '\"' + "<br/>and all its contents?", [('DELETE', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)], ) connect(self.dialog.button_clicked, _on_delete_action) self.dialog.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: """ def close_tribler_gui(): self.close_tribler() # Since the core has already stopped at this point, it will not terminate the GUI. # So, we quit the GUI separately here. if not QApplication.closingDown(): QApplication.quit() 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)], ) connect(close_dialog.button_clicked, lambda _: close_tribler_gui()) close_dialog.show()
def initialize_trust_page(self): vlayout = self.window().plot_widget.layout() if vlayout.isEmpty(): self.trust_plot = TrustSeriesPlot(self.window().plot_widget) vlayout.addWidget(self.trust_plot) connect(self.window().trust_explain_button.clicked, self.on_info_button_clicked)
def on_channel_unsubscribe(self, channel_info): def _on_unsubscribe_action(action): if action == 0: patch_data = [{"public_key": channel_info['public_key'], "id": channel_info['id'], "subscribed": False}] TriblerNetworkRequest( "metadata", lambda data: self.core_manager.events_manager.node_info_updated.emit(data[0]), raw_data=json.dumps(patch_data), method='PATCH', ) if self.dialog: self.dialog.close_dialog() self.dialog = None self.dialog = ConfirmationDialog( self, "Unsubscribe from channel", "Are you sure you want to <b>unsubscribe</b> from channel<br/>" + '\"' + f"<b>{channel_info['name']}</b>" + '\"' + "<br/>and remove its contents?", [('UNSUBSCRIBE', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)], ) connect(self.dialog.button_clicked, _on_unsubscribe_action) self.dialog.show()
def tribler_api(api_port, tmpdir_factory): # Run real Core and record responses core_env = QProcessEnvironment.systemEnvironment() core_env.insert("CORE_BASE_PATH", str(RUN_TRIBLER_PY.parent / "tribler-core")) core_env.insert("CORE_PROCESS", "1") core_env.insert("CORE_API_PORT", f"{api_port}") core_env.insert("CORE_API_KEY", "") core_env.insert("TRIBLER_CORE_TEST_MODE", "1") temp_state_dir = tmpdir_factory.mktemp('tribler_state_dir') core_env.insert("TSTATEDIR", str(temp_state_dir)) core_process = QProcess() def on_core_read_ready(): raw_output = bytes(core_process.readAll()) decoded_output = raw_output.decode(errors="replace") print(decoded_output.strip()) # noqa: T001 core_process.setProcessEnvironment(core_env) core_process.setReadChannel(QProcess.StandardOutput) core_process.setProcessChannelMode(QProcess.MergedChannels) connect(core_process.readyRead, on_core_read_ready) core_process.start("python3", [str(RUN_TRIBLER_PY.absolute())]) yield core_process core_process.kill() core_process.waitForFinished()
def on_create_clicked(self, checked): 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)]) connect(self.dialog.button_clicked, self.on_dialog_ok_clicked) self.dialog.show() return self.window().edit_channel_create_torrent_button.setEnabled(False) files_list = [] for ind in range(self.window().create_torrent_files_list.count()): file_str = self.window().create_torrent_files_list.item(ind).text() files_list.append(file_str) name = self.window().create_torrent_name_field.text() description = self.window( ).create_torrent_description_field.toPlainText() post_data = { "name": name, "description": description, "files": files_list } url = "createtorrent?download=1" if self.window( ).seed_after_adding_checkbox.isChecked() else "createtorrent" TriblerNetworkRequest(url, self.on_torrent_created, data=post_data, method='POST') # Show creating torrent text self.window().edit_channel_create_torrent_progress_label.show()
def __init__(self, api_port, api_key, error_handler): QObject.__init__(self, None) self._logger = logging.getLogger(self.__class__.__name__) self.base_path = get_base_path() if not is_frozen(): self.base_path = os.path.join(get_base_path(), "..") self.core_process = None self.api_port = api_port self.api_key = api_key self.events_manager = EventRequestManager(self.api_port, self.api_key, error_handler) self.shutting_down = False self.should_stop_on_shutdown = False self.use_existing_core = True self.is_core_running = False self.core_traceback = None self.core_traceback_timestamp = 0 self.check_state_timer = QTimer() self.check_state_timer.setSingleShot(True) connect(self.check_state_timer.timeout, self.check_core_ready)
def __init__(self, root_state_dir, api_port, api_key, error_handler): QObject.__init__(self, None) self._logger = logging.getLogger(self.__class__.__name__) self.root_state_dir = root_state_dir self.core_process = None self.api_port = api_port self.api_key = api_key self.events_manager = EventRequestManager(self.api_port, self.api_key, error_handler) self.upgrade_manager = None self.core_args = None self.core_env = None self.core_started = False self.core_running = False self.core_connected = False self.shutting_down = False self.core_finished = False self.quitting_app = False self.should_quit_app_on_core_finished = False self.use_existing_core = True self.last_core_stdout_output: str = '' self.last_core_stderr_output: str = '' connect(self.events_manager.tribler_started, self.on_core_connected) app = QApplication.instance() if app is not None: # app can be None in tests where Qt application is not created connect(app.aboutToQuit, self.on_about_to_quit)
def __init__(self, parent=None): QTableView.__init__(self, parent) self.setMouseTracking(True) self.delegate = TriblerContentDelegate() self.setItemDelegate(self.delegate) connect(self.mouse_moved, self.delegate.on_mouse_moved) connect(self.delegate.redraw_required, self.redraw) # Stop triggering editor events on doubleclick, because we already use doubleclick to start downloads. # Editing should be started manually, from drop-down menu instead. self.setEditTriggers(QAbstractItemView.NoEditTriggers) # Mix-in connects connect(self.clicked, self.on_table_item_clicked) connect( self.doubleClicked, lambda item: self.on_table_item_clicked(item, doubleclick=True)) self.loading_animation_widget = FloatingAnimationWidget(self) # We add a small delay to show the loading animation to avoid flickering on fast-loaded data self.loading_animation_delay_timer = QTimer() self.loading_animation_delay_timer.setSingleShot(True) self.loading_animation_delay = 100 # Milliseconds connect(self.loading_animation_delay_timer.timeout, self.show_loading_animation) self.hide_loading_animation()
def perform_files_request(self): if self.closed: return direct = not self.dialog_widget.anon_download_checkbox.isChecked() request = f"torrentinfo?uri={quote_plus_unicode(self.download_uri)}" if direct is True: request = request + f"&hops=0" self.rest_request = TriblerNetworkRequest(request, self.on_received_metainfo, capture_core_errors=False) if self.metainfo_retries <= METAINFO_MAX_RETRIES: fetch_mode = 'directly' if direct else 'anonymously' loading_message = f"Loading torrent files {fetch_mode}..." timeout_message = ( f"Timeout in fetching files {fetch_mode}. Retrying ({self.metainfo_retries}/{METAINFO_MAX_RETRIES})" ) self.dialog_widget.loading_files_label.setText( loading_message if not self.metainfo_retries else timeout_message) self.metainfo_fetch_timer = QTimer() connect(self.metainfo_fetch_timer.timeout, self.perform_files_request) self.metainfo_fetch_timer.setSingleShot(True) self.metainfo_fetch_timer.start(METAINFO_TIMEOUT) self.metainfo_retries += 1
def on_read_data(self): if self.receivers(self.finished) == 0: connect(self.finished, lambda reply: self.on_finished()) self.connect_timer.stop() data = self.reply.readAll() self.current_event_string += bytes(data).decode('utf8') if len(self.current_event_string ) > 0 and self.current_event_string[-2:] == '\n\n': for event in self.current_event_string.split('\n\n'): if len(event) == 0: continue event = event[5:] if event.startswith('data:') else event json_dict = json.loads(event) received_events.insert(0, (json_dict, time.time())) if len(received_events ) > 100: # Only buffer the last 100 events received_events.pop() event_type, event = json_dict.get("type"), json_dict.get( "event") reaction = self.reactions_dict.get(event_type) if reaction: if event: reaction(event) else: reaction() self.current_event_string = ""
def __init__(self, parent=None): super().__init__(parent) # Unique identifier mapping for items. For torrents, it is infohash and for channels, it is concatenated value # of public key and channel id self.item_uid_map = {} # ACHTUNG! The reason why this is here and not in the class variable is, QT i18 only works for # tr() entries defined in the class instance constructor self.columns_dict = define_columns() self.data_items = [] self.remote_items = [] self.max_rowid = None self.local_total = None self.item_load_batch = 50 self.sort_by = self.columns[ self. default_sort_column].dict_key if self.default_sort_column >= 0 else None self.sort_desc = True self.saved_header_state = None self.saved_scroll_state = None self.qt_object_destroyed = False connect(self.destroyed, self.on_destroy) # Every remote query must be attributed to its specific model to avoid updating wrong models # on receiving a result. We achieve this by maintaining a set of in-flight remote queries. # Note that this only applies to results that are returned through the events notification # mechanism, because REST requests attribution is maintained by the RequestManager. # We do not clean it up after receiving a result because we don't know if the result was the # last one. In a sense, the queries' UUIDs play the role of "subscription topics" for the model. self.remote_queries = set() self.loaded = False
def set_model(self, model): if not model.loaded: self.table_view.show_loading_animation_delayed() connect(model.query_complete, self.table_view.hide_loading_animation) connect(model.query_started, self.table_view.show_loading_animation_delayed) super().set_model(model)
def on_create_torrent(self, checked): if self.create_dialog: self.create_dialog.close_dialog() self.create_dialog = CreateTorrentDialog(self) connect(self.create_dialog.create_torrent_notification, self.on_create_torrent_updates) self.create_dialog.show()
def perform_files_request(self): if self.closed or self.has_metainfo: return direct = not self.dialog_widget.anon_download_checkbox.isChecked() params = {'uri': self.download_uri} if direct: params['hops'] = 0 self.rest_request = TriblerNetworkRequest('torrentinfo', self.on_received_metainfo, capture_core_errors=False, url_params=params) if self.metainfo_retries <= METAINFO_MAX_RETRIES: fetch_mode = tr("directly") if direct else tr("anonymously") loading_message = tr("Loading torrent files %s...") % fetch_mode timeout_message = tr( "Timeout in fetching files %s. Retrying %i/%i") % ( fetch_mode, self.metainfo_retries, METAINFO_MAX_RETRIES, ) self.dialog_widget.loading_files_label.setText( loading_message if not self.metainfo_retries else timeout_message) self.metainfo_fetch_timer = QTimer() connect(self.metainfo_fetch_timer.timeout, self.perform_files_request) self.metainfo_fetch_timer.setSingleShot(True) self.metainfo_fetch_timer.start(METAINFO_TIMEOUT) self.metainfo_retries += 1
def close_tribler(self, checked=False): if self.core_manager.shutting_down: return def show_force_shutdown(): self.window().force_shutdown_btn.show() self.delete_tray_icon() self.show_loading_screen() self.hide_status_bar() self.loading_text_label.setText("Shutting down...") if self.debug_window: self.debug_window.setHidden(True) self.shutdown_timer = QTimer() connect(self.shutdown_timer.timeout, 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_manager.clear() # Stop the token balance timer if self.token_refresh_timer: self.token_refresh_timer.stop()
def add_request(self, request): if len(self.requests_in_flight) > self.max_in_flight: self.evict_timed_out_requests() self.requests_in_flight[id(request)] = request log = [request, 0] performed_requests.append(log) # qt_request is managed by QNetworkAccessManager, so we don't have to qt_request = QNetworkRequest(QUrl(request.url)) qt_request.setPriority(request.priority) qt_request.setHeader(QNetworkRequest.ContentTypeHeader, "application/x-www-form-urlencoded") qt_request.setRawHeader(b'X-Api-Key', self.key) buf = QBuffer() if request.raw_data: buf.setData(request.raw_data) buf.open(QIODevice.ReadOnly) request.reply = self.sendCustomRequest(qt_request, request.method.encode("utf8"), buf) buf.setParent(request.reply) connect(request.reply.finished, lambda: request.on_finished(request))
def initialize_trust_graph(self): pg.setConfigOption('background', '222222') pg.setConfigOption('foreground', '555') pg.setConfigOption('antialias', True) graph_layout = pg.GraphicsLayoutWidget() self.graph_view = graph_layout.addViewBox() self.graph_view.setAspectLocked() self.graph_view.setMenuEnabled(False) self.reset_graph() # To disable zoom in the graph, wheel event is overridden. To enable it again, remove the statement below. self.graph_view.wheelEvent = lambda evt: None self.trust_graph = TrustGraph() self.trust_graph.set_node_selection_listener(self.on_node_clicked) self.graph_view.addItem(self.trust_graph) self.graph_view.addItem(pg.TextItem(text='YOU')) self.window().trust_graph_plot_widget.layout().addWidget(graph_layout) connect(self.window().tr_control_refresh_btn.clicked, self.fetch_graph_data) self.window().tr_selected_node_pub_key.setHidden(True) self.window().tr_selected_node_stats.setHidden(True) self.window().trust_graph_progress_bar.setHidden(True)
def on_event_manager_initial_error(self, _): if self.upgrade_manager: # Start Tribler Upgrader. When it finishes, start Tribler Core connect(self.upgrade_manager.upgrader_finished, self.start_tribler_core) self.upgrade_manager.start() else: self.start_tribler_core()
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) connect( self.table_view.delegate.health_status_widget.clicked, lambda index: self.check_torrent_health(index.model().data_items[index.row()], forced=True), ) connect(self.table_view.torrent_clicked, self.check_torrent_health)
def connect(self, reschedule_on_err=True): self._logger.info("Will connect to events endpoint") self.reply = self.get(self.request) connect(self.reply.readyRead, self.on_read_data) connect( self.reply.error, lambda error: self.on_error(error, reschedule_on_err=reschedule_on_err))
def on_edit_tags_clicked(self, index: QModelIndex) -> None: data_item = index.model().data_items[index.row()] self.add_tags_dialog = AddTagsDialog(self.window(), data_item["infohash"]) self.add_tags_dialog.index = index if data_item.get("tags", ()): self.add_tags_dialog.dialog_widget.edit_tags_input.set_tags(data_item.get("tags", ())) self.add_tags_dialog.dialog_widget.content_name_label.setText(data_item["name"]) self.add_tags_dialog.show() connect(self.add_tags_dialog.save_button_clicked, self.save_edited_tags)