Merge pull request #103 from hgiesel/master

Extend guiAddCards so that it's possible to preset notes
This commit is contained in:
Alex Yatskov 2019-05-03 20:30:27 -07:00 committed by GitHub
commit 5d90bbf1e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 213 additions and 5 deletions

View File

@ -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):

View File

@ -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"
]
}
}
} }
``` ```

View File

@ -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')