Merge pull request #103 from hgiesel/master
Extend guiAddCards so that it's possible to preset notes
This commit is contained in:
commit
5d90bbf1e0
181
AnkiConnect.py
181
AnkiConnect.py
@ -31,6 +31,8 @@ import sys
|
|||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from time import time
|
from time import time
|
||||||
from unicodedata import normalize
|
from unicodedata import normalize
|
||||||
|
from random import choice
|
||||||
|
from string import ascii_letters
|
||||||
|
|
||||||
#
|
#
|
||||||
# Constants
|
# Constants
|
||||||
@ -45,6 +47,7 @@ TICK_INTERVAL = 25
|
|||||||
URL_TIMEOUT = 10
|
URL_TIMEOUT = 10
|
||||||
URL_UPGRADE = 'https://raw.githubusercontent.com/FooSoft/anki-connect/master/AnkiConnect.py'
|
URL_UPGRADE = 'https://raw.githubusercontent.com/FooSoft/anki-connect/master/AnkiConnect.py'
|
||||||
|
|
||||||
|
ANKI21 = anki.version.startswith('2.1')
|
||||||
|
|
||||||
#
|
#
|
||||||
# Helpers
|
# Helpers
|
||||||
@ -1071,10 +1074,186 @@ class AnkiConnect:
|
|||||||
|
|
||||||
|
|
||||||
@api()
|
@api()
|
||||||
def guiAddCards(self):
|
def guiAddCards(self, note=None):
|
||||||
|
|
||||||
|
if note is not None:
|
||||||
|
collection = self.collection()
|
||||||
|
|
||||||
|
deck = collection.decks.byName(note['deckName'])
|
||||||
|
if deck is None:
|
||||||
|
raise Exception('deck was not found: {}'.format(note['deckName']))
|
||||||
|
|
||||||
|
self.collection().decks.select(deck['id'])
|
||||||
|
savedMid = deck.pop('mid', None)
|
||||||
|
|
||||||
|
model = collection.models.byName(note['modelName'])
|
||||||
|
if model is None:
|
||||||
|
raise Exception('model was not found: {}'.format(note['modelName']))
|
||||||
|
|
||||||
|
self.collection().models.setCurrent(model)
|
||||||
|
self.collection().models.update(model)
|
||||||
|
|
||||||
|
closeAfterAdding = False
|
||||||
|
if note is not None and 'options' in note:
|
||||||
|
if 'closeAfterAdding' in note['options']:
|
||||||
|
closeAfterAdding = note['options']['closeAfterAdding']
|
||||||
|
if type(closeAfterAdding) is not bool:
|
||||||
|
raise Exception('option parameter \'closeAfterAdding\' must be boolean')
|
||||||
|
|
||||||
|
addCards = None
|
||||||
|
|
||||||
|
if closeAfterAdding:
|
||||||
|
|
||||||
|
randomString = ''.join(choice(ascii_letters) for _ in range(10))
|
||||||
|
windowName = 'AddCardsAndClose' + randomString
|
||||||
|
|
||||||
|
if ANKI21:
|
||||||
|
class AddCardsAndClose(aqt.addcards.AddCards):
|
||||||
|
|
||||||
|
def __init__(self, mw):
|
||||||
|
# the window must only reset if
|
||||||
|
# * function `onModelChange` has been called prior
|
||||||
|
# * window was newly opened
|
||||||
|
|
||||||
|
self.modelHasChanged = True
|
||||||
|
super().__init__(mw)
|
||||||
|
|
||||||
|
self.addButton.setText("Add and Close")
|
||||||
|
self.addButton.setShortcut(aqt.qt.QKeySequence("Ctrl+Return"))
|
||||||
|
|
||||||
|
def _addCards(self):
|
||||||
|
super()._addCards()
|
||||||
|
|
||||||
|
# if adding was successful it must mean it was added to the history of the window
|
||||||
|
if len(self.history):
|
||||||
|
self.reject()
|
||||||
|
|
||||||
|
def onModelChange(self):
|
||||||
|
if self.isActiveWindow():
|
||||||
|
super().onModelChange()
|
||||||
|
self.modelHasChanged = True
|
||||||
|
|
||||||
|
def onReset(self, model=None, keep=False):
|
||||||
|
if self.isActiveWindow() or self.modelHasChanged:
|
||||||
|
super().onReset(model, keep)
|
||||||
|
self.modelHasChanged = False
|
||||||
|
|
||||||
|
else:
|
||||||
|
# modelchoosers text is changed by a reset hook
|
||||||
|
# therefore we need to change it back manually
|
||||||
|
self.modelChooser.models.setText(self.editor.note.model()['name'])
|
||||||
|
self.modelHasChanged = False
|
||||||
|
|
||||||
|
def _reject(self):
|
||||||
|
savedMarkClosed = aqt.dialogs.markClosed
|
||||||
|
aqt.dialogs.markClosed = lambda _: savedMarkClosed(windowName)
|
||||||
|
super()._reject()
|
||||||
|
aqt.dialogs.markClosed = savedMarkClosed
|
||||||
|
|
||||||
|
else:
|
||||||
|
class AddCardsAndClose(aqt.addcards.AddCards):
|
||||||
|
|
||||||
|
def __init__(self, mw):
|
||||||
|
self.modelHasChanged = True
|
||||||
|
super(AddCardsAndClose, self).__init__(mw)
|
||||||
|
|
||||||
|
self.addButton.setText("Add and Close")
|
||||||
|
self.addButton.setShortcut(aqt.qt.QKeySequence("Ctrl+Return"))
|
||||||
|
|
||||||
|
def addCards(self):
|
||||||
|
super(AddCardsAndClose, self).addCards()
|
||||||
|
|
||||||
|
# if adding was successful it must mean it was added to the history of the window
|
||||||
|
if len(self.history):
|
||||||
|
self.reject()
|
||||||
|
|
||||||
|
def onModelChange(self):
|
||||||
|
if self.isActiveWindow():
|
||||||
|
super(AddCardsAndClose, self).onModelChange()
|
||||||
|
self.modelHasChanged = True
|
||||||
|
|
||||||
|
def onReset(self, model=None, keep=False):
|
||||||
|
if self.isActiveWindow() or self.modelHasChanged:
|
||||||
|
super(AddCardsAndClose, self).onReset(model, keep)
|
||||||
|
self.modelHasChanged = False
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.modelChooser.models.setText(self.editor.note.model()['name'])
|
||||||
|
self.modelHasChanged = False
|
||||||
|
|
||||||
|
def reject(self):
|
||||||
|
savedClose = aqt.dialogs.close
|
||||||
|
aqt.dialogs.close = lambda _: savedClose(windowName)
|
||||||
|
super(AddCardsAndClose, self).reject()
|
||||||
|
aqt.dialogs.close = savedClose
|
||||||
|
|
||||||
|
aqt.dialogs._dialogs[windowName] = [AddCardsAndClose, None]
|
||||||
|
addCards = aqt.dialogs.open(windowName, self.window())
|
||||||
|
|
||||||
|
if savedMid:
|
||||||
|
deck['mid'] = savedMid
|
||||||
|
|
||||||
|
editor = addCards.editor
|
||||||
|
ankiNote = editor.note
|
||||||
|
|
||||||
|
if 'fields' in note:
|
||||||
|
for name, value in note['fields'].items():
|
||||||
|
if name in ankiNote:
|
||||||
|
ankiNote[name] = value
|
||||||
|
editor.loadNote()
|
||||||
|
|
||||||
|
if 'tags' in note:
|
||||||
|
ankiNote.tags = note['tags']
|
||||||
|
editor.updateTags()
|
||||||
|
|
||||||
|
# if Anki does not Focus, the window will not notice that the
|
||||||
|
# fields are actually filled
|
||||||
|
aqt.dialogs.open(windowName, self.window())
|
||||||
|
if ANKI21:
|
||||||
|
addCards.setAndFocusNote(editor.note)
|
||||||
|
|
||||||
|
elif note is not None:
|
||||||
|
currentWindow = aqt.dialogs._dialogs['AddCards'][1]
|
||||||
|
|
||||||
|
def openNewWindow():
|
||||||
addCards = aqt.dialogs.open('AddCards', self.window())
|
addCards = aqt.dialogs.open('AddCards', self.window())
|
||||||
|
|
||||||
|
if savedMid:
|
||||||
|
deck['mid'] = savedMid
|
||||||
|
|
||||||
|
editor = addCards.editor
|
||||||
|
ankiNote = editor.note
|
||||||
|
|
||||||
|
# we have to fill out the card in the callback
|
||||||
|
# otherwise we can't assure, the new window is open
|
||||||
|
if 'fields' in note:
|
||||||
|
for name, value in note['fields'].items():
|
||||||
|
if name in ankiNote:
|
||||||
|
ankiNote[name] = value
|
||||||
|
editor.loadNote()
|
||||||
|
|
||||||
|
if 'tags' in note:
|
||||||
|
ankiNote.tags = note['tags']
|
||||||
|
editor.updateTags()
|
||||||
|
|
||||||
addCards.activateWindow()
|
addCards.activateWindow()
|
||||||
|
|
||||||
|
aqt.dialogs.open('AddCards', self.window())
|
||||||
|
if ANKI21:
|
||||||
|
addCards.setAndFocusNote(editor.note)
|
||||||
|
|
||||||
|
if currentWindow is not None:
|
||||||
|
if ANKI21:
|
||||||
|
currentWindow.closeWithCallback(openNewWindow)
|
||||||
|
else:
|
||||||
|
currentWindow.reject()
|
||||||
|
openNewWindow()
|
||||||
|
else:
|
||||||
|
openNewWindow()
|
||||||
|
|
||||||
|
else:
|
||||||
|
addCards = aqt.dialogs.open('AddCards', self.window())
|
||||||
|
addCards.activateWindow()
|
||||||
|
|
||||||
@api()
|
@api()
|
||||||
def guiReviewActive(self):
|
def guiReviewActive(self):
|
||||||
|
24
README.md
24
README.md
@ -1490,13 +1490,33 @@ guarantee that your application continues to function properly in the future.
|
|||||||
|
|
||||||
* **guiAddCards**
|
* **guiAddCards**
|
||||||
|
|
||||||
Invokes the *Add Cards* dialog.
|
Invokes the *Add Cards* dialog, presets the note using the given deck and model, with the provided field values and tags.
|
||||||
|
Invoking it multiple times closes the old window and _reopen the window_ with the new provided values.
|
||||||
|
|
||||||
|
The `closeAfterAdding` member inside `options` group can be set to true to create a dialog that closes upon adding the note.
|
||||||
|
Invoking the action mutliple times with this option will create _multiple windows_.
|
||||||
|
|
||||||
*Sample request*:
|
*Sample request*:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"action": "guiAddCards",
|
"action": "guiAddCards",
|
||||||
"version": 6
|
"version": 6,
|
||||||
|
"params": {
|
||||||
|
"note": {
|
||||||
|
"deckName": "Default",
|
||||||
|
"modelName": "Cloze",
|
||||||
|
"fields": {
|
||||||
|
"Text": "The capital of Romania is {{c1::Bucharest}}",
|
||||||
|
"Extra": "Romania is a country in Europe"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"closeAfterAdding": true
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"countries"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -12,6 +12,15 @@ class TestGui(unittest.TestCase):
|
|||||||
# guiAddCards
|
# guiAddCards
|
||||||
util.invoke('guiAddCards')
|
util.invoke('guiAddCards')
|
||||||
|
|
||||||
|
# guiAddCards with preset
|
||||||
|
util.invoke('createDeck', deck='test')
|
||||||
|
note = {'deckName': 'test', 'modelName': 'Basic', 'fields': {'Front': 'front1', 'Back': 'back1'}, 'tags': ['tag1']}
|
||||||
|
util.invoke('guiAddCards', note=note)
|
||||||
|
|
||||||
|
# guiAddCards with preset and closeAfterAdding
|
||||||
|
noteWithOption = {'deckName': 'test', 'modelName': 'Basic', 'fields': {'Front': 'front1', 'Back': 'back1'}, 'options': { 'closeAfterAdding': True }, 'tags': ['tag1']}
|
||||||
|
util.invoke('guiAddCards', note=noteWithOption)
|
||||||
|
|
||||||
# guiCurrentCard
|
# guiCurrentCard
|
||||||
# util.invoke('guiCurrentCard')
|
# util.invoke('guiCurrentCard')
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user