def __init__(self): component.Component.__init__(self, "ToolBar") log.debug("ToolBar Init..") self.window = component.get("MainWindow") self.toolbar = self.window.main_glade.get_widget("toolbar") self.config = ConfigManager("gtkui.conf") ### Connect Signals ### self.window.main_glade.signal_autoconnect({ "on_toolbutton_add_clicked": self.on_toolbutton_add_clicked, "on_toolbutton_remove_clicked": self.on_toolbutton_remove_clicked, "on_toolbutton_pause_clicked": self.on_toolbutton_pause_clicked, "on_toolbutton_resume_clicked": self.on_toolbutton_resume_clicked, "on_toolbutton_preferences_clicked": \ self.on_toolbutton_preferences_clicked, "on_toolbutton_connectionmanager_clicked": \ self.on_toolbutton_connectionmanager_clicked, "on_toolbutton_queue_up_clicked": self.on_toolbutton_queue_up_clicked, "on_toolbutton_queue_down_clicked": self.on_toolbutton_queue_down_clicked }) self.change_sensitivity = [ "toolbutton_add", "toolbutton_remove", "toolbutton_pause", "toolbutton_resume", "toolbutton_queue_up", "toolbutton_queue_down", "toolbutton_filter", "find_menuitem" ] self.config.register_set_function("classic_mode", self._on_classic_mode, True) # Hide if necessary self.visible(self.config["show_toolbar"])
def __init__(self): component.Component.__init__(self, "ConnectionManager") self.gtkui_config = ConfigManager("gtkui.conf") self.config = ConfigManager("hostlist.conf.1.2", DEFAULT_CONFIG) self.running = False
def __init__(self): component.Component.__init__(self, 'SystemTray', interval=4) self.mainwindow = component.get('MainWindow') self.config = ConfigManager('gtk3ui.conf') # List of widgets that need to be hidden when not connected to a host self.hide_widget_list = [ 'menuitem_add_torrent', 'menuitem_pause_session', 'menuitem_resume_session', 'menuitem_download_limit', 'menuitem_upload_limit', 'menuitem_quitdaemon', 'separatormenuitem1', 'separatormenuitem2', 'separatormenuitem3', 'separatormenuitem4', ] self.config.register_set_function('enable_system_tray', self.on_enable_system_tray_set) # bit of a hack to prevent function from doing something on startup self.__enabled_set_once = False self.config.register_set_function('enable_appindicator', self.on_enable_appindicator_set) self.max_download_speed = -1.0 self.download_rate = 0.0 self.max_upload_speed = -1.0 self.upload_rate = 0.0 self.config_value_changed_dict = { 'max_download_speed': self._on_max_download_speed, 'max_upload_speed': self._on_max_upload_speed, }
def enable(self): log.info("*** Start Label plugin ***") self.plugin = component.get("CorePluginManager") self.plugin.register_status_field("label", self._status_get_label) #__init__ core = component.get("Core") self.config = ConfigManager("label.conf", defaults=CONFIG_DEFAULTS) self.core_cfg = ConfigManager("core.conf") #reduce typing, assigning some values to self... self.torrents = core.torrentmanager.torrents self.labels = self.config["labels"] self.torrent_labels = self.config["torrent_labels"] self.clean_initial_config() component.get("EventManager").register_event_handler( "TorrentAddedEvent", self.post_torrent_add) component.get("EventManager").register_event_handler( "TorrentRemovedEvent", self.post_torrent_remove) #register tree: component.get("FilterManager").register_tree_field( "label", self.init_filter_dict) log.debug("Label plugin enabled..")
def enable(self): self.config = ConfigManager('execute.conf', DEFAULT_CONFIG) event_manager = component.get('EventManager') self.registered_events = {} self.preremoved_cache = {} # Go through the commands list and register event handlers for command in self.config['commands']: event = command[EXECUTE_EVENT] if event in self.registered_events: continue def create_event_handler(event): def event_handler(torrent_id, *arg): self.execute_commands(torrent_id, event, *arg) return event_handler event_handler = create_event_handler(event) event_manager.register_event_handler(EVENT_MAP[event], event_handler) if event == 'removed': event_manager.register_event_handler('PreTorrentRemovedEvent', self.on_preremoved) self.registered_events[event] = event_handler log.debug('Execute core plugin enabled!')
def enable(self): self.config = ConfigManager("execute.conf", DEFAULT_CONFIG) event_manager = component.get("EventManager") self.registered_events = {} self.preremoved_cache = {} # Go through the commands list and register event handlers for command in self.config["commands"]: event = command[EXECUTE_EVENT] if event in self.registered_events: continue def create_event_handler(event): def event_handler(torrent_id, *arg): #log.debug("Bleh %s", *arg) #log.debug("Bleh %s", arg) self.execute_commands(torrent_id, event, *arg) return event_handler event_handler = create_event_handler(event) event_manager.register_event_handler(EVENT_MAP[event], event_handler) if event == "removed": event_manager.register_event_handler("PreTorrentRemovedEvent", self.on_preremoved) self.registered_events[event] = event_handler log.debug("Execute core plugin enabled!")
def enable(self): log.info('*** Start Label plugin ***') self.plugin = component.get('CorePluginManager') self.plugin.register_status_field('label', self._status_get_label) # __init__ core = component.get('Core') self.config = ConfigManager('label.conf', defaults=CONFIG_DEFAULTS) self.core_cfg = ConfigManager('core.conf') # reduce typing, assigning some values to self... self.torrents = core.torrentmanager.torrents self.labels = self.config['labels'] self.torrent_labels = self.config['torrent_labels'] self.clean_initial_config() component.get('EventManager').register_event_handler( 'TorrentAddedEvent', self.post_torrent_add ) component.get('EventManager').register_event_handler( 'TorrentRemovedEvent', self.post_torrent_remove ) # register tree: component.get('FilterManager').register_tree_field( 'label', self.init_filter_dict ) log.debug('Label plugin enabled..')
def __init__(self): component.Component.__init__(self, "SystemTray", interval=4) self.window = component.get("MainWindow") self.config = ConfigManager("gtkui.conf") # List of widgets that need to be hidden when not connected to a host self.hide_widget_list = [ "menuitem_add_torrent", "menuitem_pause_all", "menuitem_resume_all", "menuitem_download_limit", "menuitem_upload_limit", "menuitem_quitdaemon", "separatormenuitem1", "separatormenuitem2", "separatormenuitem3", "separatormenuitem4" ] self.config.register_set_function("enable_system_tray", self.on_enable_system_tray_set) # bit of a hack to prevent function from doing something on startup self.__enabled_set_once = False self.config.register_set_function("enable_appindicator", self.on_enable_appindicator_set) self.max_download_speed = -1.0 self.download_rate = 0.0 self.max_upload_speed = -1.0 self.upload_rate = 0.0 self.config_value_changed_dict = { "max_download_speed": self._on_max_download_speed, "max_upload_speed": self._on_max_upload_speed }
def __init__(self, stdscr, encoding=None): self.popup = None self.statuses = {} self.messages = deque() self.config = ConfigManager("hostlist.conf.1.2", DEFAULT_CONFIG) BaseMode.__init__(self, stdscr, encoding) self.__update_statuses() self.__update_popup()
class AutoAdd(component.Component): def __init__(self): component.Component.__init__(self, "AutoAdd", depend=["TorrentManager"], interval=5) # Get the core config self.config = ConfigManager("core.conf") # A list of filenames self.invalid_torrents = [] # Filename:Attempts self.attempts = {} # Register set functions self.config.register_set_function("autoadd_enable", self._on_autoadd_enable, apply_now=True) self.config.register_set_function("autoadd_location", self._on_autoadd_location) def update(self): if not self.config["autoadd_enable"]: # We shouldn't be updating because autoadd is not enabled component.pause("AutoAdd") return # Check the auto add folder for new torrents to add if not os.path.isdir(self.config["autoadd_location"]): log.warning("Invalid AutoAdd folder: %s", self.config["autoadd_location"]) component.pause("AutoAdd") return for filename in os.listdir(self.config["autoadd_location"]): try: filepath = os.path.join(self.config["autoadd_location"], filename) except UnicodeDecodeError, e: log.error("Unable to auto add torrent due to improper filename encoding: %s", e) continue if os.path.isfile(filepath) and filename.endswith(".torrent"): try: filedump = self.load_torrent(filepath) except (RuntimeError, Exception), e: # If the torrent is invalid, we keep track of it so that we # can try again on the next pass. This is because some # torrents may not be fully saved during the pass. log.debug("Torrent is invalid: %s", e) if filename in self.invalid_torrents: self.attempts[filename] += 1 if self.attempts[filename] >= MAX_NUM_ATTEMPTS: os.rename(filepath, filepath + ".invalid") del self.attempts[filename] self.invalid_torrents.remove(filename) else: self.invalid_torrents.append(filename) self.attempts[filename] = 1 continue # The torrent looks good, so lets add it to the session component.get("TorrentManager").add(filedump=filedump, filename=filename) os.remove(filepath)
def __init__(self): super(WebApi, self).__init__("Web", depend=["SessionProxy"]) self.host_list = ConfigManager("hostlist.conf.1.2", DEFAULT_HOSTS) if not os.path.isfile(self.host_list.config_file): self.host_list.save() self.core_config = CoreConfig() self.event_queue = EventQueue() try: self.sessionproxy = component.get("SessionProxy") except KeyError: self.sessionproxy = SessionProxy()
def __init__(self): if wnck: self.screen = wnck.screen_get_default() component.Component.__init__(self, 'MainWindow', interval=2) self.config = ConfigManager('gtkui.conf') self.main_builder = gtk.Builder() # Patch this GtkBuilder to avoid connecting signals from elsewhere # # Think about splitting up mainwindow gtkbuilder file into the necessary parts # to avoid GtkBuilder monkey patch. Those parts would then need adding to mainwindow 'by hand'. self.gtk_builder_signals_holder = _GtkBuilderSignalsHolder() self.main_builder.prev_connect_signals = copy.deepcopy(self.main_builder.connect_signals) def patched_connect_signals(*a, **k): raise RuntimeError('In order to connect signals to this GtkBuilder instance please use ' '"component.get(\'MainWindow\').connect_signals()"') self.main_builder.connect_signals = patched_connect_signals # Get Gtk Builder files Main Window, New release dialog, and Tabs. for filename in ('main_window.ui', 'main_window.new_release.ui', 'main_window.tabs.ui', 'main_window.tabs.menu_file.ui', 'main_window.tabs.menu_peer.ui'): self.main_builder.add_from_file( resource_filename('deluge.ui.gtkui', os.path.join('glade', filename))) self.window = self.main_builder.get_object('main_window') self.window.set_icon(deluge.ui.gtkui.common.get_deluge_icon()) self.vpaned = self.main_builder.get_object('vpaned') self.initial_vpaned_position = self.config['window_pane_position'] # Keep a list of components to pause and resume when changing window state. self.child_components = ['TorrentView', 'StatusBar', 'TorrentDetails'] # Load the window state self.load_window_state() # Keep track of window minimization state so we don't update UI when it is minimized. self.is_minimized = False self.restart = False self.window.drag_dest_set(gtk.DEST_DEFAULT_ALL, [('text/uri-list', 0, 80)], ACTION_COPY) # Connect events self.window.connect('window-state-event', self.on_window_state_event) self.window.connect('configure-event', self.on_window_configure_event) self.window.connect('delete-event', self.on_window_delete_event) self.window.connect('drag-data-received', self.on_drag_data_received_event) self.vpaned.connect('notify::position', self.on_vpaned_position_event) self.window.connect('expose-event', self.on_expose_event) self.config.register_set_function('show_rate_in_title', self._on_set_show_rate_in_title, apply_now=False) client.register_event_handler('NewVersionAvailableEvent', self.on_newversionavailable_event)
def get_labels(self, torrent_id): labels = [] label_config = ConfigManager('label.conf', defaults=False) if label_config is not False: if 'torrent_labels' in label_config: if torrent_id in label_config['torrent_labels']: labels.append(label_config['torrent_labels'][torrent_id]) label_plus_config = ConfigManager('labelplus.conf', defaults=False) if label_plus_config is not False: if 'mappings' in label_plus_config: if torrent_id in label_plus_config['mappings']: mapping = label_plus_config['mappings'][torrent_id] labels.append(label_plus_config['labels'][mapping]['name']) return labels
def __init__(self): super(StatusTab, self).__init__('Status', 'status_tab', 'status_tab_label') self.config = ConfigManager('gtk3ui.conf') self.progressbar = self.main_builder.get_object('progressbar') self.piecesbar = None self.add_tab_widget('summary_availability', fratio, ('distributed_copies',)) self.add_tab_widget( 'summary_total_downloaded', ftotal_sized, ('all_time_download', 'total_payload_download'), ) self.add_tab_widget( 'summary_total_uploaded', ftotal_sized, ('total_uploaded', 'total_payload_upload'), ) self.add_tab_widget( 'summary_download_speed', fspeed_max, ('download_payload_rate', 'max_download_speed'), ) self.add_tab_widget( 'summary_upload_speed', fspeed_max, ('upload_payload_rate', 'max_upload_speed'), ) self.add_tab_widget('summary_seeds', fpeer, ('num_seeds', 'total_seeds')) self.add_tab_widget('summary_peers', fpeer, ('num_peers', 'total_peers')) self.add_tab_widget('summary_eta', ftime_or_dash, ('eta',)) self.add_tab_widget('summary_share_ratio', fratio, ('ratio',)) self.add_tab_widget('summary_active_time', ftime_or_dash, ('active_time',)) self.add_tab_widget('summary_seed_time', ftime_or_dash, ('seeding_time',)) self.add_tab_widget( 'summary_seed_rank', fseed_rank_or_dash, ('seed_rank', 'seeding_time') ) self.add_tab_widget('progressbar', fpcnt, ('progress', 'state', 'message')) self.add_tab_widget( 'summary_last_seen_complete', fdate_or_never, ('last_seen_complete',) ) self.add_tab_widget( 'summary_last_transfer', ftime_or_dash, ('time_since_transfer',) ) self.config.register_set_function( 'show_piecesbar', self.on_show_piecesbar_config_changed, apply_now=True )
def __load_config(self): auth_file = deluge.configmanager.get_config_dir("auth") if not os.path.exists(auth_file): from deluge.common import create_localclient_account create_localclient_account() localclient_username, localclient_password = get_localhost_auth() DEFAULT_CONFIG = { "hosts": [(hashlib.sha1(str(time.time())).hexdigest(), DEFAULT_HOST, DEFAULT_PORT, localclient_username, localclient_password)] } config = ConfigManager("hostlist.conf.1.2", DEFAULT_CONFIG) config.run_converter((0, 1), 2, self.__migrate_config_1_to_2) return config
def __init__(self): if Wnck: self.screen = Wnck.Screen.get_default() component.Component.__init__(self, "MainWindow", interval=2) self.config = ConfigManager("gtkui.conf") # Get the glade file for the main window self.main_glade = Gtk.Builder() self.main_glade.add_from_file( pkg_resources.resource_filename("deluge.ui.gtkui", "builder/main_window.ui")) self.window_signals = {} self.window = self.main_glade.get_object("main_window") self.window.set_icon(common.get_deluge_icon()) self.vpaned = self.main_glade.get_object("vpaned") self.initial_vpaned_position = self.config["window_pane_position"] # Load the window state self.load_window_state() # Keep track of window's minimization state so that we don't update the # UI when it is minimized. self.is_minimized = False self.window.drag_dest_set( Gtk.DestDefaults.ALL, [Gtk.TargetEntry.new('text/uri-list', 0, 80)], Gdk.DragAction.COPY) # Connect events self.window.connect("window-state-event", self.on_window_state_event) self.window.connect("configure-event", self.on_window_configure_event) self.window.connect("delete-event", self.on_window_delete_event) self.window.connect("drag-data-received", self.on_drag_data_received_event) self.vpaned.connect("notify::position", self.on_vpaned_position_event) self.window.connect("draw", self.on_expose_event) self.config.register_set_function("show_rate_in_title", self._on_set_show_rate_in_title, apply_now=False) client.register_event_handler("NewVersionAvailableEvent", self.on_newversionavailable_event) client.register_event_handler("TorrentFinishedEvent", self.on_torrentfinished_event)
def __init__(self, parent): QtGui.QDialog.__init__( self, parent, QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowSystemMenuHint) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.setupUi(self) self.ui_config = ConfigManager("qtui.conf") self.default_options = {} self._selected_item = None self.download_location_browse.setVisible(client.is_localhost()) self.EMPTY_FILE_MODEL = TorrentFileModel([], self) self.tree_files.setModel(self.EMPTY_FILE_MODEL) header = self.tree_files.header() header.setStretchLastSection(False) header.setMinimumSectionSize(header.fontMetrics().width("M") * 10) header.setResizeMode(0, QtGui.QHeaderView.Stretch) header.setResizeMode( 1, QtGui.QHeaderView.Fixed) # NB: ResizeToContents is slow HeightFixItemDelegate.install(self.tree_files) self._update_default_options()
def __init__(self): component.Component.__init__(self, "ToolBar") log.debug("ToolBar Init..") self.window = component.get("MainWindow") self.toolbar = self.window.main_glade.get_widget("toolbar") self.config = ConfigManager("gtkui.conf") ### Connect Signals ### self.window.main_glade.signal_autoconnect({ "on_toolbutton_add_clicked": self.on_toolbutton_add_clicked, "on_toolbutton_remove_clicked": self.on_toolbutton_remove_clicked, "on_toolbutton_pause_clicked": self.on_toolbutton_pause_clicked, "on_toolbutton_resume_clicked": self.on_toolbutton_resume_clicked, "on_toolbutton_preferences_clicked": \ self.on_toolbutton_preferences_clicked, "on_toolbutton_connectionmanager_clicked": \ self.on_toolbutton_connectionmanager_clicked, "on_toolbutton_queue_up_clicked": self.on_toolbutton_queue_up_clicked, "on_toolbutton_queue_down_clicked": self.on_toolbutton_queue_down_clicked }) self.change_sensitivity = [ "toolbutton_add", "toolbutton_remove", "toolbutton_pause", "toolbutton_resume", "toolbutton_queue_up", "toolbutton_queue_down" ] self.config.register_set_function("classic_mode", self._on_classic_mode, True) # Hide if necessary self.visible(self.config["show_toolbar"])
def __init__(self): super(StatusTab, self).__init__('Status', 'status_tab', 'status_tab_label') self.config = ConfigManager('gtkui.conf') self.progressbar = self.main_builder.get_object('progressbar') self.piecesbar = None self.add_tab_widget('summary_availability', fratio, ('distributed_copies',)) self.add_tab_widget('summary_total_downloaded', ftotal_sized, ('all_time_download', 'total_payload_download')) self.add_tab_widget('summary_total_uploaded', ftotal_sized, ('total_uploaded', 'total_payload_upload')) self.add_tab_widget('summary_download_speed', fspeed_max, ('download_payload_rate', 'max_download_speed')) self.add_tab_widget('summary_upload_speed', fspeed_max, ('upload_payload_rate', 'max_upload_speed')) self.add_tab_widget('summary_seeds', fpeer, ('num_seeds', 'total_seeds')) self.add_tab_widget('summary_peers', fpeer, ('num_peers', 'total_peers')) self.add_tab_widget('summary_eta', ftime_or_dash, ('eta',)) self.add_tab_widget('summary_share_ratio', fratio, ('ratio',)) self.add_tab_widget('summary_active_time', ftime_or_dash, ('active_time',)) self.add_tab_widget('summary_seed_time', ftime_or_dash, ('seeding_time',)) self.add_tab_widget('summary_seed_rank', fseed_rank_or_dash, ('seed_rank', 'seeding_time')) self.add_tab_widget('progressbar', fpcnt, ('progress', 'state', 'message')) self.add_tab_widget('summary_last_seen_complete', fdate_or_never, ('last_seen_complete',)) self.add_tab_widget('summary_last_transfer', ftime_or_dash, ('time_since_transfer',)) self.config.register_set_function('show_piecesbar', self.on_show_piecesbar_config_changed, apply_now=True)
def __init__(self): super(PiecesBar, self).__init__() # Get progress bar styles, in order to keep font consistency pb = ProgressBar() pb_style = pb.get_style_context() # Get a copy of Pango.FontDescription since original needs freed. self.text_font = pb_style.get_property('font', StateFlags.NORMAL).copy() self.text_font.set_weight(Weight.BOLD) # Done with the ProgressBar styles, don't keep refs of it del pb, pb_style self.set_size_request(-1, 25) self.gtkui_config = ConfigManager('gtk3ui.conf') self.width = self.prev_width = 0 self.height = self.prev_height = 0 self.pieces = self.prev_pieces = () self.num_pieces = None self.text = self.prev_text = '' self.fraction = self.prev_fraction = 0 self.progress_overlay = self.text_overlay = self.pieces_overlay = None self.cr = None self.connect('size-allocate', self.do_size_allocate_event) self.show()
def __init__(self): component.Component.__init__(self, 'SystemTray', interval=4) self.mainwindow = component.get('MainWindow') self.config = ConfigManager('gtkui.conf') # List of widgets that need to be hidden when not connected to a host self.hide_widget_list = [ 'menuitem_add_torrent', 'menuitem_pause_session', 'menuitem_resume_session', 'menuitem_download_limit', 'menuitem_upload_limit', 'menuitem_quitdaemon', 'separatormenuitem1', 'separatormenuitem2', 'separatormenuitem3', 'separatormenuitem4' ] self.config.register_set_function('enable_system_tray', self.on_enable_system_tray_set) # bit of a hack to prevent function from doing something on startup self.__enabled_set_once = False self.config.register_set_function('enable_appindicator', self.on_enable_appindicator_set) self.max_download_speed = -1.0 self.download_rate = 0.0 self.max_upload_speed = -1.0 self.upload_rate = 0.0 self.config_value_changed_dict = { 'max_download_speed': self._on_max_download_speed, 'max_upload_speed': self._on_max_upload_speed }
def __init__(self): gtk.DrawingArea.__init__(self) # Get progress bar styles, in order to keep font consistency pb = gtk.ProgressBar() pb_style = pb.get_style() self.__text_font = pb_style.font_desc self.__text_font.set_weight(pango.WEIGHT_BOLD) # Done with the ProgressBar styles, don't keep refs of it del pb, pb_style self.set_size_request(-1, 25) self.gtkui_config = ConfigManager("gtkui.conf") self.__width = self.__old_width = 0 self.__height = self.__old_height = 0 self.__pieces = self.__old_pieces = () self.__num_pieces = self.__old_num_pieces = None self.__text = self.__old_text = "" self.__fraction = self.__old_fraction = 0.0 self.__state = self.__old_state = None self.__progress_overlay = self.__text_overlay = self.__pieces_overlay = None self.__cr = None self.connect('size-allocate', self.do_size_allocate_event) self.set_colormap(self.get_screen().get_rgba_colormap()) self.show()
def __init__(self): super(PiecesBar, self).__init__() # Get progress bar styles, in order to keep font consistency pb = ProgressBar() pb_style = pb.get_style() self.text_font = pb_style.font_desc self.text_font.set_weight(WEIGHT_BOLD) # Done with the ProgressBar styles, don't keep refs of it del pb, pb_style self.set_size_request(-1, 25) self.gtkui_config = ConfigManager('gtkui.conf') self.width = self.prev_width = 0 self.height = self.prev_height = 0 self.pieces = self.prev_pieces = () self.num_pieces = None self.text = self.prev_text = '' self.fraction = self.prev_fraction = 0 self.progress_overlay = self.text_overlay = self.pieces_overlay = None self.cr = None self.connect('size-allocate', self.do_size_allocate_event) self.set_colormap(colormap_get_system()) self.show()
def update_config(self): self.config = ConfigManager("console.conf",DEFAULT_PREFS) s_primary = self.config["sort_primary"] s_secondary = self.config["sort_secondary"] self.__cols_to_show = [ pref for pref in column_pref_names if ("show_%s" % pref) not in self.config or self.config["show_%s"%pref] ] self.__columns = [prefs_to_names[col] for col in self.__cols_to_show] self.__status_fields = column.get_required_fields(self.__columns) # we always need these, even if we're not displaying them for rf in ["state", "name", "queue", "progress"]: if rf not in self.__status_fields: self.__status_fields.append(rf) # same with sort keys if s_primary and (s_primary not in self.__status_fields): self.__status_fields.append(s_primary) if s_secondary and (s_secondary not in self.__status_fields): self.__status_fields.append(s_secondary) self.__update_columns()
def show(self, available_version): self.config = ConfigManager("gtkui.conf") glade = component.get("MainWindow").main_glade self.dialog = glade.get_object("new_release_dialog") # Set the version labels if deluge.common.windows_check() or deluge.common.osx_check(): glade.get_object("image_new_release").set_from_file( deluge.common.get_pixmap("deluge16.png")) else: glade.get_object("image_new_release").set_from_icon_name( "deluge", 4) glade.get_object("label_available_version").set_text(available_version) glade.get_object("label_client_version").set_text( deluge.common.get_version()) self.chk_not_show_dialog = glade.get_object( "chk_do_not_show_new_release") glade.get_object("button_goto_downloads").connect( "clicked", self._on_button_goto_downloads) glade.get_object("button_close_new_release").connect( "clicked", self._on_button_close_new_release) if client.connected(): def on_info(version): glade.get_object("label_server_version").set_text(version) glade.get_object("label_server_version").show() glade.get_object("label_server_version_text").show() if not client.is_classicmode(): glade.get_object("label_client_version_text").set_label( _("<i>Client Version</i>")) client.daemon.info().addCallback(on_info) self.dialog.show()
def __init__(self): component.Component.__init__(self, "ToolBar") log.debug("ToolBar Init..") self.window = component.get("MainWindow") self.toolbar = self.window.main_glade.get_object("toolbar") self.config = ConfigManager("gtkui.conf") ### Connect Signals ### self.window.insert_signals({ "on_toolbutton_add_clicked": self.on_toolbutton_add_clicked, "on_toolbutton_remove_clicked": self.on_toolbutton_remove_clicked, "on_toolbutton_pause_clicked": self.on_toolbutton_pause_clicked, "on_toolbutton_resume_clicked": self.on_toolbutton_resume_clicked, "on_toolbutton_preferences_clicked": \ self.on_toolbutton_preferences_clicked, "on_toolbutton_connectionmanager_clicked": \ self.on_toolbutton_connectionmanager_clicked, "on_toolbutton_queue_up_clicked": self.on_toolbutton_queue_up_clicked, "on_toolbutton_queue_down_clicked": self.on_toolbutton_queue_down_clicked }) self.change_sensitivity = [ "toolbutton_add", "toolbutton_remove", "toolbutton_pause", "toolbutton_resume", "toolbutton_queue_up", "toolbutton_queue_down" ] # Hide if necessary self.visible(self.config["show_toolbar"])
def __init__(self): component.Component.__init__(self, 'QueuedTorrents', depend=['StatusBar', 'AddTorrentDialog']) self.queue = [] self.status_item = None self.config = ConfigManager('gtkui.conf') self.builder = Builder() self.builder.add_from_file( deluge.common.resource_filename( 'deluge.ui.gtkui', os.path.join('glade', 'queuedtorrents.ui'))) self.builder.get_object('chk_autoadd').set_active( self.config['autoadd_queued']) self.dialog = self.builder.get_object('queued_torrents_dialog') self.dialog.set_icon(get_logo(32)) self.builder.connect_signals(self) self.treeview = self.builder.get_object('treeview') self.treeview.append_column( TreeViewColumn(_('Torrent'), CellRendererText(), text=0)) self.liststore = ListStore(str, str) self.treeview.set_model(self.liststore) self.treeview.set_tooltip_column(1)
def on_menuitem_move_activate(self, data=None): log.debug("on_menuitem_move_activate") if client.is_localhost(): from deluge.configmanager import ConfigManager config = ConfigManager("gtkui.conf") chooser = gtk.FileChooserDialog( _("Choose a directory to move files to"), component.get("MainWindow").window, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) chooser.set_local_only(True) if not deluge.common.windows_check(): chooser.set_icon(common.get_deluge_icon()) chooser.set_property("skip-taskbar-hint", True) chooser.set_current_folder(config["choose_directory_dialog_path"]) if chooser.run() == gtk.RESPONSE_OK: result = chooser.get_filename() config["choose_directory_dialog_path"] = result client.core.move_storage( component.get("TorrentView").get_selected_torrents(), result) chooser.destroy() else: component.get("SessionProxy").get_torrent_status( component.get("TorrentView").get_selected_torrent(), ["save_path"]).addCallback(self.show_move_storage_dialog)
def __init__(self): component.Component.__init__(self, "QueuedTorrents", depend=["StatusBar", "AddTorrentDialog"]) self.queue = [] self.status_item = None self.config = ConfigManager("gtkui.conf") self.glade = gtk.glade.XML( pkg_resources.resource_filename("deluge.ui.gtkui", "glade/queuedtorrents.glade")) self.glade.get_widget("chk_autoadd").set_active( self.config["autoadd_queued"]) self.dialog = self.glade.get_widget("queued_torrents_dialog") self.dialog.set_icon(common.get_logo(32)) self.glade.signal_autoconnect({ "on_button_remove_clicked": self.on_button_remove_clicked, "on_button_clear_clicked": self.on_button_clear_clicked, "on_button_close_clicked": self.on_button_close_clicked, "on_button_add_clicked": self.on_button_add_clicked, "on_chk_autoadd_toggled": self.on_chk_autoadd_toggled }) self.treeview = self.glade.get_widget("treeview") self.treeview.append_column( gtk.TreeViewColumn(_("Torrent"), gtk.CellRendererText(), text=0)) self.liststore = gtk.ListStore(str, str) self.treeview.set_model(self.liststore)
def run(self, stdscr): """This method is called by the curses.wrapper to start the mainloop and screen. Args: stdscr (_curses.curses window): curses screen passed in from curses.wrapper. """ # We want to do an interactive session, so start up the curses screen and # pass it the function that handles commands colors.init_colors() self.stdscr = stdscr self.config = ConfigManager('console.conf', defaults=DEFAULT_CONSOLE_PREFS, file_version=2) self.config.run_converter((0, 1), 2, self._migrate_config_1_to_2) self.statusbars = StatusBars() from deluge.ui.console.modes.connectionmanager import ConnectionManager self.register_mode(ConnectionManager(stdscr, self.encoding), set_mode=True) torrentlist = self.register_mode(TorrentList(self.stdscr, self.encoding)) self.register_mode(CmdLine(self.stdscr, self.encoding)) self.register_mode(EventView(torrentlist, self.stdscr, self.encoding)) self.register_mode(TorrentDetail(torrentlist, self.stdscr, self.config, self.encoding)) self.register_mode(Preferences(torrentlist, self.stdscr, self.config, self.encoding)) self.register_mode(AddTorrents(torrentlist, self.stdscr, self.config, self.encoding)) self.eventlog = EventLog() self.active_mode.topbar = '{!status!}Deluge ' + deluge.common.get_version() + ' Console' self.active_mode.bottombar = '{!status!}' self.active_mode.refresh() # Start the twisted mainloop reactor.run()
def update_config(self): self.config = ConfigManager("console.conf",DEFAULT_PREFS) self.__cols_to_show = [pref for pref in column_pref_names if self.config["show_%s"%pref]] self.__columns = [prefs_to_names[col] for col in self.__cols_to_show] self.__status_fields = column.get_required_fields(self.__columns) for rf in ["state","name","queue"]: # we always need these, even if we're not displaying them if not rf in self.__status_fields: self.__status_fields.append(rf) self.__update_columns()
def menubar_osx(gtkui, osxapp): main_builder = gtkui.mainwindow.get_builder() menubar = main_builder.get_object('menubar') group = accel_groups_from_object(gtkui.mainwindow.window)[0] config = ConfigManager('gtk3ui.conf') # NOTE: accel maps doesn't work with glade file format # because of libglade not setting MenuItem accel groups # That's why we remove / set accelerators by hand... (dirty) # Clean solution: migrate glades files to gtkbuilder format file_menu = main_builder.get_object('menu_file').get_submenu() file_items = file_menu.get_children() accel_meta(file_items[0], group, 'o') accel_meta(file_items[1], group, 'n') quit_all_item = file_items[3] accel_swap( quit_all_item, group, 'q', ModifierType.SHIFT_MASK | ModifierType.CONTROL_MASK, 'q', ModifierType.SHIFT_MASK | ModifierType.META_MASK, ) for item in range(2, len(file_items)): # remove quits file_menu.remove(file_items[item]) menu_widget = main_builder.get_object('menu_edit') edit_menu = menu_widget.get_submenu() edit_items = edit_menu.get_children() pref_item = edit_items[0] accel_swap(pref_item, group, 'p', ModifierType.CONTROL_MASK, ',', ModifierType.META_MASK) edit_menu.remove(pref_item) conn_item = edit_items[1] accel_meta(conn_item, group, 'm') edit_menu.remove(conn_item) menubar.remove(menu_widget) help_menu = main_builder.get_object('menu_help').get_submenu() help_items = help_menu.get_children() about_item = help_items[4] help_menu.remove(about_item) help_menu.remove(help_items[3]) # separator menubar.hide() osxapp.set_menu_bar(menubar) # populate app menu osxapp.insert_app_menu_item(about_item, 0) osxapp.insert_app_menu_item(SeparatorMenuItem(), 1) osxapp.insert_app_menu_item(pref_item, 2) if not config['standalone']: osxapp.insert_app_menu_item(conn_item, 3) if quit_all_item.get_visible(): osxapp.insert_app_menu_item(SeparatorMenuItem(), 4) osxapp.insert_app_menu_item(quit_all_item, 5)
def __init__(self): if wnck: self.screen = wnck.screen_get_default() component.Component.__init__(self, "MainWindow", interval=2) self.config = ConfigManager("gtkui.conf") # Get the glade file for the main window self.main_glade = gtk.glade.XML( pkg_resources.resource_filename("deluge.ui.gtkui", "glade/main_window.glade")) self.window = self.main_glade.get_widget("main_window") self.window.set_icon(common.get_deluge_icon()) self.vpaned = self.main_glade.get_widget("vpaned") self.initial_vpaned_position = self.config["window_pane_position"] # Load the window state self.load_window_state() # Keep track of window's minimization state so that we don't update the # UI when it is minimized. self.is_minimized = False self.window.drag_dest_set(gtk.DEST_DEFAULT_ALL, [('text/uri-list', 0, 80)], gtk.gdk.ACTION_COPY) # Connect events self.window.connect("window-state-event", self.on_window_state_event) self.window.connect("configure-event", self.on_window_configure_event) self.window.connect("delete-event", self.on_window_delete_event) self.window.connect("drag-data-received", self.on_drag_data_received_event) self.vpaned.connect("notify::position", self.on_vpaned_position_event) self.window.connect("expose-event", self.on_expose_event) self.config.register_set_function("show_rate_in_title", self._on_set_show_rate_in_title, apply_now=False) client.register_event_handler("NewVersionAvailableEvent", self.on_newversionavailable_event) client.register_event_handler("TorrentFinishedEvent", self.on_torrentfinished_event)
def process_args(args): """Process arguments sent to already running Deluge""" # Make sure args is a list args = list(args) log.debug("Processing args from other process: %s", args) if not client.connected(): # We're not connected so add these to the queue log.debug("Not connected to host.. Adding to queue.") component.get("QueuedTorrents").add_to_queue(args) return config = ConfigManager("gtkui.conf") for arg in args: if not arg.strip(): continue log.debug("arg: %s", arg) if deluge.common.is_url(arg): log.debug("Attempting to add url (%s) from external source...", arg) if config["interactive_add"]: component.get("AddTorrentDialog").add_from_url(arg) component.get("AddTorrentDialog").show( config["focus_add_dialog"]) else: client.core.add_torrent_url(arg, None) elif deluge.common.is_magnet(arg): log.debug("Attempting to add magnet (%s) from external source...", arg) if config["interactive_add"]: component.get("AddTorrentDialog").add_from_magnets([arg]) component.get("AddTorrentDialog").show( config["focus_add_dialog"]) else: client.core.add_torrent_magnet(arg, {}) else: log.debug("Attempting to add file (%s) from external source...", arg) if urlparse(arg).scheme == "file": arg = url2pathname(urlparse(arg).path) path = os.path.abspath(deluge.common.decode_string(arg)) if not os.path.exists(path): log.error("No such file: %s", path) continue if config["interactive_add"]: component.get("AddTorrentDialog").add_from_files([path]) component.get("AddTorrentDialog").show( config["focus_add_dialog"]) else: with open(path, "rb") as _file: filedump = base64.encodestring(_file.read()) client.core.add_torrent_file( os.path.split(path)[-1], filedump, None)
def process_args(args): """Process arguments sent to already running Deluge""" # Make sure args is a list args = list(args) log.debug('Processing args from other process: %s', args) if not client.connected(): # We're not connected so add these to the queue log.debug('Not connected to host.. Adding to queue.') component.get('QueuedTorrents').add_to_queue(args) return config = ConfigManager('gtkui.conf') for arg in args: if not arg.strip(): continue log.debug('arg: %s', arg) if is_url(arg): log.debug('Attempting to add url (%s) from external source...', arg) if config['interactive_add']: component.get('AddTorrentDialog').add_from_url(arg) component.get('AddTorrentDialog').show( config['focus_add_dialog']) else: client.core.add_torrent_url(arg, None) elif is_magnet(arg): log.debug('Attempting to add magnet (%s) from external source...', arg) if config['interactive_add']: component.get('AddTorrentDialog').add_from_magnets([arg]) component.get('AddTorrentDialog').show( config['focus_add_dialog']) else: client.core.add_torrent_magnet(arg, {}) else: log.debug('Attempting to add file (%s) from external source...', arg) if urlparse(arg).scheme == 'file': arg = url2pathname(urlparse(arg).path) path = os.path.abspath(decode_bytes(arg)) if not os.path.exists(path): log.error('No such file: %s', path) continue if config['interactive_add']: component.get('AddTorrentDialog').add_from_files([path]) component.get('AddTorrentDialog').show( config['focus_add_dialog']) else: with open(path, 'rb') as _file: filedump = b64encode(_file.read()) client.core.add_torrent_file( os.path.split(path)[-1], filedump, None)
def __init__(self): component.Component.__init__(self, "Web.PluginManager") self.config = ConfigManager("web.conf") PluginManagerBase.__init__(self, "web.conf", "deluge.plugin.web") client.register_event_handler("PluginEnabledEvent", self._on_plugin_enabled_event) client.register_event_handler("PluginDisabledEvent", self._on_plugin_disabled_event)
def __load_config(self): auth_file = deluge.configmanager.get_config_dir("auth") if not os.path.exists(auth_file): from deluge.common import create_localclient_account create_localclient_account() localclient_username, localclient_password = get_localhost_auth() DEFAULT_CONFIG = { "hosts": [( hashlib.sha1(str(time.time())).hexdigest(), DEFAULT_HOST, DEFAULT_PORT, localclient_username, localclient_password )] } config = ConfigManager("hostlist.conf.1.2", DEFAULT_CONFIG) config.run_converter((0, 1), 2, self.__migrate_config_1_to_2) return config
def __init__(self): component.Component.__init__(self, "AutoAdd", depend=["TorrentManager"], interval=5) # Get the core config self.config = ConfigManager("core.conf") # A list of filenames self.invalid_torrents = [] # Filename:Attempts self.attempts = {} # Register set functions self.config.register_set_function("autoadd_enable", self._on_autoadd_enable, apply_now=True) self.config.register_set_function("autoadd_location", self._on_autoadd_location)
def __init__(self): if Wnck: self.screen = Wnck.Screen.get_default() component.Component.__init__(self, "MainWindow", interval=2) self.config = ConfigManager("gtkui.conf") # Get the glade file for the main window self.main_glade = Gtk.Builder() self.main_glade.add_from_file( pkg_resources.resource_filename("deluge.ui.gtkui", "builder/main_window.ui")) self.window_signals = {} self.window = self.main_glade.get_object("main_window") self.window.set_icon(common.get_deluge_icon()) self.vpaned = self.main_glade.get_object("vpaned") self.initial_vpaned_position = self.config["window_pane_position"] # Load the window state self.load_window_state() # Keep track of window's minimization state so that we don't update the # UI when it is minimized. self.is_minimized = False self.window.drag_dest_set(Gtk.DestDefaults.ALL, [Gtk.TargetEntry.new('text/uri-list', 0, 80)], Gdk.DragAction.COPY) # Connect events self.window.connect("window-state-event", self.on_window_state_event) self.window.connect("configure-event", self.on_window_configure_event) self.window.connect("delete-event", self.on_window_delete_event) self.window.connect("drag-data-received", self.on_drag_data_received_event) self.vpaned.connect("notify::position", self.on_vpaned_position_event) self.window.connect("draw", self.on_expose_event) self.config.register_set_function("show_rate_in_title", self._on_set_show_rate_in_title, apply_now=False) client.register_event_handler("NewVersionAvailableEvent", self.on_newversionavailable_event) client.register_event_handler("TorrentFinishedEvent", self.on_torrentfinished_event)
def enable(self): self.config = ConfigManager("execute.conf", DEFAULT_CONFIG) event_manager = component.get("EventManager") self.registered_events = {} # Go through the commands list and register event handlers for command in self.config["commands"]: event = command[EXECUTE_EVENT] if event in self.registered_events: continue def create_event_handler(event): def event_handler(torrent_id): self.execute_commands(torrent_id, event) return event_handler event_handler = create_event_handler(event) event_manager.register_event_handler(EVENT_MAP[event], event_handler) self.registered_events[event] = event_handler log.debug("Execute core plugin enabled!")
def enable(self): log.info("*** Start Label plugin ***") self.plugin = component.get("CorePluginManager") self.plugin.register_status_field("label", self._status_get_label) #__init__ core = component.get("Core") self.config = ConfigManager("label.conf", defaults=CONFIG_DEFAULTS) self.core_cfg = ConfigManager("core.conf") #reduce typing, assigning some values to self... self.torrents = core.torrentmanager.torrents self.labels = self.config["labels"] self.torrent_labels = self.config["torrent_labels"] self.clean_initial_config() component.get("EventManager").register_event_handler("TorrentAddedEvent", self.post_torrent_add) component.get("EventManager").register_event_handler("TorrentRemovedEvent", self.post_torrent_remove) #register tree: component.get("FilterManager").register_tree_field("label", self.init_filter_dict) log.debug("Label plugin enabled..")
class MainWindow(component.Component): def __init__(self): if wnck: self.screen = wnck.screen_get_default() component.Component.__init__(self, 'MainWindow', interval=2) self.config = ConfigManager('gtkui.conf') self.main_builder = gtk.Builder() # Patch this GtkBuilder to avoid connecting signals from elsewhere # # Think about splitting up mainwindow gtkbuilder file into the necessary parts # to avoid GtkBuilder monkey patch. Those parts would then need adding to mainwindow 'by hand'. self.gtk_builder_signals_holder = _GtkBuilderSignalsHolder() self.main_builder.prev_connect_signals = copy.deepcopy(self.main_builder.connect_signals) def patched_connect_signals(*a, **k): raise RuntimeError('In order to connect signals to this GtkBuilder instance please use ' '"component.get(\'MainWindow\').connect_signals()"') self.main_builder.connect_signals = patched_connect_signals # Get Gtk Builder files Main Window, New release dialog, and Tabs. for filename in ('main_window.ui', 'main_window.new_release.ui', 'main_window.tabs.ui', 'main_window.tabs.menu_file.ui', 'main_window.tabs.menu_peer.ui'): self.main_builder.add_from_file( resource_filename('deluge.ui.gtkui', os.path.join('glade', filename))) self.window = self.main_builder.get_object('main_window') self.window.set_icon(deluge.ui.gtkui.common.get_deluge_icon()) self.vpaned = self.main_builder.get_object('vpaned') self.initial_vpaned_position = self.config['window_pane_position'] # Keep a list of components to pause and resume when changing window state. self.child_components = ['TorrentView', 'StatusBar', 'TorrentDetails'] # Load the window state self.load_window_state() # Keep track of window minimization state so we don't update UI when it is minimized. self.is_minimized = False self.restart = False self.window.drag_dest_set(gtk.DEST_DEFAULT_ALL, [('text/uri-list', 0, 80)], ACTION_COPY) # Connect events self.window.connect('window-state-event', self.on_window_state_event) self.window.connect('configure-event', self.on_window_configure_event) self.window.connect('delete-event', self.on_window_delete_event) self.window.connect('drag-data-received', self.on_drag_data_received_event) self.vpaned.connect('notify::position', self.on_vpaned_position_event) self.window.connect('expose-event', self.on_expose_event) self.config.register_set_function('show_rate_in_title', self._on_set_show_rate_in_title, apply_now=False) client.register_event_handler('NewVersionAvailableEvent', self.on_newversionavailable_event) def connect_signals(self, mapping_or_class): self.gtk_builder_signals_holder.connect_signals(mapping_or_class) def first_show(self): self.main_builder.prev_connect_signals(self.gtk_builder_signals_holder) self.vpaned.set_position(self.initial_vpaned_position) if not ( self.config['start_in_tray'] and self.config['enable_system_tray'] ) and not self.window.get_property('visible'): log.debug('Showing window') self.show() while gtk.events_pending(): gtk.main_iteration() def show(self): component.resume(self.child_components) self.window.show() def hide(self): component.get('TorrentView').save_state() component.pause(self.child_components) # Store the x, y positions for when we restore the window self.config['window_x_pos'], self.config['window_y_pos'] = self.window.get_position() self.window.hide() def present(self): def restore(): # Restore the proper x,y coords for the window prior to showing it component.resume(self.child_components) self.window.present() self.load_window_state() if self.config['lock_tray'] and not self.visible(): dialog = PasswordDialog(_('Enter your password to show Deluge...')) def on_dialog_response(response_id): if response_id == gtk.RESPONSE_OK: if self.config['tray_password'] == sha(dialog.get_password()).hexdigest(): restore() dialog.run().addCallback(on_dialog_response) else: restore() def active(self): """Returns True if the window is active, False if not.""" return self.window.is_active() def visible(self): """Returns True if window is visible, False if not.""" return self.window.get_property('visible') def get_builder(self): """Returns a reference to the main window GTK builder object.""" return self.main_builder def quit(self, shutdown=False, restart=False): """Quits the GtkUI application. Args: shutdown (bool): Whether or not to shutdown the daemon as well. restart (bool): Whether or not to restart the application after closing. """ def quit_gtkui(): def stop_gtk_reactor(result=None): self.restart = restart try: reactor.callLater(0, reactor.fireSystemEvent, 'gtkui_close') except ReactorNotRunning: log.debug('Attempted to stop the reactor but it is not running...') if shutdown: client.daemon.shutdown().addCallback(stop_gtk_reactor) elif not client.is_standalone() and client.connected(): client.disconnect().addCallback(stop_gtk_reactor) else: stop_gtk_reactor() if self.config['lock_tray'] and not self.visible(): dialog = PasswordDialog(_('Enter your password to Quit Deluge...')) def on_dialog_response(response_id): if response_id == gtk.RESPONSE_OK: if self.config['tray_password'] == sha(dialog.get_password()).hexdigest(): quit_gtkui() dialog.run().addCallback(on_dialog_response) else: quit_gtkui() def load_window_state(self): if self.config['window_x_pos'] == -32000 or self.config['window_x_pos'] == -32000: self.config['window_x_pos'] = self.config['window_y_pos'] = 0 self.window.move(self.config['window_x_pos'], self.config['window_y_pos']) self.window.resize(self.config['window_width'], self.config['window_height']) if self.config['window_maximized']: self.window.maximize() def on_window_configure_event(self, widget, event): if not self.config['window_maximized'] and self.visible: self.config['window_x_pos'], self.config['window_y_pos'] = self.window.get_position() self.config['window_width'] = event.width self.config['window_height'] = event.height def on_window_state_event(self, widget, event): if event.changed_mask & WINDOW_STATE_MAXIMIZED: if event.new_window_state & WINDOW_STATE_MAXIMIZED: log.debug('pos: %s', self.window.get_position()) self.config['window_maximized'] = True elif not event.new_window_state & WINDOW_STATE_WITHDRAWN: self.config['window_maximized'] = False if event.changed_mask & WINDOW_STATE_ICONIFIED: if event.new_window_state & WINDOW_STATE_ICONIFIED: log.debug('MainWindow is minimized..') component.get('TorrentView').save_state() component.pause(self.child_components) self.is_minimized = True else: log.debug('MainWindow is not minimized..') component.resume(self.child_components) self.is_minimized = False return False def on_window_delete_event(self, widget, event): if self.config['close_to_tray'] and self.config['enable_system_tray']: self.hide() else: self.quit() return True def on_vpaned_position_event(self, obj, param): self.config['window_pane_position'] = self.vpaned.get_position() def on_drag_data_received_event(self, widget, drag_context, x, y, selection_data, info, timestamp): log.debug('Selection(s) dropped on main window %s', selection_data.get_text()) if selection_data.get_uris(): process_args(selection_data.get_uris()) else: process_args(selection_data.get_text().split()) drag_context.finish(True, True, timestamp) def on_expose_event(self, widget, event): component.get('SystemTray').blink(False) def stop(self): self.window.set_title('Deluge') def update(self): # Update the window title def _on_get_session_status(status): download_rate = fspeed(status['payload_download_rate'], precision=0, shortform=True) upload_rate = fspeed(status['payload_upload_rate'], precision=0, shortform=True) self.window.set_title(_('D: %s U: %s - Deluge' % (download_rate, upload_rate))) if self.config['show_rate_in_title']: client.core.get_session_status( ['payload_download_rate', 'payload_upload_rate'] ).addCallback(_on_get_session_status) def _on_set_show_rate_in_title(self, key, value): if value: self.update() else: self.window.set_title(_('Deluge')) def on_newversionavailable_event(self, new_version): if self.config['show_new_releases']: from deluge.ui.gtkui.new_release_dialog import NewReleaseDialog reactor.callLater(5.0, NewReleaseDialog().show, new_version) def is_on_active_workspace(self): """Determines if MainWindow is on the active workspace. Returns: bool: True if on active workspace (or wnck module not available), otherwise False. """ if wnck: self.screen.force_update() win = wnck.window_get(self.window.get_window().xid) if win: active_wksp = win.get_screen().get_active_workspace() if active_wksp: return win.is_on_workspace(active_wksp) return False return True
def __init__(self): component.Component.__init__(self, "MainWindow", interval=2) self.config = ConfigManager("gtkui.conf") self.gtk_builder_signals_holder = _GtkBuilderSignalsHolder() self.main_builder = gtk.Builder() # Patch this GtkBuilder to avoid connecting signals from elsewhere # # Think about splitting up the main window gtkbuilder file into the necessary parts # in order not to have to monkey patch GtkBuilder. Those parts would then need to # be added to the main window "by hand". self.main_builder.prev_connect_signals = copy.deepcopy(self.main_builder.connect_signals) def patched_connect_signals(*a, **k): raise RuntimeError( "In order to connect signals to this GtkBuilder instance please use " "'component.get(\"MainWindow\").connect_signals()'" ) self.main_builder.connect_signals = patched_connect_signals # Get the gtk builder file for the main window self.main_builder.add_from_file( deluge.common.resource_filename("deluge.ui.gtkui", os.path.join("glade", "main_window.ui")) ) # The new release dialog self.main_builder.add_from_file( deluge.common.resource_filename("deluge.ui.gtkui", os.path.join("glade", "main_window.new_release.ui")) ) # The move storage dialog self.main_builder.add_from_file( deluge.common.resource_filename("deluge.ui.gtkui", os.path.join("glade", "main_window.move_storage.ui")) ) # The tabs self.main_builder.add_from_file( deluge.common.resource_filename("deluge.ui.gtkui", os.path.join("glade", "main_window.tabs.ui")) ) # The tabs file menu self.main_builder.add_from_file( deluge.common.resource_filename("deluge.ui.gtkui", os.path.join("glade", "main_window.tabs.menu_file.ui")) ) # The tabs peer menu self.main_builder.add_from_file( deluge.common.resource_filename("deluge.ui.gtkui", os.path.join("glade", "main_window.tabs.menu_peer.ui")) ) self.window = self.main_builder.get_object("main_window") self.window.set_icon(common.get_deluge_icon()) self.vpaned = self.main_builder.get_object("vpaned") self.initial_vpaned_position = self.config["window_pane_position"] # Load the window state self.load_window_state() # Keep track of window's minimization state so that we don't update the # UI when it is minimized. self.is_minimized = False self.window.drag_dest_set(gtk.DEST_DEFAULT_ALL, [("text/uri-list", 0, 80)], gtk.gdk.ACTION_COPY) # Connect events self.window.connect("window-state-event", self.on_window_state_event) self.window.connect("configure-event", self.on_window_configure_event) self.window.connect("delete-event", self.on_window_delete_event) self.window.connect("drag-data-received", self.on_drag_data_received_event) self.vpaned.connect("notify::position", self.on_vpaned_position_event) self.window.connect("expose-event", self.on_expose_event) self.config.register_set_function("show_rate_in_title", self._on_set_show_rate_in_title, apply_now=False) client.register_event_handler("NewVersionAvailableEvent", self.on_newversionavailable_event) client.register_event_handler("TorrentFinishedEvent", self.on_torrentfinished_event)
class ConnectionManager(component.Component): def __init__(self): component.Component.__init__(self, "ConnectionManager") self.gtkui_config = ConfigManager("gtkui.conf") self.config = ConfigManager("hostlist.conf.1.2", DEFAULT_CONFIG) self.running = False # Component overrides def start(self): pass def stop(self): # Close this dialog when we are shutting down if self.running: self.connection_manager.response(gtk.RESPONSE_CLOSE) def shutdown(self): pass # Public methods def show(self): """ Show the ConnectionManager dialog. """ # Get the glade file for the connection manager self.glade = gtk.glade.XML( pkg_resources.resource_filename("deluge.ui.gtkui", "glade/connection_manager.glade")) self.window = component.get("MainWindow") # Setup the ConnectionManager dialog self.connection_manager = self.glade.get_widget("connection_manager") self.connection_manager.set_transient_for(self.window.window) self.connection_manager.set_icon(common.get_deluge_icon()) self.glade.get_widget("image1").set_from_pixbuf(common.get_logo(32)) self.hostlist = self.glade.get_widget("hostlist") # Create status pixbufs if not HOSTLIST_PIXBUFS: for stock_id in (gtk.STOCK_NO, gtk.STOCK_YES, gtk.STOCK_CONNECT): HOSTLIST_PIXBUFS.append(self.connection_manager.render_icon(stock_id, gtk.ICON_SIZE_MENU)) # Create the host list gtkliststore # id-hash, hostname, port, status, username, password, version self.liststore = gtk.ListStore(str, str, int, str, str, str, str) # Setup host list treeview self.hostlist.set_model(self.liststore) render = gtk.CellRendererPixbuf() column = gtk.TreeViewColumn(_("Status"), render) column.set_cell_data_func(render, cell_render_status, 3) self.hostlist.append_column(column) render = gtk.CellRendererText() column = gtk.TreeViewColumn(_("Host"), render, text=HOSTLIST_COL_HOST) column.set_cell_data_func(render, cell_render_host, (1, 2, 4)) column.set_expand(True) self.hostlist.append_column(column) render = gtk.CellRendererText() column = gtk.TreeViewColumn(_("Version"), render, text=HOSTLIST_COL_VERSION) self.hostlist.append_column(column) # Load any saved host entries self.__load_hostlist() self.__load_options() # Select the first host if possible if len(self.liststore) > 0: self.hostlist.get_selection().select_path("0") # Connect the signals to the handlers self.glade.signal_autoconnect(self) self.hostlist.get_selection().connect("changed", self.on_hostlist_selection_changed) self.__update_list() self.running = True response = self.connection_manager.run() self.running = False # Save the toggle options self.__save_options() self.__save_hostlist() self.connection_manager.destroy() del self.glade del self.window del self.connection_manager del self.liststore del self.hostlist def add_host(self, host, port, username="", password=""): """ Adds a host to the list. :param host: str, the hostname :param port: int, the port :param username: str, the username to login as :param password: str, the password to login with """ # Check to see if there is already an entry for this host and return # if thats the case for entry in self.liststore: if [entry[HOSTLIST_COL_HOST], entry[HOSTLIST_COL_PORT], entry[HOSTLIST_COL_USER]] == [host, port, username]: raise Exception("Host already in list!") # Host isn't in the list, so lets add it row = self.liststore.append() import time import hashlib self.liststore[row][HOSTLIST_COL_ID] = hashlib.sha1(str(time.time())).hexdigest() self.liststore[row][HOSTLIST_COL_HOST] = host self.liststore[row][HOSTLIST_COL_PORT] = port self.liststore[row][HOSTLIST_COL_USER] = username self.liststore[row][HOSTLIST_COL_PASS] = password self.liststore[row][HOSTLIST_COL_STATUS] = "Offline" # Save the host list to file self.__save_hostlist() # Update the status of the hosts self.__update_list() # Private methods def __save_hostlist(self): """ Save the current hostlist to the config file. """ # Grab the hosts from the liststore self.config["hosts"] = [] for row in self.liststore: self.config["hosts"].append((row[HOSTLIST_COL_ID], row[HOSTLIST_COL_HOST], row[HOSTLIST_COL_PORT], row[HOSTLIST_COL_USER], row[HOSTLIST_COL_PASS])) self.config.save() def __load_hostlist(self): """ Load saved host entries """ for host in self.config["hosts"]: new_row = self.liststore.append() self.liststore[new_row][HOSTLIST_COL_ID] = host[0] self.liststore[new_row][HOSTLIST_COL_HOST] = host[1] self.liststore[new_row][HOSTLIST_COL_PORT] = host[2] self.liststore[new_row][HOSTLIST_COL_USER] = host[3] self.liststore[new_row][HOSTLIST_COL_PASS] = host[4] self.liststore[new_row][HOSTLIST_COL_STATUS] = "Offline" def __get_host_row(self, host_id): """ Returns the row in the liststore for `:param:host_id` or None """ for row in self.liststore: if host_id == row[HOSTLIST_COL_ID]: return row return None def __update_list(self): """Updates the host status""" if not hasattr(self, "liststore"): # This callback was probably fired after the window closed return def on_connect(result, c, host_id): # Return if the deferred callback was done after the dialog was closed if not self.running: return row = self.__get_host_row(host_id) def on_info(info, c): if not self.running: return if row: row[HOSTLIST_COL_STATUS] = "Online" row[HOSTLIST_COL_VERSION] = info self.__update_buttons() c.disconnect() def on_info_fail(reason, c): if not self.running: return if row: row[HOSTLIST_COL_STATUS] = "Offline" self.__update_buttons() c.disconnect() d = c.daemon.info() d.addCallback(on_info, c) d.addErrback(on_info_fail, c) def on_connect_failed(reason, host_id): if not self.running: return row = self.__get_host_row(host_id) if row: row[HOSTLIST_COL_STATUS] = "Offline" self.__update_buttons() for row in self.liststore: host_id = row[HOSTLIST_COL_ID] host = row[HOSTLIST_COL_HOST] port = row[HOSTLIST_COL_PORT] user = row[HOSTLIST_COL_USER] password = row[HOSTLIST_COL_PASS] if client.connected() and \ (host, port, "localclient" if not user and host in ("127.0.0.1", "localhost") else user) == client.connection_info(): def on_info(info): if not self.running: return row[HOSTLIST_COL_VERSION] = info self.__update_buttons() row[HOSTLIST_COL_STATUS] = "Connected" client.daemon.info().addCallback(on_info) continue # Create a new Client instance c = deluge.ui.client.Client() d = c.connect(host, port, user, password) d.addCallback(on_connect, c, host_id) d.addErrback(on_connect_failed, host_id) def __load_options(self): """ Set the widgets to show the correct options from the config. """ self.glade.get_widget("chk_autoconnect").set_active(self.gtkui_config["autoconnect"]) self.glade.get_widget("chk_autostart").set_active(self.gtkui_config["autostart_localhost"]) self.glade.get_widget("chk_donotshow").set_active(not self.gtkui_config["show_connection_manager_on_start"]) def __save_options(self): """ Set options in gtkui config from the toggle buttons. """ self.gtkui_config["autoconnect"] = self.glade.get_widget("chk_autoconnect").get_active() self.gtkui_config["autostart_localhost"] = self.glade.get_widget("chk_autostart").get_active() self.gtkui_config["show_connection_manager_on_start"] = not self.glade.get_widget("chk_donotshow").get_active() def __update_buttons(self): """ Updates the buttons states. """ if len(self.liststore) == 0: # There is nothing in the list self.glade.get_widget("button_startdaemon").set_sensitive(True) self.glade.get_widget("button_connect").set_sensitive(False) self.glade.get_widget("button_removehost").set_sensitive(False) self.glade.get_widget("image_startdaemon").set_from_stock( gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU) self.glade.get_widget("label_startdaemon").set_text("_Start Daemon") model, row = self.hostlist.get_selection().get_selected() if not row: return # Get some values about the selected host status = model[row][HOSTLIST_COL_STATUS] host = model[row][HOSTLIST_COL_HOST] log.debug("Status: %s", status) # Check to see if we have a localhost entry selected localhost = False if host in ("127.0.0.1", "localhost"): localhost = True # Make sure buttons are sensitive at start self.glade.get_widget("button_startdaemon").set_sensitive(True) self.glade.get_widget("button_connect").set_sensitive(True) self.glade.get_widget("button_removehost").set_sensitive(True) # See if this is the currently connected host if status == "Connected": # Display a disconnect button if we're connected to this host self.glade.get_widget("button_connect").set_label("gtk-disconnect") self.glade.get_widget("button_removehost").set_sensitive(False) else: self.glade.get_widget("button_connect").set_label("gtk-connect") if status == "Offline" and not localhost: self.glade.get_widget("button_connect").set_sensitive(False) # Check to see if the host is online if status == "Connected" or status == "Online": self.glade.get_widget("image_startdaemon").set_from_stock( gtk.STOCK_STOP, gtk.ICON_SIZE_MENU) self.glade.get_widget("label_startdaemon").set_text( _("_Stop Daemon")) # Update the start daemon button if the selected host is localhost if localhost and status == "Offline": # The localhost is not online self.glade.get_widget("image_startdaemon").set_from_stock( gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU) self.glade.get_widget("label_startdaemon").set_text( _("_Start Daemon")) if not localhost: # An offline host self.glade.get_widget("button_startdaemon").set_sensitive(False) # Make sure label is displayed correctly using mnemonics self.glade.get_widget("label_startdaemon").set_use_underline( True) def start_daemon(self, port, config): """ Attempts to start a daemon process and will show an ErrorDialog if unable to. """ try: return client.start_daemon(port, config) except OSError, e: from errno import ENOENT if e.errno == ENOENT: dialogs.ErrorDialog( _("Unable to start daemon!"), _("Deluge cannot find the 'deluged' executable, it is likely \ that you forgot to install the deluged package or it's not in your PATH.")).run() else: raise e except Exception, e: import traceback import sys tb = sys.exc_info() dialogs.ErrorDialog( _("Unable to start daemon!"), _("Please examine the details for more information."), details=traceback.format_exc(tb[2])).run()
class GtkUI(object): def __init__(self, args): # Setup gtkbuilder/glade translation setup_translations(setup_gettext=False, setup_pygtk=True) # Setup signals def on_die(*args): log.debug('OS signal "die" caught with args: %s', args) reactor.stop() if windows_check(): from win32api import SetConsoleCtrlHandler SetConsoleCtrlHandler(on_die, True) log.debug('Win32 "die" handler registered') elif osx_check() and WINDOWING == 'quartz': import gtkosx_application self.osxapp = gtkosx_application.gtkosx_application_get() self.osxapp.connect('NSApplicationWillTerminate', on_die) log.debug('OSX quartz "die" handler registered') # Set process name again to fix gtk issue setproctitle(getproctitle()) # Attempt to register a magnet URI handler with gconf, but do not overwrite # if already set by another program. associate_magnet_links(False) # Make sure gtkui.conf has at least the defaults set self.config = ConfigManager('gtkui.conf', DEFAULT_PREFS) # Make sure the gtkui state folder has been created if not os.path.exists(os.path.join(get_config_dir(), 'gtkui_state')): os.makedirs(os.path.join(get_config_dir(), 'gtkui_state')) # Set language if self.config['language'] is not None: set_language(self.config['language']) # Start the IPC Interface before anything else.. Just in case we are # already running. self.queuedtorrents = QueuedTorrents() self.ipcinterface = IPCInterface(args.torrents) # Initialize gdk threading threads_init() # We make sure that the UI components start once we get a core URI client.set_disconnect_callback(self.__on_disconnect) self.trackericons = TrackerIcons() self.sessionproxy = SessionProxy() # Initialize various components of the gtkui self.mainwindow = MainWindow() self.menubar = MenuBar() self.toolbar = ToolBar() self.torrentview = TorrentView() self.torrentdetails = TorrentDetails() self.sidebar = SideBar() self.filtertreeview = FilterTreeView() self.preferences = Preferences() self.systemtray = SystemTray() self.statusbar = StatusBar() self.addtorrentdialog = AddTorrentDialog() if osx_check() and WINDOWING == 'quartz': def nsapp_open_file(osxapp, filename): # Ignore command name which is raised at app launch (python opening main script). if filename == sys.argv[0]: return True process_args([filename]) self.osxapp.connect('NSApplicationOpenFile', nsapp_open_file) from deluge.ui.gtkui.menubar_osx import menubar_osx menubar_osx(self, self.osxapp) self.osxapp.ready() # Initalize the plugins self.plugins = PluginManager() # Show the connection manager self.connectionmanager = ConnectionManager() # Setup RPC stats logging # daemon_bps: time, bytes_sent, bytes_recv self.daemon_bps = (0, 0, 0) self.rpc_stats = LoopingCall(self.log_rpc_stats) self.closing = False # Twisted catches signals to terminate, so have it call a pre_shutdown method. reactor.addSystemEventTrigger('before', 'gtkui_close', self.close) def gtkui_sigint_handler(num, frame): log.debug('SIGINT signal caught, firing event: gtkui_close') reactor.callLater(0, reactor.fireSystemEvent, 'gtkui_close') signal.signal(signal.SIGINT, gtkui_sigint_handler) def start(self): reactor.callWhenRunning(self._on_reactor_start) # Initialize gdk threading threads_enter() reactor.run() # Reactor no longer running so async callbacks (Deferreds) cannot be # processed after this point. threads_leave() def shutdown(self, *args, **kwargs): log.debug('GTKUI shutting down...') # Shutdown all components if client.is_standalone: return component.shutdown() @defer.inlineCallbacks def close(self): if self.closing: return self.closing = True # Make sure the config is saved. self.config.save() # Ensure columns state is saved self.torrentview.save_state() # Shut down components yield self.shutdown() # The gtk modal dialogs (e.g. Preferences) can prevent the application # quitting, so force exiting by destroying MainWindow. Must be done here # to avoid hanging when quitting with SIGINT (CTRL-C). self.mainwindow.window.destroy() reactor.stop() # Restart the application after closing if MainWindow restart attribute set. if component.get('MainWindow').restart: os.execv(sys.argv[0], sys.argv) def log_rpc_stats(self): """Log RPC statistics for thinclient mode.""" if not client.connected(): return t = time.time() recv = client.get_bytes_recv() sent = client.get_bytes_sent() delta_time = t - self.daemon_bps[0] delta_sent = sent - self.daemon_bps[1] delta_recv = recv - self.daemon_bps[2] self.daemon_bps = (t, sent, recv) sent_rate = fspeed(delta_sent / delta_time) recv_rate = fspeed(delta_recv / delta_time) log.debug('RPC: Sent %s (%s) Recv %s (%s)', fsize(sent), sent_rate, fsize(recv), recv_rate) def _on_reactor_start(self): log.debug('_on_reactor_start') self.mainwindow.first_show() if not self.config['standalone']: return self._start_thinclient() err_msg = '' try: client.start_standalone() except DaemonRunningError: err_msg = _('A Deluge daemon (deluged) is already running.\n' 'To use Standalone mode, stop local daemon and restart Deluge.') except ImportError as ex: if 'No module named libtorrent' in ex.message: err_msg = _('Only Thin Client mode is available because libtorrent is not installed.\n' 'To use Standalone mode, please install libtorrent package.') else: log.exception(ex) err_msg = _('Only Thin Client mode is available due to unknown Import Error.\n' 'To use Standalone mode, please see logs for error details.') except Exception as ex: log.exception(ex) err_msg = _('Only Thin Client mode is available due to unknown Import Error.\n' 'To use Standalone mode, please see logs for error details.') else: component.start() return def on_dialog_response(response): """User response to switching mode dialog.""" if response == RESPONSE_YES: # Turning off standalone self.config['standalone'] = False self._start_thinclient() else: # User want keep Standalone Mode so just quit. self.mainwindow.quit() # An error occurred so ask user to switch from Standalone to Thin Client mode. err_msg += '\n\n' + _('Continue in Thin Client mode?') d = YesNoDialog(_('Change User Interface Mode'), err_msg).run() d.addCallback(on_dialog_response) def _start_thinclient(self): """Start the gtkui in thinclient mode""" if log.isEnabledFor(logging.DEBUG): self.rpc_stats.start(10) # Check to see if we need to start the localhost daemon if self.config['autostart_localhost']: def on_localhost_status(status_info, port): if status_info[1] == 'Offline': log.debug('Autostarting localhost: %s', host_config[0:3]) self.connectionmanager.start_daemon(port, get_config_dir()) for host_config in self.connectionmanager.hostlist.config['hosts']: if host_config[1] in LOCALHOST: d = self.connectionmanager.hostlist.get_host_status(host_config[0]) d.addCallback(on_localhost_status, host_config[2]) break # Autoconnect to a host if self.config['autoconnect']: for host_config in self.connectionmanager.hostlist.config['hosts']: host_id, host, port, user, __ = host_config if host_id == self.config['autoconnect_host_id']: log.debug('Trying to connect to %s@%s:%s', user, host, port) self.connectionmanager._connect(host_id, try_counter=6) break if self.config['show_connection_manager_on_start']: # Dialog is blocking so call last. self.connectionmanager.show() def __on_disconnect(self): """ Called when disconnected from the daemon. We basically just stop all the components here. """ self.daemon_bps = (0, 0, 0) component.stop()
class MainWindow(component.Component): def __init__(self): component.Component.__init__(self, "MainWindow", interval=2) self.config = ConfigManager("gtkui.conf") self.gtk_builder_signals_holder = _GtkBuilderSignalsHolder() self.main_builder = gtk.Builder() # Patch this GtkBuilder to avoid connecting signals from elsewhere # # Think about splitting up the main window gtkbuilder file into the necessary parts # in order not to have to monkey patch GtkBuilder. Those parts would then need to # be added to the main window "by hand". self.main_builder.prev_connect_signals = copy.deepcopy(self.main_builder.connect_signals) def patched_connect_signals(*a, **k): raise RuntimeError( "In order to connect signals to this GtkBuilder instance please use " "'component.get(\"MainWindow\").connect_signals()'" ) self.main_builder.connect_signals = patched_connect_signals # Get the gtk builder file for the main window self.main_builder.add_from_file( deluge.common.resource_filename("deluge.ui.gtkui", os.path.join("glade", "main_window.ui")) ) # The new release dialog self.main_builder.add_from_file( deluge.common.resource_filename("deluge.ui.gtkui", os.path.join("glade", "main_window.new_release.ui")) ) # The move storage dialog self.main_builder.add_from_file( deluge.common.resource_filename("deluge.ui.gtkui", os.path.join("glade", "main_window.move_storage.ui")) ) # The tabs self.main_builder.add_from_file( deluge.common.resource_filename("deluge.ui.gtkui", os.path.join("glade", "main_window.tabs.ui")) ) # The tabs file menu self.main_builder.add_from_file( deluge.common.resource_filename("deluge.ui.gtkui", os.path.join("glade", "main_window.tabs.menu_file.ui")) ) # The tabs peer menu self.main_builder.add_from_file( deluge.common.resource_filename("deluge.ui.gtkui", os.path.join("glade", "main_window.tabs.menu_peer.ui")) ) self.window = self.main_builder.get_object("main_window") self.window.set_icon(common.get_deluge_icon()) self.vpaned = self.main_builder.get_object("vpaned") self.initial_vpaned_position = self.config["window_pane_position"] # Load the window state self.load_window_state() # Keep track of window's minimization state so that we don't update the # UI when it is minimized. self.is_minimized = False self.window.drag_dest_set(gtk.DEST_DEFAULT_ALL, [("text/uri-list", 0, 80)], gtk.gdk.ACTION_COPY) # Connect events self.window.connect("window-state-event", self.on_window_state_event) self.window.connect("configure-event", self.on_window_configure_event) self.window.connect("delete-event", self.on_window_delete_event) self.window.connect("drag-data-received", self.on_drag_data_received_event) self.vpaned.connect("notify::position", self.on_vpaned_position_event) self.window.connect("expose-event", self.on_expose_event) self.config.register_set_function("show_rate_in_title", self._on_set_show_rate_in_title, apply_now=False) client.register_event_handler("NewVersionAvailableEvent", self.on_newversionavailable_event) client.register_event_handler("TorrentFinishedEvent", self.on_torrentfinished_event) def connect_signals(self, mapping_or_class): self.gtk_builder_signals_holder.connect_signals(mapping_or_class) def first_show(self): if not (self.config["start_in_tray"] and self.config["enable_system_tray"]) and not self.window.get_property( "visible" ): log.debug("Showing window") self.main_builder.prev_connect_signals(self.gtk_builder_signals_holder) self.show() while gtk.events_pending(): gtk.main_iteration(False) self.vpaned.set_position(self.initial_vpaned_position) def show(self): try: component.resume("TorrentView") component.resume("StatusBar") component.resume("TorrentDetails") except: pass self.window.show() def hide(self): component.pause("TorrentView") component.get("TorrentView").save_state() component.pause("StatusBar") component.pause("TorrentDetails") # Store the x, y positions for when we restore the window self.window_x_pos = self.window.get_position()[0] self.window_y_pos = self.window.get_position()[1] self.window.hide() def present(self): # Restore the proper x,y coords for the window prior to showing it try: self.config["window_x_pos"] = self.window_x_pos self.config["window_y_pos"] = self.window_y_pos except: pass try: component.resume("TorrentView") component.resume("StatusBar") component.resume("TorrentDetails") except: pass self.window.present() self.load_window_state() def active(self): """Returns True if the window is active, False if not.""" return self.window.is_active() def visible(self): """Returns True if window is visible, False if not.""" return self.window.get_property("visible") def get_builder(self): """Returns a reference to the main window GTK builder object.""" return self.main_builder def quit(self, shutdown=False): """ Quits the GtkUI :param shutdown: whether or not to shutdown the daemon as well :type shutdown: boolean """ if shutdown: def on_daemon_shutdown(result): try: reactor.stop() except ReactorNotRunning: log.debug("Attempted to stop the reactor but it is not running...") client.daemon.shutdown().addCallback(on_daemon_shutdown) return if client.is_classicmode(): reactor.stop() return if not client.connected(): reactor.stop() return def on_client_disconnected(result): reactor.stop() client.disconnect().addCallback(on_client_disconnected) def load_window_state(self): x = self.config["window_x_pos"] y = self.config["window_y_pos"] w = self.config["window_width"] h = self.config["window_height"] self.window.move(x, y) self.window.resize(w, h) if self.config["window_maximized"]: self.window.maximize() def on_window_configure_event(self, widget, event): if not self.config["window_maximized"] and self.visible: self.config["window_x_pos"] = self.window.get_position()[0] self.config["window_y_pos"] = self.window.get_position()[1] self.config["window_width"] = event.width self.config["window_height"] = event.height def on_window_state_event(self, widget, event): if event.changed_mask & gtk.gdk.WINDOW_STATE_MAXIMIZED: if event.new_window_state & gtk.gdk.WINDOW_STATE_MAXIMIZED: log.debug("pos: %s", self.window.get_position()) self.config["window_maximized"] = True elif not event.new_window_state & gtk.gdk.WINDOW_STATE_WITHDRAWN: self.config["window_maximized"] = False if event.changed_mask & gtk.gdk.WINDOW_STATE_ICONIFIED: if event.new_window_state & gtk.gdk.WINDOW_STATE_ICONIFIED: log.debug("MainWindow is minimized..") component.pause("TorrentView") component.pause("StatusBar") self.is_minimized = True else: log.debug("MainWindow is not minimized..") try: component.resume("TorrentView") component.resume("StatusBar") except: pass self.is_minimized = False return False def on_window_delete_event(self, widget, event): if self.config["close_to_tray"] and self.config["enable_system_tray"]: self.hide() else: self.quit() return True def on_vpaned_position_event(self, obj, param): self.config["window_pane_position"] = self.vpaned.get_position() def on_drag_data_received_event(self, widget, drag_context, x, y, selection_data, info, timestamp): log.debug("Selection(s) dropped on main window %s", selection_data.data) if selection_data.get_uris(): process_args(selection_data.get_uris()) else: process_args(selection_data.data.split()) drag_context.finish(True, True) def on_expose_event(self, widget, event): component.get("SystemTray").blink(False) def stop(self): self.window.set_title("Deluge") def update(self): # Update the window title def _on_get_session_status(status): download_rate = deluge.common.fsize_short(status["payload_download_rate"]) upload_rate = deluge.common.fsize_short(status["payload_upload_rate"]) self.window.set_title("%s%s %s%s - Deluge" % (_("D:"), download_rate, _("U:"), upload_rate)) if self.config["show_rate_in_title"]: client.core.get_session_status(["payload_download_rate", "payload_upload_rate"]).addCallback( _on_get_session_status ) def _on_set_show_rate_in_title(self, key, value): if value: self.update() else: self.window.set_title("Deluge") def on_newversionavailable_event(self, new_version): if self.config["show_new_releases"]: from deluge.ui.gtkui.new_release_dialog import NewReleaseDialog reactor.callLater(5.0, NewReleaseDialog().show, new_version) def on_torrentfinished_event(self, torrent_id): from deluge.ui.gtkui.notification import Notification Notification().notify(torrent_id)
def __init__(self, args): # Setup gtkbuilder/glade translation setup_translations(setup_gettext=False, setup_pygtk=True) # Setup signals def on_die(*args): log.debug('OS signal "die" caught with args: %s', args) reactor.stop() if windows_check(): from win32api import SetConsoleCtrlHandler SetConsoleCtrlHandler(on_die, True) log.debug('Win32 "die" handler registered') elif osx_check() and WINDOWING == 'quartz': import gtkosx_application self.osxapp = gtkosx_application.gtkosx_application_get() self.osxapp.connect('NSApplicationWillTerminate', on_die) log.debug('OSX quartz "die" handler registered') # Set process name again to fix gtk issue setproctitle(getproctitle()) # Attempt to register a magnet URI handler with gconf, but do not overwrite # if already set by another program. associate_magnet_links(False) # Make sure gtkui.conf has at least the defaults set self.config = ConfigManager('gtkui.conf', DEFAULT_PREFS) # Make sure the gtkui state folder has been created if not os.path.exists(os.path.join(get_config_dir(), 'gtkui_state')): os.makedirs(os.path.join(get_config_dir(), 'gtkui_state')) # Set language if self.config['language'] is not None: set_language(self.config['language']) # Start the IPC Interface before anything else.. Just in case we are # already running. self.queuedtorrents = QueuedTorrents() self.ipcinterface = IPCInterface(args.torrents) # Initialize gdk threading threads_init() # We make sure that the UI components start once we get a core URI client.set_disconnect_callback(self.__on_disconnect) self.trackericons = TrackerIcons() self.sessionproxy = SessionProxy() # Initialize various components of the gtkui self.mainwindow = MainWindow() self.menubar = MenuBar() self.toolbar = ToolBar() self.torrentview = TorrentView() self.torrentdetails = TorrentDetails() self.sidebar = SideBar() self.filtertreeview = FilterTreeView() self.preferences = Preferences() self.systemtray = SystemTray() self.statusbar = StatusBar() self.addtorrentdialog = AddTorrentDialog() if osx_check() and WINDOWING == 'quartz': def nsapp_open_file(osxapp, filename): # Ignore command name which is raised at app launch (python opening main script). if filename == sys.argv[0]: return True process_args([filename]) self.osxapp.connect('NSApplicationOpenFile', nsapp_open_file) from deluge.ui.gtkui.menubar_osx import menubar_osx menubar_osx(self, self.osxapp) self.osxapp.ready() # Initalize the plugins self.plugins = PluginManager() # Show the connection manager self.connectionmanager = ConnectionManager() # Setup RPC stats logging # daemon_bps: time, bytes_sent, bytes_recv self.daemon_bps = (0, 0, 0) self.rpc_stats = LoopingCall(self.log_rpc_stats) self.closing = False # Twisted catches signals to terminate, so have it call a pre_shutdown method. reactor.addSystemEventTrigger('before', 'gtkui_close', self.close) def gtkui_sigint_handler(num, frame): log.debug('SIGINT signal caught, firing event: gtkui_close') reactor.callLater(0, reactor.fireSystemEvent, 'gtkui_close') signal.signal(signal.SIGINT, gtkui_sigint_handler)
class MainWindow(component.Component): def __init__(self): if wnck: self.screen = wnck.screen_get_default() component.Component.__init__(self, "MainWindow", interval=2) self.config = ConfigManager("gtkui.conf") # Get the glade file for the main window self.main_glade = gtk.glade.XML( pkg_resources.resource_filename("deluge.ui.gtkui", "glade/main_window.glade")) self.window = self.main_glade.get_widget("main_window") self.window.set_icon(common.get_deluge_icon()) self.vpaned = self.main_glade.get_widget("vpaned") self.initial_vpaned_position = self.config["window_pane_position"] # Load the window state self.load_window_state() # Keep track of window's minimization state so that we don't update the # UI when it is minimized. self.is_minimized = False self.window.drag_dest_set(gtk.DEST_DEFAULT_ALL, [('text/uri-list', 0, 80)], gtk.gdk.ACTION_COPY) # Connect events self.window.connect("window-state-event", self.on_window_state_event) self.window.connect("configure-event", self.on_window_configure_event) self.window.connect("delete-event", self.on_window_delete_event) self.window.connect("drag-data-received", self.on_drag_data_received_event) self.vpaned.connect("notify::position", self.on_vpaned_position_event) self.window.connect("expose-event", self.on_expose_event) self.config.register_set_function("show_rate_in_title", self._on_set_show_rate_in_title, apply_now=False) client.register_event_handler("NewVersionAvailableEvent", self.on_newversionavailable_event) client.register_event_handler("TorrentFinishedEvent", self.on_torrentfinished_event) def first_show(self): if not(self.config["start_in_tray"] and \ self.config["enable_system_tray"]) and not \ self.window.get_property("visible"): log.debug("Showing window") self.show() while gtk.events_pending(): gtk.main_iteration(False) self.vpaned.set_position(self.initial_vpaned_position) def show(self): try: component.resume("TorrentView") component.resume("StatusBar") component.resume("TorrentDetails") except: pass self.window.show() def hide(self): component.pause("TorrentView") component.get("TorrentView").save_state() component.pause("StatusBar") component.pause("TorrentDetails") # Store the x, y positions for when we restore the window self.window_x_pos = self.window.get_position()[0] self.window_y_pos = self.window.get_position()[1] self.window.hide() def present(self): def restore(): # Restore the proper x,y coords for the window prior to showing it try: if self.window_x_pos == -32000 or self.window_y_pos == -32000: self.config["window_x_pos"] = 0 self.config["window_y_pos"] = 0 else: self.config["window_x_pos"] = self.window_x_pos self.config["window_y_pos"] = self.window_y_pos except: pass try: component.resume("TorrentView") component.resume("StatusBar") component.resume("TorrentDetails") except: pass self.window.present() self.load_window_state() if self.config["lock_tray"] and not self.visible(): dialog = PasswordDialog(_("Enter your password to show Deluge...")) def on_dialog_response(response_id): if response_id == gtk.RESPONSE_OK: if self.config["tray_password"] == sha(dialog.get_password()).hexdigest(): restore() dialog.run().addCallback(on_dialog_response) else: restore() def active(self): """Returns True if the window is active, False if not.""" return self.window.is_active() def visible(self): """Returns True if window is visible, False if not.""" return self.window.get_property("visible") def get_glade(self): """Returns a reference to the main window glade object.""" return self.main_glade def quit(self, shutdown=False): """ Quits the GtkUI :param shutdown: whether or not to shutdown the daemon as well :type shutdown: boolean """ def quit_gtkui(): def shutdown_daemon(result): return client.daemon.shutdown() def disconnect_client(result): return client.disconnect() def stop_reactor(result): try: reactor.stop() except ReactorNotRunning: log.debug("Attempted to stop the reactor but it is not running...") def log_failure(failure, action): log.error("Encountered error attempting to %s: %s" % \ (action, failure.getErrorMessage())) d = defer.succeed(None) if shutdown: d.addCallback(shutdown_daemon) d.addErrback(log_failure, "shutdown daemon") if not client.is_classicmode() and client.connected(): d.addCallback(disconnect_client) d.addErrback(log_failure, "disconnect client") d.addBoth(stop_reactor) if self.config["lock_tray"] and not self.visible(): dialog = PasswordDialog(_("Enter your password to Quit Deluge...")) def on_dialog_response(response_id): if response_id == gtk.RESPONSE_OK: if self.config["tray_password"] == sha(dialog.get_password()).hexdigest(): quit_gtkui() dialog.run().addCallback(on_dialog_response) else: quit_gtkui() def load_window_state(self): x = self.config["window_x_pos"] y = self.config["window_y_pos"] w = self.config["window_width"] h = self.config["window_height"] self.window.move(x, y) self.window.resize(w, h) if self.config["window_maximized"]: self.window.maximize() def on_window_configure_event(self, widget, event): if not self.config["window_maximized"] and self.visible: self.config["window_x_pos"] = self.window.get_position()[0] self.config["window_y_pos"] = self.window.get_position()[1] self.config["window_width"] = event.width self.config["window_height"] = event.height def on_window_state_event(self, widget, event): if event.changed_mask & gtk.gdk.WINDOW_STATE_MAXIMIZED: if event.new_window_state & gtk.gdk.WINDOW_STATE_MAXIMIZED: log.debug("pos: %s", self.window.get_position()) self.config["window_maximized"] = True elif not event.new_window_state & gtk.gdk.WINDOW_STATE_WITHDRAWN: self.config["window_maximized"] = False if event.changed_mask & gtk.gdk.WINDOW_STATE_ICONIFIED: if event.new_window_state & gtk.gdk.WINDOW_STATE_ICONIFIED: log.debug("MainWindow is minimized..") component.pause("TorrentView") component.pause("StatusBar") self.is_minimized = True else: log.debug("MainWindow is not minimized..") try: component.resume("TorrentView") component.resume("StatusBar") except: pass self.is_minimized = False return False def on_window_delete_event(self, widget, event): if self.config["close_to_tray"] and self.config["enable_system_tray"]: self.hide() else: self.quit() return True def on_vpaned_position_event(self, obj, param): self.config["window_pane_position"] = self.vpaned.get_position() def on_drag_data_received_event(self, widget, drag_context, x, y, selection_data, info, timestamp): log.debug("Selection(s) dropped on main window %s", selection_data.data) if selection_data.get_uris(): process_args(selection_data.get_uris()) else: process_args(selection_data.data.split()) drag_context.finish(True, True) def on_expose_event(self, widget, event): component.get("SystemTray").blink(False) def stop(self): self.window.set_title("SharkByte") def update(self): # Update the window title def _on_get_session_status(status): download_rate = deluge.common.fsize_short(status["payload_download_rate"]) upload_rate = deluge.common.fsize_short(status["payload_upload_rate"]) self.window.set_title("%s%s %s%s - Deluge" % (_("D:"), download_rate, _("U:"), upload_rate)) if self.config["show_rate_in_title"]: client.core.get_session_status(["payload_download_rate", "payload_upload_rate"]).addCallback(_on_get_session_status) def _on_set_show_rate_in_title(self, key, value): if value: self.update() else: self.window.set_title("SharkByte") def on_newversionavailable_event(self, new_version): if self.config["show_new_releases"]: from deluge.ui.gtkui.new_release_dialog import NewReleaseDialog reactor.callLater(5.0, NewReleaseDialog().show, new_version) def on_torrentfinished_event(self, torrent_id): from deluge.ui.gtkui.notification import Notification Notification().notify(torrent_id) def is_on_active_workspace(self): """Determines if MainWindow is on the active workspace. Returns: bool: True if on active workspace (or wnck module not available), otherwise False. """ if wnck: self.screen.force_update() win = wnck.window_get(self.window.window.xid) if win: active_wksp = win.get_screen().get_active_workspace() if active_wksp: return win.is_on_workspace(active_wksp) else: return False return True
def __init__(self): component.Component.__init__(self, "TorrentManager", interval=5, depend=["CorePluginManager"]) log.debug("TorrentManager init..") # Set the libtorrent session self.session = component.get("Core").session # Set the alertmanager self.alerts = component.get("AlertManager") # Get the core config self.config = ConfigManager("core.conf") # Make sure the state folder has been created if not os.path.exists(os.path.join(get_config_dir(), "state")): os.makedirs(os.path.join(get_config_dir(), "state")) # Create the torrents dict { torrent_id: Torrent } self.torrents = {} self.queued_torrents = set() # This is a list of torrent_id when we shutdown the torrentmanager. # We use this list to determine if all active torrents have been paused # and that their resume data has been written. self.shutdown_torrent_pause_list = [] # self.num_resume_data used to save resume_data in bulk self.num_resume_data = 0 # Keep track of torrents finished but moving storage self.waiting_on_finish_moving = [] # Keeps track of resume data that needs to be saved to disk self.resume_data = {} # Workaround to determine if TorrentAddedEvent is from state file self.session_started = False # Register set functions self.config.register_set_function("max_connections_per_torrent", self.on_set_max_connections_per_torrent) self.config.register_set_function("max_upload_slots_per_torrent", self.on_set_max_upload_slots_per_torrent) self.config.register_set_function("max_upload_speed_per_torrent", self.on_set_max_upload_speed_per_torrent) self.config.register_set_function("max_download_speed_per_torrent", self.on_set_max_download_speed_per_torrent) # Register alert functions self.alerts.register_handler("torrent_finished_alert", self.on_alert_torrent_finished) self.alerts.register_handler("torrent_paused_alert", self.on_alert_torrent_paused) self.alerts.register_handler("torrent_checked_alert", self.on_alert_torrent_checked) self.alerts.register_handler("tracker_reply_alert", self.on_alert_tracker_reply) self.alerts.register_handler("tracker_announce_alert", self.on_alert_tracker_announce) self.alerts.register_handler("tracker_warning_alert", self.on_alert_tracker_warning) self.alerts.register_handler("tracker_error_alert", self.on_alert_tracker_error) self.alerts.register_handler("storage_moved_alert", self.on_alert_storage_moved) self.alerts.register_handler("storage_moved_failed_alert", self.on_alert_storage_moved_failed) self.alerts.register_handler("torrent_resumed_alert", self.on_alert_torrent_resumed) self.alerts.register_handler("state_changed_alert", self.on_alert_state_changed) self.alerts.register_handler("save_resume_data_alert", self.on_alert_save_resume_data) self.alerts.register_handler("save_resume_data_failed_alert", self.on_alert_save_resume_data_failed) self.alerts.register_handler("file_renamed_alert", self.on_alert_file_renamed) self.alerts.register_handler("metadata_received_alert", self.on_alert_metadata_received) self.alerts.register_handler("file_error_alert", self.on_alert_file_error) self.alerts.register_handler("file_completed_alert", self.on_alert_file_completed)
class TorrentManager(component.Component): """ TorrentManager contains a list of torrents in the current libtorrent session. This object is also responsible for saving the state of the session for use on restart. """ def __init__(self): component.Component.__init__(self, "TorrentManager", interval=5, depend=["CorePluginManager"]) log.debug("TorrentManager init..") # Set the libtorrent session self.session = component.get("Core").session # Set the alertmanager self.alerts = component.get("AlertManager") # Get the core config self.config = ConfigManager("core.conf") # Make sure the state folder has been created if not os.path.exists(os.path.join(get_config_dir(), "state")): os.makedirs(os.path.join(get_config_dir(), "state")) # Create the torrents dict { torrent_id: Torrent } self.torrents = {} self.queued_torrents = set() # This is a list of torrent_id when we shutdown the torrentmanager. # We use this list to determine if all active torrents have been paused # and that their resume data has been written. self.shutdown_torrent_pause_list = [] # self.num_resume_data used to save resume_data in bulk self.num_resume_data = 0 # Keep track of torrents finished but moving storage self.waiting_on_finish_moving = [] # Keeps track of resume data that needs to be saved to disk self.resume_data = {} # Workaround to determine if TorrentAddedEvent is from state file self.session_started = False # Register set functions self.config.register_set_function("max_connections_per_torrent", self.on_set_max_connections_per_torrent) self.config.register_set_function("max_upload_slots_per_torrent", self.on_set_max_upload_slots_per_torrent) self.config.register_set_function("max_upload_speed_per_torrent", self.on_set_max_upload_speed_per_torrent) self.config.register_set_function("max_download_speed_per_torrent", self.on_set_max_download_speed_per_torrent) # Register alert functions self.alerts.register_handler("torrent_finished_alert", self.on_alert_torrent_finished) self.alerts.register_handler("torrent_paused_alert", self.on_alert_torrent_paused) self.alerts.register_handler("torrent_checked_alert", self.on_alert_torrent_checked) self.alerts.register_handler("tracker_reply_alert", self.on_alert_tracker_reply) self.alerts.register_handler("tracker_announce_alert", self.on_alert_tracker_announce) self.alerts.register_handler("tracker_warning_alert", self.on_alert_tracker_warning) self.alerts.register_handler("tracker_error_alert", self.on_alert_tracker_error) self.alerts.register_handler("storage_moved_alert", self.on_alert_storage_moved) self.alerts.register_handler("storage_moved_failed_alert", self.on_alert_storage_moved_failed) self.alerts.register_handler("torrent_resumed_alert", self.on_alert_torrent_resumed) self.alerts.register_handler("state_changed_alert", self.on_alert_state_changed) self.alerts.register_handler("save_resume_data_alert", self.on_alert_save_resume_data) self.alerts.register_handler("save_resume_data_failed_alert", self.on_alert_save_resume_data_failed) self.alerts.register_handler("file_renamed_alert", self.on_alert_file_renamed) self.alerts.register_handler("metadata_received_alert", self.on_alert_metadata_received) self.alerts.register_handler("file_error_alert", self.on_alert_file_error) self.alerts.register_handler("file_completed_alert", self.on_alert_file_completed) def start(self): # Get the pluginmanager reference self.plugins = component.get("CorePluginManager") # Run the old state upgrader before loading state deluge.core.oldstateupgrader.OldStateUpgrader() # Try to load the state from file self.load_state() # Save the state every 5 minutes self.save_state_timer = LoopingCall(self.save_state) self.save_state_timer.start(200, False) self.save_resume_data_timer = LoopingCall(self.save_resume_data) self.save_resume_data_timer.start(190) def stop(self): # Stop timers if self.save_state_timer.running: self.save_state_timer.stop() if self.save_resume_data_timer.running: self.save_resume_data_timer.stop() # Save state on shutdown self.save_state() # Make another list just to make sure all paused torrents will be # passed to self.save_resume_data(). With # self.shutdown_torrent_pause_list it is possible to have a case when # torrent_id is removed from it in self.on_alert_torrent_paused() # before we call self.save_resume_data() here. save_resume_data_list = [] for key in self.torrents: # Stop the status cleanup LoopingCall here self.torrents[key].prev_status_cleanup_loop.stop() if not self.torrents[key].handle.is_paused(): # We set auto_managed false to prevent lt from resuming the torrent self.torrents[key].handle.auto_managed(False) self.torrents[key].handle.pause() self.shutdown_torrent_pause_list.append(key) save_resume_data_list.append(key) self.save_resume_data(save_resume_data_list) # We have to wait for all torrents to pause and write their resume data wait = True while wait: if self.shutdown_torrent_pause_list: wait = True else: wait = False for torrent in self.torrents.values(): if torrent.waiting_on_resume_data: wait = True break time.sleep(0.01) # Wait for all alerts self.alerts.handle_alerts(True) def update(self): for torrent_id, torrent in self.torrents.items(): if torrent.options["stop_at_ratio"] and torrent.state not in ("Checking", "Allocating", "Paused", "Queued"): # If the global setting is set, but the per-torrent isn't.. Just skip to the next torrent # This is so that a user can turn-off the stop at ratio option on a per-torrent basis if not torrent.options["stop_at_ratio"]: continue if torrent.get_ratio() >= torrent.options["stop_ratio"] and torrent.is_finished: if torrent.options["remove_at_ratio"]: self.remove(torrent_id) break if not torrent.handle.is_paused(): torrent.pause() def __getitem__(self, torrent_id): """Return the Torrent with torrent_id""" return self.torrents[torrent_id] def get_torrent_list(self): """Returns a list of torrent_ids""" return self.torrents.keys() def get_torrent_info_from_file(self, filepath): """Returns a torrent_info for the file specified or None""" torrent_info = None # Get the torrent data from the torrent file try: log.debug("Attempting to create torrent_info from %s", filepath) _file = open(filepath, "rb") torrent_info = lt.torrent_info(lt.bdecode(_file.read())) _file.close() except (IOError, RuntimeError), e: log.warning("Unable to open %s: %s", filepath, e) return torrent_info
class Core(CorePluginBase): def enable(self): self.config = ConfigManager('execute.conf', DEFAULT_CONFIG) event_manager = component.get('EventManager') self.registered_events = {} self.preremoved_cache = {} # Go through the commands list and register event handlers for command in self.config['commands']: event = command[EXECUTE_EVENT] if event in self.registered_events: continue def create_event_handler(event): def event_handler(torrent_id, *arg): self.execute_commands(torrent_id, event, *arg) return event_handler event_handler = create_event_handler(event) event_manager.register_event_handler(EVENT_MAP[event], event_handler) if event == 'removed': event_manager.register_event_handler('PreTorrentRemovedEvent', self.on_preremoved) self.registered_events[event] = event_handler log.debug('Execute core plugin enabled!') def on_preremoved(self, torrent_id): # Get and store the torrent info before it is removed torrent = component.get('TorrentManager').torrents[torrent_id] info = torrent.get_status(['name', 'download_location']) self.preremoved_cache[torrent_id] = [torrent_id, info['name'], info['download_location']] def execute_commands(self, torrent_id, event, *arg): if event == 'added' and arg[0]: # No futher action as from_state (arg[0]) is True return elif event == 'removed': torrent_id, torrent_name, download_location = self.preremoved_cache.pop(torrent_id) else: torrent = component.get('TorrentManager').torrents[torrent_id] info = torrent.get_status(['name', 'download_location']) # Grab the torrent name and download location # getProcessOutputAndValue requires args to be str torrent_name = info['name'] download_location = info['download_location'] log.debug('Running commands for %s', event) def log_error(result, command): (stdout, stderr, exit_code) = result if exit_code: log.warn('Command "%s" failed with exit code %d', command, exit_code) if stdout: log.warn('stdout: %s', stdout) if stderr: log.warn('stderr: %s', stderr) # Go through and execute all the commands for command in self.config['commands']: if command[EXECUTE_EVENT] == event: command = os.path.expandvars(command[EXECUTE_COMMAND]) command = os.path.expanduser(command) cmd_args = [torrent_id.encode('utf8'), torrent_name.encode('utf8'), download_location.encode('utf8')] if windows_check(): # Escape ampersand on windows (see #2784) cmd_args = [cmd_arg.replace('&', '^^^&') for cmd_arg in cmd_args] if os.path.isfile(command) and os.access(command, os.X_OK): log.debug('Running %s with args: %s', command, cmd_args) d = getProcessOutputAndValue(command, cmd_args, env=os.environ) d.addCallback(log_error, command) else: log.error('Execute script not found or not executable') def disable(self): self.config.save() event_manager = component.get('EventManager') for event, handler in self.registered_events.items(): event_manager.deregister_event_handler(event, handler) log.debug('Execute core plugin disabled!') # Exported RPC methods # @export def add_command(self, event, command): command_id = hashlib.sha1(str(time.time())).hexdigest() self.config['commands'].append((command_id, event, command)) self.config.save() component.get('EventManager').emit(ExecuteCommandAddedEvent(command_id, event, command)) @export def get_commands(self): return self.config['commands'] @export def remove_command(self, command_id): for command in self.config['commands']: if command[EXECUTE_ID] == command_id: self.config['commands'].remove(command) component.get('EventManager').emit(ExecuteCommandRemovedEvent(command_id)) break self.config.save() @export def save_command(self, command_id, event, cmd): for i, command in enumerate(self.config['commands']): if command[EXECUTE_ID] == command_id: self.config['commands'][i] = (command_id, event, cmd) break self.config.save()
class SystemTray(component.Component): def __init__(self): component.Component.__init__(self, "SystemTray", interval=4) self.window = component.get("MainWindow") self.config = ConfigManager("gtkui.conf") # List of widgets that need to be hidden when not connected to a host self.hide_widget_list = [ "menuitem_add_torrent", "menuitem_pause_all", "menuitem_resume_all", "menuitem_download_limit", "menuitem_upload_limit", "menuitem_quitdaemon", "separatormenuitem1", "separatormenuitem2", "separatormenuitem3", "separatormenuitem4" ] self.config.register_set_function("enable_system_tray", self.on_enable_system_tray_set) # bit of a hack to prevent function from doing something on startup self.__enabled_set_once = False self.config.register_set_function("enable_appindicator", self.on_enable_appindicator_set) self.max_download_speed = -1.0 self.download_rate = 0.0 self.max_upload_speed = -1.0 self.upload_rate = 0.0 self.config_value_changed_dict = { "max_download_speed": self._on_max_download_speed, "max_upload_speed": self._on_max_upload_speed } def enable(self): """Enables the system tray icon.""" self.builder = gtk.Builder() self.builder.add_from_file(deluge.common.resource_filename( "deluge.ui.gtkui", os.path.join("glade", "tray_menu.ui")) ) self.builder.connect_signals({ "on_menuitem_show_deluge_activate": self.on_menuitem_show_deluge_activate, "on_menuitem_add_torrent_activate": self.on_menuitem_add_torrent_activate, "on_menuitem_pause_all_activate": self.on_menuitem_pause_all_activate, "on_menuitem_resume_all_activate": self.on_menuitem_resume_all_activate, "on_menuitem_quit_activate": self.on_menuitem_quit_activate, "on_menuitem_quitdaemon_activate": self.on_menuitem_quitdaemon_activate }) self.tray_menu = self.builder.get_object("tray_menu") if appindicator and self.config["enable_appindicator"]: log.debug("Enabling the Application Indicator..") self.indicator = appindicator.Indicator ( "deluge", "deluge", appindicator.CATEGORY_APPLICATION_STATUS) # Pass the menu to the Application Indicator self.indicator.set_menu(self.tray_menu) # Make sure the status of the Show Window MenuItem is correct self._sig_win_hide = self.window.window.connect("hide", self._on_window_hide) self._sig_win_show = self.window.window.connect("show", self._on_window_show) if self.window.visible(): self.builder.get_object("menuitem_show_deluge").set_active(True) else: self.builder.get_object("menuitem_show_deluge").set_active(False) # Show the Application Indicator self.indicator.set_status(appindicator.STATUS_ACTIVE) else: log.debug("Enabling the system tray icon..") if deluge.common.windows_check() or deluge.common.osx_check(): self.tray = gtk.status_icon_new_from_pixbuf(common.get_logo(32)) else: try: self.tray = gtk.status_icon_new_from_icon_name("deluge") except: log.warning("Update PyGTK to 2.10 or greater for SystemTray..") return self.tray.connect("activate", self.on_tray_clicked) self.tray.connect("popup-menu", self.on_tray_popup) # For some reason these icons do not display in appindicator self.builder.get_object("download-limit-image").set_from_file( deluge.common.get_pixmap("downloading16.png")) self.builder.get_object("upload-limit-image").set_from_file( deluge.common.get_pixmap("seeding16.png")) client.register_event_handler("ConfigValueChangedEvent", self.config_value_changed) if client.connected(): # We're connected so we need to get some values from the core self.__start() else: # Hide menu widgets because we're not connected to a host. for widget in self.hide_widget_list: self.builder.get_object(widget).hide() def __start(self): if self.config["enable_system_tray"]: if self.config["classic_mode"]: self.hide_widget_list.remove("menuitem_quitdaemon") self.hide_widget_list.remove("separatormenuitem4") self.builder.get_object("menuitem_quitdaemon").hide() self.builder.get_object("separatormenuitem4").hide() # These do not work with appindicator currently and can crash Deluge. # Related to Launchpad bug #608219 if appindicator and self.config["enable_appindicator"]: self.hide_widget_list.remove("menuitem_download_limit") self.hide_widget_list.remove("menuitem_upload_limit") self.hide_widget_list.remove("separatormenuitem3") self.builder.get_object("menuitem_download_limit").hide() self.builder.get_object("menuitem_upload_limit").hide() self.builder.get_object("separatormenuitem3").hide() # Show widgets in the hide list because we've connected to a host for widget in self.hide_widget_list: self.builder.get_object(widget).show() # Build the bandwidth speed limit menus self.build_tray_bwsetsubmenu() # Get some config values client.core.get_config_value( "max_download_speed").addCallback(self._on_max_download_speed) client.core.get_config_value( "max_upload_speed").addCallback(self._on_max_upload_speed) self.send_status_request() def start(self): self.__start() def stop(self): if self.config["enable_system_tray"] and not self.config["enable_appindicator"]: try: # Hide widgets in hide list because we're not connected to a host for widget in self.hide_widget_list: self.builder.get_object(widget).hide() except Exception, e: log.debug("Unable to hide system tray menu widgets: %s", e) self.tray.set_tooltip(_("Deluge") + "\n" + _("Not Connected..."))
class Core(CorePluginBase): def enable(self): self.config = ConfigManager("execute.conf", DEFAULT_CONFIG) event_manager = component.get("EventManager") self.registered_events = {} # Go through the commands list and register event handlers for command in self.config["commands"]: event = command[EXECUTE_EVENT] if event in self.registered_events: continue def create_event_handler(event): def event_handler(torrent_id, *arg): self.execute_commands(torrent_id, event, *arg) return event_handler event_handler = create_event_handler(event) event_manager.register_event_handler(EVENT_MAP[event], event_handler) self.registered_events[event] = event_handler log.debug("Execute core plugin enabled!") def execute_commands(self, torrent_id, event, *arg): torrent = component.get("TorrentManager").torrents[torrent_id] info = torrent.get_status(["name", "save_path", "move_on_completed", "move_on_completed_path"]) # Grab the torrent name and save path torrent_name = info["name"] if event == "complete": save_path = info["move_on_completed_path"] if info["move_on_completed"] else info["save_path"] elif event == "added" and arg[0]: # No futher action as from_state (arg[0]) is True return else: save_path = info["save_path"] log.debug("[execute] Running commands for %s", event) def log_error(result, command): (stdout, stderr, exit_code) = result if exit_code: log.warn("[execute] command '%s' failed with exit code %d", command, exit_code) if stdout: log.warn("[execute] stdout: %s", stdout) if stderr: log.warn("[execute] stderr: %s", stderr) # Go through and execute all the commands for command in self.config["commands"]: if command[EXECUTE_EVENT] == event: command = os.path.expandvars(command[EXECUTE_COMMAND]) command = os.path.expanduser(command) log.debug("[execute] running %s", command) d = getProcessOutputAndValue(command, (torrent_id, torrent_name, save_path), env=os.environ) d.addCallback(log_error, command) def disable(self): self.config.save() event_manager = component.get("EventManager") for event, handler in self.registered_events.iteritems(): event_manager.deregister_event_handler(event, handler) log.debug("Execute core plugin disabled!") ### Exported RPC methods ### @export def add_command(self, event, command): command_id = hashlib.sha1(str(time.time())).hexdigest() self.config["commands"].append((command_id, event, command)) self.config.save() component.get("EventManager").emit(ExecuteCommandAddedEvent(command_id, event, command)) @export def get_commands(self): return self.config["commands"] @export def remove_command(self, command_id): for command in self.config["commands"]: if command[EXECUTE_ID] == command_id: self.config["commands"].remove(command) component.get("EventManager").emit(ExecuteCommandRemovedEvent(command_id)) break self.config.save() @export def save_command(self, command_id, event, cmd): for i, command in enumerate(self.config["commands"]): if command[EXECUTE_ID] == command_id: self.config["commands"][i] = (command_id, event, cmd) break self.config.save()