Skip to content

Commit

Permalink
Merge pull request #3 from lovac42/enhance_editor
Browse files Browse the repository at this point in the history
Enhance editor
  • Loading branch information
lovac42 committed May 26, 2019
2 parents 00a0956 + 8486503 commit 768ed75
Show file tree
Hide file tree
Showing 26 changed files with 360 additions and 196 deletions.
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@
This is Anki 2.1.13 with Qt4 & QtWebkit.

## About:
The reason for this is because QtWebEngine is too sluggish for ebook reading. After importing any uncompressed 200kb file or "The Complete History of Supermemo" or "Supermemo 20 Rules", the webpage really starts to lag behind and in some cases freeze five seconds after every extraction. This project takes the codes from 2.1 and 2.0 and meshed them together.
The reason for this is because QWebEngine is too sluggish for ebook reading. After importing any uncompressed 200kb file or "The Complete History of Supermemo" or "Supermemo 20 Rules", the webpage really starts to lag behind and in some cases freeze five seconds after every extraction. This project takes the codes from 2.1 and 2.0 and meshed them together.


### Why QWebEngine is bad for IR?
"setHtml works by converting the html code you provide to percent-encoding, putting data: in front and using it as url which it navigates to, so the html code you provide becomes a url which exceeds the 2mb limit." <a href="https://bugreports.qt.io/browse/QTBUG-59369?focusedCommentId=352654&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-352654">Source</a>

In other words, a 100kb webpage with the space character encoded as "%20", as well as user highlights and annotations added on top could potentially become more than 2MB, freezing Anki as a result.



### Naming:
What does CCBC stand for?
Expand Down Expand Up @@ -41,3 +49,17 @@ Pennywise, https://ankiweb.net/shared/info/1032766035

## Sync:
Sync has been disabled, but can be enabled for custom servers using modules.


## Screenshots:

<img src="https://github.com/lovac42/CCBC/blob/master/screenshots/Clipboard-1.png?raw=true">

<img src="https://github.com/lovac42/CCBC/blob/master/screenshots/Clipboard-2.png?raw=true">

<img src="https://github.com/lovac42/CCBC/blob/master/screenshots/Clipboard-3.png?raw=true">

<img src="https://github.com/lovac42/CCBC/blob/master/screenshots/Clipboard-4.png?raw=true">

<img src="https://github.com/lovac42/CCBC/blob/master/screenshots/nm_heatmap.png?raw=true">

10 changes: 6 additions & 4 deletions anki/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
intTime, splitFields, joinFields, maxID, devMode, stripHTMLMedia
from anki.hooks import runFilter, runHook
from anki.models import ModelManager
from anki.media import MediaManager
# from anki.media import MediaManager
from anki.decks import DeckManager
from anki.tags import TagManager
from anki.consts import *
Expand All @@ -30,6 +30,8 @@
import anki.template
import anki.find

from ccbc.media import ExtMediaManager


defaultConf = {
# review options
Expand Down Expand Up @@ -61,7 +63,7 @@ def __init__(self, db, server=False, log=False):
self.server = server
self._lastSave = time.time()
self.clearUndo()
self.media = MediaManager(self, server)
self.media = ExtMediaManager(self, server)
self.models = ModelManager(self)
self.decks = DeckManager(self)
self.tags = TagManager(self)
Expand Down Expand Up @@ -869,11 +871,11 @@ def fixIntegrity(self):
# v2 sched had a bug that could create decimal intervals
curs.execute("update cards set ivl=round(ivl),due=round(due) where ivl!=round(ivl) or due!=round(due)")
if curs.rowcount:
problems.append("Fixed %d cards with v2 scheduler bug." % curs.rowcount)
problems.append("Fixed %d cards using float point for intervals." % curs.rowcount)

curs.execute("update revlog set ivl=round(ivl),lastIvl=round(lastIvl) where ivl!=round(ivl) or lastIvl!=round(lastIvl)")
if curs.rowcount:
problems.append("Fixed %d review history entries with v2 scheduler bug." % curs.rowcount)
problems.append("Fixed %d review history entries using float point for intervals." % curs.rowcount)
# models
if self.models.ensureNotEmpty():
problems.append("Added missing note type.")
Expand Down
1 change: 1 addition & 0 deletions anki/media.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ def writeData(self, opath, data, typeHint=None):
typeMap = {
"image/jpeg": ".jpg",
"image/png": ".png",
"text/css": ".css",
}
if typeHint in typeMap:
fname += typeMap[typeHint]
Expand Down
2 changes: 0 additions & 2 deletions aqt/addcards.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ def __init__(self, mw):
self.mw = mw
self.form = aqt.forms.addcards.Ui_Dialog()
self.form.setupUi(self)
self.setStyleSheet(ccbc.css.editor_ui)


self.setWindowTitle(_("Add"))
self.setMinimumHeight(300)
Expand Down
34 changes: 6 additions & 28 deletions aqt/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
import re
from operator import itemgetter
from anki.lang import ngettext, _
from bs4 import BeautifulSoup
# from bs4 import BeautifulSoup
from anki.sound import play

from aqt.qt import *
import anki
import aqt.forms
from anki.utils import fmtTimeSpan, ids2str, stripHTMLMedia, isWin, intTime, isMac
from anki.utils import fmtTimeSpan, ids2str, htmlToTextLine, stripHTMLMedia, isWin, intTime, isMac
from aqt.utils import saveGeom, restoreGeom, saveSplitter, restoreSplitter, \
saveHeader, restoreHeader, saveState, restoreState, applyStyles, getTag, \
showInfo, askUser, tooltip, showWarning, shortcut, getBase, mungeQA
Expand Down Expand Up @@ -293,20 +293,7 @@ def answer(self, c):
return a

def formatQA(self, txt):
soup = BeautifulSoup(txt, 'html.parser')
for hide in soup.findAll(True, {'class': re.compile(
'\\bbrowserhide\\b')}):
hide.extract()
txt=str(soup)
s = txt.replace("<br>", u" ")
s = s.replace("<br />", u" ")
s = s.replace("<div>", u" ")
s = s.replace("\n", u" ")
s = re.sub("\[sound:[^]]+\]", "", s)
s = re.sub("\[\[type:[^]]+\]\]", "", s)
s = stripHTMLMedia(s)
s = s.strip()
return s
return htmlToTextLine(txt)

def nextDue(self, c, index):
if c.odid:
Expand Down Expand Up @@ -775,6 +762,8 @@ def buildTree(self):
self._modelTree(root)
self._userTagTree(root)
self.form.tree.setIndentation(15)
self.sidebarTree=root #for 2.1


def onTreeClick(self, item, col):
if getattr(item, 'onclick', None):
Expand Down Expand Up @@ -1770,18 +1759,7 @@ def borderImg(link, icon, on, title, tooltip=None):
right += "</div>"
self.web.page().currentFrame().setScrollBarPolicy(
Qt.Horizontal, Qt.ScrollBarAlwaysOff)

self.web.stdHtml(self._body%right, self._css + """
#header { font-weight: normal; }
a { margin-right: 1em; }
.hitem { overflow: hidden; white-space: nowrap;}
""")






self.web.stdHtml(self._body%right, self._css)

# Link handling
######################################################################
Expand Down
63 changes: 47 additions & 16 deletions aqt/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,35 @@
import os
import ctypes
import base64
import random
import urllib.request, urllib.parse, urllib.error
from PyQt4 import QtCore
from anki.utils import tmpdir

from anki.lang import _
from aqt.qt import *
from PyQt4 import QtCore
from anki.utils import stripHTML, isWin, isMac, namedtmp, json, stripHTMLMedia
import anki.sound
from anki.hooks import runHook, runFilter
from aqt.sound import getAudio
from aqt.webview import AnkiWebView
from aqt.utils import shortcut, showInfo, showWarning, getBase, getFile, \
tooltip, downArrow
from aqt.utils import shortcut, showInfo, showWarning, getBase, getFile, tooltip, downArrow

import aqt
import ccbc.js
import ccbc.html
import ccbc.css

from ccbc.cleaner import Cleaner
from bs4 import BeautifulSoup
from anki.utils import checksum


pics = ("jpg", "jpeg", "png", "tif", "tiff", "gif", "svg", "webp")
audio = ("wav", "mp3", "ogg", "flac", "mp4", "swf", "mov", "mpeg", "mkv", "m4a", "3gp", "spx", "oga")

RE_NAS_URI = re.compile(r"^file://[^/]")

_html = ccbc.html.editor


Expand Down Expand Up @@ -420,11 +425,19 @@ def onHtmlEdit(self):
# meta and other junk will be stripped in
# _filterHTML() with BeautifulSoup

# filter html to strip out things like a leading </div>
c = Cleaner(self.mw)
c.feed(html)
html = c.toString()
c.close()
# self.mw.progress.start(
# immediate=True, parent=self.parentWindow)
try:
# filter html to strip out things like a leading </div>
c = Cleaner(self.mw)
# TODO: Use HTMLCleaner for IR models
c.feed(html)
html = c.toString()
c.close()
except:
showWarning(_("An error occurred while parsing html or downloading resources"))
# finally:
# self.mw.progress.finish()

self.note.fields[self.currentField] = html
self.loadNote()
Expand Down Expand Up @@ -643,6 +656,22 @@ def isURL(self, s):

def _retrieveURL(self, url):
"Download file into media folder and return local filename or None."

# If samba path, copy to temp dir first before import
if RE_NAS_URI.search(url):
dir = tmpdir()
tmp = os.path.join(dir, "img%s%s" % (
random.randrange(0, 1000000), os.path.splitext(url)[1]))
# print(tmp)

f = open(tmp, "wb")
url=url.replace('file://','\\\\')
f.write(open(url, "rb").read())
f.close()

url='file:///'+tmp.replace("\\", "/")
# print(url)

# urllib doesn't understand percent-escaped utf8, but requires things like
# '#' to be escaped. we don't try to unquote the incoming URL, because
# we should only be receiving file:// urls from url mime, which is unquoted
Expand Down Expand Up @@ -751,16 +780,18 @@ def _filterHTML(self, html, localize=False):
fname = self.urlToFile(src)
if fname:
tag['src'] = fname
# elif src.lower().startswith("data:image/"):
# and convert inlined data
# tag['src'] = self.inlinedImageToFilename(src)
elif src.lower().startswith("data:image/"):
# convert inlined data
src = re.sub(r'\r|\n|\t','',src)
tag['src'] = self.inlinedImageToFilename(src)

#REMOVE: to keep the formating/alignment of ebooks
#copy image references and strip junk attributes
ntag={}
for attr, val in tag.attrs.items():
if attr == "src":
ntag[attr]=val
tag=ntag
# ntag={}
# for attr, val in tag.attrs.items():
# if attr == "src":
# ntag[attr]=val
# tag.attrs=ntag

# strip superfluous elements
for elem in "html", "head", "body", "meta":
Expand Down
4 changes: 0 additions & 4 deletions aqt/profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,11 +295,7 @@ def _defaultBase(self):
return os.path.join(dataDir, "Anki2")

def _loadMeta(self):
opath = os.path.join(self.base, "prefs.db")
path = os.path.join(self.base, "prefz.db")
if os.path.exists(opath) and not os.path.exists(path):
shutil.copy(opath, path)

new = not os.path.exists(path)
def recover():
# if we can't load profile, start with a new one
Expand Down
9 changes: 7 additions & 2 deletions aqt/reviewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,13 @@ def replayAudio(self, previewer=None):
# Initializing the webview
##########################################################################


def revHtml(self):
return self._revHtml

_revHtml = """
<img src="qrc:/icons/rating.png" id=star class=marked>
<div id=_mark><img src="qrc:/icons/rating.png" id=star class=marked></div>
<div id=_flag>&#x2691;</div>
<div id=qa></div>
<script>
var ankiPlatform = "desktop";
Expand Down Expand Up @@ -185,7 +190,7 @@ def _initWeb(self):
self._bottomReady = False
base = getBase(self.mw.col)
# main window
self.web.stdHtml(self._revHtml, self._styles(),
self.web.stdHtml(self.revHtml(), self._styles(),
loadCB=lambda x: self._showQuestion(),
head=base)
# show answer / ease buttons
Expand Down
8 changes: 7 additions & 1 deletion aqt/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from anki.lang import _
import aqt
import ccbc.js
import ccbc.css


# Deck Stats
Expand All @@ -27,6 +28,7 @@ def __init__(self, mw):
self.oldPos = None
self.wholeCollection = False
self.setMinimumWidth(700)
self.setStyleSheet(ccbc.css.stats)
f = self.form
f.setupUi(self)
restoreGeom(self, self.name)
Expand Down Expand Up @@ -126,7 +128,11 @@ def refresh(self):
stats = self.mw.col.stats()
stats.wholeCollection = self.wholeCollection
txt = stats.report(type=self.period)
self.report="<script>%s\n</script>%s"%(
self.report="<style>%s</style><script>%s\n</script>%s"%(
ccbc.css.stats,
ccbc.js.jquery+ccbc.js.plot,txt)
self.form.web.setHtml(self.report)
klass=self.mw.web.page().mainFrame().evaluateJavaScript(
'document.body.className')
self.form.web.eval('document.body.className += "%s";'%klass)
self.mw.progress.finish()
11 changes: 6 additions & 5 deletions aqt/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def __init__(self, mw):
addHook('afterStateChange', self.stateChanged)
addHook('profileLoaded', self.onProfileLoaded)
self.setupMenu()
self.mwCSS=mw.styleSheet()


def setupMenu(self):
Expand Down Expand Up @@ -71,24 +72,24 @@ def onFullScreen(self):


def stateChanged(self, newS, oldS, *args):
self.mwCSS=mw.styleSheet().replace("QMenuBar{height:0 !important;}","")
self.reset()

#yikes
g,h,b=('height:0 !important;',0,self.mw.bottomWeb.hide) if \
self.mw.isFullScreen() and \
newS=='review' else \
g,h,b=('QMenuBar{height:0 !important;}',0,self.mw.bottomWeb.hide) if \
self.mw.isFullScreen() and newS=='review' else \
('',self.tb_height,self.mw.bottomWeb.show)

if self.menubar.isChecked():
self.mw.setStyleSheet(g) #hide by css to keep hotkeys active
self.mw.setStyleSheet(self.mwCSS+g) #hide by css to keep hotkeys active
if self.toolbar.isChecked():
self.mw.toolbar.web.setFixedHeight(h) #menubar
if self.bottombar.isChecked():
b()


def reset(self):
self.mw.setStyleSheet('')
self.mw.setStyleSheet(self.mwCSS)
self.mw.toolbar.web.setFixedHeight(self.tb_height)
self.mw.bottomWeb.show()

Loading

0 comments on commit 768ed75

Please sign in to comment.