class Downloads(QTreeView): def __init__(self, parent): #TODO: Create wrapper or subclass list to append and remove from items and rows_buffer. QTreeView.__init__(self) self.weak_parent = weakref.ref(parent) #listview look self.setWordWrap(True) #search textElideMode self.setRootIsDecorated(False) self.setIndentation(0) self.setAlternatingRowColors(True) self.icons_dict = self.get_icons() self.items = [] self.rows_buffer = {} #{id_item: row_obj, } headers = ["hidden_id_item", "", _("File Name"), _("Host"), _("Size"), _("Complete"), _("Progress"), _("Time"), _("Remain"), _("Speed"), _("Status Message")] self.__model = SimpleListModel(headers, self.items) self.setModel(self.__model) self.setColumnHidden(0, True) self.setColumnWidth(1, 27) self.header().setResizeMode(1, QHeaderView.Fixed) #self.header().setResizeMode(3, QHeaderView.ResizeToContents) self.im_delegate = ImageDelegate(self) self.setItemDelegateForColumn(1, self.im_delegate) self.im2_delegate = ImageDelegate(self) self.setItemDelegateForColumn(3, self.im2_delegate) self.nf_delegate = NoFocusDelegate(self) self.setItemDelegate(self.nf_delegate) self.pb_delegate = ProgressBarDelegate(self) self.setItemDelegateForColumn(6, self.pb_delegate) #see http://stackoverflow.com/questions/1987546/qt4-stylesheets-and-focus-rect for removing the drawed border on focus self.setSelectionMode(QAbstractItemView.ExtendedSelection) #self.setDragEnabled(True) #self.setAcceptDrops(True) #self.setDropIndicatorShown(False) self.setDragDropMode(QAbstractItemView.InternalMove) #self.setDefaultDropAction(Qt.MoveAction) #drop indicator self.line = QWidget(self.viewport()) self.line.setAutoFillBackground(True) pal = self.line.palette() pal.setColor(self.line.backgroundRole(), Qt.black) self.line.setPalette(pal) self.line.setGeometry(0, 0, 0, 0) self.line.hide() #custom signals signals.store_items.connect(self.store_items) signals.on_stop_all.connect(self.on_stop_all) #update list self.timer = parent.idle_timeout(1000, self.update_) @property def parent(self): return self.weak_parent() def remove_row(self, id_item): item = self.rows_buffer.pop(id_item) self.__model.remove(self.items.index(item)) def get_selected_rows(self): """""" selected_rows = [index.row() for index in self.selectionModel().selectedRows()] selected_rows.sort() return selected_rows def dragEnterEvent(self, event): #FIXME: weird things may happen if draggin when an item is removed or added. #event.setDropAction(Qt.MoveAction) event.accept() def dragMoveEvent(self, event): #paint drop indicator at the current position. q_index = self.indexAt(event.pos()) if q_index.isValid(): rect = self.visualRect(q_index) self.line.setGeometry(0, rect.top(), self.viewport().width(), 1) else: q_index = self.__model.index(self.__model.rowCount() - 1) rect = self.visualRect(q_index) self.line.setGeometry(0, rect.bottom(), self.viewport().width(), 1) self.line.show() def dragLeaveEvent(self, event): #out of the drop area. self.line.hide() def dropEvent(self, event): self.line.hide() q_index = self.indexAt(event.pos()) if q_index.isValid(): index = q_index.row() else: index = -1 items = [self.items[row] for row in self.get_selected_rows()] dest_item = self.items[index] [self.__model.remove(self.items.index(item)) for item in items if item != dest_item] if index >= 0: index = self.items.index(dest_item) [self.__model.insert(index, item) for item in reversed(items) if item != dest_item] else: [self.__model.append(item) for item in items if item != dest_item] api.reorder_queue([row[0] for row in self.items]) #def keyboard_event(self, widget, event): #if gtk.gdk.keyval_name(event.keyval) == "Delete": #supr. key #self.on_delete() def contextMenuEvent(self, event): rows = self.get_selected_rows() is_selection = True if rows else False options = [(_('Open destination folder'), self.on_open_folder, is_selection), (_('Copy link'), self.on_copy_link, is_selection), #(_('Add password'), self.on_password, is_selection), None, (_('Delete'), self.on_delete, is_selection), None, (_('Start all'), self.on_start_all, True), (_('Stop all'), self.on_stop_all, True), (_('Clear Completed'), self.on_clear_completed, True)] menu = Menu(options) menu.exec_(event.globalPos()) def selectionChanged(self, selected, deselected): #overridden slot QTreeView.selectionChanged(self, selected, deselected) BTN = 0 rows = self.get_selected_rows() self.parent.stop[BTN].setEnabled(False) self.parent.start[BTN].setEnabled(False) if len(rows) == 1: #single selection. row_index = rows[0] id_item = self.items[row_index][0] self.parent.stop[BTN].setEnabled(True) self.parent.start[BTN].setEnabled(False) stopped_downloads = api.get_stopped_downloads() try: item = stopped_downloads[id_item] self.parent.stop[BTN].setEnabled(False) self.parent.start[BTN].setEnabled(True) except KeyError: pass elif rows: #multi selection self.parent.stop[BTN].setEnabled(True) self.parent.start[BTN].setEnabled(True) def on_open_folder(self): rows = self.get_selected_rows() if rows: items_list = api.get_download_items([self.items[row_index][0] for row_index in rows]) paths_list = {download_item.path for download_item in items_list} for folder_path in paths_list: utils.open_folder_window(folder_path) def on_copy_link(self): rows = self.get_selected_rows() if rows: items_list = api.get_download_items([self.items[row_index][0] for row_index in rows]) links_list = [download_item.link for download_item in items_list if download_item.can_copy_link] clipboard = QApplication.clipboard() clipboard.setText('\n'.join(links_list)) def on_password(self): rows = self.get_selected_rows() if rows: pass #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.add_password.emit(pwd) def on_delete(self): rows = self.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) m = True if m: id_items_list = [] for row_index in rows: id_item = self.items[row_index][0] id_items_list.append(id_item) [self.remove_row(id_item) for id_item in id_items_list] api.delete_download(id_items_list) def on_clear_completed(self): finished_icon = self.icons_dict[cons.STATUS_FINISHED] for row in self.items[:]: if row[1] == finished_icon: self.remove_row(row[0]) api.clear_complete() def on_start_all(self): """ BUG: El boton start y stop no cambia. """ id_item_list = [row[0] for row in self.items] api.start_all(id_item_list) stopped_icon = self.icons_dict[cons.STATUS_STOPPED] queue_icon = self.icons_dict[cons.STATUS_QUEUE] for row in self.items: if row[1] == stopped_icon: row[1] = queue_icon def on_stop_all(self): api.stop_all() stopped_icon = self.icons_dict[cons.STATUS_STOPPED] queue_icon = self.icons_dict[cons.STATUS_QUEUE] for row in self.items: if row[1] == queue_icon: row[1] = stopped_icon def store_items(self, item_list): for download_item in item_list: size_file = utils.size_format(download_item.size) if download_item.size else None size_complete = utils.size_format(download_item.size_complete) if download_item.size_complete else None time = utils.time_format(download_item.time) if download_item.time else None host_icon = self.get_host_icon(download_item.host) item = [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] self.__model.append(item) self.rows_buffer[item[0]] = item def update_(self): active_downloads = api.get_active_downloads() api.update_active_downloads() for download_item in active_downloads.itervalues(): try: row = self.rows_buffer[download_item.id] #row[0] = download_item.id row[1] = self.icons_dict[download_item.status] #col 1 row[2] = download_item.name #col 2 #row[3][0] = download_item.host row[3][1] = self.icons_dict[cons.DL_RESUME] if download_item.can_resume else None row[3][2] = self.icons_dict[cons.DL_PREMIUM] if download_item.is_premium else None row[4] = utils.size_format(download_item.size) if download_item.size else None row[5] = utils.size_format(download_item.size_complete) if download_item.size_complete else None row[6] = download_item.progress row[7] = utils.time_format(download_item.time) if download_item.time else None row[8] = utils.time_format(download_item.time_remain) if download_item.time_remain else None row[9] = utils.speed_format(download_item.speed) if download_item.speed else None row[10] = self.get_status_msg(download_item) except KeyError as err: logger.debug(err) self.__model.refresh() def get_status_msg(self, download_item): if download_item.fail_count: return "{0} ({1} #{2})".format(download_item.status_msg, _("Retry"), download_item.fail_count) else: return download_item.status_msg def get_host_icon(self, host): try: return self.icons_dict[host] except KeyError: self.icons_dict[host] = QPixmap(os.path.join(cons.PLUGINS_PATH, host, "favicon.ico")) return self.icons_dict[host] def get_icons(self): running = media.get_pixmap(media.START, media.SMALL) stopped = media.get_pixmap(media.STOP, media.SMALL) queue = media.get_pixmap(media.QUEUE, media.SMALL) finished = media.get_pixmap(media.CHECK, media.SMALL) error = media.get_pixmap(media.X_MARK, media.SMALL) resume = media.get_pixmap(media.REFRESH, media.SMALL) premium = media.get_pixmap(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}
class ConfigAccounts(QDialog): def __init__(self, parent): QDialog.__init__(self, parent, Qt.WindowSystemMenuHint | Qt.WindowTitleHint) self.setWindowTitle(_('Host Accounts')) self.resize(500, 450) vbox = QVBoxLayout() self.setLayout(vbox) # ####################### # vbox_accounts = QVBoxLayout() group_accounts = QGroupBox(_('Accounts:')) group_accounts.setLayout(vbox_accounts) self.tree_view = QTreeView() # #listview look self.tree_view.setWordWrap(True) #search textElideMode self.tree_view.setRootIsDecorated(False) self.tree_view.setIndentation(0) self.tree_view.setAlternatingRowColors(True) # self.items = [] headers = ["hidden_id_account", _("Host"), _("Status"), _("Username"), _("Password"), _("Enable")] bool_cols = [ENABLE, ] # self.__model = SimpleListModel(headers, self.items, bool_cols) self.tree_view.setModel(self.__model) vbox_accounts.addWidget(self.tree_view) self.tree_view.setColumnHidden(0, True) hbox_accounts = QHBoxLayout() hbox_accounts.addStretch(0) #stretch/align widget to right. vbox_accounts.addLayout(hbox_accounts) self.btn_remove = QPushButton(_('Remove')) self.btn_remove.clicked.connect(self.on_remove) self.btn_remove.setFixedHeight(35) self.btn_remove.setMaximumWidth(80) self.btn_remove.setEnabled(False) hbox_accounts.addWidget(self.btn_remove) self.btn_check = QPushButton(_('Check')) self.btn_check.clicked.connect(self.on_check) self.btn_check.setFixedHeight(35) self.btn_check.setMaximumWidth(80) self.btn_check.setEnabled(False) hbox_accounts.addWidget(self.btn_check) vbox.addWidget(group_accounts) # ####################### # vbox_login = QVBoxLayout() group_login = QGroupBox(_('Login:'******'Server:')) #label_server.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) label_user = QLabel(_('Username:'******'Password:'******'Add')) btn_add.clicked.connect(self.on_add) btn_add.setFixedHeight(35) btn_add.setMaximumWidth(80) if not self.cb.count(): btn_add.setEnabled(False) hbox_add.addStretch(0) hbox_add.addWidget(btn_add) vbox_login.addLayout(hbox_add) vbox.addWidget(group_login) # ####################### # vbox.addSpacing(20) hbox_btns = QHBoxLayout() hbox_btns.addStretch() btn_accept = QPushButton(_('Close')) btn_accept.clicked.connect(self.accept) btn_accept.setDefault(True) btn_accept.setFixedHeight(35) btn_accept.setMaximumWidth(80) hbox_btns.addWidget(btn_accept) vbox.addLayout(hbox_btns) # ####################### # #self.tree_view.selectionModel().selectionChanged.connect(self.on_selected) self.load_accounts() self.timer = parent.idle_timeout(1000, self.update_) #self.show() self.exec_() self.deleteLater() def get_selected_row(self): """""" selected_row = self.tree_view.selectionModel().selectedRows()[0].row() return selected_row #def on_selected(self, selected, unselected): #"""""" #todo: heredar treeView y subclass selectionChanged #row = self.get_selected_row() #self.btn_remove.setEnabled(True) #self.btn_check.setEnabled(True) def load_accounts(self): accounts_list = [account for service, accounts in sorted(accounts_manager.accounts_dict.iteritems()) for account in accounts.itervalues()] for account in accounts_list: password = "".join(["*" for _ in account.password]) self.__model.append([account.id_account, account.host, account.status, account.username, password, account.enable]) #TODO: Automatic accounts checking on load. def on_add(self): username = self.entry_user.text() password = self.entry_pass.text() if username and password: accounts_manager.new_account(self.cb.currentText(), username, password) self.__model.clear() self.load_accounts() def on_remove(self): row = self.get_selected_row() accounts_manager.remove_account(self.items[row][HOST], self.items[row][ACCOUNT_ID]) self.__model.remove(row) def on_check(self): row = self.get_selected_row() accounts_manager.manual_checking(self.items[row][HOST], self.items[row][ACCOUNT_ID]) def update_(self): account_list = accounts_manager.update() for account_item in account_list: for row in self.items: if row[ACCOUNT_ID] == account_item.id_account: row[STATUS] = account_item.status def save(self): for row in self.items: if row[ENABLE]: accounts_manager.enable_account(row[HOST], row[ACCOUNT_ID]) else: accounts_manager.disable_account(row[HOST], row[ACCOUNT_ID]) def accept(self, *args, **kwargs): self.timer.stop() self.save() self.hide() QDialog.accept(self, *args, **kwargs) def reject(self, *args, **kwargs): self.timer.stop() self.save() self.hide() QDialog.reject(self, *args, **kwargs)
class AddDownloads(QVBoxLayout): def __init__(self, downloads, parent=None): QVBoxLayout.__init__(self) self.setContentsMargins(0, 0, 0, 0) self.setSpacing(5) self.downloads = downloads self.parent = parent self.tree_view = QTreeView(parent) # self.tree_view.setContextMenuPolicy(Qt.CustomContextMenu) self.tree_view.customContextMenuRequested.connect(self.context_menu) # #listview look self.tree_view.setWordWrap(True) #search textElideMode self.tree_view.setRootIsDecorated(False) self.tree_view.setIndentation(0) self.tree_view.setAlternatingRowColors(True) # self.items = [] self.rows_buffer = {} #{id_item: row_obj, } #self.items = [[True, "1", "11", "t5est", "t3est"], [True, "1", "11", "t5est", "t3est"], [True, "1", "11", "t5est", "t3est"]] bool_cols = [ 1, ] headers = [ "hidden_id_item", "", _("Status"), _("File Name"), _("Host"), _("Size"), _("Status Message") ] # self.__model = SimpleListModel(headers, self.items, bool_cols) self.tree_view.setModel(self.__model) self.addWidget(self.tree_view) # self.tree_view.setColumnHidden(0, True) self.tree_view.setColumnWidth(1, 27) self.tree_view.header().setResizeMode(1, QHeaderView.Fixed) hbox = QHBoxLayout() hbox.setContentsMargins(0, 0, 0, 0) hbox.setSpacing(10) self.cb = QComboBox() #self.cb.setFixedWidth(100) self.cb.setEditable(True) self.cb.setFixedHeight(35) self.cb.setMinimumWidth(1) cb_view = self.cb.view() cb_view.setAlternatingRowColors(True) # self.paths_list = conf.get_save_dl_paths() if not self.paths_list: self.cb.addItem(cons.DLFOLDER_PATH) else: [self.cb.addItem(path) for path in reversed(self.paths_list)] # hbox.addWidget(self.cb) btn_examine = QPushButton('...') btn_examine.clicked.connect(self.on_examine) btn_examine.setFixedHeight(35) btn_examine.setMaximumWidth(80) hbox.addWidget(btn_examine) hbox.addSpacing(40) #index, size btn_download = QPushButton() btn_download.setIcon(media.get_icon(media.DOWN, media.MEDIUM)) btn_download.setIconSize(QSize(24, 24)) btn_download.clicked.connect(self.on_download_selected) btn_download.setFixedHeight(35) btn_download.setMaximumWidth(40) hbox.addWidget(btn_download) btn_add = QPushButton() btn_add.setIcon(media.get_icon(media.ADD, media.MEDIUM)) btn_add.setIconSize(QSize(24, 24)) btn_add.clicked.connect(self.on_add_links) btn_add.setFixedHeight(35) btn_add.setMaximumWidth(40) hbox.addWidget(btn_add) self.menu = QMenu(parent) import_action = self.menu.addAction(_("Import Container"), self.on_import_container) self.menu.addSeparator() recheck_action = self.menu.addAction(_("Re-check"), self.on_recheck) clear_action = self.menu.addAction(_("Clear list"), self.on_clear_list) # btn_menu = QPushButton() btn_menu.setMenu(self.menu) btn_menu.setFixedHeight(35) btn_menu.setMaximumWidth(22) btn_menu.setFlat(True) hbox.addWidget(btn_menu) self.addLayout(hbox) #update list parent.idle_timeout(1000, self.update) def context_menu(self, position): menu = QMenu() #indexes = self.selectedIndexes() #sensitive = True if indexes else False #individual_items = [('Open destination folder', self.on_open_folder),] #[menu.addAction(title, callback).setEnabled(sensitive) for title, callback in individual_items] #menu.addSeparator() generic_items = [(_('Download Selected'), self.on_download_selected), (None, None), (_('Select all'), self.on_select_all), (_('Select none'), self.on_select_none), (_('Select inverse'), self.on_select_inverse), (None, None), (_('Re-check'), self.on_recheck), (_('Clear list'), self.on_clear_list)] [ menu.addAction(title, callback) if title is not None else menu.addSeparator() for title, callback in generic_items ] menu.exec_(self.tree_view.viewport().mapToGlobal(position)) def on_clear_list(self): self.__model.clear() self.rows_buffer.clear() api.clear_pending() def on_recheck(self): api.recheck_items() def on_select_all(self): for row in self.items: row[1] = True def on_select_none(self): for row in self.items: row[1] = False def on_select_inverse(self): for row in self.items: row[1] = False if row[1] else True def on_import_container(self): file_name, filter = QFileDialog.getOpenFileName( filter='OCH Files (*.och)') if file_name: container = Container(file_name) container.extract_links() links_list = container.get_linklist() if links_list: self.links_checking(links_list, copy_link=False) def on_add_links(self): add_links = AddLinks(self.parent) result_code = add_links.result() links_list = add_links.links_list if result_code == QDialog.Accepted and links_list: self.links_checking(links_list) def on_examine(self): folder = QFileDialog.getExistingDirectory() if folder: self.cb.setEditText(folder) def cb_remove(self, text): index = self.cb.findText(text) if index >= 0: self.cb.removeItem(index) def on_download_selected(self): current_path = self.cb.currentText() self.cb_remove(current_path) self.cb.insertItem(0, current_path) if current_path in self.paths_list: self.paths_list.remove(current_path) self.paths_list.append(current_path) if len(self.paths_list) > 5: self.paths_list.pop(0) self.cb.removeItem(5) self.cb.setCurrentIndex(0) conf.set_save_dl_paths(self.paths_list) id_items_list = [] iters = [] for row in self.items: if row[1]: # and row[4] != cons.UNSUPPORTED: #tmp iters.append(row) id_item = row[0] id_items_list.append(id_item) del self.rows_buffer[id_item] [self.__model.remove(self.items.index(iter)) for iter in iters] item_list = api.get_added_items(id_items_list) api.downloader_init(item_list, current_path) #iniciar threads de descarga. #TODO: use a signal. self.downloads.store_items(item_list) def links_checking(self, links_list, copy_link=True): for link in links_list: download_item = api.create_download_item( cons.UNKNOWN, 0, link, copy_link) #return download_item object item = [ download_item.id, True, cons.LINK_CHECKING, cons.UNKNOWN, None, None, None ] #self.items.append(item) self.__model.append(item) self.rows_buffer[item[0]] = item api.start_checking() def update(self): #this method steals cycles, its not a new thread items_list = api.get_checking_update() for download_item in items_list: try: row = self.rows_buffer[download_item.id] row[1] = True if download_item.link_status != cons.LINK_DEAD else False 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 except KeyError as err: logger.debug(err) self.__model.refresh()
class ConfigAccounts(QDialog): def __init__(self, parent): QDialog.__init__(self, parent, Qt.WindowSystemMenuHint | Qt.WindowTitleHint) self.setWindowTitle(_('Host Accounts')) self.resize(500, 450) vbox = QVBoxLayout() self.setLayout(vbox) # ####################### # vbox_accounts = QVBoxLayout() group_accounts = QGroupBox(_('Accounts:')) group_accounts.setLayout(vbox_accounts) self.tree_view = QTreeView() # #listview look self.tree_view.setWordWrap(True) #search textElideMode self.tree_view.setRootIsDecorated(False) self.tree_view.setIndentation(0) self.tree_view.setAlternatingRowColors(True) # self.items = [] headers = [ "hidden_id_account", _("Host"), _("Status"), _("Username"), _("Password"), _("Enable") ] bool_cols = [ ENABLE, ] # self.__model = SimpleListModel(headers, self.items, bool_cols) self.tree_view.setModel(self.__model) vbox_accounts.addWidget(self.tree_view) self.tree_view.setColumnHidden(0, True) hbox_accounts = QHBoxLayout() hbox_accounts.addStretch(0) #stretch/align widget to right. vbox_accounts.addLayout(hbox_accounts) self.btn_remove = QPushButton(_('Remove')) self.btn_remove.clicked.connect(self.on_remove) self.btn_remove.setFixedHeight(35) self.btn_remove.setMaximumWidth(80) self.btn_remove.setEnabled(False) hbox_accounts.addWidget(self.btn_remove) self.btn_check = QPushButton(_('Check')) self.btn_check.clicked.connect(self.on_check) self.btn_check.setFixedHeight(35) self.btn_check.setMaximumWidth(80) self.btn_check.setEnabled(False) hbox_accounts.addWidget(self.btn_check) vbox.addWidget(group_accounts) # ####################### # vbox_login = QVBoxLayout() group_login = QGroupBox(_('Login:'******'Server:')) #label_server.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) label_user = QLabel(_('Username:'******'Password:'******'Add')) btn_add.clicked.connect(self.on_add) btn_add.setFixedHeight(35) btn_add.setMaximumWidth(80) if not self.cb.count(): btn_add.setEnabled(False) hbox_add.addStretch(0) hbox_add.addWidget(btn_add) vbox_login.addLayout(hbox_add) vbox.addWidget(group_login) # ####################### # vbox.addSpacing(20) hbox_btns = QHBoxLayout() hbox_btns.addStretch() btn_accept = QPushButton(_('Close')) btn_accept.clicked.connect(self.accept) btn_accept.setDefault(True) btn_accept.setFixedHeight(35) btn_accept.setMaximumWidth(80) hbox_btns.addWidget(btn_accept) vbox.addLayout(hbox_btns) # ####################### # #self.tree_view.selectionModel().selectionChanged.connect(self.on_selected) self.load_accounts() self.timer = parent.idle_timeout(1000, self.update_) #self.show() self.exec_() self.deleteLater() def get_selected_row(self): """""" selected_row = self.tree_view.selectionModel().selectedRows()[0].row() return selected_row #def on_selected(self, selected, unselected): #"""""" #todo: heredar treeView y subclass selectionChanged #row = self.get_selected_row() #self.btn_remove.setEnabled(True) #self.btn_check.setEnabled(True) def load_accounts(self): accounts_list = [ account for service, accounts in sorted( accounts_manager.accounts_dict.iteritems()) for account in accounts.itervalues() ] for account in accounts_list: password = "".join(["*" for _ in account.password]) self.__model.append([ account.id_account, account.host, account.status, account.username, password, account.enable ]) #TODO: Automatic accounts checking on load. def on_add(self): username = self.entry_user.text() password = self.entry_pass.text() if username and password: accounts_manager.new_account(self.cb.currentText(), username, password) self.__model.clear() self.load_accounts() def on_remove(self): row = self.get_selected_row() accounts_manager.remove_account(self.items[row][HOST], self.items[row][ACCOUNT_ID]) self.__model.remove(row) def on_check(self): row = self.get_selected_row() accounts_manager.manual_checking(self.items[row][HOST], self.items[row][ACCOUNT_ID]) def update_(self): account_list = accounts_manager.update() for account_item in account_list: for row in self.items: if row[ACCOUNT_ID] == account_item.id_account: row[STATUS] = account_item.status def save(self): for row in self.items: if row[ENABLE]: accounts_manager.enable_account(row[HOST], row[ACCOUNT_ID]) else: accounts_manager.disable_account(row[HOST], row[ACCOUNT_ID]) def accept(self, *args, **kwargs): self.timer.stop() self.save() self.hide() QDialog.accept(self, *args, **kwargs) def reject(self, *args, **kwargs): self.timer.stop() self.save() self.hide() QDialog.reject(self, *args, **kwargs)
class AddDownloads(QVBoxLayout): def __init__(self, parent): QVBoxLayout.__init__(self) self.setContentsMargins(0, 0, 0, 0) self.setSpacing(5) self.weak_parent = weakref.ref(parent) self.tree_view = QTreeView() self.tree_view.setContextMenuPolicy(Qt.CustomContextMenu) self.tree_view.customContextMenuRequested.connect(self.context_menu) #listview look self.tree_view.setWordWrap(True) #search textElideMode self.tree_view.setRootIsDecorated(False) self.tree_view.setIndentation(0) self.tree_view.setAlternatingRowColors(True) self.icons_dict = self.get_icons() self.items = [] self.rows_buffer = {} #{id_item: row_obj, } bool_cols = [1, ] image_cols = [2, ] headers = ["hidden_id_item", "", "", _("File Name"), _("Host"), _("Size"), _("Status Message")] self.__model = SimpleListModel(headers, self.items, bool_cols, image_cols) self.tree_view.setModel(self.__model) self.addWidget(self.tree_view) self.tree_view.setColumnHidden(0, True) self.tree_view.setColumnWidth(1, 27) self.tree_view.setColumnWidth(2, 27) self.tree_view.header().setResizeMode(1, QHeaderView.Fixed) hbox = QHBoxLayout() hbox.setContentsMargins(0, 0, 0, 0) hbox.setSpacing(10) self.cb = QComboBox() #self.cb.setFixedWidth(100) self.cb.setEditable(True) self.cb.setFixedHeight(35) self.cb.setMinimumWidth(1) cb_view = self.cb.view() cb_view.setAlternatingRowColors(True) self.paths_list = conf.get_save_dl_paths() if not self.paths_list: self.cb.addItem(cons.DLFOLDER_PATH) else: [self.cb.addItem(path) for path in reversed(self.paths_list)] hbox.addWidget(self.cb) btn_examine = QPushButton('...') btn_examine.clicked.connect(self.on_examine) btn_examine.setFixedHeight(35) btn_examine.setMaximumWidth(80) hbox.addWidget(btn_examine) hbox.addSpacing(40) #index, size btn_download = QPushButton() btn_download.setIcon(media.get_icon(media.DOWN, media.MEDIUM)) btn_download.setIconSize(QSize(24, 24)) btn_download.clicked.connect(self.on_download_selected) btn_download.setFixedHeight(35) btn_download.setMaximumWidth(40) hbox.addWidget(btn_download) btn_add = QPushButton() btn_add.setIcon(media.get_icon(media.ADD, media.MEDIUM)) btn_add.setIconSize(QSize(24, 24)) btn_add.clicked.connect(self.on_add_links) btn_add.setFixedHeight(35) btn_add.setMaximumWidth(40) hbox.addWidget(btn_add) self.menu = QMenu() import_action = self.menu.addAction(_("Import Container"), self.on_import_container) self.menu.addSeparator() recheck_action = self.menu.addAction(_("Re-check"), self.on_recheck) clear_action = self.menu.addAction(_("Clear list"), self.on_clear_list) btn_menu = QPushButton() btn_menu.setMenu(self.menu) btn_menu.setFixedHeight(35) btn_menu.setMaximumWidth(22) btn_menu.setFlat(True) hbox.addWidget(btn_menu) self.addLayout(hbox) #custom signals signals.add_downloads_to_check.connect(self.add_downloads_to_check) #update list self.timer = parent.idle_timeout(1000, self.update_) @property def parent(self): return self.weak_parent() def get_selected_rows(self): """""" selected_rows = [index.row() for index in self.tree_view.selectionModel().selectedRows()] selected_rows.sort() return selected_rows def context_menu(self, position): rows = self.get_selected_rows() is_single_row = True if len(rows) == 1 else False options = [(_('Save as...'), self.on_save_as, is_single_row), None, (_('Download Selected'), self.on_download_selected, True), None, (_('Select all'), self.on_select_all, True), (_('Select none'), self.on_select_none, True), (_('Select inverse'), self.on_select_inverse, True), None, (_('Re-check'), self.on_recheck, True), (_('Clear list'), self.on_clear_list, True)] menu = Menu(options) menu.exec_(self.tree_view.viewport().mapToGlobal(position)) def on_save_as(self): rows = self.get_selected_rows() row = self.items[rows[0]] item_id = row[0] download_item = api.get_checking_download_item(item_id) widget = QLineEdit() # set the line entry file name if we have one. if download_item.save_as: widget.setText(download_item.save_as) elif download_item.name != cons.UNKNOWN: widget.setText(download_item.name) dialog = Dialog(self.parent, "Save as", widget) if dialog.result() == QDialog.Accepted: save_as = widget.text() # change the file name in treeview and DownloadItem if save_as: api.save_download_as(download_item, save_as) api.set_download_name(download_item, save_as) row[3] = save_as def on_clear_list(self): self.__model.clear() self.rows_buffer.clear() api.clear_pending() def on_recheck(self): api.recheck_items() def on_select_all(self): for row in self.items: row[1] = True def on_select_none(self): for row in self.items: row[1] = False def on_select_inverse(self): for row in self.items: row[1] = False if row[1] else True def on_import_container(self): file_name, filter = QFileDialog.getOpenFileName(filter='OCH Files (*.och)') if file_name: container = Container(file_name) container.extract_links() links_list = container.get_linklist() if links_list: self.add_downloads_to_check(links_list, copy_link=False) def on_add_links(self): dialog = AddLinks(self.parent) result_code = dialog.result() links_list = dialog.links_list if result_code == QDialog.Accepted and links_list: self.add_downloads_to_check(links_list) def on_examine(self): folder = QFileDialog.getExistingDirectory() if folder: self.cb.setEditText(folder) def cb_remove(self, text): index = self.cb.findText(text) if index >= 0: self.cb.removeItem(index) def on_download_selected(self): # save the selected path current_path = self.cb.currentText() self.cb_remove(current_path) self.cb.insertItem(0, current_path) if current_path in self.paths_list: self.paths_list.remove(current_path) self.paths_list.append(current_path) if len(self.paths_list) > 5: self.paths_list.pop(0) self.cb.removeItem(5) self.cb.setCurrentIndex(0) conf.set_save_dl_paths(self.paths_list) # move selected items to downloads tab id_items_list = [] iters = [] for row in self.items: if row[1]: iters.append(row) id_item = row[0] id_items_list.append(id_item) del self.rows_buffer[id_item] [self.__model.remove(self.items.index(iter)) for iter in iters] item_list = api.pop_checking_items(id_items_list) for download_item in item_list: download_item.path = current_path signals.add_to_downloader.emit(item_list) def add_downloads_to_check(self, links_list, copy_link=True): for link in links_list: download_item = api.create_download_item(cons.UNKNOWN, link, copy_link=copy_link) api.add_to_checker(download_item) item = [download_item.id, True, self.icons_dict[cons.LINK_CHECKING], cons.UNKNOWN, None, None, None] #self.items.append(item) self.__model.append(item) self.rows_buffer[item[0]] = item api.start_checking() def update_(self): checking_downloads = api.get_checking_downloads() api.update_checking_downloads() for download_item in checking_downloads.itervalues(): row = self.rows_buffer[download_item.id] if download_item.link_status == cons.LINK_DEAD: row[1] = False row[2] = self.icons_dict[download_item.link_status] row[3] = download_item.name if not download_item.host == cons.UNSUPPORTED: row[4] = download_item.host if download_item.size: row[5] = utils.size_format(download_item.size) row[6] = download_item.link_status_msg self.__model.refresh() def get_icons(self): alive = media.get_pixmap(media.ALIVE, media.SMALL) dead = media.get_pixmap(media.DEAD, media.SMALL) error = media.get_pixmap(media.ERROR, media.SMALL) checking = media.get_pixmap(media.CHECKING, media.SMALL) #unavailable = media.get_pixmap(media.ERROR, media.SMALL) return {cons.LINK_ALIVE: alive, cons.LINK_DEAD: dead, cons.LINK_UNAVAILABLE: error, cons.LINK_ERROR: error, cons.LINK_CHECKING: checking}
class Downloads(QTreeView): def __init__(self, parent): #TODO: Create wrapper or subclass list to append and remove from items and rows_buffer. QTreeView.__init__(self) self.weak_parent = weakref.ref(parent) #listview look self.setWordWrap(True) #search textElideMode self.setRootIsDecorated(False) self.setIndentation(0) self.setAlternatingRowColors(True) self.icons_dict = self.get_icons() self.items = [] self.rows_buffer = {} #{id_item: row_obj, } headers = [ "hidden_id_item", "", _("File Name"), _("Host"), _("Size"), _("Complete"), _("Progress"), _("Time"), _("Remain"), _("Speed"), _("Status Message") ] self.__model = SimpleListModel(headers, self.items) self.setModel(self.__model) self.setColumnHidden(0, True) self.setColumnWidth(1, 27) self.header().setResizeMode(1, QHeaderView.Fixed) #self.header().setResizeMode(3, QHeaderView.ResizeToContents) self.im_delegate = ImageDelegate(self) self.setItemDelegateForColumn(1, self.im_delegate) self.im2_delegate = ImageDelegate(self) self.setItemDelegateForColumn(3, self.im2_delegate) self.nf_delegate = NoFocusDelegate(self) self.setItemDelegate(self.nf_delegate) self.pb_delegate = ProgressBarDelegate(self) self.setItemDelegateForColumn(6, self.pb_delegate) #see http://stackoverflow.com/questions/1987546/qt4-stylesheets-and-focus-rect for removing the drawed border on focus self.setSelectionMode(QAbstractItemView.ExtendedSelection) #self.setDragEnabled(True) #self.setAcceptDrops(True) #self.setDropIndicatorShown(False) self.setDragDropMode(QAbstractItemView.InternalMove) #self.setDefaultDropAction(Qt.MoveAction) #drop indicator self.line = QWidget(self.viewport()) self.line.setAutoFillBackground(True) pal = self.line.palette() pal.setColor(self.line.backgroundRole(), Qt.black) self.line.setPalette(pal) self.line.setGeometry(0, 0, 0, 0) self.line.hide() #custom signals signals.add_to_downloader.connect(self.add_to_downloader) signals.on_stop_all.connect(self.on_stop_all) #update list self.timer = parent.idle_timeout(1000, self.update_) @property def parent(self): return self.weak_parent() def remove_row(self, id_item): item = self.rows_buffer.pop(id_item) self.__model.remove(self.items.index(item)) def get_selected_rows(self): """""" selected_rows = [ index.row() for index in self.selectionModel().selectedRows() ] selected_rows.sort() return selected_rows def dragEnterEvent(self, event): #FIXME: weird things may happen if draggin when an item is removed or added. #event.setDropAction(Qt.MoveAction) event.accept() def dragMoveEvent(self, event): #paint drop indicator at the current position. q_index = self.indexAt(event.pos()) if q_index.isValid(): rect = self.visualRect(q_index) self.line.setGeometry(0, rect.top(), self.viewport().width(), 1) else: q_index = self.__model.index(self.__model.rowCount() - 1) rect = self.visualRect(q_index) self.line.setGeometry(0, rect.bottom(), self.viewport().width(), 1) self.line.show() def dragLeaveEvent(self, event): #out of the drop area. self.line.hide() def dropEvent(self, event): self.line.hide() q_index = self.indexAt(event.pos()) if q_index.isValid(): index = q_index.row() else: index = -1 items = [self.items[row] for row in self.get_selected_rows()] dest_item = self.items[index] [ self.__model.remove(self.items.index(item)) for item in items if item != dest_item ] if index >= 0: index = self.items.index(dest_item) [ self.__model.insert(index, item) for item in reversed(items) if item != dest_item ] else: [self.__model.append(item) for item in items if item != dest_item] api.reorder_queue([row[0] for row in self.items]) #def keyboard_event(self, widget, event): #if gtk.gdk.keyval_name(event.keyval) == "Delete": #supr. key #self.on_delete() def contextMenuEvent(self, event): rows = self.get_selected_rows() is_selection = True if rows else False options = [ (_('Open destination folder'), self.on_open_folder, is_selection), (_('Copy link'), self.on_copy_link, is_selection), #(_('Add password'), self.on_password, is_selection), None, (_('Delete'), self.on_delete, is_selection), None, (_('Start all'), self.on_start_all, True), (_('Stop all'), self.on_stop_all, True), (_('Clear Completed'), self.on_clear_completed, True) ] menu = Menu(options) menu.exec_(event.globalPos()) def selectionChanged(self, selected, deselected): #overridden slot QTreeView.selectionChanged(self, selected, deselected) BTN = 0 rows = self.get_selected_rows() if len(rows) == 1: # single selection. row_index = rows[0] id_item = self.items[row_index][0] stopped_downloads = api.get_stopped_downloads() if id_item in stopped_downloads: self.parent.stop[BTN].setEnabled(False) self.parent.start[BTN].setEnabled(True) else: self.parent.stop[BTN].setEnabled(True) self.parent.start[BTN].setEnabled(False) elif rows: # multi selection self.parent.stop[BTN].setEnabled(True) self.parent.start[BTN].setEnabled(True) else: # no selection self.parent.stop[BTN].setEnabled(False) self.parent.start[BTN].setEnabled(False) def on_open_folder(self): rows = self.get_selected_rows() if rows: items_list = api.get_download_items( [self.items[row_index][0] for row_index in rows]) paths_list = {download_item.path for download_item in items_list} for folder_path in paths_list: utils.open_folder_window(folder_path) def on_copy_link(self): rows = self.get_selected_rows() if rows: items_list = api.get_download_items( [self.items[row_index][0] for row_index in rows]) links_list = [ download_item.link for download_item in items_list if download_item.can_copy_link ] clipboard = QApplication.clipboard() clipboard.setText('\n'.join(links_list)) def on_password(self): rows = self.get_selected_rows() if rows: pass #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.add_password.emit(pwd) def on_delete(self): rows = self.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) m = True if m: id_items_list = [] for row_index in rows: id_item = self.items[row_index][0] id_items_list.append(id_item) [self.remove_row(id_item) for id_item in id_items_list] api.delete_download(id_items_list) def on_clear_completed(self): finished_icon = self.icons_dict[cons.STATUS_FINISHED] for row in self.items[:]: if row[1] == finished_icon: self.remove_row(row[0]) api.clear_complete() def on_start_all(self): """ BUG: El boton start y stop no cambia. """ id_item_list = [row[0] for row in self.items] api.start_all(id_item_list) stopped_icon = self.icons_dict[cons.STATUS_STOPPED] queue_icon = self.icons_dict[cons.STATUS_QUEUE] for row in self.items: if row[1] == stopped_icon: row[1] = queue_icon def on_stop_all(self): api.stop_all() stopped_icon = self.icons_dict[cons.STATUS_STOPPED] queue_icon = self.icons_dict[cons.STATUS_QUEUE] for row in self.items: if row[1] == queue_icon: row[1] = stopped_icon def add_to_downloader(self, download_items): for download_item in download_items: api.add_to_downloader(download_item) self.store_item(download_item) if conf.get_auto_switch_tab(): signals.switch_tab.emit(0) api.next_download() def store_item(self, download_item): size_file = utils.size_format( download_item.size) if download_item.size else None size_complete = utils.size_format( download_item.size_complete ) if download_item.size_complete else None time = utils.time_format( download_item.time) if download_item.time else None host_icon = self.get_host_icon(download_item.host) item = [ 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 ] self.__model.append(item) self.rows_buffer[item[0]] = item def update_(self): active_downloads = api.get_active_downloads() api.update_active_downloads() for download_item in active_downloads.itervalues(): row = self.rows_buffer[download_item.id] #row[0] = download_item.id row[1] = self.icons_dict[download_item.status] row[2] = download_item.name #row[3][0] = download_item.host row[3][1] = self.icons_dict[ cons.DL_RESUME] if download_item.can_resume else None row[3][2] = self.icons_dict[ cons.DL_PREMIUM] if download_item.is_premium else None row[4] = utils.size_format( download_item.size) if download_item.size else None row[5] = utils.size_format( download_item.size_complete ) if download_item.size_complete else None row[6] = download_item.progress row[7] = utils.time_format( download_item.time) if download_item.time else None row[8] = utils.time_format(download_item.time_remain ) if download_item.time_remain else None row[9] = utils.speed_format( download_item.speed) if download_item.speed else None row[10] = self.get_status_msg(download_item) self.__model.refresh() def get_status_msg(self, download_item): if download_item.fail_count: return "{0} ({1} #{2})".format(download_item.status_msg, _("Retry"), download_item.fail_count) else: return download_item.status_msg def get_host_icon(self, host): try: return self.icons_dict[host] except KeyError: self.icons_dict[host] = QPixmap( os.path.join(cons.PLUGINS_PATH, host.replace('.', '_'), "favicon.ico")) return self.icons_dict[host] def get_icons(self): running = media.get_pixmap(media.START, media.SMALL) stopped = media.get_pixmap(media.STOP, media.SMALL) queue = media.get_pixmap(media.QUEUE, media.SMALL) finished = media.get_pixmap(media.CHECK, media.SMALL) error = media.get_pixmap(media.X_MARK, media.SMALL) resume = media.get_pixmap(media.REFRESH, media.SMALL) premium = media.get_pixmap(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 }
class AddDownloads(QVBoxLayout): def __init__(self, downloads, parent=None): QVBoxLayout.__init__(self) self.setContentsMargins(0, 0, 0, 0) self.setSpacing(5) self.downloads = downloads self.parent = parent self.tree_view = QTreeView(parent) # self.tree_view.setContextMenuPolicy(Qt.CustomContextMenu) self.tree_view.customContextMenuRequested.connect(self.context_menu) # #listview look self.tree_view.setWordWrap(True) #search textElideMode self.tree_view.setRootIsDecorated(False) self.tree_view.setIndentation(0) self.tree_view.setAlternatingRowColors(True) # self.items = [] self.rows_buffer = {} #{id_item: row_obj, } #self.items = [[True, "1", "11", "t5est", "t3est"], [True, "1", "11", "t5est", "t3est"], [True, "1", "11", "t5est", "t3est"]] bool_cols = [1, ] headers = ["hidden_id_item", "", _("Status"), _("File Name"), _("Host"), _("Size"), _("Status Message")] # self.__model = SimpleListModel(headers, self.items, bool_cols) self.tree_view.setModel(self.__model) self.addWidget(self.tree_view) # self.tree_view.setColumnHidden(0, True) self.tree_view.setColumnWidth(1, 27) self.tree_view.header().setResizeMode(1, QHeaderView.Fixed) hbox = QHBoxLayout() hbox.setContentsMargins(0, 0, 0, 0) hbox.setSpacing(10) self.cb = QComboBox() #self.cb.setFixedWidth(100) self.cb.setEditable(True) self.cb.setFixedHeight(35) self.cb.setMinimumWidth(1) cb_view = self.cb.view() cb_view.setAlternatingRowColors(True) # self.paths_list = conf.get_save_dl_paths() if not self.paths_list: self.cb.addItem(cons.DLFOLDER_PATH) else: [self.cb.addItem(path) for path in reversed(self.paths_list)] # hbox.addWidget(self.cb) btn_examine = QPushButton('...') btn_examine.clicked.connect(self.on_examine) btn_examine.setFixedHeight(35) btn_examine.setMaximumWidth(80) hbox.addWidget(btn_examine) hbox.addSpacing(40) #index, size btn_download = QPushButton() btn_download.setIcon(media.get_icon(media.DOWN, media.MEDIUM)) btn_download.setIconSize(QSize(24, 24)) btn_download.clicked.connect(self.on_download_selected) btn_download.setFixedHeight(35) btn_download.setMaximumWidth(40) hbox.addWidget(btn_download) btn_add = QPushButton() btn_add.setIcon(media.get_icon(media.ADD, media.MEDIUM)) btn_add.setIconSize(QSize(24, 24)) btn_add.clicked.connect(self.on_add_links) btn_add.setFixedHeight(35) btn_add.setMaximumWidth(40) hbox.addWidget(btn_add) self.menu = QMenu(parent) import_action = self.menu.addAction(_("Import Container"), self.on_import_container) self.menu.addSeparator() recheck_action = self.menu.addAction(_("Re-check"), self.on_recheck) clear_action = self.menu.addAction(_("Clear list"), self.on_clear_list) # btn_menu = QPushButton() btn_menu.setMenu(self.menu) btn_menu.setFixedHeight(35) btn_menu.setMaximumWidth(22) btn_menu.setFlat(True) hbox.addWidget(btn_menu) self.addLayout(hbox) #update list parent.idle_timeout(1000, self.update) def context_menu(self, position): menu = QMenu() #indexes = self.selectedIndexes() #sensitive = True if indexes else False #individual_items = [('Open destination folder', self.on_open_folder),] #[menu.addAction(title, callback).setEnabled(sensitive) for title, callback in individual_items] #menu.addSeparator() generic_items = [(_('Download Selected'), self.on_download_selected), (None, None), (_('Select all'), self.on_select_all), (_('Select none'), self.on_select_none), (_('Select inverse'), self.on_select_inverse), (None, None), (_('Re-check'), self.on_recheck), (_('Clear list'), self.on_clear_list)] [menu.addAction(title, callback) if title is not None else menu.addSeparator() for title, callback in generic_items] menu.exec_(self.tree_view.viewport().mapToGlobal(position)) def on_clear_list(self): self.__model.clear() self.rows_buffer.clear() api.clear_pending() def on_recheck(self): api.recheck_items() def on_select_all(self): for row in self.items: row[1] = True def on_select_none(self): for row in self.items: row[1] = False def on_select_inverse(self): for row in self.items: row[1] = False if row[1] else True def on_import_container(self): file_name, filter = QFileDialog.getOpenFileName(filter='OCH Files (*.och)') if file_name: container = Container(file_name) container.extract_links() links_list = container.get_linklist() if links_list: self.links_checking(links_list, copy_link=False) def on_add_links(self): add_links = AddLinks(self.parent) result_code = add_links.result() links_list = add_links.links_list if result_code == QDialog.Accepted and links_list: self.links_checking(links_list) def on_examine(self): folder = QFileDialog.getExistingDirectory() if folder: self.cb.setEditText(folder) def cb_remove(self, text): index = self.cb.findText(text) if index >= 0: self.cb.removeItem(index) def on_download_selected(self): current_path = self.cb.currentText() self.cb_remove(current_path) self.cb.insertItem(0, current_path) if current_path in self.paths_list: self.paths_list.remove(current_path) self.paths_list.append(current_path) if len(self.paths_list) > 5: self.paths_list.pop(0) self.cb.removeItem(5) self.cb.setCurrentIndex(0) conf.set_save_dl_paths(self.paths_list) id_items_list = [] iters = [] for row in self.items: if row[1]: # and row[4] != cons.UNSUPPORTED: #tmp iters.append(row) id_item = row[0] id_items_list.append(id_item) del self.rows_buffer[id_item] [self.__model.remove(self.items.index(iter)) for iter in iters] item_list = api.get_added_items(id_items_list) api.downloader_init(item_list, current_path) #iniciar threads de descarga. #TODO: use a signal. self.downloads.store_items(item_list) def links_checking(self, links_list, copy_link=True): for link in links_list: download_item = api.create_download_item(cons.UNKNOWN, 0, link, copy_link) #return download_item object item = [download_item.id, True, cons.LINK_CHECKING, cons.UNKNOWN, None, None, None] #self.items.append(item) self.__model.append(item) self.rows_buffer[item[0]] = item api.start_checking() def update(self): #this method steals cycles, its not a new thread items_list = api.get_checking_update() for download_item in items_list: try: row = self.rows_buffer[download_item.id] row[1] = True if download_item.link_status != cons.LINK_DEAD else False 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 except KeyError as err: logger.debug(err) self.__model.refresh()
class Downloads(QTreeView): def __init__(self, parent=None): #TODO: Create wrapper or subclass list to append and remove from items and rows_buffer. QTreeView.__init__(self, parent) #listview look self.setWordWrap(True) #search textElideMode self.setRootIsDecorated(False) self.setIndentation(0) self.setAlternatingRowColors(True) self.icons_dict = self.get_icons() self.items = [] self.rows_buffer = {} #{id_item: row_obj, } #self.ico = QPixmap('stop.png') #self.items = [[[], "1", "11", "t5est", "t3est"], [[self.ico, self.ico], "22", "22", "t5ests", "as3est"], #[[self.ico, self.ico], "33", "24", "t5edd", "t3edd"], [[self.ico, self.ico], "44", "24", "t5edd", "t3edd"]] #for item in range(10000): #self.items.append([[QIcon('stop.png'), QIcon('stop.png')], "22", "24", "t5edd", "t3edd"]) headers = [ "hidden_id_item", "", _("File Name"), _("Host"), _("Size"), _("Complete"), _("Progress"), _("Time"), _("Remain"), _("Speed"), _("Status Message") ] self.__model = SimpleListModel(headers, self.items) self.setModel(self.__model) self.setColumnHidden(0, True) self.setColumnWidth(1, 27) header_view = self.header() header_view.setResizeMode(1, QHeaderView.Fixed) header_view.setResizeMode(3, QHeaderView.ResizeToContents) self.im_delegate = ImageDelegate(self) self.setItemDelegateForColumn(1, self.im_delegate) self.im2_delegate = ImageDelegate(self) self.setItemDelegateForColumn(3, self.im2_delegate) self.nf_delegate = NoFocusDelegate(self) self.setItemDelegate(self.nf_delegate) self.pb_delegate = ProgressBarDelegate(self) self.setItemDelegateForColumn(6, self.pb_delegate) #see http://stackoverflow.com/questions/1987546/qt4-stylesheets-and-focus-rect for removing the drawed border on focus self.setSelectionMode(QAbstractItemView.ExtendedSelection) #self.setDragEnabled(True) #self.setAcceptDrops(True) #self.setDropIndicatorShown(False) self.setDragDropMode(QAbstractItemView.InternalMove) #self.setDefaultDropAction(Qt.MoveAction) #drop indicator self.line = QWidget(self.viewport()) self.line.setAutoFillBackground(True) pal = self.line.palette() pal.setColor(self.line.backgroundRole(), Qt.black) self.line.setPalette(pal) self.line.setGeometry(0, 0, 0, 0) self.line.hide() #update list parent.idle_timeout(1000, self.update) def remove_row(self, id_item): item = self.rows_buffer.pop(id_item) self.__model.remove(self.items.index(item)) def get_selected_rows(self): """""" selected_rows = [ index.row() for index in self.selectionModel().selectedRows() ] selected_rows.sort() return selected_rows def dragEnterEvent(self, event): #FIXME: weird things may happen if draggin when an item is removed or added. #event.setDropAction(Qt.MoveAction) event.accept() def dragMoveEvent(self, event): #paint drop indicator at the current position. q_index = self.indexAt(event.pos()) if q_index.isValid(): rect = self.visualRect(q_index) self.line.setGeometry(0, rect.top(), self.viewport().width(), 1) else: q_index = self.__model.index(self.__model.rowCount() - 1) rect = self.visualRect(q_index) self.line.setGeometry(0, rect.bottom(), self.viewport().width(), 1) self.line.show() def dragLeaveEvent(self, event): #out of the drop area. self.line.hide() def dropEvent(self, event): self.line.hide() q_index = self.indexAt(event.pos()) if q_index.isValid(): index = q_index.row() else: index = -1 items = [self.items[row] for row in self.get_selected_rows()] dest_item = self.items[index] [ self.__model.remove(self.items.index(item)) for item in items if item != dest_item ] if index >= 0: index = self.items.index(dest_item) [ self.__model.insert(index, item) for item in reversed(items) if item != dest_item ] else: [self.__model.append(item) for item in items if item != dest_item] api.reorder_queue([row[0] for row in self.items]) #def keyboard_event(self, widget, event): #if gtk.gdk.keyval_name(event.keyval) == "Delete": #supr. key #self.on_delete() def contextMenuEvent(self, event): menu = QMenu() indexes = self.selectedIndexes() sensitive = True if indexes else False individual_items = [(_('Open destination folder'), self.on_open_folder), (_('Copy link'), self.on_copy_link), (_('Add password'), self.on_password), (_('Delete'), self.on_delete)] [ menu.addAction(title, callback).setEnabled(sensitive) for title, callback in individual_items ] menu.addSeparator() generic_items = [(_('Clear Completed'), self.on_clear_completed), (_('Start all'), self.on_start_all), (_('Stop all'), self.on_stop_all)] [menu.addAction(title, callback) for title, callback in generic_items] menu.exec_(event.globalPos()) def on_open_folder(self): rows = self.get_selected_rows() if rows: items_list = api.get_download_items( [self.items[row_index][0] for row_index in rows]) paths_list = set( [download_item.path for download_item in items_list]) for folder_path in paths_list: #misc.open_folder_window(folder_path) threading.Thread(group=None, target=misc.open_folder_window, name=None, args=(folder_path, )).start() def on_copy_link(self): rows = self.get_selected_rows() if rows: items_list = api.get_download_items( [self.items[row_index][0] for row_index in rows]) links_list = [ download_item.link for download_item in items_list if download_item.can_copy_link ] clipboard = QApplication.clipboard() clipboard.setText('\n'.join(links_list)) def on_password(self): rows = self.get_selected_rows() if rows: pass #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): rows = self.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) m = True if m: id_items_list = [] for row_index in rows: id_item = self.items[row_index][0] id_items_list.append(id_item) [self.remove_row(id_item) for id_item in id_items_list] api.delete_download(id_items_list) def on_clear_completed(self): finished_icon = self.icons_dict[cons.STATUS_FINISHED] for row in self.items[:]: if row[1] == finished_icon: self.remove_row(row[0]) #todo: remove from complete_downloads def on_start_all(self): """ BUG: El boton start y stop no cambia. """ id_item_list = [row[0] for row in self.items] api.start_all(id_item_list) stopped_icon = self.icons_dict[cons.STATUS_STOPPED] queue_icon = self.icons_dict[cons.STATUS_QUEUE] for row in self.items: if row[1] == stopped_icon: row[1] = queue_icon def on_stop_all(self): api.stop_all() stopped_icon = self.icons_dict[cons.STATUS_STOPPED] queue_icon = self.icons_dict[cons.STATUS_QUEUE] for row in self.items: if row[1] == queue_icon: row[1] = stopped_icon def store_items(self, item_list): for download_item in item_list: 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) item = [ 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 ] self.__model.append(item) self.rows_buffer[item[0]] = item def update(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][0] = download_item.host #download_item.host #col 3 row[3][1] = self.icons_dict[ cons. DL_RESUME] if download_item.can_resume else None #download_item.host #col 3 row[3][2] = self.icons_dict[ cons. DL_PREMIUM] if download_item.is_premium else None #download_item.host #col 3 row[4] = misc.size_format( download_item.size) if download_item.size else None row[5] = misc.size_format( download_item.size_complete ) if download_item.size_complete else None row[6] = download_item.progress row[7] = misc.time_format( download_item.time) if download_item.time else None row[8] = misc.time_format( download_item.time_remain ) if download_item.time_remain else None row[9] = misc.speed_format( download_item.speed) if download_item.speed else None row[10] = self.get_status_msg(download_item) except KeyError as err: logger.debug(err) #uncomment if model doesnt get upated. self.__model.refresh() def get_status_msg(self, download_item): if download_item.fail_count: return "{0} ({1} #{2})".format(download_item.status_msg, _("Retry"), download_item.fail_count) else: return download_item.status_msg def get_host_icon(self, host): try: return self.icons_dict[host] except KeyError: self.icons_dict[host] = QPixmap( os.path.join(cons.PLUGINS_PATH, host, "favicon.ico")) return self.icons_dict[host] def get_icons(self): running = media.get_pixmap(media.START, media.SMALL) stopped = media.get_pixmap(media.STOP, media.SMALL) queue = media.get_pixmap(media.QUEUE, media.SMALL) finished = media.get_pixmap(media.CHECK, media.SMALL) error = media.get_pixmap(media.X_MARK, media.SMALL) resume = media.get_pixmap(media.REFRESH, media.SMALL) premium = media.get_pixmap(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 }