def __init__(self, parent=None): """""" gtk.VBox.__init__(self) self.__parent = parent scroll = gtk.ScrolledWindow() scroll.set_shadow_type(gtk.SHADOW_ETCHED_IN) scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.store = self.create_model() #crear columnas y self.update_flag = False #flag para que el update de la lista se haga solo si ya no esta corriendo. False = detenido. True = Corriendo. self.active_tab_flag = True #self.rows_buffer = {} #{id_item: row_obj, } #arma el cuadro con los items self.treeView = DnDTreeView(self.store) self.treeView.set_rules_hint(True) #turna el color de los items, creo. #self.treeView.set_reorderable(True) #change order by drag'n'drop. scroll.add(self.treeView) self.rows_buffer = self.treeView.rows_buffer #{id_item: row_obj, } #self.treeView.get_selection().set_mode(gtk.SELECTION_MULTIPLE) #Columns item names. Host = 3 columns in 1 col_list = ["hidden_id_item", _("Status"), _("File Name"), _("Host"), _("Size"), _("Complete"), _("Progress"), _("Time"), _("Remain"), _("Speed"), _("Status Message")] #podria usar un frozenset, no se si lo soporta cython. self.create_columns(col_list) #status icons self.icons_dict = self.get_icons() #return dict {cons.status_running: icon, } #self.treeView.add_events(gtk.gdk.KEY_PRESS) self.treeView.connect("key_press_event", self.keyboard_event) #Context Menu (pop-up) open_folder_item = (gtk.MenuItem(), _("Open destination folder"), self.on_open_folder) copy_link_item = (gtk.MenuItem(), _("Copy link"), self.on_copy_link) password_item = (gtk.MenuItem(), _("Add password"), self.on_password) delete_item = (gtk.MenuItem(), _("Delete"), self.on_delete) clear_completed_item = (gtk.MenuItem(), _("Clear Completed"), self.on_clear_completed) start_all_item = (gtk.MenuItem(), _("Start all"), self.on_start_all) stop_all_item = (gtk.MenuItem(), _("Stop all"), self.on_stop_all) self.menu_items = (open_folder_item, copy_link_item, password_item, delete_item) menu_generic_items = (None, start_all_item, stop_all_item, clear_completed_item) self.ctx_menu = Menu(self.menu_items+menu_generic_items) #items selected self.treeView.connect("button-press-event", self.on_context_menu) self.pack_start(scroll)
def __init__(self, parent=None): """""" gtk.VBox.__init__(self) self.__parent = parent scroll = gtk.ScrolledWindow() scroll.set_shadow_type(gtk.SHADOW_ETCHED_IN) scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.store = self.create_model() #crear columnas y self.update_flag = False #flag para que el update de la lista se haga solo si ya no esta corriendo. False = detenido. True = Corriendo. self.active_tab_flag = True #self.rows_buffer = {} #{id_item: row_obj, } #arma el cuadro con los items self.treeView = DnDTreeView(self.store) self.treeView.set_rules_hint(True) #turna el color de los items, creo. #self.treeView.set_reorderable(True) #change order by drag'n'drop. scroll.add(self.treeView) self.rows_buffer = self.treeView.rows_buffer #{id_item: row_obj, } #self.treeView.get_selection().set_mode(gtk.SELECTION_MULTIPLE) #Columns item names. Host = 3 columns in 1 col_list = [ "hidden_id_item", _("Status"), _("File Name"), _("Host"), _("Size"), _("Complete"), _("Progress"), _("Time"), _("Remain"), _("Speed"), _("Status Message") ] #podria usar un frozenset, no se si lo soporta cython. self.create_columns(col_list) #status icons self.icons_dict = self.get_icons( ) #return dict {cons.status_running: icon, } #self.treeView.add_events(gtk.gdk.KEY_PRESS) self.treeView.connect("key_press_event", self.keyboard_event) #Context Menu (pop-up) open_folder_item = (gtk.MenuItem(), _("Open destination folder"), self.on_open_folder) copy_link_item = (gtk.MenuItem(), _("Copy link"), self.on_copy_link) password_item = (gtk.MenuItem(), _("Add password"), self.on_password) delete_item = (gtk.MenuItem(), _("Delete"), self.on_delete) clear_completed_item = (gtk.MenuItem(), _("Clear Completed"), self.on_clear_completed) start_all_item = (gtk.MenuItem(), _("Start all"), self.on_start_all) stop_all_item = (gtk.MenuItem(), _("Stop all"), self.on_stop_all) self.menu_items = (open_folder_item, copy_link_item, password_item, delete_item) menu_generic_items = (None, start_all_item, stop_all_item, clear_completed_item) self.ctx_menu = Menu(self.menu_items + menu_generic_items) #items selected self.treeView.connect("button-press-event", self.on_context_menu) self.pack_start(scroll)
class List(gtk.VBox): #DownloadsList """ Lista de archivos descargando. """ def __init__(self, parent=None): """""" gtk.VBox.__init__(self) self.__parent = parent scroll = gtk.ScrolledWindow() scroll.set_shadow_type(gtk.SHADOW_ETCHED_IN) scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.store = self.create_model() #crear columnas y self.update_flag = False #flag para que el update de la lista se haga solo si ya no esta corriendo. False = detenido. True = Corriendo. self.active_tab_flag = True #self.rows_buffer = {} #{id_item: row_obj, } #arma el cuadro con los items self.treeView = DnDTreeView(self.store) self.treeView.set_rules_hint(True) #turna el color de los items, creo. #self.treeView.set_reorderable(True) #change order by drag'n'drop. scroll.add(self.treeView) self.rows_buffer = self.treeView.rows_buffer #{id_item: row_obj, } #self.treeView.get_selection().set_mode(gtk.SELECTION_MULTIPLE) #Columns item names. Host = 3 columns in 1 col_list = [ "hidden_id_item", _("Status"), _("File Name"), _("Host"), _("Size"), _("Complete"), _("Progress"), _("Time"), _("Remain"), _("Speed"), _("Status Message") ] #podria usar un frozenset, no se si lo soporta cython. self.create_columns(col_list) #status icons self.icons_dict = self.get_icons( ) #return dict {cons.status_running: icon, } #self.treeView.add_events(gtk.gdk.KEY_PRESS) self.treeView.connect("key_press_event", self.keyboard_event) #Context Menu (pop-up) open_folder_item = (gtk.MenuItem(), _("Open destination folder"), self.on_open_folder) copy_link_item = (gtk.MenuItem(), _("Copy link"), self.on_copy_link) password_item = (gtk.MenuItem(), _("Add password"), self.on_password) delete_item = (gtk.MenuItem(), _("Delete"), self.on_delete) clear_completed_item = (gtk.MenuItem(), _("Clear Completed"), self.on_clear_completed) start_all_item = (gtk.MenuItem(), _("Start all"), self.on_start_all) stop_all_item = (gtk.MenuItem(), _("Stop all"), self.on_stop_all) self.menu_items = (open_folder_item, copy_link_item, password_item, delete_item) menu_generic_items = (None, start_all_item, stop_all_item, clear_completed_item) self.ctx_menu = Menu(self.menu_items + menu_generic_items) #items selected self.treeView.connect("button-press-event", self.on_context_menu) self.pack_start(scroll) def keyboard_event(self, widget, event): """""" if gtk.gdk.keyval_name(event.keyval) == "Delete": #supr. key self.on_delete() def on_context_menu(self, widget, event): """ TODO: Mandar todo lo q tiene q ver con context_menu a otro modulo. """ if event.button == 3: #right click selection = self.treeView.get_selection() model, rows = selection.get_selected_rows() if not rows or len(rows) == 1: try: path, col, cellx, celly = self.treeView.get_path_at_pos( int(event.x), int(event.y)) except TypeError: #none selected. is_sensitive = False selection.unselect_all() else: #one selected. is_sensitive = True self.treeView.grab_focus() selection.select_path(path) else: is_sensitive = True [ item[WIDGET].set_sensitive(is_sensitive) for item in self.menu_items ] self.ctx_menu.popup(None, None, None, event.button, event.time) if len(rows) > 1: #stop signal (so the rows remain selected) return True def on_open_folder(self, widget): """""" model, rows = self.treeView.get_selection().get_selected_rows() if rows: paths_list = [] items_list = api.get_download_items( [model[row][0] for row in rows]) for download_item in items_list: folder_path = download_item.path if folder_path not in paths_list: #misc.open_folder_window(folder_path) threading.Thread(group=None, target=misc.open_folder_window, name=None, args=(folder_path, )).start() paths_list.append(folder_path) def on_copy_link(self, widget): """""" model, rows = self.treeView.get_selection().get_selected_rows() if rows: items_list = api.get_download_items( [model[row][0] for row in rows]) links_list = [ download_item.link for download_item in items_list if download_item.can_copy_link ] clipboard = gtk.Clipboard() clipboard.set_text('\n'.join(links_list)) def on_password(self, widget): """""" model, rows = self.treeView.get_selection().get_selected_rows() if rows: entry = gtk.Entry() entry.add_events(gtk.gdk.KEY_RELEASE_MASK) entry.set_width_chars(25) #entry width m = DlgGui(self.__parent, None, _("Password"), None, True, append_widget=entry) pwd = entry.get_text().strip() if m.accepted and pwd: events.trigger_pwd(pwd) def on_delete(self, widget=None): """""" model, rows = self.treeView.get_selection().get_selected_rows() if rows: message = _( "Do you want to remove this download? (downloaded segments will be deleted)" ) m = DlgGui(self.__parent, gtk.STOCK_DIALOG_WARNING, _("Remove Files"), message, True, True) if m.accepted: id_items_list = [] iters = [] for row in rows: iters.append(model[row].iter) id_item = model[row][0] id_items_list.append(id_item) del self.rows_buffer[id_item] #id_items_list = [model[row][0] for row in rows] [model.remove(iter) for iter in iters] api.delete_download(id_items_list) def on_start_all(self, widget): """ BUG: El boton start y stop no cambia. """ iditem_list = self.treeView.get_id_item_list() api.start_all(iditem_list) stopped_icon = self.icons_dict[cons.STATUS_STOPPED] queue_icon = self.icons_dict[cons.STATUS_QUEUE] for row in self.rows_buffer.values(): if row[1] == stopped_icon: row[1] = queue_icon self.get_status() #iniciar update de lista. def on_stop_all(self, widget=None): """ BUG: El boton start y stop no cambia. """ api.stop_all() stopped_icon = self.icons_dict[cons.STATUS_STOPPED] queue_icon = self.icons_dict[cons.STATUS_QUEUE] for row in self.rows_buffer.values(): if row[1] == queue_icon: row[1] = stopped_icon def on_clear_completed(self, widget): """""" model = self.treeView.get_model() finished_icon = self.icons_dict[cons.STATUS_FINISHED] iters = [] for row in self.rows_buffer.values(): if row[1] == finished_icon: iters.append(row.iter) del self.rows_buffer[row[0]] #todo: remove from complete_downloads [model.remove(iter) for iter in iters] def get_status(self): """ DONE: Se llama a self.update_status cada vez q se agrega un archivo (incluso si ya esta corriendo). Hacer que no se llame si ya esta corriendo. DONE: Mejorar este metodo + downloadmanager.get_thread + downloadmanager.thread_manager.get_status. Gobject roba ciclos, no es un thread aparte (en teoria) """ if not self.update_flag: #si el flag = True, el update ya esta corriendo. Si es False entramos. self.update_flag = True gobject.timeout_add( 1000, self.update_status) #auto actualizar status cada 1 seg. logger.debug("list_update = True") def update_status(self): """""" downloads_list = api.get_status() for download_item in downloads_list: try: row = self.rows_buffer[download_item.id] #row[0] = download_item.id #this column is hidden and wont be modificated. row[1] = self.icons_dict[download_item.status] #col 1 row[2] = download_item.name #col 2 #row[3] = download_item.host #download_item.host #col 3 row[4] = self.icons_dict[ cons. DL_RESUME] if download_item.can_resume else None #download_item.host #col 3 row[5] = self.icons_dict[ cons. DL_PREMIUM] if download_item.is_premium else None #download_item.host #col 3 row[6] = misc.size_format( download_item.size) if download_item.size else None row[7] = misc.size_format( download_item.size_complete ) if download_item.size_complete else None row[8] = download_item.progress row[9] = misc.time_format( download_item.time) if download_item.time else None row[10] = misc.time_format( download_item.time_remain ) if download_item.time_remain else None row[11] = misc.speed_format( download_item.speed) if download_item.speed else None row[12] = download_item.status_msg if not download_item.fail_count else "{0} ({1} #{2})".format( download_item.status_msg, _("Retry"), download_item.fail_count) except KeyError as err: logger.debug(err) #if not self.download_manager.active_downloads + self.download_manager.queue_downloads + self.download_manager.stopped_downloads: #si ya no hay mas descargas activas o en cola detener este loop. #logger.debug("list_update = False") #self.update_flag = False #cuando update_flag = False, sabemos que ya no esta corriendo... #return False return True #hace que se actualicen los valores mas de una vez (hasta el final). def create_model(self): """ Crear columnas """ store = gtk.ListStore( str, gtk.gdk.Pixbuf, str, gtk.gdk.Pixbuf, gtk.gdk.Pixbuf, gtk.gdk.Pixbuf, str, str, int, str, str, str, str ) #tipo de item de cada columana (nombre de link, host, tamanio, porcentaje, vel.) return store def store_items(self, item_list): """ Agregar nuevos items a las columnas """ #self.dnd_flag = False #no afectar metodo dragndrop for download_item in item_list: #in self.download_manager.pending_downloads: self.get_status() #iniciar update_list del gui size_file = misc.size_format( download_item.size) if download_item.size else None size_complete = misc.size_format( download_item.size_complete ) if download_item.size_complete else None time = misc.time_format( download_item.time) if download_item.time else None host_icon = self.get_host_icon(download_item.host) self.store.append( [ download_item.id, self.icons_dict[download_item.status], download_item.name, host_icon, None, None, size_file, size_complete, download_item.progress, time, None, None, download_item.status_msg ] ) #store.append([act[0], act[1], act[2]], ) futura lista con tamanio de archivo, icono, etc self.rows_buffer[download_item.id] = self.store[ -1] #row just added #self.dnd_flag = True #volver a habilitar el metodo dragndrop def create_columns(self, col_list): """ Crea las columnas de la lista TODO: Guardar el tamanio de la columna nombre en el config.ini al resizear. """ id_col = 0 for item in col_list: #id_col = col_list.index(item) if item not in ( _("Status"), _("Progress"), _("Host")): #si no es la barra de progreso ni el estado rendererText = gtk.CellRendererText( ) #pide el primer item que ira en la columna (text=0) o segundo, etc... rendererText.set_property("ellipsize", 3) #2= middle, 3 = right, 1 = left column = gtk.TreeViewColumn(item, rendererText, text=id_col) column.set_resizable(True) column.set_expand(True) column.set_min_width(1) if item == "hidden_id_item": #no mostrar columna de id_item column.set_visible(False) column.set_resizable(False) elif item == _("File Name"): rendererText.set_property("ellipsize", 2) rendererText.set_fixed_size(150, -1) #elif item == _("Status Message"): #column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) #column.set_min_width(110) #dont let expand like crazy... elif item == _("Status"): renderPixbuf = gtk.CellRendererPixbuf() column = gtk.TreeViewColumn(None, renderPixbuf) #name = None column.add_attribute(renderPixbuf, 'pixbuf', id_col) column.set_expand(False) column.set_min_width(21) elif item == _("Host"): renderPixbuf = gtk.CellRendererPixbuf() renderPixbuf_2 = gtk.CellRendererPixbuf() renderPixbuf_3 = gtk.CellRendererPixbuf() column = gtk.TreeViewColumn(item) #name = None column.pack_start(renderPixbuf, False) column.pack_start(renderPixbuf_2, False) column.pack_start(renderPixbuf_3, False) column.add_attribute(renderPixbuf, 'pixbuf', id_col) id_col += 1 column.add_attribute(renderPixbuf_2, 'pixbuf', id_col) id_col += 1 column.add_attribute(renderPixbuf_3, 'pixbuf', id_col) column.set_expand(True) column.set_min_width(1) else: #barra de progreso. rendererProgress = gtk.CellRendererProgress() column = gtk.TreeViewColumn(item, rendererProgress) column.add_attribute(rendererProgress, 'value', id_col) #aniadir variable a la barra. column.set_expand(True) column.set_min_width(1) #column.set_sort_column_id(id_col) #ordenar columna #column.set_spacing(25) self.treeView.append_column(column) id_col += 1 def get_host_icon(self, host): """""" try: return self.icons_dict[host] except KeyError: try: self.icons_dict[host] = gtk.gdk.pixbuf_new_from_file_at_size( os.path.join(cons.PLUGINS_PATH, host, "favicon.ico").decode( sys.getfilesystemencoding()), 16, 16) except Exception as err: logger.warning(err) self.icons_dict[host] = None return self.icons_dict[host] def get_icons(self): """""" #running = self.treeView.render_icon(gtk.STOCK_MEDIA_PLAY, gtk.ICON_SIZE_MENU) running = media.get_pixbuf(media.START, media.SMALL) stopped = media.get_pixbuf(media.STOP, media.SMALL) queue = media.get_pixbuf(media.QUEUE, media.SMALL) finished = media.get_pixbuf(media.CHECK, media.SMALL) error = media.get_pixbuf(media.X_MARK, media.SMALL) resume = media.get_pixbuf(media.REFRESH, media.SMALL) premium = media.get_pixbuf(media.ACCOUNTS, media.SMALL) return { cons.STATUS_RUNNING: running, cons.STATUS_STOPPED: stopped, cons.STATUS_QUEUE: queue, cons.STATUS_FINISHED: finished, cons.STATUS_ERROR: error, cons.DL_RESUME: resume, cons.DL_PREMIUM: premium }
def __init__(self): """""" gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL) self.set_title(cons.APP_TITLE) self.set_size_request(MIN_WIDTH, MIN_HEIGHT) self.set_position(gtk.WIN_POS_CENTER) self.resize(600, 300) self.config = conf #config.py #app window position and size x, y, w, h = self.config.get_window_settings() if gtk.gdk.screen_width() <= w or gtk.gdk.screen_height() <= h: self.maximize() elif w >= MIN_WIDTH or h >= MIN_HEIGHT: self.resize(w, h) if x >= 0 and y >= 0: self.move(x, y) #widgets for tabs. self.downloads_list_gui = List(parent=self) self.downloads_list_gui.treeView.get_selection().connect("changed", self.on_selected) self.add_downloads_gui = AddDownloads(self.downloads_list_gui, parent=self) self.addons_gui = addons_gui.AddonsManager(self) self.preferences = Preferences(self.addons_gui.addons_list) self.log_gui = Log() self.vbox = gtk.VBox() self.add(self.vbox) #toolbar. button = ToolButton(icon, label), handler, sensitive. #add_download = gtk.ToolButton(gtk.ToolButton(gtk.STOCK_ADD), "Add Download"), self.add_links, True stop = (gtk.ToolButton(media.get_image(media.STOP, media.MEDIUM), None), _("Stop Download"), self.stop_download, False) #tuple start = (gtk.ToolButton(media.get_image(media.START, media.MEDIUM), None), _("Start Download"), self.start_download, False) accounts = (gtk.ToolButton(media.get_image(media.ACCOUNTS, media.MEDIUM), None), _("Accounts"), self.accounts_app, True) preferences = (gtk.MenuToolButton(media.get_image(media.PREFERENCES, media.MEDIUM), None), _("Preferences"), self.on_preferences, True) about = (gtk.ToolButton(media.get_image(media.ABOUT, media.MEDIUM), None), _("About"), self.about_dlg, True) self.stop = stop[WIDGET] #self.stop = gtk.ToolButton(gtk.ToolButton(gtk.STOCK_STOP), "Stop Download"). Para poder cambiar el set_sensitive self.start = start[WIDGET] self.Toolbar = Toolbar([start, stop, None, accounts, preferences, None, about]) #los botones se agregan al Toolbar en el orden de esta lista. None = separador self.vbox.pack_start(self.Toolbar, False) #menu config_preferences = (gtk.MenuItem(), _("Preferences"), self.on_preferences) config_about = (gtk.MenuItem(), _("About"), self.about_dlg) #addons-menu menu_items = [menu_item for menu_item in [addon.get_menu_item() for addon in self.addons_gui.addons_list] if menu_item is not None] menu_items.extend([None, config_preferences, config_about]) menu = Menu(menu_items) preferences[WIDGET].set_menu(menu) #Sessions self.load_session() #tabs (notebooks) self.vbox2 = gtk.VBox() self.notebook = Notebook() self.notebook.set_tab_pos(gtk.POS_BOTTOM) #weird butg in gtk+ 2.24.10 on resizing to often (make the app crash). self.notebook.set_show_border(False) self.notebook.append_page(self.downloads_list_gui, gtk.Label(_("Downloads"))) self.notebook.append_page(self.add_downloads_gui, gtk.Label(_("Add downloads"))) [self.notebook.append_page(tab, gtk.Label(addon.name)) for tab, addon in [(addon.get_tab(), addon) for addon in self.addons_gui.addons_list] if tab is not None] self.notebook.append_page(self.log_gui, gtk.Label(_("Log"))) self.notebook.connect("switch-page", self.on_tab_switch) self.vbox2.pack_start(self.notebook) self.vbox.pack_start(self.vbox2) #status bar self.status_bar = StatusBar(self.add_downloads_gui) #self.pack_start(self.status_bar, False, False, 0) self.vbox.pack_start(self.status_bar, False, False) #Quit Event events.connect(cons.EVENT_QUIT, self.on_close) #self.connect("destroy", self.on_close) #boton cerrar de la barra de arriba self.connect("delete-event", self.on_quit) self.show_all()
def __init__(self, list_gui, parent=None): gtk.VBox.__init__(self) self.__parent = parent self.list_gui = list_gui self.update_flag = False self.active_tab_flag = True #Estructura: #-------------------vbox1_start--------------- #-------------------hbox4[halign1-left + halign4-right]----------------------- #-------------------vbox1_end---------------- #detallado: #-------------------vbox1_start--------------- #-------------------hbox4_start--------------- #-------------------halign1_start------------- #-------------------hbox1-----------------------(save to field + examine button, left aligned) #-------------------halign1_end-------------- #-------------------halign4_start------------(add links button, etc, right aligned) #-------------------halign2_start------------- #-------------------hbox2----------------------- #-------------------halign2_end-------------- #-------------------halign4_end-------------- #-------------------hbox4_end---------------- #-------------------vbox1_end---------------- #containers-separators. #self.vbox no puede ser cambiado en gtk.Dialog. Crear variable vbox y luego meterla en self.vbox. vbox1 = gtk.VBox(False, 5) #gtk.VBox(homogeneous=False, spacing=0) homogeneous: mismo espacio cada hijo, spacing: espacio horizontal entre hijos. #vbox1_start #Check links field. #------------------------------------------- scroll = gtk.ScrolledWindow() scroll.set_shadow_type(gtk.SHADOW_ETCHED_IN) scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.store = gtk.ListStore(str, bool, str, str, str, str, str)#modelo de columnas. (4 columnas de strings y 1 booleana) #arma el cuadro con los items self.treeView = gtk.TreeView(self.store) self.treeView.set_rules_hint(True) #turna el color de los items, creo. scroll.add(self.treeView) #Columns item names. col_list = ["hidden_id_item", _("Add"), _("Status"), _("File Name"), _("Host"), _("Size"), _("Status Message")] #podria usar un frozenset, no se si lo soporta cython. self.create_columns(col_list) vbox1.pack_start(scroll) #Context Menu (pop-up) dl_selected_item = (gtk.MenuItem(), _("Download Selected"), self.on_accept) recheck_item = (gtk.MenuItem(), _("Re-check"), self.on_recheck) clear_item = (gtk.MenuItem(), _("Clear list"), self.on_clear_list) menu_items = (dl_selected_item, None, recheck_item, clear_item) self.ctx_menu = Menu(menu_items) #items selected self.treeView.connect("button-press-event", self.on_context_menu) #Select file field. #------------------------------------------- #containers-separators. hbox1 = gtk.HBox(False, 10) #file field and button examine. #gtk.HBox(homogeneous=False, spacing=0) homogeneous: mismo espacio cada hijo, spacing: espacio horizontal entre hijos. hbox2 = gtk.HBox(False, 10) #buttons cancel and accept. #hbox1 #label_save_to = gtk.Label("Save to:") #hbox1.add(label_save_to) self.paths_liststore = gtk.ListStore(str) cb_entry = gtk.ComboBoxEntry(self.paths_liststore) self.entry = cb_entry.child self.entry.add_events(gtk.gdk.KEY_RELEASE_MASK) self.entry.set_width_chars(35) #entry width self.paths_list = conf.get_save_dl_paths() self.load_save_paths() if not self.paths_list: default_path = cons.DLFOLDER_PATH else: default_path = self.paths_list[-1] #.decode(sys.getfilesystemencoding()) self.entry.set_text(default_path.decode("utf-8")) #default entry path. Cuando cree el ejecutable, se puede crear una ruta al directorio asi: sys.path.append(path) hbox1.add(cb_entry) button = gtk.Button(_("...")) button.set_size_request(80, 35) button.connect("clicked", self.save_folder) hbox1.add(button) halign1 = gtk.Alignment(0, 0, 0, 0) #horizontal container. left liagment. #vertical container (estara vacio). gtk.Alignment(xalign=0.0, yalign=0.0, xscale=0.0, yscale=0.0) #xalign: espacio libre a la izquierda del hijo. 0.0 = sin espacio arriba. 1.0 = todo el espacio arriba. #yalign: espacio libre vertical arriba del hijo. 0.0 = sin espacio arriba. 1.0 = todo el espacio arriba. halign1.add(hbox1) down_arrow_image = media.get_image(media.DOWN, media.MEDIUM) download_selected_button = gtk.Button() download_selected_button.add(down_arrow_image) #self.button2.set_size_request(180, 35) download_selected_button.connect("clicked", self.on_accept) add_image = media.get_image(media.ADD, media.MEDIUM) add_button = gtk.Button() add_button.add(add_image) #button.set_size_request(80, 35) add_button.connect("clicked", self.on_add_links) icon_down = media.get_image(media.ARROW_DOWN, media.SMALL) #icon_down = gtk.image_new_from_file(os.path.join(cons.APP_PATH, "media", "arrow_down9.png")) #drop_down_image = gtk.image_new_from_stock(gtk.STOCK_GO_DOWN, gtk.ICON_SIZE_BUTTON) drop_down_button = PopupMenuButton(image=icon_down) #gtk.Button() drop_down_button.set_size_request(20, 35) import_item = (gtk.MenuItem(), _("Import Container"), self.on_import_container) recheck_item = (gtk.MenuItem(), _("Re-check"), self.on_recheck) clear_item = (gtk.MenuItem(), _("Clear list"), self.on_clear_list) menu_items = (import_item, None, recheck_item, clear_item) menu = Menu(menu_items) #items selected drop_down_button.set_menu(menu) #button5 = gtk.Button(_("Re-check")) #button5.set_size_request(80, 35) #button5.connect("clicked", self.on_recheck) halign4 = gtk.Alignment(1, 0, 0, 0) #horizontal container. right liagment. hbox2.add(download_selected_button) hbox2.add(add_button) hbox2.add(drop_down_button) halign4.add(hbox2) hbox4 = gtk.HBox(False, 0) #two aligment widgets hbox4.add(halign1) hbox4.add(halign4) vbox1.pack_start(hbox4, False, False) #pack_start(child, expand=True, fill=True, padding=0) #------------------------------------------- #vbox1_end #entry.get_text() #checking thread stuff. self.cancelled = False #si se cancelo el checkeo, terminar thread. #self.th = threading.Thread(group=None, target=self.checking_links, name=None).start() #ckeck links. self.pack_start(vbox1)
class AddDownloads(gtk.VBox): """""" def __init__(self, list_gui, parent=None): gtk.VBox.__init__(self) self.__parent = parent self.list_gui = list_gui self.update_flag = False self.active_tab_flag = True #Estructura: #-------------------vbox1_start--------------- #-------------------hbox4[halign1-left + halign4-right]----------------------- #-------------------vbox1_end---------------- #detallado: #-------------------vbox1_start--------------- #-------------------hbox4_start--------------- #-------------------halign1_start------------- #-------------------hbox1-----------------------(save to field + examine button, left aligned) #-------------------halign1_end-------------- #-------------------halign4_start------------(add links button, etc, right aligned) #-------------------halign2_start------------- #-------------------hbox2----------------------- #-------------------halign2_end-------------- #-------------------halign4_end-------------- #-------------------hbox4_end---------------- #-------------------vbox1_end---------------- #containers-separators. #self.vbox no puede ser cambiado en gtk.Dialog. Crear variable vbox y luego meterla en self.vbox. vbox1 = gtk.VBox(False, 5) #gtk.VBox(homogeneous=False, spacing=0) homogeneous: mismo espacio cada hijo, spacing: espacio horizontal entre hijos. #vbox1_start #Check links field. #------------------------------------------- scroll = gtk.ScrolledWindow() scroll.set_shadow_type(gtk.SHADOW_ETCHED_IN) scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.store = gtk.ListStore(str, bool, str, str, str, str, str)#modelo de columnas. (4 columnas de strings y 1 booleana) #arma el cuadro con los items self.treeView = gtk.TreeView(self.store) self.treeView.set_rules_hint(True) #turna el color de los items, creo. scroll.add(self.treeView) #Columns item names. col_list = ["hidden_id_item", _("Add"), _("Status"), _("File Name"), _("Host"), _("Size"), _("Status Message")] #podria usar un frozenset, no se si lo soporta cython. self.create_columns(col_list) vbox1.pack_start(scroll) #Context Menu (pop-up) dl_selected_item = (gtk.MenuItem(), _("Download Selected"), self.on_accept) recheck_item = (gtk.MenuItem(), _("Re-check"), self.on_recheck) clear_item = (gtk.MenuItem(), _("Clear list"), self.on_clear_list) menu_items = (dl_selected_item, None, recheck_item, clear_item) self.ctx_menu = Menu(menu_items) #items selected self.treeView.connect("button-press-event", self.on_context_menu) #Select file field. #------------------------------------------- #containers-separators. hbox1 = gtk.HBox(False, 10) #file field and button examine. #gtk.HBox(homogeneous=False, spacing=0) homogeneous: mismo espacio cada hijo, spacing: espacio horizontal entre hijos. hbox2 = gtk.HBox(False, 10) #buttons cancel and accept. #hbox1 #label_save_to = gtk.Label("Save to:") #hbox1.add(label_save_to) self.paths_liststore = gtk.ListStore(str) cb_entry = gtk.ComboBoxEntry(self.paths_liststore) self.entry = cb_entry.child self.entry.add_events(gtk.gdk.KEY_RELEASE_MASK) self.entry.set_width_chars(35) #entry width self.paths_list = conf.get_save_dl_paths() self.load_save_paths() if not self.paths_list: default_path = cons.DLFOLDER_PATH else: default_path = self.paths_list[-1] #.decode(sys.getfilesystemencoding()) self.entry.set_text(default_path.decode("utf-8")) #default entry path. Cuando cree el ejecutable, se puede crear una ruta al directorio asi: sys.path.append(path) hbox1.add(cb_entry) button = gtk.Button(_("...")) button.set_size_request(80, 35) button.connect("clicked", self.save_folder) hbox1.add(button) halign1 = gtk.Alignment(0, 0, 0, 0) #horizontal container. left liagment. #vertical container (estara vacio). gtk.Alignment(xalign=0.0, yalign=0.0, xscale=0.0, yscale=0.0) #xalign: espacio libre a la izquierda del hijo. 0.0 = sin espacio arriba. 1.0 = todo el espacio arriba. #yalign: espacio libre vertical arriba del hijo. 0.0 = sin espacio arriba. 1.0 = todo el espacio arriba. halign1.add(hbox1) down_arrow_image = media.get_image(media.DOWN, media.MEDIUM) download_selected_button = gtk.Button() download_selected_button.add(down_arrow_image) #self.button2.set_size_request(180, 35) download_selected_button.connect("clicked", self.on_accept) add_image = media.get_image(media.ADD, media.MEDIUM) add_button = gtk.Button() add_button.add(add_image) #button.set_size_request(80, 35) add_button.connect("clicked", self.on_add_links) icon_down = media.get_image(media.ARROW_DOWN, media.SMALL) #icon_down = gtk.image_new_from_file(os.path.join(cons.APP_PATH, "media", "arrow_down9.png")) #drop_down_image = gtk.image_new_from_stock(gtk.STOCK_GO_DOWN, gtk.ICON_SIZE_BUTTON) drop_down_button = PopupMenuButton(image=icon_down) #gtk.Button() drop_down_button.set_size_request(20, 35) import_item = (gtk.MenuItem(), _("Import Container"), self.on_import_container) recheck_item = (gtk.MenuItem(), _("Re-check"), self.on_recheck) clear_item = (gtk.MenuItem(), _("Clear list"), self.on_clear_list) menu_items = (import_item, None, recheck_item, clear_item) menu = Menu(menu_items) #items selected drop_down_button.set_menu(menu) #button5 = gtk.Button(_("Re-check")) #button5.set_size_request(80, 35) #button5.connect("clicked", self.on_recheck) halign4 = gtk.Alignment(1, 0, 0, 0) #horizontal container. right liagment. hbox2.add(download_selected_button) hbox2.add(add_button) hbox2.add(drop_down_button) halign4.add(hbox2) hbox4 = gtk.HBox(False, 0) #two aligment widgets hbox4.add(halign1) hbox4.add(halign4) vbox1.pack_start(hbox4, False, False) #pack_start(child, expand=True, fill=True, padding=0) #------------------------------------------- #vbox1_end #entry.get_text() #checking thread stuff. self.cancelled = False #si se cancelo el checkeo, terminar thread. #self.th = threading.Thread(group=None, target=self.checking_links, name=None).start() #ckeck links. self.pack_start(vbox1) def create_columns(self, col_list): """""" for item in col_list: id_col = col_list.index(item) if item != _("Add"): rendererText = gtk.CellRendererText() #pide el primer item que ira en la columna (text=0) o segundo, etc... rendererText.set_property("ellipsize", 3) #2= middle, 3 = right, 1 = left column = gtk.TreeViewColumn(item, rendererText, text=id_col) column.set_sort_column_id(id_col) #ordenar columna column.set_resizable(True) column.set_expand(True) column.set_min_width(1) if item == "hidden_id_item": #no mostrar columna de id_item column.set_visible(False) elif item == _("File Name"): rendererText.set_property("ellipsize", 2) else: #selection box column. rendererToggle = gtk.CellRendererToggle() #rendererToggle.set_property('activatable', True) column = gtk.TreeViewColumn(None, rendererToggle) #name = None column.add_attribute(rendererToggle, 'active', id_col) column.set_min_width(21) rendererToggle.connect("toggled", self.on_toggled, id_col) self.treeView.append_column(column) def load_save_paths(self): """""" for path in self.paths_list: self.paths_liststore.prepend([path]) def on_context_menu(self, widget, event): """""" if event.button == 3: #model, rows = self.treeView.get_selection().get_selected_rows() #if rows: self.ctx_menu.popup(None, None, None, event.button, event.time) def on_toggled(self, celltoggled, path, id_col): #id_col = numero de columna, path= numero de fila """""" model = self.treeView.get_model() if celltoggled.get_active(): #devuelve True, si el check estaba activado, sino false. model[path][id_col] = False else: model[path][id_col] = True def on_recheck(self, widget): """ Recheck only non alive items. """ api.recheck_items() def on_clear_list(self, widget): """""" self.store.clear() #clear all rows in the liststore api.clear_pending() def on_import_container(self, widget=None): #import_links (importar contenedor) """""" openfile = FileChooserDialog(title=_("Open File"), parent=self.__parent, action=gtk.FILE_CHOOSER_ACTION_OPEN, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) openfile.set_default_response(gtk.RESPONSE_OK) filter = gtk.FileFilter() filter.set_name("OCH Files") filter.add_pattern("*.och") openfile.add_filter(filter) response = openfile.run() if response == gtk.RESPONSE_OK: #.decode(sys.getfilesystemencoding()) fileh = openfile.get_filename().decode("utf-8") if response == gtk.RESPONSE_OK else None #ruta + nombre de archivo seleccionado logger.info("File opened: {0}".format(openfile.get_filename().decode(sys.getfilesystemencoding()))) if fileh: container = Container(fileh) container.extract_links() links_list = container.get_linklist() if links_list: self.checking_links(links_list, copy_link=False) #SaveFiles(linklist, self.download_manager, self.list_gui) #instancia de clase. SaveFiles(lista, clase, clase) openfile.destroy() def on_add_links(self, widget): """ DONE: luego de aniadir links, se deberia poder seguir aniadiendo. Y limpiar aniadidos """ add_links_dlg = AddLinks(parent=self.__parent) links_list = add_links_dlg.links_list if links_list: #se agregaron items. self.checking_links(links_list) def checking_links(self, links_list, copy_link=True): """ Agregar items a las columnas DONE: Agrergar posibilidad de recherckear items que no estan vivos. DONE: detener el update de la lista (desde el main_gui), cuando se cambia a otra pestania (senial switch-notebook) TODO: Agregar columna que muestre la url del enlace, none para los agregados desde un .och """ for link in links_list: download_item = api.create_download_item(cons.UNKNOWN, 0, link, copy_link) #return download_item object self.store.append([download_item.id, True, cons.LINK_CHECKING, cons.UNKNOWN, None, None, None]) #checking start. #threading.Thread(group=None, target=self.download_manager.plugin_link_checking, name=None, args=(download_item, )).start() api.start_checking() self.start_update() def start_update(self): """""" if not self.update_flag: self.update_flag = True gobject.timeout_add(1000, self.update_checking_status) def update_checking_status(self): #this method steals cycles, its not a new thread """""" #self.download_manager.clear_pending() #Erase pending_downloads list. items_list = api.get_checking_update() for download_item in items_list: #link_status, file_name, host, size = self.download_manager.plugin_link_checking(download_item) for row in self.store: if row[0] == download_item.id: row[2] = download_item.link_status row[3] = download_item.name row[4] = download_item.host row[5] = misc.size_format(download_item.size) row[6] = download_item.link_status_msg #if not self.download_manager.pending_downloads: #return False return True #keep it updating, def save_folder(self, widget): """ Cuadro de dialogo para elegir donde se quiere guardar lo descargado. """ openfolder = FileChooserDialog(title=_("Open Folder"), parent=self.__parent, action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) openfolder.set_default_response(gtk.RESPONSE_OK) response = openfolder.run() if response == gtk.RESPONSE_OK: self.entry.set_text(openfolder.get_filename().decode("utf-8")) #.decode(sys.getfilesystemencoding())) #("utf-8")) openfolder.destroy() def on_accept(self, widget): """""" save_to_path = self.entry.get_text().decode("utf-8") #(sys.getfilesystemencoding()) try: self.paths_list.remove(save_to_path) except ValueError: if len(self.paths_list) > 5: self.paths_list.pop(0) self.paths_list.append(save_to_path) self.paths_liststore.clear() self.load_save_paths() conf.set_save_dl_paths(self.paths_list) #remover items no seleccionados de pending_downloads. model = self.treeView.get_model() id_add_list = [] iters = [] for row in model: #desactivar los otros antes de activar este. if row[1] and row[2] != cons.LINK_DEAD and row[4] != cons.UNSUPPORTED: #id_col = 1, toggle. True (active) or False id_add_list.append(row[0]) iters.append(row.iter) #row.iter returns a treeiter [model.remove(iter) for iter in iters] item_list = api.get_added_items(id_add_list) api.downloader_init(item_list, save_to_path) #iniciar threads de descarga. self.list_gui.store_items(item_list) #agregar links a la lista GUI
class List(gtk.VBox): #DownloadsList """ Lista de archivos descargando. """ def __init__(self, parent=None): """""" gtk.VBox.__init__(self) self.__parent = parent scroll = gtk.ScrolledWindow() scroll.set_shadow_type(gtk.SHADOW_ETCHED_IN) scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.store = self.create_model() #crear columnas y self.update_flag = False #flag para que el update de la lista se haga solo si ya no esta corriendo. False = detenido. True = Corriendo. self.active_tab_flag = True #self.rows_buffer = {} #{id_item: row_obj, } #arma el cuadro con los items self.treeView = DnDTreeView(self.store) self.treeView.set_rules_hint(True) #turna el color de los items, creo. #self.treeView.set_reorderable(True) #change order by drag'n'drop. scroll.add(self.treeView) self.rows_buffer = self.treeView.rows_buffer #{id_item: row_obj, } #self.treeView.get_selection().set_mode(gtk.SELECTION_MULTIPLE) #Columns item names. Host = 3 columns in 1 col_list = ["hidden_id_item", _("Status"), _("File Name"), _("Host"), _("Size"), _("Complete"), _("Progress"), _("Time"), _("Remain"), _("Speed"), _("Status Message")] #podria usar un frozenset, no se si lo soporta cython. self.create_columns(col_list) #status icons self.icons_dict = self.get_icons() #return dict {cons.status_running: icon, } #self.treeView.add_events(gtk.gdk.KEY_PRESS) self.treeView.connect("key_press_event", self.keyboard_event) #Context Menu (pop-up) open_folder_item = (gtk.MenuItem(), _("Open destination folder"), self.on_open_folder) copy_link_item = (gtk.MenuItem(), _("Copy link"), self.on_copy_link) password_item = (gtk.MenuItem(), _("Add password"), self.on_password) delete_item = (gtk.MenuItem(), _("Delete"), self.on_delete) clear_completed_item = (gtk.MenuItem(), _("Clear Completed"), self.on_clear_completed) start_all_item = (gtk.MenuItem(), _("Start all"), self.on_start_all) stop_all_item = (gtk.MenuItem(), _("Stop all"), self.on_stop_all) self.menu_items = (open_folder_item, copy_link_item, password_item, delete_item) menu_generic_items = (None, start_all_item, stop_all_item, clear_completed_item) self.ctx_menu = Menu(self.menu_items+menu_generic_items) #items selected self.treeView.connect("button-press-event", self.on_context_menu) self.pack_start(scroll) def keyboard_event(self, widget, event): """""" if gtk.gdk.keyval_name(event.keyval) == "Delete": #supr. key self.on_delete() def on_context_menu(self, widget, event): """ TODO: Mandar todo lo q tiene q ver con context_menu a otro modulo. """ if event.button == 3: #right click selection = self.treeView.get_selection() model, rows = selection.get_selected_rows() if not rows or len(rows) == 1: try: path, col, cellx, celly = self.treeView.get_path_at_pos(int(event.x), int(event.y)) except TypeError: #none selected. is_sensitive = False selection.unselect_all() else: #one selected. is_sensitive = True self.treeView.grab_focus() selection.select_path(path) else: is_sensitive = True [item[WIDGET].set_sensitive(is_sensitive) for item in self.menu_items] self.ctx_menu.popup(None, None, None, event.button, event.time) if len(rows) > 1: #stop signal (so the rows remain selected) return True def on_open_folder(self, widget): """""" model, rows = self.treeView.get_selection().get_selected_rows() if rows: paths_list = [] items_list = api.get_download_items([model[row][0] for row in rows]) for download_item in items_list: folder_path = download_item.path if folder_path not in paths_list: #misc.open_folder_window(folder_path) threading.Thread(group=None, target=misc.open_folder_window, name=None, args=(folder_path, )).start() paths_list.append(folder_path) def on_copy_link(self, widget): """""" model, rows = self.treeView.get_selection().get_selected_rows() if rows: items_list = api.get_download_items([model[row][0] for row in rows]) links_list = [download_item.link for download_item in items_list if download_item.can_copy_link] clipboard = gtk.Clipboard() clipboard.set_text('\n'.join(links_list)) def on_password(self, widget): """""" model, rows = self.treeView.get_selection().get_selected_rows() if rows: entry = gtk.Entry() entry.add_events(gtk.gdk.KEY_RELEASE_MASK) entry.set_width_chars(25) #entry width m = DlgGui(self.__parent, None, _("Password"), None, True, append_widget=entry) pwd = entry.get_text().strip() if m.accepted and pwd: events.trigger_pwd(pwd) def on_delete(self, widget=None): """""" model, rows = self.treeView.get_selection().get_selected_rows() if rows: message = _("Do you want to remove this download? (downloaded segments will be deleted)") m = DlgGui(self.__parent, gtk.STOCK_DIALOG_WARNING, _("Remove Files"), message, True, True) if m.accepted: id_items_list = [] iters = [] for row in rows: iters.append(model[row].iter) id_item = model[row][0] id_items_list.append(id_item) del self.rows_buffer[id_item] #id_items_list = [model[row][0] for row in rows] [model.remove(iter) for iter in iters] api.delete_download(id_items_list) def on_start_all(self, widget): """ BUG: El boton start y stop no cambia. """ iditem_list = self.treeView.get_id_item_list() api.start_all(iditem_list) stopped_icon = self.icons_dict[cons.STATUS_STOPPED] queue_icon = self.icons_dict[cons.STATUS_QUEUE] for row in self.rows_buffer.values(): if row[1] == stopped_icon: row[1] = queue_icon self.get_status() #iniciar update de lista. def on_stop_all(self, widget=None): """ BUG: El boton start y stop no cambia. """ api.stop_all() stopped_icon = self.icons_dict[cons.STATUS_STOPPED] queue_icon = self.icons_dict[cons.STATUS_QUEUE] for row in self.rows_buffer.values(): if row[1] == queue_icon: row[1] = stopped_icon def on_clear_completed(self, widget): """""" model = self.treeView.get_model() finished_icon = self.icons_dict[cons.STATUS_FINISHED] iters = [] for row in self.rows_buffer.values(): if row[1] == finished_icon: iters.append(row.iter) del self.rows_buffer[row[0]] #todo: remove from complete_downloads [model.remove(iter) for iter in iters] def get_status(self): """ DONE: Se llama a self.update_status cada vez q se agrega un archivo (incluso si ya esta corriendo). Hacer que no se llame si ya esta corriendo. DONE: Mejorar este metodo + downloadmanager.get_thread + downloadmanager.thread_manager.get_status. Gobject roba ciclos, no es un thread aparte (en teoria) """ if not self.update_flag: #si el flag = True, el update ya esta corriendo. Si es False entramos. self.update_flag = True gobject.timeout_add(1000, self.update_status) #auto actualizar status cada 1 seg. logger.debug("list_update = True") def update_status(self): """""" downloads_list = api.get_status() for download_item in downloads_list: try: row = self.rows_buffer[download_item.id] #row[0] = download_item.id #this column is hidden and wont be modificated. row[1] = self.icons_dict[download_item.status] #col 1 row[2] = download_item.name #col 2 #row[3] = download_item.host #download_item.host #col 3 row[4] = self.icons_dict[cons.DL_RESUME] if download_item.can_resume else None #download_item.host #col 3 row[5] = self.icons_dict[cons.DL_PREMIUM] if download_item.is_premium else None #download_item.host #col 3 row[6] = misc.size_format(download_item.size) if download_item.size else None row[7] = misc.size_format(download_item.size_complete) if download_item.size_complete else None row[8] = download_item.progress row[9] = misc.time_format(download_item.time) if download_item.time else None row[10] = misc.time_format(download_item.time_remain) if download_item.time_remain else None row[11] = misc.speed_format(download_item.speed) if download_item.speed else None row[12] = download_item.status_msg if not download_item.fail_count else "{0} ({1} #{2})".format(download_item.status_msg,_("Retry"), download_item.fail_count) except KeyError as err: logger.debug(err) #if not self.download_manager.active_downloads + self.download_manager.queue_downloads + self.download_manager.stopped_downloads: #si ya no hay mas descargas activas o en cola detener este loop. #logger.debug("list_update = False") #self.update_flag = False #cuando update_flag = False, sabemos que ya no esta corriendo... #return False return True #hace que se actualicen los valores mas de una vez (hasta el final). def create_model(self): """ Crear columnas """ store = gtk.ListStore(str, gtk.gdk.Pixbuf, str, gtk.gdk.Pixbuf, gtk.gdk.Pixbuf, gtk.gdk.Pixbuf, str, str, int, str, str, str, str) #tipo de item de cada columana (nombre de link, host, tamanio, porcentaje, vel.) return store def store_items(self, item_list): """ Agregar nuevos items a las columnas """ #self.dnd_flag = False #no afectar metodo dragndrop for download_item in item_list: #in self.download_manager.pending_downloads: self.get_status() #iniciar update_list del gui size_file = misc.size_format(download_item.size) if download_item.size else None size_complete = misc.size_format(download_item.size_complete) if download_item.size_complete else None time = misc.time_format(download_item.time) if download_item.time else None host_icon = self.get_host_icon(download_item.host) self.store.append([download_item.id, self.icons_dict[download_item.status], download_item.name, host_icon, None, None, size_file, size_complete, download_item.progress, time, None, None, download_item.status_msg]) #store.append([act[0], act[1], act[2]], ) futura lista con tamanio de archivo, icono, etc self.rows_buffer[download_item.id] = self.store[-1] #row just added #self.dnd_flag = True #volver a habilitar el metodo dragndrop def create_columns(self, col_list): """ Crea las columnas de la lista TODO: Guardar el tamanio de la columna nombre en el config.ini al resizear. """ id_col = 0 for item in col_list: #id_col = col_list.index(item) if item not in(_("Status"), _("Progress"), _("Host")): #si no es la barra de progreso ni el estado rendererText = gtk.CellRendererText() #pide el primer item que ira en la columna (text=0) o segundo, etc... rendererText.set_property("ellipsize", 3) #2= middle, 3 = right, 1 = left column = gtk.TreeViewColumn(item, rendererText, text=id_col) column.set_resizable(True) column.set_expand(True) column.set_min_width(1) if item == "hidden_id_item": #no mostrar columna de id_item column.set_visible(False) column.set_resizable(False) elif item == _("File Name"): rendererText.set_property("ellipsize", 2) rendererText.set_fixed_size(150, -1) #elif item == _("Status Message"): #column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) #column.set_min_width(110) #dont let expand like crazy... elif item == _("Status"): renderPixbuf = gtk.CellRendererPixbuf() column = gtk.TreeViewColumn(None, renderPixbuf) #name = None column.add_attribute(renderPixbuf, 'pixbuf', id_col) column.set_expand(False) column.set_min_width(21) elif item == _("Host"): renderPixbuf = gtk.CellRendererPixbuf() renderPixbuf_2 = gtk.CellRendererPixbuf() renderPixbuf_3 = gtk.CellRendererPixbuf() column = gtk.TreeViewColumn(item) #name = None column.pack_start(renderPixbuf, False) column.pack_start(renderPixbuf_2, False) column.pack_start(renderPixbuf_3, False) column.add_attribute(renderPixbuf, 'pixbuf', id_col) id_col += 1 column.add_attribute(renderPixbuf_2, 'pixbuf', id_col) id_col += 1 column.add_attribute(renderPixbuf_3, 'pixbuf', id_col) column.set_expand(True) column.set_min_width(1) else: #barra de progreso. rendererProgress = gtk.CellRendererProgress() column = gtk.TreeViewColumn(item, rendererProgress) column.add_attribute(rendererProgress, 'value', id_col) #aniadir variable a la barra. column.set_expand(True) column.set_min_width(1) #column.set_sort_column_id(id_col) #ordenar columna #column.set_spacing(25) self.treeView.append_column(column) id_col += 1 def get_host_icon(self, host): """""" try: return self.icons_dict[host] except KeyError: try: self.icons_dict[host] = gtk.gdk.pixbuf_new_from_file_at_size(os.path.join(cons.PLUGINS_PATH, host, "favicon.ico").decode(sys.getfilesystemencoding()), 16, 16) except Exception as err: logger.warning(err) self.icons_dict[host] = None return self.icons_dict[host] def get_icons(self): """""" #running = self.treeView.render_icon(gtk.STOCK_MEDIA_PLAY, gtk.ICON_SIZE_MENU) running = media.get_pixbuf(media.START, media.SMALL) stopped = media.get_pixbuf(media.STOP, media.SMALL) queue = media.get_pixbuf(media.QUEUE, media.SMALL) finished = media.get_pixbuf(media.CHECK, media.SMALL) error = media.get_pixbuf(media.X_MARK, media.SMALL) resume = media.get_pixbuf(media.REFRESH, media.SMALL) premium = media.get_pixbuf(media.ACCOUNTS, media.SMALL) return {cons.STATUS_RUNNING: running, cons.STATUS_STOPPED: stopped, cons.STATUS_QUEUE: queue, cons.STATUS_FINISHED: finished, cons.STATUS_ERROR: error, cons.DL_RESUME: resume, cons.DL_PREMIUM: premium}
def __init__(self, list_gui, parent=None): gtk.VBox.__init__(self) self.__parent = parent self.list_gui = list_gui self.update_flag = False self.active_tab_flag = True #Estructura: #-------------------vbox1_start--------------- #-------------------hbox4[halign1-left + halign4-right]----------------------- #-------------------vbox1_end---------------- #detallado: #-------------------vbox1_start--------------- #-------------------hbox4_start--------------- #-------------------halign1_start------------- #-------------------hbox1-----------------------(save to field + examine button, left aligned) #-------------------halign1_end-------------- #-------------------halign4_start------------(add links button, etc, right aligned) #-------------------halign2_start------------- #-------------------hbox2----------------------- #-------------------halign2_end-------------- #-------------------halign4_end-------------- #-------------------hbox4_end---------------- #-------------------vbox1_end---------------- #containers-separators. #self.vbox no puede ser cambiado en gtk.Dialog. Crear variable vbox y luego meterla en self.vbox. vbox1 = gtk.VBox( False, 5 ) #gtk.VBox(homogeneous=False, spacing=0) homogeneous: mismo espacio cada hijo, spacing: espacio horizontal entre hijos. #vbox1_start #Check links field. #------------------------------------------- scroll = gtk.ScrolledWindow() scroll.set_shadow_type(gtk.SHADOW_ETCHED_IN) scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.store = gtk.ListStore( str, bool, str, str, str, str, str) #modelo de columnas. (4 columnas de strings y 1 booleana) #arma el cuadro con los items self.treeView = gtk.TreeView(self.store) self.treeView.set_rules_hint(True) #turna el color de los items, creo. scroll.add(self.treeView) #Columns item names. col_list = [ "hidden_id_item", _("Add"), _("Status"), _("File Name"), _("Host"), _("Size"), _("Status Message") ] #podria usar un frozenset, no se si lo soporta cython. self.create_columns(col_list) vbox1.pack_start(scroll) #Context Menu (pop-up) dl_selected_item = (gtk.MenuItem(), _("Download Selected"), self.on_accept) recheck_item = (gtk.MenuItem(), _("Re-check"), self.on_recheck) clear_item = (gtk.MenuItem(), _("Clear list"), self.on_clear_list) menu_items = (dl_selected_item, None, recheck_item, clear_item) self.ctx_menu = Menu(menu_items) #items selected self.treeView.connect("button-press-event", self.on_context_menu) #Select file field. #------------------------------------------- #containers-separators. hbox1 = gtk.HBox(False, 10) #file field and button examine. #gtk.HBox(homogeneous=False, spacing=0) homogeneous: mismo espacio cada hijo, spacing: espacio horizontal entre hijos. hbox2 = gtk.HBox(False, 10) #buttons cancel and accept. #hbox1 #label_save_to = gtk.Label("Save to:") #hbox1.add(label_save_to) self.paths_liststore = gtk.ListStore(str) cb_entry = gtk.ComboBoxEntry(self.paths_liststore) self.entry = cb_entry.child self.entry.add_events(gtk.gdk.KEY_RELEASE_MASK) self.entry.set_width_chars(35) #entry width self.paths_list = conf.get_save_dl_paths() self.load_save_paths() if not self.paths_list: default_path = cons.DLFOLDER_PATH else: default_path = self.paths_list[-1] #.decode(sys.getfilesystemencoding()) self.entry.set_text( default_path.decode("utf-8") ) #default entry path. Cuando cree el ejecutable, se puede crear una ruta al directorio asi: sys.path.append(path) hbox1.add(cb_entry) button = gtk.Button(_("...")) button.set_size_request(80, 35) button.connect("clicked", self.save_folder) hbox1.add(button) halign1 = gtk.Alignment( 0, 0, 0, 0 ) #horizontal container. left liagment. #vertical container (estara vacio). gtk.Alignment(xalign=0.0, yalign=0.0, xscale=0.0, yscale=0.0) #xalign: espacio libre a la izquierda del hijo. 0.0 = sin espacio arriba. 1.0 = todo el espacio arriba. #yalign: espacio libre vertical arriba del hijo. 0.0 = sin espacio arriba. 1.0 = todo el espacio arriba. halign1.add(hbox1) down_arrow_image = media.get_image(media.DOWN, media.MEDIUM) download_selected_button = gtk.Button() download_selected_button.add(down_arrow_image) #self.button2.set_size_request(180, 35) download_selected_button.connect("clicked", self.on_accept) add_image = media.get_image(media.ADD, media.MEDIUM) add_button = gtk.Button() add_button.add(add_image) #button.set_size_request(80, 35) add_button.connect("clicked", self.on_add_links) icon_down = media.get_image(media.ARROW_DOWN, media.SMALL) #icon_down = gtk.image_new_from_file(os.path.join(cons.APP_PATH, "media", "arrow_down9.png")) #drop_down_image = gtk.image_new_from_stock(gtk.STOCK_GO_DOWN, gtk.ICON_SIZE_BUTTON) drop_down_button = PopupMenuButton(image=icon_down) #gtk.Button() drop_down_button.set_size_request(20, 35) import_item = (gtk.MenuItem(), _("Import Container"), self.on_import_container) recheck_item = (gtk.MenuItem(), _("Re-check"), self.on_recheck) clear_item = (gtk.MenuItem(), _("Clear list"), self.on_clear_list) menu_items = (import_item, None, recheck_item, clear_item) menu = Menu(menu_items) #items selected drop_down_button.set_menu(menu) #button5 = gtk.Button(_("Re-check")) #button5.set_size_request(80, 35) #button5.connect("clicked", self.on_recheck) halign4 = gtk.Alignment(1, 0, 0, 0) #horizontal container. right liagment. hbox2.add(download_selected_button) hbox2.add(add_button) hbox2.add(drop_down_button) halign4.add(hbox2) hbox4 = gtk.HBox(False, 0) #two aligment widgets hbox4.add(halign1) hbox4.add(halign4) vbox1.pack_start( hbox4, False, False) #pack_start(child, expand=True, fill=True, padding=0) #------------------------------------------- #vbox1_end #entry.get_text() #checking thread stuff. self.cancelled = False #si se cancelo el checkeo, terminar thread. #self.th = threading.Thread(group=None, target=self.checking_links, name=None).start() #ckeck links. self.pack_start(vbox1)
class AddDownloads(gtk.VBox): """""" def __init__(self, list_gui, parent=None): gtk.VBox.__init__(self) self.__parent = parent self.list_gui = list_gui self.update_flag = False self.active_tab_flag = True #Estructura: #-------------------vbox1_start--------------- #-------------------hbox4[halign1-left + halign4-right]----------------------- #-------------------vbox1_end---------------- #detallado: #-------------------vbox1_start--------------- #-------------------hbox4_start--------------- #-------------------halign1_start------------- #-------------------hbox1-----------------------(save to field + examine button, left aligned) #-------------------halign1_end-------------- #-------------------halign4_start------------(add links button, etc, right aligned) #-------------------halign2_start------------- #-------------------hbox2----------------------- #-------------------halign2_end-------------- #-------------------halign4_end-------------- #-------------------hbox4_end---------------- #-------------------vbox1_end---------------- #containers-separators. #self.vbox no puede ser cambiado en gtk.Dialog. Crear variable vbox y luego meterla en self.vbox. vbox1 = gtk.VBox( False, 5 ) #gtk.VBox(homogeneous=False, spacing=0) homogeneous: mismo espacio cada hijo, spacing: espacio horizontal entre hijos. #vbox1_start #Check links field. #------------------------------------------- scroll = gtk.ScrolledWindow() scroll.set_shadow_type(gtk.SHADOW_ETCHED_IN) scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.store = gtk.ListStore( str, bool, str, str, str, str, str) #modelo de columnas. (4 columnas de strings y 1 booleana) #arma el cuadro con los items self.treeView = gtk.TreeView(self.store) self.treeView.set_rules_hint(True) #turna el color de los items, creo. scroll.add(self.treeView) #Columns item names. col_list = [ "hidden_id_item", _("Add"), _("Status"), _("File Name"), _("Host"), _("Size"), _("Status Message") ] #podria usar un frozenset, no se si lo soporta cython. self.create_columns(col_list) vbox1.pack_start(scroll) #Context Menu (pop-up) dl_selected_item = (gtk.MenuItem(), _("Download Selected"), self.on_accept) recheck_item = (gtk.MenuItem(), _("Re-check"), self.on_recheck) clear_item = (gtk.MenuItem(), _("Clear list"), self.on_clear_list) menu_items = (dl_selected_item, None, recheck_item, clear_item) self.ctx_menu = Menu(menu_items) #items selected self.treeView.connect("button-press-event", self.on_context_menu) #Select file field. #------------------------------------------- #containers-separators. hbox1 = gtk.HBox(False, 10) #file field and button examine. #gtk.HBox(homogeneous=False, spacing=0) homogeneous: mismo espacio cada hijo, spacing: espacio horizontal entre hijos. hbox2 = gtk.HBox(False, 10) #buttons cancel and accept. #hbox1 #label_save_to = gtk.Label("Save to:") #hbox1.add(label_save_to) self.paths_liststore = gtk.ListStore(str) cb_entry = gtk.ComboBoxEntry(self.paths_liststore) self.entry = cb_entry.child self.entry.add_events(gtk.gdk.KEY_RELEASE_MASK) self.entry.set_width_chars(35) #entry width self.paths_list = conf.get_save_dl_paths() self.load_save_paths() if not self.paths_list: default_path = cons.DLFOLDER_PATH else: default_path = self.paths_list[-1] #.decode(sys.getfilesystemencoding()) self.entry.set_text( default_path.decode("utf-8") ) #default entry path. Cuando cree el ejecutable, se puede crear una ruta al directorio asi: sys.path.append(path) hbox1.add(cb_entry) button = gtk.Button(_("...")) button.set_size_request(80, 35) button.connect("clicked", self.save_folder) hbox1.add(button) halign1 = gtk.Alignment( 0, 0, 0, 0 ) #horizontal container. left liagment. #vertical container (estara vacio). gtk.Alignment(xalign=0.0, yalign=0.0, xscale=0.0, yscale=0.0) #xalign: espacio libre a la izquierda del hijo. 0.0 = sin espacio arriba. 1.0 = todo el espacio arriba. #yalign: espacio libre vertical arriba del hijo. 0.0 = sin espacio arriba. 1.0 = todo el espacio arriba. halign1.add(hbox1) down_arrow_image = media.get_image(media.DOWN, media.MEDIUM) download_selected_button = gtk.Button() download_selected_button.add(down_arrow_image) #self.button2.set_size_request(180, 35) download_selected_button.connect("clicked", self.on_accept) add_image = media.get_image(media.ADD, media.MEDIUM) add_button = gtk.Button() add_button.add(add_image) #button.set_size_request(80, 35) add_button.connect("clicked", self.on_add_links) icon_down = media.get_image(media.ARROW_DOWN, media.SMALL) #icon_down = gtk.image_new_from_file(os.path.join(cons.APP_PATH, "media", "arrow_down9.png")) #drop_down_image = gtk.image_new_from_stock(gtk.STOCK_GO_DOWN, gtk.ICON_SIZE_BUTTON) drop_down_button = PopupMenuButton(image=icon_down) #gtk.Button() drop_down_button.set_size_request(20, 35) import_item = (gtk.MenuItem(), _("Import Container"), self.on_import_container) recheck_item = (gtk.MenuItem(), _("Re-check"), self.on_recheck) clear_item = (gtk.MenuItem(), _("Clear list"), self.on_clear_list) menu_items = (import_item, None, recheck_item, clear_item) menu = Menu(menu_items) #items selected drop_down_button.set_menu(menu) #button5 = gtk.Button(_("Re-check")) #button5.set_size_request(80, 35) #button5.connect("clicked", self.on_recheck) halign4 = gtk.Alignment(1, 0, 0, 0) #horizontal container. right liagment. hbox2.add(download_selected_button) hbox2.add(add_button) hbox2.add(drop_down_button) halign4.add(hbox2) hbox4 = gtk.HBox(False, 0) #two aligment widgets hbox4.add(halign1) hbox4.add(halign4) vbox1.pack_start( hbox4, False, False) #pack_start(child, expand=True, fill=True, padding=0) #------------------------------------------- #vbox1_end #entry.get_text() #checking thread stuff. self.cancelled = False #si se cancelo el checkeo, terminar thread. #self.th = threading.Thread(group=None, target=self.checking_links, name=None).start() #ckeck links. self.pack_start(vbox1) def create_columns(self, col_list): """""" for item in col_list: id_col = col_list.index(item) if item != _("Add"): rendererText = gtk.CellRendererText( ) #pide el primer item que ira en la columna (text=0) o segundo, etc... rendererText.set_property("ellipsize", 3) #2= middle, 3 = right, 1 = left column = gtk.TreeViewColumn(item, rendererText, text=id_col) column.set_sort_column_id(id_col) #ordenar columna column.set_resizable(True) column.set_expand(True) column.set_min_width(1) if item == "hidden_id_item": #no mostrar columna de id_item column.set_visible(False) elif item == _("File Name"): rendererText.set_property("ellipsize", 2) else: #selection box column. rendererToggle = gtk.CellRendererToggle() #rendererToggle.set_property('activatable', True) column = gtk.TreeViewColumn(None, rendererToggle) #name = None column.add_attribute(rendererToggle, 'active', id_col) column.set_min_width(21) rendererToggle.connect("toggled", self.on_toggled, id_col) self.treeView.append_column(column) def load_save_paths(self): """""" for path in self.paths_list: self.paths_liststore.prepend([path]) def on_context_menu(self, widget, event): """""" if event.button == 3: #model, rows = self.treeView.get_selection().get_selected_rows() #if rows: self.ctx_menu.popup(None, None, None, event.button, event.time) def on_toggled(self, celltoggled, path, id_col): #id_col = numero de columna, path= numero de fila """""" model = self.treeView.get_model() if celltoggled.get_active( ): #devuelve True, si el check estaba activado, sino false. model[path][id_col] = False else: model[path][id_col] = True def on_recheck(self, widget): """ Recheck only non alive items. """ api.recheck_items() def on_clear_list(self, widget): """""" self.store.clear() #clear all rows in the liststore api.clear_pending() def on_import_container(self, widget=None): #import_links (importar contenedor) """""" openfile = FileChooserDialog(title=_("Open File"), parent=self.__parent, action=gtk.FILE_CHOOSER_ACTION_OPEN, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) openfile.set_default_response(gtk.RESPONSE_OK) filter = gtk.FileFilter() filter.set_name("OCH Files") filter.add_pattern("*.och") openfile.add_filter(filter) response = openfile.run() if response == gtk.RESPONSE_OK: #.decode(sys.getfilesystemencoding()) fileh = openfile.get_filename().decode( "utf-8" ) if response == gtk.RESPONSE_OK else None #ruta + nombre de archivo seleccionado logger.info("File opened: {0}".format( openfile.get_filename().decode(sys.getfilesystemencoding()))) if fileh: container = Container(fileh) container.extract_links() links_list = container.get_linklist() if links_list: self.checking_links(links_list, copy_link=False) #SaveFiles(linklist, self.download_manager, self.list_gui) #instancia de clase. SaveFiles(lista, clase, clase) openfile.destroy() def on_add_links(self, widget): """ DONE: luego de aniadir links, se deberia poder seguir aniadiendo. Y limpiar aniadidos """ add_links_dlg = AddLinks(parent=self.__parent) links_list = add_links_dlg.links_list if links_list: #se agregaron items. self.checking_links(links_list) def checking_links(self, links_list, copy_link=True): """ Agregar items a las columnas DONE: Agrergar posibilidad de recherckear items que no estan vivos. DONE: detener el update de la lista (desde el main_gui), cuando se cambia a otra pestania (senial switch-notebook) TODO: Agregar columna que muestre la url del enlace, none para los agregados desde un .och """ for link in links_list: download_item = api.create_download_item( cons.UNKNOWN, 0, link, copy_link) #return download_item object self.store.append([ download_item.id, True, cons.LINK_CHECKING, cons.UNKNOWN, None, None, None ]) #checking start. #threading.Thread(group=None, target=self.download_manager.plugin_link_checking, name=None, args=(download_item, )).start() api.start_checking() self.start_update() def start_update(self): """""" if not self.update_flag: self.update_flag = True gobject.timeout_add(1000, self.update_checking_status) def update_checking_status( self): #this method steals cycles, its not a new thread """""" #self.download_manager.clear_pending() #Erase pending_downloads list. items_list = api.get_checking_update() for download_item in items_list: #link_status, file_name, host, size = self.download_manager.plugin_link_checking(download_item) for row in self.store: if row[0] == download_item.id: row[2] = download_item.link_status row[3] = download_item.name row[4] = download_item.host row[5] = misc.size_format(download_item.size) row[6] = download_item.link_status_msg #if not self.download_manager.pending_downloads: #return False return True #keep it updating, def save_folder(self, widget): """ Cuadro de dialogo para elegir donde se quiere guardar lo descargado. """ openfolder = FileChooserDialog( title=_("Open Folder"), parent=self.__parent, action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) openfolder.set_default_response(gtk.RESPONSE_OK) response = openfolder.run() if response == gtk.RESPONSE_OK: self.entry.set_text(openfolder.get_filename().decode( "utf-8")) #.decode(sys.getfilesystemencoding())) #("utf-8")) openfolder.destroy() def on_accept(self, widget): """""" save_to_path = self.entry.get_text().decode( "utf-8") #(sys.getfilesystemencoding()) try: self.paths_list.remove(save_to_path) except ValueError: if len(self.paths_list) > 5: self.paths_list.pop(0) self.paths_list.append(save_to_path) self.paths_liststore.clear() self.load_save_paths() conf.set_save_dl_paths(self.paths_list) #remover items no seleccionados de pending_downloads. model = self.treeView.get_model() id_add_list = [] iters = [] for row in model: #desactivar los otros antes de activar este. if row[1] and row[2] != cons.LINK_DEAD and row[ 4] != cons.UNSUPPORTED: #id_col = 1, toggle. True (active) or False id_add_list.append(row[0]) iters.append(row.iter) #row.iter returns a treeiter [model.remove(iter) for iter in iters] item_list = api.get_added_items(id_add_list) api.downloader_init(item_list, save_to_path) #iniciar threads de descarga. self.list_gui.store_items(item_list) #agregar links a la lista GUI