diff --git a/pylib/anki/decks.py b/pylib/anki/decks.py index c2998896f..605404d5b 100644 --- a/pylib/anki/decks.py +++ b/pylib/anki/decks.py @@ -470,6 +470,9 @@ class DeckManager: dids.append(id) return self.col.db.list("select id from cards where did in " + ids2str(dids)) + def for_card_ids(self, cids: List[int]) -> List[int]: + return self.col.db.list(f"select did from cards where id in {ids2str(cids)}") + def _recoverOrphans(self) -> None: dids = list(self.decks.keys()) mod = self.col.db.mod diff --git a/pylib/anki/exporting.py b/pylib/anki/exporting.py index 84196f5c5..c6ba97f20 100644 --- a/pylib/anki/exporting.py +++ b/pylib/anki/exporting.py @@ -8,7 +8,7 @@ import shutil import unicodedata import zipfile from io import BufferedWriter -from typing import Any, Dict, List, Tuple, Union +from typing import Any, Dict, List, Optional, Tuple, Union from zipfile import ZipFile from anki import hooks @@ -21,9 +21,17 @@ from anki.utils import ids2str, namedtmp, splitFields, stripHTML class Exporter: includeHTML: Union[bool, None] = None - def __init__(self, col: _Collection, did: None = None) -> None: + did: Optional[int] + + def __init__( + self, + col: _Collection, + did: Optional[int] = None, + cids: Optional[List[int]] = None, + ) -> None: self.col = col self.did = did + self.cids = cids def doExport(self, path) -> None: raise Exception("not implemented") @@ -65,7 +73,9 @@ class Exporter: return s def cardIds(self) -> Any: - if not self.did: + if self.cids is not None: + cids = self.cids + elif not self.did: cids = self.col.db.list("select id from cards") else: cids = self.col.decks.cids(self.did, children=True) @@ -160,10 +170,12 @@ class AnkiExporter(Exporter): Exporter.__init__(self, col) def deckIds(self) -> List[int]: - if not self.did: - return [] - else: + if self.cids: + return self.col.decks.for_card_ids(self.cids) + elif self.did: return [self.did] + [x[1] for x in self.src.decks.children(self.did)] + else: + return [] def exportInto(self, path: str) -> None: # sched info+v2 scheduler not compatible w/ older clients diff --git a/qt/aqt/browser.py b/qt/aqt/browser.py index 36026759c..04e4ee3f4 100644 --- a/qt/aqt/browser.py +++ b/qt/aqt/browser.py @@ -23,6 +23,7 @@ from anki.notes import Note from anki.utils import fmtTimeSpan, htmlToTextLine, ids2str, intTime, isMac, isWin from aqt import AnkiQt, gui_hooks from aqt.editor import Editor +from aqt.exporting import ExportDialog from aqt.qt import * from aqt.sound import av_player, play_clicked_audio from aqt.theme import theme_manager @@ -614,6 +615,7 @@ class Browser(QMainWindow): f.actionOrange_Flag.triggered.connect(lambda: self.onSetFlag(2)) f.actionGreen_Flag.triggered.connect(lambda: self.onSetFlag(3)) f.actionBlue_Flag.triggered.connect(lambda: self.onSetFlag(4)) + f.actionExport.triggered.connect(lambda: self._exporting()) # jumps f.actionPreviousCard.triggered.connect(self.onPreviousCard) f.actionNextCard.triggered.connect(self.onNextCard) @@ -1898,6 +1900,14 @@ update cards set usn=?, mod=?, did=? where id in """ self.model.reset() self.mw.requireReset() + # Exporting + ###################################################################### + + def _exporting(self): + cids = self.selectedNotesAsCards() + if cids: + ExportDialog(self.mw, cids=cids) + # Flags & Marking ###################################################################### diff --git a/qt/aqt/exporting.py b/qt/aqt/exporting.py index 964685927..731544b2d 100644 --- a/qt/aqt/exporting.py +++ b/qt/aqt/exporting.py @@ -4,6 +4,7 @@ import os import re import time +from typing import List, Optional import aqt from anki import hooks @@ -14,21 +15,22 @@ from aqt.utils import checkInvalidFilename, getSaveFile, showInfo, showWarning, class ExportDialog(QDialog): - def __init__(self, mw, did=None): + def __init__(self, mw, did: Optional[int] = None, cids: Optional[List[int]] = None): QDialog.__init__(self, mw, Qt.Window) self.mw = mw self.col = mw.col self.frm = aqt.forms.exporting.Ui_ExportDialog() self.frm.setupUi(self) self.exporter = None + self.cids = cids self.setup(did) self.exec_() - def setup(self, did): + def setup(self, did: Optional[int]): self.exporters = exporters() # if a deck specified, start with .apkg type selected idx = 0 - if did: + if did or self.cids: for c, (k, e) in enumerate(self.exporters): if e.ext == ".apkg": idx = c @@ -38,7 +40,10 @@ class ExportDialog(QDialog): self.frm.format.activated.connect(self.exporterChanged) self.exporterChanged(idx) # deck list - self.decks = [_("All Decks")] + sorted(self.col.decks.allNames()) + if self.cids is None: + self.decks = [_("All Decks")] + sorted(self.col.decks.allNames()) + else: + self.decks = [_("Browser Selection")] self.frm.deck.addItems(self.decks) # save button b = QPushButton(_("Export...")) @@ -77,9 +82,18 @@ class ExportDialog(QDialog): self.exporter.includeMedia = self.frm.includeMedia.isChecked() self.exporter.includeTags = self.frm.includeTags.isChecked() self.exporter.includeHTML = self.frm.includeHTML.isChecked() - if not self.frm.deck.currentIndex(): + idx = self.frm.deck.currentIndex() + if self.cids is not None: + # Browser Selection + self.exporter.cids = self.cids self.exporter.did = None + elif idx == 0: + # All decks + self.exporter.did = None + self.exporter.cids = None else: + # Deck idx-1 in the list of decks + self.exporter.cids = None name = self.decks[self.frm.deck.currentIndex()] self.exporter.did = self.col.decks.id(name) if self.isVerbatim: diff --git a/qt/designer/browser.ui b/qt/designer/browser.ui index 8e5944bbc..e400b993d 100644 --- a/qt/designer/browser.ui +++ b/qt/designer/browser.ui @@ -295,6 +295,7 @@ + @@ -599,6 +600,14 @@ Ctrl+K + + + &Export Notes + + + Ctrl+Shift+E + +