New upstream version 2.1.6+dfsg
Julian Gilbey
5 years ago
12 | 12 | Anki requires: |
13 | 13 | |
14 | 14 | - Python 3.6+ |
15 | - Qt 5.9.x and a PyQT that supports it | |
15 | - Qt 5.9.x/5.11.x/5.12.x and a PyQT that supports it | |
16 | 16 | - mpv |
17 | 17 | - lame |
18 | 18 | |
20 | 20 | |
21 | 21 | $ pip3 install -r requirements.txt |
22 | 22 | |
23 | If you're on a Linux distribution that packages PyQt 5.9 then you can use the | |
24 | distro's packages. Make sure you install the development tools (eg | |
23 | If you're on a Linux distribution that packages a compatible Qt then you can | |
24 | use the distro's packages. Make sure you install the development tools (eg | |
25 | 25 | pyqt5-dev-tools) as well. |
26 | 26 | |
27 | 27 | If you're on another platform or your distro has the wrong Qt version, you |
28 | 28 | can install PyQt with pip: |
29 | 29 | |
30 | $ pip3 install sip pyqt5==5.9 | |
30 | $ pip3 install sip pyqt5 | |
31 | 31 | |
32 | 32 | To use the development version: |
33 | 33 |
9 | 9 | if sys.getfilesystemencoding().lower() in ("ascii", "ansi_x3.4-1968"): |
10 | 10 | raise Exception("Anki requires a UTF-8 locale.") |
11 | 11 | |
12 | version="2.1.6-beta2" # build scripts grep this line, so preserve formatting | |
12 | version="2.1.6" # build scripts grep this line, so preserve formatting | |
13 | 13 | from anki.storage import Collection |
14 | 14 | __all__ = ["Collection"] |
439 | 439 | # nothing has that field |
440 | 440 | return |
441 | 441 | # gather nids |
442 | regex = re.escape(val).replace("_", ".").replace("\\%", ".*") | |
442 | regex = re.escape(val).replace("_", ".").replace(re.escape("%"), ".*") | |
443 | 443 | nids = [] |
444 | 444 | for (id,mid,flds) in self.col.db.execute(""" |
445 | 445 | select id, mid, flds from notes |
433 | 433 | # Learning queues |
434 | 434 | ########################################################################## |
435 | 435 | |
436 | # scan for any newly due learning cards every 5 minutes | |
436 | # scan for any newly due learning cards every minute | |
437 | 437 | def _updateLrnCutoff(self, force): |
438 | 438 | nextCutoff = intTime() + self.col.conf['collapseTime'] |
439 | if nextCutoff - self._lrnCutoff > 300 or force: | |
439 | if nextCutoff - self._lrnCutoff > 60 or force: | |
440 | 440 | self._lrnCutoff = nextCutoff |
441 | 441 | return True |
442 | 442 | return False |
5 | 5 | import re |
6 | 6 | |
7 | 7 | from anki.lang import _ |
8 | from anki.utils import intTime, json | |
8 | from anki.utils import intTime, json, isWin | |
9 | 9 | from anki.db import DB |
10 | 10 | from anki.collection import _Collection |
11 | 11 | from anki.consts import * |
12 | 12 | from anki.stdmodels import addBasicModel, addClozeModel, addForwardReverse, \ |
13 | 13 | addForwardOptionalReverse, addBasicTypingModel |
14 | 14 | |
15 | def Collection(path, lock=True, server=False, sync=True, log=False): | |
15 | def Collection(path, lock=True, server=False, log=False): | |
16 | 16 | "Open a new or existing collection. Path must be unicode." |
17 | 17 | assert path.endswith(".anki2") |
18 | 18 | path = os.path.abspath(path) |
29 | 29 | else: |
30 | 30 | ver = _upgradeSchema(db) |
31 | 31 | db.execute("pragma temp_store = memory") |
32 | if sync: | |
33 | db.execute("pragma cache_size = 10000") | |
32 | db.execute("pragma cache_size = 10000") | |
33 | if not isWin: | |
34 | 34 | db.execute("pragma journal_mode = wal") |
35 | else: | |
36 | db.execute("pragma synchronous = off") | |
37 | 35 | db.setAutocommit(False) |
38 | 36 | # add db to col and do any remaining upgrades |
39 | 37 | col = _Collection(db, server, log) |
48 | 48 | # -- setting silentlyClose=True to have it close immediately |
49 | 49 | # -- define a closeWithCallback() method |
50 | 50 | # - have the window opened via aqt.dialogs.open(<name>, self) |
51 | # - have a method reopen(*args), called if the user ask to open the window a second time. Arguments passed are the same than for original opening. | |
51 | 52 | |
52 | 53 | #- make preferences modal? cmd+q does wrong thing |
53 | 54 | |
73 | 74 | instance.setWindowState(instance.windowState() & ~Qt.WindowMinimized) |
74 | 75 | instance.activateWindow() |
75 | 76 | instance.raise_() |
77 | if hasattr(instance,"reopen"): | |
78 | instance.reopen(*args) | |
76 | 79 | return instance |
77 | 80 | else: |
78 | 81 | instance = creator(*args) |
2 | 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html |
3 | 3 | |
4 | 4 | from aqt.qt import * |
5 | from aqt.utils import tooltip | |
5 | 6 | import aqt.editor |
6 | 7 | from aqt.utils import saveGeom, restoreGeom |
7 | 8 | from anki.hooks import addHook, remHook |
8 | 9 | from anki.utils import isMac |
10 | ||
9 | 11 | |
10 | 12 | class EditCurrent(QDialog): |
11 | 13 | |
33 | 35 | def onReset(self): |
34 | 36 | # lazy approach for now: throw away edits |
35 | 37 | try: |
36 | n = self.mw.reviewer.card.note() | |
37 | n.load() | |
38 | n = self.editor.note | |
39 | n.load()#reload in case the model changed | |
38 | 40 | except: |
39 | 41 | # card's been deleted |
40 | 42 | remHook("reset", self.onReset) |
45 | 47 | return |
46 | 48 | self.editor.setNote(n) |
47 | 49 | |
50 | def reopen(self,mw): | |
51 | tooltip("Please finish editing the existing card first.") | |
52 | self.onReset() | |
53 | ||
48 | 54 | def reject(self): |
49 | 55 | self.saveAndClose() |
50 | 56 |
34 | 34 | html { background: %s; } |
35 | 35 | #topbuts { background: %s; } |
36 | 36 | </style> |
37 | <div id="topbuts">%s</div> | |
37 | <div id="topbutsOuter"><div id="topbuts" class="clearfix">%s</div></div> | |
38 | 38 | <div id="fields"></div> |
39 | 39 | <div id="dupes" style="display:none;"><a href="#" onclick="pycmd('dupes');return false;">%s</a></div> |
40 | 40 | """ |
97 | 97 | righttopbtns = runFilter("setupEditorButtons", righttopbtns, self) |
98 | 98 | topbuts = """ |
99 | 99 | <div id="topbutsleft" style="float:left;"> |
100 | <button onclick="pycmd('fields')">%(flds)s...</button> | |
101 | <button onclick="pycmd('cards')">%(cards)s...</button> | |
100 | <button title='%(fldsTitle)s' onclick="pycmd('fields')">%(flds)s...</button> | |
101 | <button title='%(cardsTitle)s' onclick="pycmd('cards')">%(cards)s...</button> | |
102 | 102 | </div> |
103 | 103 | <div id="topbutsright" style="float:right;"> |
104 | 104 | %(rightbts)s |
105 | 105 | </div> |
106 | """ % dict(flds=_("Fields"), cards=_("Cards"), rightbts="".join(righttopbtns)) | |
106 | """ % dict(flds=_("Fields"), cards=_("Cards"), rightbts="".join(righttopbtns), | |
107 | fldsTitle=_("Customize Fields"), | |
108 | cardsTitle=shortcut(_("Customize Card Templates (Ctrl+L)"))) | |
107 | 109 | bgcol = self.mw.app.palette().window().color().name() |
108 | 110 | # then load page |
109 | 111 | self.web.stdHtml(_html % ( |
335 | 335 | return |
336 | 336 | mw.progress.start(immediate=True) |
337 | 337 | try: |
338 | importer.run() | |
338 | try: | |
339 | importer.run() | |
340 | finally: | |
341 | mw.progress.finish() | |
339 | 342 | except zipfile.BadZipfile: |
340 | 343 | showWarning(invalidZipMsg()) |
341 | 344 | except Exception as e: |
359 | 362 | tooltip(log) |
360 | 363 | else: |
361 | 364 | showText(log) |
362 | finally: | |
363 | mw.progress.finish() | |
364 | 365 | mw.reset() |
365 | 366 | |
366 | 367 | def invalidZipMsg(): |
424 | 424 | |
425 | 425 | path = self._glPath() |
426 | 426 | if not os.path.exists(path): |
427 | return "software" | |
427 | if qtminor >= 12: | |
428 | return "auto" | |
429 | else: | |
430 | return "software" | |
428 | 431 | |
429 | 432 | mode = open(path, "r").read().strip() |
430 | 433 |
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
5 | 5 | overflow-wrap: break-word; |
6 | 6 | } |
7 | 7 | |
8 | /* prevent floated images from being displayed outside field */ | |
9 | .field:after { | |
8 | .clearfix:after { | |
10 | 9 | content: ""; |
11 | display: block; | |
12 | height: 0; | |
10 | display: table; | |
13 | 11 | clear: both; |
14 | visibility: hidden; | |
15 | 12 | } |
16 | 13 | |
17 | 14 | .fname { |
27 | 24 | margin: 5px; |
28 | 25 | } |
29 | 26 | |
30 | #topbuts { | |
27 | #topbutsOuter { | |
31 | 28 | position: fixed; |
32 | 29 | height: 24px; |
33 | 30 | top: 0; |
39 | 36 | .topbut { |
40 | 37 | width: 16px; |
41 | 38 | height: 16px; |
39 | margin-top: 4px; | |
42 | 40 | } |
43 | 41 | |
44 | 42 | .rainbow { |
296 | 296 | } |
297 | 297 | txt += "<tr><td class=fname>{0}</td></tr><tr><td width=100%>".format(n); |
298 | 298 | txt += "<div id=f{0} onkeydown='onKey();' oninput='onInput()' onmouseup='onKey();'".format(i); |
299 | txt += " onfocus='onFocus(this);' onblur='onBlur();' class=field "; | |
299 | txt += " onfocus='onFocus(this);' onblur='onBlur();' class='field clearfix' "; | |
300 | 300 | txt += "ondragover='onDragOver(this);' onpaste='onPaste(this);' "; |
301 | 301 | txt += "oncopy='onCutOrCopy(this);' oncut='onCutOrCopy(this);' "; |
302 | 302 | txt += "contentEditable=true class=field>{0}</div>".format(f); |
445 | 445 | node.removeAttributeNode(toRemove[i]); |
446 | 446 | } |
447 | 447 | } |
448 | }; | |
449 | ||
450 | var adjustFieldsTopMargin = function() { | |
451 | var topHeight = $("#topbuts").height(); | |
452 | var margin = topHeight + 8; | |
453 | document.getElementById("fields").style.marginTop = margin + "px"; | |
448 | 454 | }; |
449 | 455 | |
450 | 456 | var mouseDown = 0; |
476 | 482 | $("button.linkb").on("mousedown", function (e) { |
477 | 483 | e.preventDefault(); |
478 | 484 | }); |
485 | ||
486 | window.onresize = function() { | |
487 | adjustFieldsTopMargin(); | |
488 | }; | |
489 | ||
490 | adjustFieldsTopMargin(); | |
479 | 491 | }); |