def __init__( self, parent: QWidget, *, mw: AnkiQt, note_ids: Sequence[NoteId], field: str | None = None, ) -> None: """ If 'field' is passed, only this is added to the field selector. Otherwise, the fields belonging to the 'note_ids' are added. """ super().__init__(parent) self.mw = mw self.note_ids = note_ids self.field_names: list[str] = [] self._field = field if field: self._show([field]) elif note_ids: # fetch field names and then show QueryOp( parent=mw, op=lambda col: col.field_names_for_note_ids(note_ids), success=self._show, ).run_in_background() else: self._show([])
def __init__( self, mw: AnkiQt, deck_id: DeckId = DeckId(0), search: str | None = None, search_2: str | None = None, ) -> None: """If 'deck_id' is non-zero, load and modify its settings. Otherwise, build a new deck and derive settings from the current deck. If search or search_2 are provided, they will be used as the default search text. """ QDialog.__init__(self, mw) self.mw = mw self.col = self.mw.col self._desired_search_1 = search self._desired_search_2 = search_2 self._initial_dialog_setup() # set on successful query self.deck: FilteredDeckForUpdate QueryOp( parent=self.mw, op=lambda col: col.sched.get_or_create_filtered_deck(deck_id= deck_id), success=self.load_deck_and_show, ).failure(self.on_fetch_error).run_in_background()
def _on_trash_files(self, fnames: Sequence[str]) -> None: if not askUser(tr.media_check_delete_unused_confirm()): return total = len(fnames) def trash(col: Collection) -> None: last_progress = 0.0 remaining = total for chunk in chunked_list(fnames, 25): col.media.trash_files(chunk) remaining -= len(chunk) if time.time() - last_progress >= 0.1: self.mw.taskman.run_on_main( lambda: self.mw.progress.update( label=tr.media_check_files_remaining(count= remaining), value=total - remaining, max=total, )) last_progress = time.time() QueryOp( parent=aqt.mw, op=trash, success=lambda _: tooltip( tr.media_check_delete_unused_complete(count=total)), ).with_progress().run_in_background()
def rename_deck(self, item: SidebarItem, new_name: str) -> None: if not new_name: return # update UI immediately, to avoid redraw item.name = new_name full_name = item.name_prefix + new_name deck_id = DeckId(item.id) def after_fetch(deck: Deck) -> None: if full_name == deck.name: return rename_deck( parent=self, deck_id=deck_id, new_name=full_name, ).run_in_background() QueryOp( parent=self.browser, op=lambda col: col.get_deck(deck_id), success=after_fetch, ).run_in_background()
def _rename(self, did: DeckId) -> None: def prompt(name: str) -> None: new_name = getOnlyText(tr.decks_new_deck_name(), default=name) if not new_name or new_name == name: return else: rename_deck(parent=self.mw, deck_id=did, new_name=new_name).run_in_background() QueryOp(parent=self.mw, op=lambda col: col.decks.name(did), success=prompt).run_in_background()
def __init__(self, mw: aqt.main.AnkiQt) -> None: QDialog.__init__(self, mw, Qt.WindowType.Window) self.mw = mw # set on success self.deck: DeckDict QueryOp( parent=self.mw, op=lambda col: col.decks.current(), success=self._setup_and_show, ).run_in_background()
def full_apkg_import(mw: AnkiQt, file: str) -> None: def on_done(success: bool) -> None: mw.loadCollection() if success: tooltip(tr.importing_importing_complete()) def after_backup(created: bool) -> None: mw.unloadCollection(lambda: replace_with_apkg(mw, file, on_done)) QueryOp(parent=mw, op=lambda _: mw.create_backup_now(), success=after_backup).with_progress().run_in_background()
def __init__(self, parent: QWidget, *, mw: AnkiQt, note_ids: Sequence[NoteId]) -> None: super().__init__(parent) self.mw = mw self.note_ids = note_ids self.field_names: List[str] = [] # fetch field names and then show QueryOp( parent=mw, op=lambda col: col.field_names_for_note_ids(note_ids), success=self._show, ).run_in_background()
def fetch_data_and_show(mw: aqt.AnkiQt) -> None: def fetch_data( col: Collection, ) -> Tuple[DeckId, CustomStudyDefaults]: deck_id = mw.col.decks.get_current_id() defaults = col.sched.custom_study_defaults(deck_id) return (deck_id, defaults) def show_dialog(data: Tuple[DeckId, CustomStudyDefaults]) -> None: deck_id, defaults = data CustomStudy(mw=mw, deck_id=deck_id, defaults=defaults) QueryOp(parent=mw, op=fetch_data, success=show_dialog).with_progress().run_in_background()
def _check_and_update_duplicate_display_async(self) -> None: note = self.note def on_done(result: DuplicateOrEmptyResult.V) -> None: if self.note != note: return self._update_duplicate_display(result) QueryOp( parent=self.parentWindow, op=lambda _: self.note.duplicate_or_empty(), success=on_done, ).run_in_background()
def _check_and_update_duplicate_display_async(self) -> None: note = self.note if not note: return def on_done(result: NoteFieldsCheckResult.V) -> None: if self.note != note: return self._update_duplicate_display(result) QueryOp( parent=self.parentWindow, op=lambda _: note.fields_check(), success=on_done, ).run_in_background()
def refresh_list(self, *ignored_args: Any) -> None: QueryOp( parent=self, op=lambda col: col.models.all_use_counts(), success=self.updateModelsList, ).run_in_background()
class SidebarTreeView(QTreeView): def __init__(self, browser: aqt.browser.Browser) -> None: super().__init__() self.browser = browser self.mw = browser.mw self.col = self.mw.col self.current_search: str | None = None self.valid_drop_types: tuple[SidebarItemType, ...] = () self._refresh_needed = False self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect( self.onContextMenu) # type: ignore self.setUniformRowHeights(True) self.setHeaderHidden(True) self.setIndentation(15) self.setAutoExpandDelay(600) self.setDragDropOverwriteMode(False) self.setEditTriggers(QAbstractItemView.EditKeyPressed) qconnect(self.expanded, self._on_expansion) qconnect(self.collapsed, self._on_collapse) # match window background color and tweak style bgcolor = QPalette().window().color().name() border = theme_manager.color(colors.MEDIUM_BORDER) styles = [ "padding: 3px", "padding-right: 0px", "border: 0", f"background: {bgcolor}", ] if _want_right_border(): styles.append(f"border-right: 1px solid {border}") self.setStyleSheet("QTreeView { %s }" % ";".join(styles)) # these do not really belong here, they should be in a higher-level class self.toolbar = SidebarToolbar(self) self.searchBar = SidebarSearchBar(self) gui_hooks.flag_label_did_change.append(self.refresh) def cleanup(self) -> None: gui_hooks.flag_label_did_change.remove(self.refresh) @property def tool(self) -> SidebarTool: return self._tool @tool.setter def tool(self, tool: SidebarTool) -> None: self._tool = tool if tool == SidebarTool.SEARCH: selection_mode = QAbstractItemView.SingleSelection drag_drop_mode = QAbstractItemView.NoDragDrop double_click_expands = False else: selection_mode = QAbstractItemView.ExtendedSelection drag_drop_mode = QAbstractItemView.InternalMove double_click_expands = True self.setSelectionMode(selection_mode) self.setDragDropMode(drag_drop_mode) self.setExpandsOnDoubleClick(double_click_expands) def model(self) -> SidebarModel: return cast(SidebarModel, super().model()) # Refreshing ########################### def op_executed(self, changes: OpChanges, handler: object | None, focused: bool) -> None: if changes.browser_sidebar and not handler is self: self._refresh_needed = True if focused: self.refresh_if_needed() def refresh_if_needed(self) -> None: if self._refresh_needed: self.refresh() self._refresh_needed = False def refresh(self, new_current: SidebarItem = None) -> None: "Refresh list. No-op if sidebar is not visible." if not self.isVisible(): return if not new_current and self.model() and (idx := self.currentIndex()): new_current = self.model().item_for_index(idx) def on_done(root: SidebarItem) -> None: # user may have closed browser if sip.isdeleted(self): return # block repainting during refreshing to avoid flickering self.setUpdatesEnabled(False) model = SidebarModel(self, root) self.setModel(model) if self.current_search: self.search_for(self.current_search) else: self._expand_where_necessary(model) if new_current: self.restore_current(new_current) self.setUpdatesEnabled(True) # needs to be set after changing model qconnect(self.selectionModel().selectionChanged, self._on_selection_changed) QueryOp(parent=self.browser, op=lambda _: self._root_tree(), success=on_done).run_in_background()
self.setModel(model) if self.current_search: self.search_for(self.current_search) else: self._expand_where_necessary(model) if current_item: self.restore_current(current_item) self.setUpdatesEnabled(True) # needs to be set after changing model qconnect(self.selectionModel().selectionChanged, self._on_selection_changed) QueryOp( parent=self.browser, op=lambda _: self._root_tree(), success=on_done ).run_in_background() def restore_current(self, current: SidebarItem) -> None: if current := self.find_item(current.has_same_id): index = self.model().index_for_item(current) self.selectionModel().setCurrentIndex( index, QItemSelectionModel.SelectCurrent ) self.scrollTo(index) def find_item( self, is_target: Callable[[SidebarItem], bool], parent: Optional[SidebarItem] = None, ) -> Optional[SidebarItem]: