Exemple #1
0
def miMessage(text, parent=False):
    title = "Migaku"
    if parent is False:
        parent = aqt.mw.app.activeWindow() or aqt.mw
    icon = QIcon(join(addon_path, 'icons', 'migaku.png'))
    mb = QMessageBox(parent)
    mb.setWindowIcon(icon)
    mb.setWindowTitle(title)
    cb = QCheckBox("Don't show me the welcome screen again.")
    wv = AnkiWebView()
    wv._page._bridge.onCmd = attemptOpenLink
    wv.setFixedSize(680, 450)
    wv.page().setHtml(text)
    wide = QWidget()
    wide.setFixedSize(18, 18)
    mb.layout().addWidget(wv, 0, 1)
    mb.layout().addWidget(wide, 0, 2)
    mb.layout().setColumnStretch(0, 3)
    mb.layout().addWidget(cb, 1, 1)
    b = mb.addButton(QMessageBox.Ok)
    b.setFixedSize(100, 30)
    b.setDefault(True)
    mb.exec_()
    wv.deleteLater()
    if cb.isChecked():
        return True
    else:
        return False
Exemple #2
0
def add_graphs_to_congrats(webview: AnkiWebView):
    page = basename(webview.page().url().path())

    if page != "congrats.html":
        return

    graph_css = make_graph_css()
    graph_js = make_graph_js(get_active_congrats_graphs(), "deck:current")

    webview.eval(f"""
const graphsContainer = document.createElement("div")
graphsContainer.id = "graphsSection"
graphsContainer.style = "text-align: center;"
document.body.appendChild(graphsContainer)

const loadGraphs = () => {{
    {graph_css}
    {graph_js}
}}

const loadGraphScript = () => {{
    const graphScript = document.createElement("script")
    graphScript.onload = loadGraphs
    graphScript.charset = "UTF-8"
    graphScript.src = "graphs.js"
    document.head.appendChild(graphScript)
}}

const protobufScript = document.createElement("script")
protobufScript.onload = loadGraphScript
protobufScript.charset = "UTF-8"
protobufScript.src = "../js/vendor/protobuf.min.js"
document.head.appendChild(protobufScript)
""")
Exemple #3
0
 def showSetHighlightColorDialog(self):
     #Objective is a dialog to set highlight color used with 'h' key
     d = QDialog(self.mw)
     l = QVBoxLayout()
     l.setMargin(0)
     w = AnkiWebView()
     l.addWidget(w)
     #Add python object to take values back from javascript
     callback = IREHighlightColorCallback();
     w.page().mainFrame().addToJavaScriptWindowObject("callback", callback);
     getHighlightColorScript = """
     function getHighlightColor() {
         callback.setHighlightColor(document.getElementById('color').value.trim());
         if(document.getElementById('colorBackOrText').checked) {
             callback.setColorText('false');
         } else {
             callback.setColorText('true');
         }
     };
     """
     #color text box
     colorTextField = "<span style='font-weight:bold'>Source highlighting color (IRead2 model only): </span><input type='text' id='color' value='" + self.highlightColor + "' />";
     colorBackOrText = "<span style='font-weight:bold'>Apply color to: &nbsp;</span><input type='radio' id='colorBackOrText' name='colorBackOrText' value='false' checked='true' /> Background &nbsp;&nbsp;<input type='radio' name='colorBackOrText' value='true' /> Text<br />";
     html = "<html><head><script>" + getHighlightColorScript + "</script></head><body>";
     html += "<p>" + colorTextField;
     html += "<p>" + colorBackOrText;
     html += "</body></html>";
     w.stdHtml(html);
     bb = QDialogButtonBox(QDialogButtonBox.Close|QDialogButtonBox.Save)
     bb.connect(bb, SIGNAL("accepted()"), d, SLOT("accept()"))
     bb.connect(bb, SIGNAL("rejected()"), d, SLOT("reject()"))
     bb.setOrientation(QtCore.Qt.Horizontal);
     l.addWidget(bb)
     d.setLayout(l)
     d.setWindowModality(Qt.WindowModal)
     d.resize(500, 200)
     choice = d.exec_();
     if(choice == 1):
         w.eval("getHighlightColor()");
class KanjiGrid:
    def __init__(self, mw):
        if mw:
            self.menuAction = QAction("Generate Kanji Grid", mw)
            mw.connect(self.menuAction, SIGNAL("triggered()"), self.setup)
            mw.form.menuTools.addSeparator()
            mw.form.menuTools.addAction(self.menuAction)

    def generate(self, units, timeNow, saveMode=False):
        # deckname = mw.col.decks.name(self.did).rsplit('::',1)[-1]
        if saveMode:
            cols = _wide
        else:
            cols = _thin
        self.html = "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n"
        self.html += "<html><head><title>Anki Kanji Grid</title></head><body bgcolor=\"#FFF\">\n"
        # self.html += "<span style=\"font-size: 3em;color: #888;\">Kanji Grid - %s</span><br>\n" % deckname
        self.html += "<span style=\"font-size: 3em;color: #888;\">Kanji Grid</span><br>\n"
        self.html += "<div style=\"margin-bottom: 24pt;padding: 20pt;\"><p style=\"float: left\">Key:</p>"
        self.html += "<style type=\"text/css\">table {font-size:16px; font-family:meiryo;}" + \
                     ".key{display:inline-block;width:3em;}a,a:visited{color:#000;text-decoration:none;}</style>"
        self.html += "<p style=\"float: right\">Weak&nbsp;"
        for c in [n / 6.0 for n in range(6 + 1)]:
            self.html += "<span class=\"key\" style=\"background-color: %s;\">&nbsp;</span>" % hsvrgbstr(
                c / 2)
        self.html += "&nbsp;Strong</p></div>\n"
        self.html += "<div style=\"clear: both;\"><br><hr style=\"border-style: dashed;border-color: #666;width: 60%;\"><br></div>\n"
        self.html += "<center>\n"
        if _group == 0 or _group == 1:
            if _group == 0:
                _grades = _kanken
            elif _group == 1:
                _grades = _jlpt
            gc = 0
            kanji = list([u.value for u in units.values()])
            for i in range(1, len(_grades)):
                self.html += "<h2 style=\"color:#888;\">%s Kanji</h2>\n" % _grades[
                    i][0]
                table = "<table width='85%'><tr>\n"
                count = -1
                for unit in [units[c] for c in _grades[i][1] if c in kanji]:
                    if unit.count != 0 or _unseen:
                        score = "NaN"
                        count += 1
                        if count % cols == 0 and count != 0:
                            table += "</tr>\n<tr>\n"
                        if unit.count != 0:
                            bgcolour = hsvrgbstr(
                                scoreAdjust(unit.avg_interval / _interval) / 2)
                        else:
                            bgcolour = "#FFF"
                        if _tooltips:
                            tooltip = "Character: %s | Count: %s | " % (
                                unicodedata.name(unit.value), unit.count)
                            tooltip += "Avg Interval: %s | Score: %s | " % (
                                unit.avg_interval, score)
                            tooltip += "Background: %s | Index: %s" % (
                                bgcolour, count)
                            table += "\t<td align=center valign=top style=\"background:%s;\" title=\"%s\">" % (
                                bgcolour, tooltip)
                        else:
                            table += "\t<td align=center valign=top style=\"background:%s;\">" % (
                                bgcolour)
                        table += "<a href=\"https://kanji.koohii.com/study/kanji/%s\">%s</a></td>\n" % (
                            2 * (unit.value, ))
                table += "</tr></table>\n"
                n = count + 1
                t = len(_grades[i][1])
                gc += n
                if _unseen:
                    table += "<details><summary>Missing kanji</summary><table style=\"max-width:75%;\"><tr>\n"
                    count = -1
                    for char in [c for c in _grades[i][1] if c not in kanji]:
                        score = "NaN"
                        count += 1
                        if count % cols == 0: table += "</tr>\n<tr>\n"
                        if _tooltips:
                            tooltip = "Character: %s" % (
                                unicodedata.name(char))
                            table += "\t<td align=center valign=top style=\"background:#EEE;color:#FFF;\" title=\"%s\">" % (
                                tooltip)
                        else:
                            table += "\t<td align=center valign=top style=\"background:#EEE;color:#FFF;\">"
                        table += "<a href=\"https://kanji.koohii.com/study/kanji/%s\" style=\"color:#888;\">%s</a></td>\n" % (
                            2 * (char, ))
                    if count == -1:
                        table += "<strong style=\"color:#CCC\">None</strong>"
                    table += "</tr></table></details>\n"
                self.html += "<h4 style=\"color:#888;\">%d of %d - %0.2f%%</h4>\n" % (
                    n, t, n * 100.0 / t)
                self.html += table

            chars = reduce(lambda x, y: x + y, dict(_grades).values())
            self.html += "<h2 style=\"color:#888;\">%s Kanji</h2>" % _grades[
                0][0]
            table = "<table width='85%'><tr>\n"
            count = -1
            for unit in [u for u in units.values() if u.value not in chars]:
                if unit.count != 0 or _unseen:
                    score = "NaN"
                    count += 1
                    if count % cols == 0 and count != 0:
                        table += "</tr>\n<tr>\n"
                    if unit.count != 0:
                        bgcolour = hsvrgbstr(
                            scoreAdjust(unit.avg_interval / _interval) / 2)
                    else:
                        bgcolour = "#FFF"
                    if _tooltips:
                        tooltip = "Character: %s | Count: %s | " % (
                            unicodedata.name(unit.value), unit.count)
                        tooltip += "Avg Interval: %s | Score: %s | " % (
                            unit.avg_interval, score)
                        tooltip += "Background: %s | Index: %s" % (bgcolour,
                                                                   count)
                        table += "\t<td align=center valign=top style=\"background:%s;\" title=\"%s\">" % (
                            bgcolour, tooltip)
                    else:
                        table += "\t<td align=center valign=top style=\"background:%s;\">" % (
                            bgcolour)
                    table += "<a href=\"https://kanji.koohii.com/study/kanji/%s\">%s</a></td>\n" % (
                        2 * (unit.value, ))
            table += "</tr></table>\n"
            n = count + 1
            self.html += "<h4 style=\"color:#888;\">%d of %d - %0.2f%%</h4>\n" % (
                n, gc, n * 100.0 / gc)
            self.html += table
        else:
            table = "<table width='85%'><tr>\n"
            if _group == 2:  # Order found
                unitsList = sorted(units.values(),
                                   key=lambda unit: (unit.idx, unit.count))
            if _group == 3:  # Unicode index
                unitsList = sorted(units.values(),
                                   key=lambda unit:
                                   (unicodedata.name(unit.value), unit.count))
            if _group == 4:  # Character score
                unitsList = sorted(
                    units.values(),
                    key=lambda unit:
                    (scoreAdjust(unit.avg_interval / _interval), unit.count),
                    reverse=True)
            if _group == 5:  # Deck frequency
                unitsList = sorted(
                    units.values(),
                    key=lambda unit:
                    (unit.count, scoreAdjust(unit.avg_interval / _interval)),
                    reverse=True)
            count = -1
            for unit in unitsList:
                if unit.count != 0 or _unseen:
                    score = "NaN"
                    count += 1
                    if count % cols == 0 and count != 0:
                        table += "</tr>\n<tr>\n"
                    if unit.count != 0:
                        bgcolour = hsvrgbstr(
                            scoreAdjust(unit.avg_interval / _interval) / 2)
                    else:
                        bgcolour = "#FFF"
                    if _tooltips:
                        tooltip = "Character: %s | Count: %s | " % (
                            unicodedata.name(unit.value), unit.count)
                        tooltip += "Avg Interval: %s | Score: %s | " % (
                            unit.avg_interval, score)
                        tooltip += "Background: %s | Index: %s" % (bgcolour,
                                                                   count)
                        table += "\t<td align=center valign=top style=\"background:%s;\" title=\"%s\">" % (
                            bgcolour, tooltip)
                    else:
                        table += "\t<td align=center valign=top style=\"background:%s;\">" % (
                            bgcolour)
                    table += "<a href=\"https://kanji.koohii.com/study/kanji/%s\">%s</a></td>\n" % (
                        2 * (unit.value, ))
            table += "</tr></table>\n"
            self.html += "<h4 style=\"color:#888;\">%d total unique kanji</h4>\n" % (
                count + 1)
            self.html += table
        self.html += "</center></body></html>\n"

    def displaygrid(self, units, timeNow):
        self.generate(units, timeNow)
        # print("%s: %0.3f" % ("HTML generated",time.time()-_time))
        self.win = QDialog(mw)
        self.wv = AnkiWebView()
        vl = QVBoxLayout()
        vl.setMargin(0)
        vl.addWidget(self.wv)
        self.wv.stdHtml(self.html)
        hl = QHBoxLayout()
        vl.addLayout(hl)
        sh = QPushButton("Save HTML")
        hl.addWidget(sh)
        sh.connect(sh, SIGNAL("clicked()"), self.savehtml)
        sp = QPushButton("Save Image")
        hl.addWidget(sp)
        sp.connect(sp, SIGNAL("clicked()"), self.savepng)
        bb = QPushButton("Close")
        hl.addWidget(bb)
        bb.connect(bb, SIGNAL("clicked()"), self.win, SLOT("reject()"))
        self.win.setLayout(vl)
        self.win.resize(500, 400)
        # print("%s: %0.3f" % ("Window complete",time.time()-_time))
        return 0

    def savehtml(self):
        fileName = QFileDialog.getSaveFileName(
            self.win, "Save Page",
            QDesktopServices.storageLocation(QDesktopServices.DesktopLocation),
            "Web Page (*.html *.htm)")
        if fileName != "":
            mw.progress.start(immediate=True)
            if not ".htm" in fileName:
                fileName += ".html"
            fileOut = codecs.open(fileName, 'w', 'utf-8')
            (units, timeNow) = self.kanjigrid()
            self.generate(units, timeNow, True)
            fileOut.write(self.html)
            fileOut.close()
            mw.progress.finish()
            showInfo("Page saved to %s!" % os.path.abspath(fileOut.name))
        return

    def savepng(self):
        fileName = QFileDialog.getSaveFileName(
            self.win, "Save Page",
            QDesktopServices.storageLocation(QDesktopServices.DesktopLocation),
            "Portable Network Graphics (*.png)")
        if fileName != "":
            mw.progress.start(immediate=True)
            if not ".png" in fileName:
                fileName += ".png"
            p = self.wv.page()
            oldsize = p.viewportSize()
            p.setViewportSize(p.mainFrame().contentsSize())
            image = QImage(p.viewportSize(), QImage.Format_ARGB32)
            painter = QPainter(image)
            p.mainFrame().render(painter)
            painter.end()
            image.save(fileName, "png")
            p.setViewportSize(oldsize)
            mw.progress.finish()
            showInfo("Image saved to %s!" % os.path.abspath(fileName))
        return

    def kanjigrid(self):
        # self.did = mw.col.conf['curDeck']
        # dids = [self.did]
        # for name, id in mw.col.decks.children(self.did):
        #    dids.append(id)
        # print("%s: %0.3f" % ("Decks selected",time.time()-_time))
        # cids = mw.col.db.list("select id from cards where did in %s or odid in %s" % (ids2str(dids),ids2str(dids)))
        cids = mw.col.db.list("select id from cards")
        # print("%s: %0.3f" % ("Cards selected",time.time()-_time))

        units = dict()
        notes = dict()
        timeNow = time.time()
        for id, i in enumerate(cids):
            card = mw.col.getCard(i)
            if card.nid not in notes.keys():
                keys = card.note().keys()
                unitKey = None
                if _literal:
                    for s, key in ((key.lower(), key) for key in keys):
                        if _pattern == s.lower():
                            unitKey = card.note()[key]
                            break
                else:
                    for s, key in ((key.lower(), key) for key in keys):
                        if _pattern in s.lower():
                            unitKey = card.note()[key]
                            break
                notes[card.nid] = unitKey
            else:
                unitKey = notes[card.nid]
            if unitKey != None:
                for ch in unitKey:
                    addUnitData(units, ch, i, card, timeNow)
        # print("%s: %0.3f" % ("Units created",time.time()-_time))
        return units, timeNow

    def makegrid(self):
        # global _time
        # _time = time.time()
        # print("%s: %0.3f" % ("Start",time.time()-_time))
        (units, timeNow) = self.kanjigrid()
        if units is not None:
            self.displaygrid(units, timeNow)

    def setup(self):
        global _pattern, _literal
        global _interval, _thin, _wide
        global _group, _unseen, _tooltips
        swin = QDialog(mw)
        vl = QVBoxLayout()
        frm = QGroupBox("Settings")
        vl.addWidget(frm)
        il = QVBoxLayout()
        fl = QHBoxLayout()
        field = QLineEdit()
        field.setPlaceholderText(
            "e.g. \"kanji\" or \"sentence-kanji\" (default: \"kanji\")")
        il.addWidget(
            QLabel(
                "Pattern or Field name to search for (first used, case insensitive):"
            ))
        fl.addWidget(field)
        liter = QCheckBox("Match exactly")
        liter.setChecked(_literal)
        fl.addWidget(liter)
        il.addLayout(fl)
        stint = QSpinBox()
        stint.setRange(1, 65536)
        stint.setValue(_interval)
        il.addWidget(QLabel("Card interval considered strong:"))
        il.addWidget(stint)
        ttcol = QSpinBox()
        ttcol.setRange(1, 99)
        ttcol.setValue(_thin)
        il.addWidget(QLabel("Number of Columns in the in-app table:"))
        il.addWidget(ttcol)
        wtcol = QSpinBox()
        wtcol.setRange(1, 99)
        wtcol.setValue(_wide)
        il.addWidget(QLabel("Number of Columns in the exported table:"))
        il.addWidget(wtcol)
        group = QComboBox()
        group.addItems([
            "Kanji Kentei Level", "JLPT Grade", "None, sorted by order found",
            "None, sorted by unicode order", "None, sorted by score",
            "None, sorted by frequency"
        ])
        group.setCurrentIndex(_group)
        il.addWidget(QLabel("Group by:"))
        il.addWidget(group)
        shnew = QCheckBox("Show units not yet seen")
        shnew.setChecked(_unseen)
        il.addWidget(shnew)
        toolt = QCheckBox("Show informational tooltips")
        toolt.setChecked(_tooltips)
        il.addWidget(toolt)
        frm.setLayout(il)
        hl = QHBoxLayout()
        vl.addLayout(hl)
        gen = QPushButton("Generate")
        hl.addWidget(gen)
        gen.connect(gen, SIGNAL("clicked()"), swin, SLOT("accept()"))
        cls = QPushButton("Close")
        hl.addWidget(cls)
        cls.connect(cls, SIGNAL("clicked()"), swin, SLOT("reject()"))
        swin.setLayout(vl)
        swin.setTabOrder(gen, cls)
        swin.setTabOrder(cls, field)
        swin.setTabOrder(field, liter)
        swin.setTabOrder(liter, stint)
        swin.setTabOrder(stint, ttcol)
        swin.setTabOrder(ttcol, wtcol)
        swin.setTabOrder(wtcol, group)
        swin.setTabOrder(group, shnew)
        swin.setTabOrder(shnew, toolt)
        swin.resize(500, 400)
        if swin.exec_():
            mw.progress.start(immediate=True)
            if len(field.text().strip()) != 0: _pattern = field.text().lower()
            _literal = liter.isChecked()
            _interval = stint.value()
            _thin = ttcol.value()
            _wide = wtcol.value()
            _group = group.currentIndex()
            _unseen = shnew.isChecked()
            _tooltips = toolt.isChecked()
            self.makegrid()
            mw.progress.finish()
            self.win.show()
    def showDialog(self, currentCard=None):
        # Handle for dialog open without a current card from IR model
        did = None
        cid = None
        if not currentCard:
            deck = mw._selectedDeck()
            did = deck['id']
        else:
            did = currentCard.did
            cid = currentCard.id

        cardDataList = self.getCardDataList(did, cid)
        if not cardDataList:
            showInfo(_('Please select an Incremental Reading deck.'))
            return

        d = QDialog(mw)
        l = QVBoxLayout()
        w = AnkiWebView()
        l.addWidget(w)

        script = '''
        var cardList = new Array();
        '''
        index = 0
        for cardData in cardDataList:
            index += 1
            script += "card = new Object();"
            script += "card.id = " + str(cardData['id']) + ";"
            script += "card.title = '" + str(cardData['title']) + "';"
            script += "card.isCurrent = " + str(cardData['isCurrent']) + ";"
            script += "card.checkbox = document.createElement('input');"
            script += "card.checkbox.type = 'checkbox';"
            if cardData['isCurrent'] == 'true':
                script += "card.checkbox.setAttribute('checked', 'true');"
            script += "cardList[cardList.length] = card;"

        script += """
        function buildCardData() {
            var container = document.getElementById('cardList');
            container.innerHTML = '';
            var list = document.createElement('div');
            list.setAttribute('style','overflow:auto;');
            var table = document.createElement('table');
            list.appendChild(table);
            container.appendChild(list);
            var row;
            var col;
            var cardData;
            for (var i = 0; i < cardList.length; i++) {
                row = document.createElement('tr');
                row.setAttribute('id','row' + i);
                cardData = cardList[i];

                col = document.createElement('td');
                col.setAttribute('style','width:4em;');
                col.innerHTML = '' + i;
                row.appendChild(col);

                col = document.createElement('td');
                col.setAttribute('style','width:10em;');
                col.innerHTML = '' + cardData.id;
                row.appendChild(col);

                col = document.createElement('td');
                col.setAttribute('style','width:30em;');
                col.innerHTML = '' + cardData.title;
                row.appendChild(col);

                col = document.createElement('td');
                col.setAttribute('style','width:2em;');
                col.appendChild(cardData.checkbox);
                row.appendChild(col);

                table.appendChild(row);
            }
        }

        function reposition(origIndex, newIndex, isTopOfRange) {
            if (newIndex < 0 || newIndex > (cardList.length-1)) return -1;
            if (cardList[newIndex].checkbox.checked) return -1;

            if (isTopOfRange) {
                document.getElementById('newPos').value = newIndex;
            }
            var removedCards = cardList.splice(origIndex,1);
            cardList.splice(newIndex, 0, removedCards[0]);
            return newIndex;
        }

        function moveSelectedUp() {
            var topOfRange = -1;
            for (var i = 0; i < cardList.length; i++) {
                if (cardList[i].checkbox.checked) {
                    if (topOfRange == -1) {
                        topOfRange = i;
                    }
                    if (i == topOfRange) {
                        if (document.getElementById('anchor').checked) {
                            continue; //Don't move end of range if anchored.
                        } else {
                            reposition(i, i - 1, true);
                        }
                    } else {
                        reposition(i, i - 1, false);
                    }
                }
            }
            buildCardData();
        }

        function moveSelectedDown() {
            var topOfRange = -1;
            var bottomOfRange = -1;
            for (var i = 0; i < cardList.length; i++) {
                if (cardList[i].checkbox.checked) {
                    if (topOfRange == -1) {
                        topOfRange = i;
                    }
                    bottomOfRange = i;
                }
            }
            for (var i = cardList.length-1; i > -1; i--) {
                if (cardList[i].checkbox.checked) {
                    if (i == bottomOfRange &&
                            document.getElementById('anchor').checked) {
                        continue; //Don't move end of range if anchored.
                    }
                    if (i == topOfRange) {
                        reposition(i, i + 1, true);
                    } else {
                        reposition(i, i + 1, false);
                    }
                }
            }
            buildCardData();
        }

        function selectAll() {
            for (var i = 0; i < cardList.length; i++) {
                cardList[i].checkbox.checked = true;
            }
        }

        function selectNone() {
            for (var i = 0; i < cardList.length; i++) {
                cardList[i].checkbox.checked = false;
            }
        }

        function directMove() {
            var newIndex = document.getElementById('newPos').value;
            var topOfRange = -1;
            origIndex = -1;
            for (var i = 0; i < cardList.length; i++) {
                if (cardList[i].checkbox.checked) {
                    if (topOfRange == -1) {
                        topOfRange = i;
                    }
                    if (origIndex == -1) {
                        origIndex = i;
                        sizeOfMove = (newIndex - origIndex);
                    }
                }
            }
            if (sizeOfMove < 0) {
                for (var i = 0; i < cardList.length; i++) {
                    if (cardList[i].checkbox.checked) {
                        if (i == topOfRange) {
                            reposition(i, i + sizeOfMove, true);
                        } else {
                            reposition(i, i + sizeOfMove, false);
                        }
                    }
                }
            } else {
                for (var i = cardList.length-1; i > -1; i--) {
                    if (cardList[i].checkbox.checked) {
                        if (i == topOfRange) {
                            reposition(i, i + sizeOfMove, true);
                        } else {
                            reposition(i, i + sizeOfMove, false);
                        }
                    }
                }
            }
            buildCardData();
        }

        function updatePositions() {
            var cids = new Array();
            for (var i=0; i < cardList.length; i++) {
                cids[cids.length] = parseInt(cardList[i].id);
            }
            return cids.join();
        };
        """

        newPosField = "<span style='font-weight:bold'>Card Position: </span><input type='text' id='newPos' size='5' value='0' />&nbsp;<span style='font-weight:bold'>of " + str(
            len(cardDataList)) + "</span>&nbsp;&nbsp;"
        newPosField += "<input type='button' value='Apply' onclick='directMove()' />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style='font-weight:bold'>Pin Top/Bottom? </span><input type='checkbox' id='anchor'/>"

        upDownButtons = "<input type='button' value='Move Up' onclick='moveSelectedUp()'/><input type='button' value='Move Down' onclick='moveSelectedDown()'/>"
        upDownButtons += "<input type='button' value='Select All' onclick='selectAll()'/><input type='button' value='Select None' onclick='selectNone()'/>"

        html = "<html><head><script>" + script + "</script></head><body onLoad='buildCardData()'>"
        html += "<p>" + newPosField
        html += "<p>" + upDownButtons
        html += "<div id='cardList'></div>"
        html += "</body></html>"
        w.stdHtml(html)
        bb = QDialogButtonBox(QDialogButtonBox.Close | QDialogButtonBox.Save)
        bb.accepted.connect(d.accept)
        bb.rejected.connect(d.reject)
        bb.setOrientation(Qt.Horizontal)
        l.addWidget(bb)
        d.setLayout(l)
        d.setWindowModality(Qt.WindowModal)
        d.resize(500, 500)
        choice = d.exec_()
        if choice == 1:
            if ANKI_21:
                cids = w.page().runJavaScript('updatePositions()',
                                              self.callback)
            else:
                cids = w.page().mainFrame().evaluateJavaScript(
                    'updatePositions()')
                self.repositionCards(cids)
        elif currentCard:
            self.repositionCard(currentCard, -1)
Exemple #6
0
class KanjiGrid(QDialog):
    def __init__(self, mw):
        super(KanjiGrid, self).__init__(mw)
        self.mw = mw
        self.sets = KanjiSets()

        self.title = "Kanji Grid"
        self.setModal(True)
        self.setLayout(self.ui())
        self.setWindowTitle(self.title)
        self.resize(1200, 800)

        self.web.loadFinished.connect(super(KanjiGrid, self).show)
        self.web.loadFinished.connect(self.mw.progress.finish)

    def show(self, group_by):
        self.mw.progress.start(immediate=True,
                               label="Generating Kanji Grid...")
        self.web.stdHtml(self.generate(group_by))

    def ui(self):
        self.web = AnkiWebView()

        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.web)
        layout.addLayout(self.ui_buttons())

        return layout

    def ui_buttons(self):
        layout = QHBoxLayout()

        sh = QPushButton("Save HTML")
        sh.clicked.connect(self.savehtml)
        layout.addWidget(sh)

        sp = QPushButton("Save PDF")
        sp.clicked.connect(self.savepdf)
        layout.addWidget(sp)

        bb = QPushButton("Close")
        bb.clicked.connect(self.reject)
        layout.addWidget(bb)

        return layout

    def generate(self, group_by, separator=' | '):
        config = self.mw.addonManager.getConfig(__name__)
        self.last_group_by = group_by

        exclusions = {}
        deck_ids = [x for x in config['decks'] if config['decks'][x] > 0]

        for note in config['notes']:

            if note not in exclusions:
                exclusions[note] = []

            model = self.mw.col.models.get(note)
            field_names = [f['name'] for f in model['flds']]

            for fn in config['notes'][note]:
                if not config['notes'][note][fn]:
                    exclusions[note].append(field_names.index(fn))

        kanji = kanji_search(deck_ids, exclusions)
        tiers = self.sets[group_by]
        tier_docs = []

        for tier in tiers:
            tier_name = list(tier.keys())[0]
            tier_char = list(tier.values())[0]

            found, missing = find_kanji_in_tier(kanji, tier_char)
            tier_docs.append(
                tier_html(
                    tier_name,
                    kanji,
                    found,
                    missing,
                    cols=int(config['cols']),
                    threshold=int(config['threshold']),
                    separator=separator,
                    force_percent=True,
                ))
            for k in found:
                del kanji[k]

        tier_docs.append(
            tier_html(
                "Additional Kanji" if group_by != 'None' else 'All Kanji',
                kanji,
                list(kanji.keys()),
                [],
                cols=int(config['cols']),
                threshold=int(config['threshold']),
                separator=separator,
            ))

        return html_doc(', '.join(
            sorted([self.mw.col.decks.get(x)['name'] for x in deck_ids])),
                        ''.join(tier_docs),
                        threshold=config['threshold'])

    def savehtml(self):
        filename, _ = QFileDialog.getSaveFileName(self,
                                                  "Save KanjiGrid - HTML", "",
                                                  "Web Files (*.html)")

        if filename != "":
            self.mw.progress.start(immediate=True,
                                   label="Saving HTML Document")
            html = self.generate(self.last_group_by, separator='\n')

            with codecs.open(filename, 'w', 'utf-8') as fh:
                fh.write(html)

            self.mw.progress.finish()
            showInfo("HTML Document saved to: %s!" % filename)

    def savepdf(self):
        filename, _ = QFileDialog.getSaveFileName(
            self, "Save KanjiGrid - PDF", "",
            "Portable Document Files (*.pdf)")

        if filename != "":
            self.mw.progress.start(immediate=True, label="Saving PDF")
            self.web.page().printToPdf(filename)
            self.mw.progress.finish()
            showInfo("PDF saved to: %s!" % filename)
Exemple #7
0
 def showIRSchedulerDialog(self, currentCard):
     #Handle for dialog open without a current card from IRead2 model
     deckID = None;
     cardID = None;
     if(currentCard == None):
         deck = mw._selectedDeck();
         deckID = deck['id'];
     else:
         deckID = currentCard.did;
         cardID = currentCard.id;
     
     #Get the card data for the deck. Make sure it is an Incremental Reading deck (has IRead2 cards) before showing dialog
     cardDataList = self.getCardDataList(deckID, cardID);
     hasIRead2Cards = False;
     for cd in cardDataList:
         if(cd['title'] != 'No Title'): hasIRead2Cards = True;
     if(hasIRead2Cards == False):
         showInfo(_("Please select an Incremental Reading deck."))
         return;
     
     d = QDialog(self.mw)
     l = QVBoxLayout()
     l.setMargin(0)
     w = AnkiWebView()
     l.addWidget(w)
     #Add python object to take values back from javascript
     callback = IRSchedulerCallback();
     #callback.setCard(currentCard);
     w.page().mainFrame().addToJavaScriptWindowObject("callback", callback);
     #Script functions move up / move down / delete / open
     getIRSchedulerDialogScript = """       
     var cardList = new Array();
     """
     index = 0;
     for cardData in cardDataList:
         index+=1;
         getIRSchedulerDialogScript += "card = new Object();";
         getIRSchedulerDialogScript += "card.id = " + str(cardData['id']) + ";";
         getIRSchedulerDialogScript += "card.title = '" + str(cardData['title']) + "';";
         getIRSchedulerDialogScript += "card.isCurrent = " + str(cardData['isCurrent']) + ";";
         getIRSchedulerDialogScript += "card.checkbox = document.createElement('input');";
         getIRSchedulerDialogScript += "card.checkbox.type = 'checkbox';";
         if(cardData['isCurrent'] == 'true'): getIRSchedulerDialogScript += "card.checkbox.setAttribute('checked', 'true');";
         getIRSchedulerDialogScript += "cardList[cardList.length] = card;";
     
     getIRSchedulerDialogScript += """
     function buildCardData() {
         var container = document.getElementById('cardList');
         container.innerHTML = '';
         var list = document.createElement('div');
         list.setAttribute('style','overflow:auto;');
         var table = document.createElement('table');
         list.appendChild(table);
         container.appendChild(list);
         var row;
         var col;
         var cardData;
         for(var i = 0; i < cardList.length; i++) {
             row = document.createElement('tr');
             row.setAttribute('id','row' + i);
             cardData = cardList[i];
             
             col = document.createElement('td');
             col.setAttribute('style','width:4em;');
             col.innerHTML = '' + i;
             row.appendChild(col);
             
             col = document.createElement('td');
             col.setAttribute('style','width:10em;');
             col.innerHTML = '' + cardData.id;
             row.appendChild(col);
             
             col = document.createElement('td');
             col.setAttribute('style','width:30em;');
             col.innerHTML = '' + cardData.title;
             row.appendChild(col);
             
             col = document.createElement('td');
             col.setAttribute('style','width:2em;');
             col.appendChild(cardData.checkbox);
             row.appendChild(col);
             
             table.appendChild(row);
         }
     }
     
     function reposition(origIndex, newIndex, isTopOfRange) {
         if(newIndex < 0 || newIndex > (cardList.length-1)) return -1;
         if(cardList[newIndex].checkbox.checked) return -1;
         
         if(isTopOfRange) {
             document.getElementById('newPos').value = newIndex;
         }
         var removedCards = cardList.splice(origIndex,1);
         cardList.splice(newIndex, 0, removedCards[0]);
         return newIndex;
     }
     
     function moveSelectedUp() {
         var topOfRange = -1;
         for(var i = 0; i < cardList.length; i++) {
             if(cardList[i].checkbox.checked) {
                 if(topOfRange == -1) topOfRange = i;
                 if(i == topOfRange) {
                     if(document.getElementById('anchor').checked) continue; //Don't move end of range if anchored.
                     else reposition(i, i - 1, true);
                 } else reposition(i, i - 1, false);
             }
         }
         buildCardData();
     }
     
     function moveSelectedDown() {
         var topOfRange = -1;
         var bottomOfRange = -1
         for(var i = 0; i < cardList.length; i++) {
             if(cardList[i].checkbox.checked) {
                 if(topOfRange == -1) topOfRange = i;
                 bottomOfRange = i;
             }
         }
         for(var i = cardList.length-1; i > -1; i--) {
             if(cardList[i].checkbox.checked) {
                 if(i == bottomOfRange && document.getElementById('anchor').checked) {
                     continue; //Don't move end of range if anchored.
                 }
                 if(i == topOfRange) reposition(i, i + 1, true);
                 else reposition(i, i + 1, false);
             }
         }
         buildCardData();
     }
     
     function selectAll() {
         for(var i = 0; i < cardList.length; i++) {
             cardList[i].checkbox.checked = true;
         }
     }
     
     function selectNone() {
         for(var i = 0; i < cardList.length; i++) {
             cardList[i].checkbox.checked = false;
         }
     }
     
     function directMove() {
         var newIndex = document.getElementById('newPos').value;
         var topOfRange = -1;
         origIndex = -1;
         for(var i = 0; i < cardList.length; i++) {
             if(cardList[i].checkbox.checked) {
                 if(topOfRange == -1) topOfRange = i;
                 if(origIndex == -1) {
                     origIndex = i;
                     sizeOfMove = (newIndex - origIndex);
                 }
             }
         }
         if(sizeOfMove < 0) {
             for(var i = 0; i < cardList.length; i++) {
                 if(cardList[i].checkbox.checked) {
                     if(i == topOfRange) reposition(i, i + sizeOfMove, true);
                     else reposition(i, i + sizeOfMove, false);
                 }
             }
         } else {
             for(var i = cardList.length-1; i > -1; i--) {
                 if(cardList[i].checkbox.checked) {
                     if(i == topOfRange) reposition(i, i + sizeOfMove, true);
                     else reposition(i, i + sizeOfMove, false);
                 }
             }
         }
         buildCardData();
     }
     
     function updatePositions() {
         var cids = new Array();
         for(var i=0; i < cardList.length; i++) {
             cids[cids.length] = parseInt(cardList[i].id);
         }
         callback.updatePositions(cids);
     };
     """;
     
     #Incremental Reading list as a list of nested <div> tags (like a table, but more flexible)
     #position,title,series id, sequence number,card id (hidden)
     newPosField = "<span style='font-weight:bold'>Card Position: </span><input type='text' id='newPos' size='5' value='0' />&nbsp;<span style='font-weight:bold'>of " + str(len(cardDataList)) + "</span>&nbsp;&nbsp;";
     newPosField += "<input type='button' value='Apply' onclick='directMove()' />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style='font-weight:bold'>Pin Top/Bottom? </span><input type='checkbox' id='anchor'/>";
     
     upDownButtons = "<input type='button' value='Move Up' onclick='moveSelectedUp()'/><input type='button' value='Move Down' onclick='moveSelectedDown()'/>";
     upDownButtons += "<input type='button' value='Select All' onclick='selectAll()'/><input type='button' value='Select None' onclick='selectNone()'/>";
     
     html = "<html><head><script>" + getIRSchedulerDialogScript + "</script></head><body onLoad='buildCardData()'>";
     html += "<p>" + newPosField;
     html += "<p>" + upDownButtons;
     html += "<div id='cardList'></div>";
     html += "</body></html>";
     w.stdHtml(html);
     bb = QDialogButtonBox(QDialogButtonBox.Close|QDialogButtonBox.Save)
     bb.connect(bb, SIGNAL("accepted()"), d, SLOT("accept()"))
     bb.connect(bb, SIGNAL("rejected()"), d, SLOT("reject()"))
     bb.setOrientation(QtCore.Qt.Horizontal);
     l.addWidget(bb)
     d.setLayout(l)
     d.setWindowModality(Qt.WindowModal)
     d.resize(500, 500)
     choice = d.exec_();
     if(choice == 1):
         w.eval("updatePositions()");
     else:
         if(currentCard != None): self.repositionCard(currentCard, -1);
Exemple #8
0
    def callIRSchedulerOptionsDialog(self):
        d = QDialog(self.mw)
        l = QVBoxLayout()
        l.setMargin(0)
        w = AnkiWebView()
        l.addWidget(w)
        #Add python object to take values back from javascript
        callback = IROptionsCallback();
        w.page().mainFrame().addToJavaScriptWindowObject("callback", callback);
        getScript = """
        function updateIRSchedulerOptions() {
            //invoke the callback object
            var soonTypeCnt = document.getElementById('soonCntButton').checked;
            var laterTypeCnt = document.getElementById('laterCntButton').checked;
            var soonRandom = document.getElementById('soonRandom').checked;
            var laterRandom = document.getElementById('laterRandom').checked;
            var options = ''
            //Soon Button
            if(soonTypeCnt) options += 'cnt,';
            else options += 'pct,';
            options += document.getElementById('soonValue').value + ',';
            if(soonRandom) options += 'true,';
            else options += 'false,';
            //Later Button
            if(laterTypeCnt) options += 'cnt,';
            else options += 'pct,';
            options += document.getElementById('laterValue').value + ',';
            if(laterRandom) options += 'true';
            else options += 'false';
            callback.updateOptions(options);
        };
        """

        isCntChecked = '';
        isPctChecked = '';
        isRandomChecked = '';
        if(self.schedSoonType == 'cnt'): 
            isCntChecked = 'checked';
            isPctChecked = '';
        else: 
            isCntChecked = '';
            isPctChecked = 'checked';
        if(self.schedSoonRandom): isRandomChecked = 'checked';
        else: isRandomChecked = '';
        soonButtonConfig = "<span style='font-weight:bold'>Soon Button: &nbsp;</span>";
        soonButtonConfig += "<input type='radio' id='soonCntButton' name='soonCntOrPct' value='cnt' " + isCntChecked + " /> Position &nbsp;&nbsp;";
        soonButtonConfig += "<input type='radio' id='soonPctButton' name='soonCntOrPct' value='pct' " + isPctChecked + " /> Percent&nbsp;";
        soonButtonConfig += "<input type='text' size='5' id='soonValue' value='" + str(self.schedSoonInt) + "'/>";
        soonButtonConfig += "<span style='font-weight:bold'>&nbsp;&nbsp;&nbsp;&nbsp;Randomize?&nbsp;</span><input type='checkbox' id='soonRandom' " + isRandomChecked + " /><br/>";
        if(self.schedLaterType == 'cnt'): 
            isCntChecked = 'checked';
            isPctChecked = '';
        else: 
            isCntChecked = '';
            isPctChecked = 'checked';
        if(self.schedLaterRandom): isRandomChecked = 'checked';
        else: isRandomChecked = '';
        laterButtonConfig = "<span style='font-weight:bold'>Later Button: &nbsp;</span>";
        laterButtonConfig += "<input type='radio' id='laterCntButton' name='laterCntOrPct' value='cnt' " + isCntChecked + " /> Position &nbsp;&nbsp;";
        laterButtonConfig += "<input type='radio'  id='laterPctButton' name='laterCntOrPct' value='pct' " + isPctChecked + " /> Percent&nbsp;";
        laterButtonConfig += "<input type='text' size='5' id='laterValue' value='" + str(self.schedLaterInt) + "'/>";
        laterButtonConfig += "<span style='font-weight:bold'>&nbsp;&nbsp;&nbsp;&nbsp;Randomize?&nbsp;</span><input type='checkbox' id='laterRandom' " + isRandomChecked + " /><br/>";
        
        html = "<html><head><script>" + getScript + "</script></head><body>";
        html += "<p>" + soonButtonConfig;
        html += "<p>" + laterButtonConfig;
        html += "</body></html>";
        w.stdHtml(html);
        bb = QDialogButtonBox(QDialogButtonBox.Close|QDialogButtonBox.Save)
        bb.connect(bb, SIGNAL("accepted()"), d, SLOT("accept()"))
        bb.connect(bb, SIGNAL("rejected()"), d, SLOT("reject()"))
        bb.setOrientation(QtCore.Qt.Horizontal);
        l.addWidget(bb)
        d.setLayout(l)
        d.setWindowModality(Qt.WindowModal)
        d.resize(500, 140)
        choice = d.exec_();
        if(choice == 1):
            w.eval("updateIRSchedulerOptions()");
Exemple #9
0
    def showAddCardQuickKeysDialog(self):
        #set values from lastDialogQuickKey or use default
        if (len(self.lastDialogQuickKey.keys()) < 1):
            self.setDefaultDialogValues(self.lastDialogQuickKey)

        d = QDialog(self.mw)
        l = QVBoxLayout()
        l.setMargin(0)
        w = AnkiWebView()
        l.addWidget(w)
        #Add python object to take values back from javascript
        quickKeyModel = QuickKeyModel()
        w.page().mainFrame().addToJavaScriptWindowObject(
            "quickKeyModel", quickKeyModel)
        #deck combo box
        deckComboBox = "<span style='font-weight:bold'>Deck: </span><select id='decks'>"
        allDecks = mw.col.decks.all()
        allDecks.sort(key=lambda dck: dck['name'], reverse=False)
        for deck in allDecks:
            isSelected = ''
            if (self.lastDialogQuickKey.get('deckName', None) == deck['name']):
                isSelected = 'selected'
            deckComboBox = deckComboBox + (
                "<option value='" + str(deck['id']) + "' " + isSelected + ">" +
                deck['name'] + "</option>")
        deckComboBox = deckComboBox + "</select>"
        #model combo box
        fieldChooserByModel = {}
        modelComboBox = "<span style='font-weight:bold'>Model: </span><select id='models'>"
        allModels = mw.col.models.all()
        allModels.sort(key=lambda mod: mod['name'], reverse=False)
        for model in allModels:
            isSelected = ''
            if (self.lastDialogQuickKey.get('modelName',
                                            None) == model['name']):
                isSelected = 'selected'
            modelComboBox = modelComboBox + (
                "<option value='" + str(model['id']) + "' " + isSelected +
                ">" + model['name'] + "</option>")
            listOfFields = model['flds']
            fieldComboBox = ""
            for field in listOfFields:
                fieldComboBox = fieldComboBox + ("<option value='" +
                                                 field['name'] + "'>" +
                                                 field['name'] + "</option>")
            fieldChooserByModel[str(model['id'])] = fieldComboBox
        modelComboBox = modelComboBox + "</select>"

        ctrl = ''
        if (self.lastDialogQuickKey.get('ctrl', 1) == 1): ctrl = 'checked'
        shift = ''
        if (self.lastDialogQuickKey.get('shift', 0) == 1): shift = 'checked'
        alt = ''
        if (self.lastDialogQuickKey.get('alt', 0) == 1): alt = 'checked'

        #Ctrl checkbox
        ctrlCheckbox = "<span style='font-weight:bold'>Ctrl: </span><input type='checkbox' id='ctrl' " + ctrl + " />"
        #Shift checkbox
        shiftCheckbox = "<span style='font-weight:bold'>Shift: </span><input type='checkbox' id='shift' " + shift + "/>"
        #Alt checkbox
        altCheckbox = "<span style='font-weight:bold'>Alt: </span><input type='checkbox' id='alt' " + alt + "/>"

        #shortcut key combo box
        keyComboBox = "<span style='font-weight:bold'>Key: </span><select id='keys'>"
        isSelected = ''
        for val in range(0, 10):
            if (str(val) == str(self.lastDialogQuickKey.get('keyName', '0'))):
                isSelected = 'selected'
            keyComboBox = keyComboBox + ("<option value='" + str(val) + "' " +
                                         isSelected + ">" + str(val) +
                                         "</option>")
            isSelected = ''
        for code in range(ord('a'), ord('z') + 1):
            if (str(chr(code)) == str(
                    self.lastDialogQuickKey.get('keyName', '0'))):
                isSelected = 'selected'
            keyComboBox = keyComboBox + ("<option value='" + chr(code) + "' " +
                                         isSelected + ">" + chr(code) +
                                         "</option>")
            isSelected = ''
        keyComboBox = keyComboBox + "</select>"
        #color text box
        colorValue = self.lastDialogQuickKey.get('color', 'yellow')
        colorTextField = "<span style='font-weight:bold'>Source highlighting color (IRead2 model only): </span><input type='text' id='color' value='" + colorValue + "' />"
        #radio buttons to chose if hilight or color text
        colorBackground = 'checked'
        colorText = ''
        if (self.lastDialogQuickKey.get('colorText', 'false') == 'true'):
            colorText = 'checked'
            colorBackground = ''
        colorBackOrText = "<span style='font-weight:bold'>Apply color to: &nbsp;</span><input type='radio' id='colorBackOrText' name='colorBackOrText' value='false' " + colorBackground + "/> Background &nbsp;&nbsp;<input type='radio' name='colorBackOrText' value='true' " + colorText + " /> Text<br />"
        #show editor checkbox
        doShowEditor = ''
        if (self.lastDialogQuickKey.get('showEditor', 1) == 1):
            doShowEditor = 'checked'
        showEditorCheckbox = "<span style='font-weight:bold'>Show Add Cards dialog?: </span><input type='checkbox' id='showEditor' " + doShowEditor + " />"
        #show current card editor checkbox
        doShowEditCurrent = ''
        if (self.lastDialogQuickKey.get('showEditCurrent', 0) == 1):
            doShowEditCurrent = 'checked'
        showEditCurrentCheckbox = "<span style='font-weight:bold'>Show Edit Current dialog?: </span><input type='checkbox' id='showEditCurrent' " + doShowEditCurrent + "/>"
        #remove shortcut checkbox
        doEnable = ''
        if (self.lastDialogQuickKey.get('enabled', 1) == 1):
            doEnable = 'checked'
        enabledCheckbox = "<span style='font-weight:bold'>Enable (uncheck to disable): </span><input type='checkbox' id='enabled' " + doEnable + " />"

        #javascript to populate field box based on selected model
        javascript = "var fieldsByModel = {};\n"
        for model in mw.col.models.all():
            listOfFields = model['flds']
            javascript += "fieldsByModel['" + model['name'] + "'] = ["
            for field in listOfFields:
                javascript += "'" + re.escape(field['name']) + "',"
            javascript = javascript[:-1]
            javascript += "];\n"
        javascript += """
        function setFieldsForModel(mName) {
            var list = fieldsByModel[mName];
            var options = '';
            for(var i=0; i < list.length; i++) {
                var isSelected = '';
                if(list[i] == pasteToFieldValue) isSelected = 'selected';
                options += '<option value=\\'' + list[i] + '\\' ' + isSelected + '>' + list[i] + '</option>';
            }
            document.getElementById('fields').innerHTML = options;
        }
        """
        javascript += "var pasteToFieldValue = '" + str(
            self.lastDialogQuickKey.get('fieldName', '')) + "';\n"
        html = "<html><head><script>" + javascript + "</script></head><body>"
        html += deckComboBox + "<p>"
        html += modelComboBox
        html += "<p><span style='font-weight:bold'>Paste Text to Field: </span><select id='fields'>"
        html += fieldComboBox + "</select>"
        html += "<p><span style='font-weight:bold'>Key Combination:</span>&nbsp;&nbsp;" + ctrlCheckbox + "&nbsp;&nbsp;" + shiftCheckbox + "&nbsp;&nbsp;" + altCheckbox + "&nbsp;&nbsp;" + keyComboBox
        #html += "<p>" + keyComboBox;
        html += "<p>" + colorTextField
        html += "<p>" + colorBackOrText
        html += "<p>" + showEditorCheckbox
        html += "<p>" + showEditCurrentCheckbox
        html += "<p>" + enabledCheckbox
        html += "</body></html>"
        #print html;
        w.stdHtml(html)
        #Dynamically add the javascript hook to call the setFieldsForModel function
        addHooksScript = """
        document.getElementById('models').onchange=function() {
            var sel = document.getElementById('models'); 
            setFieldsForModel(sel.options[sel.selectedIndex].text);
        };
        function getValues() {
            var sel = document.getElementById('decks'); 
            quickKeyModel.setDeck(sel.options[sel.selectedIndex].text);
            sel = document.getElementById('models'); 
            quickKeyModel.setModel(sel.options[sel.selectedIndex].text);
            sel = document.getElementById('fields'); 
            quickKeyModel.setField(sel.options[sel.selectedIndex].text);
            sel = document.getElementById('ctrl'); 
            quickKeyModel.setCtrl(sel.checked);
            sel = document.getElementById('shift'); 
            quickKeyModel.setShift(sel.checked);
            sel = document.getElementById('alt'); 
            quickKeyModel.setAlt(sel.checked);
            sel = document.getElementById('keys'); 
            quickKeyModel.setKey(sel.options[sel.selectedIndex].text);
            quickKeyModel.setSourceHighlightColor(document.getElementById('color').value.trim());
            sel = document.getElementById('colorBackOrText'); 
            if(sel.checked) {
                quickKeyModel.setColorText('false');
            } else {
                quickKeyModel.setColorText('true');
            }
            sel = document.getElementById('showEditor'); 
            quickKeyModel.setShowEditor(sel.checked);
            sel = document.getElementById('showEditCurrent'); 
            quickKeyModel.setShowEditCurrent(sel.checked);
            sel = document.getElementById('enabled'); 
            quickKeyModel.setEnabled(sel.checked);
        };
        //Set the fields for the selected model
	    var sel = document.getElementById('models'); 
        setFieldsForModel(sel.options[sel.selectedIndex].text);
        """
        w.eval(addHooksScript)
        bb = QDialogButtonBox(QDialogButtonBox.Close | QDialogButtonBox.Save)
        bb.connect(bb, SIGNAL("accepted()"), d, SLOT("accept()"))
        bb.connect(bb, SIGNAL("rejected()"), d, SLOT("reject()"))
        bb.setOrientation(QtCore.Qt.Horizontal)
        l.addWidget(bb)
        d.setLayout(l)
        d.setWindowModality(Qt.WindowModal)
        d.resize(700, 500)
        choice = d.exec_()

        w.eval("getValues()")
        #move values to a map so they can be serialized to file later (Qt objects don't pickle well)
        keyModel = {}
        keyModel['deckName'] = quickKeyModel.deckName
        keyModel['modelName'] = quickKeyModel.modelName
        keyModel['fieldName'] = quickKeyModel.fieldName

        #Ctrl + Shift + Alt + Key
        ctrl = 0
        if (quickKeyModel.ctrl == 'true'): ctrl = 1
        keyModel['ctrl'] = ctrl
        shift = 0
        if (quickKeyModel.shift == 'true'): shift = 1
        keyModel['shift'] = shift
        alt = 0
        if (quickKeyModel.alt == 'true'): alt = 1
        keyModel['alt'] = alt
        keyModel['keyName'] = quickKeyModel.keyName

        keyModel['color'] = quickKeyModel.color
        keyModel['colorText'] = quickKeyModel.colorText
        doShowEditor = 0
        if (quickKeyModel.showEditor == 'true'):
            doShowEditor = 1
        keyModel['showEditor'] = doShowEditor
        doShowEditCurrent = 0
        if (quickKeyModel.showEditCurrent == 'true'):
            doShowEditCurrent = 1
        keyModel['showEditCurrent'] = doShowEditCurrent
        keyModel['enabled'] = 1 if (quickKeyModel.enabled) else 0
        #Save the last selected values in the dialog for later use
        self.lastDialogQuickKey = keyModel
        #If SAVE chosen, then save the model as a new shortcut
        if (choice == 1):
            self.setQuickKey(keyModel)