This commit is contained in:
Soren I. Bjornstad 2013-10-19 10:41:44 -05:00
commit 367a961bba
12 changed files with 162 additions and 51 deletions

View file

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright: Damien Elmes <anki@ichi2.net> # Copyright: Damien Elmes <anki@ichi2.net>
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import pprint
import time import time
from anki.utils import intTime, timestampID, joinFields from anki.utils import intTime, timestampID, joinFields
@ -106,6 +107,7 @@ insert or replace into cards values
self.odid, self.odid,
self.flags, self.flags,
self.data) self.data)
self.col.log(self)
def flushSched(self): def flushSched(self):
self.mod = intTime() self.mod = intTime()
@ -121,6 +123,7 @@ lapses=?, left=?, odue=?, odid=?, did=? where id = ?""",
self.mod, self.usn, self.type, self.queue, self.due, self.ivl, self.mod, self.usn, self.type, self.queue, self.due, self.ivl,
self.factor, self.reps, self.lapses, self.factor, self.reps, self.lapses,
self.left, self.odue, self.odid, self.did, self.id) self.left, self.odue, self.odid, self.did, self.id)
self.col.log(self)
def q(self, reload=False, browser=False): def q(self, reload=False, browser=False):
return self.css() + self._getQA(reload, browser)['q'] return self.css() + self._getQA(reload, browser)['q']
@ -180,3 +183,12 @@ lapses=?, left=?, odue=?, odid=?, did=? where id = ?""",
self.model(), joinFields(self.note().fields)) self.model(), joinFields(self.note().fields))
if self.ord not in ords: if self.ord not in ords:
return True return True
def __repr__(self):
d = dict(self.__dict__)
# remove non-useful elements
del d['_note']
del d['_qa']
del d['col']
del d['timerStarted']
return pprint.pformat(d, width=300)

View file

@ -51,6 +51,7 @@ class _Collection(object):
def __init__(self, db, server=False): def __init__(self, db, server=False):
self.db = db self.db = db
self.path = db._path self.path = db._path
self.log(self.path, anki.version)
self.server = server self.server = server
self._lastSave = time.time() self._lastSave = time.time()
self.clearUndo() self.clearUndo()
@ -204,8 +205,11 @@ crt=?, mod=?, scm=?, dty=?, usn=?, ls=?, conf=?""",
# Object creation helpers # Object creation helpers
########################################################################## ##########################################################################
def getCard(self, id): def getCard(self, id, log=True):
return anki.cards.Card(self, id) c = anki.cards.Card(self, id)
if log:
self.log(c, stack=1)
return c
def getNote(self, id): def getNote(self, id):
return anki.notes.Note(self, id=id) return anki.notes.Note(self, id=id)
@ -767,3 +771,9 @@ and queue = 0""", intTime(), self.usn())
self.db.execute("vacuum") self.db.execute("vacuum")
self.db.execute("analyze") self.db.execute("analyze")
self.lock() self.lock()
# Logging
##########################################################################
def log(self, *args, **kwargs):
runHook("log", args, kwargs)

View file

@ -71,6 +71,8 @@ defaultConf = {
'minSpace': 1, # not currently used 'minSpace': 1, # not currently used
'ivlFct': 1, 'ivlFct': 1,
'maxIvl': 36500, 'maxIvl': 36500,
# may not be set on old decks
'bury': True,
}, },
'maxTaken': 60, 'maxTaken': 60,
'timer': 0, 'timer': 0,

View file

@ -225,7 +225,7 @@ class MediaManager(object):
nfcFile = unicodedata.normalize("NFC", file) nfcFile = unicodedata.normalize("NFC", file)
# we enforce NFC fs encoding on non-macs; on macs we'll have gotten # we enforce NFC fs encoding on non-macs; on macs we'll have gotten
# NFD so we use the above variable for comparing references # NFD so we use the above variable for comparing references
if not isMac: if not isMac and local:
if file != nfcFile: if file != nfcFile:
# delete if we already have the NFC form, otherwise rename # delete if we already have the NFC form, otherwise rename
if os.path.exists(nfcFile): if os.path.exists(nfcFile):

View file

@ -69,7 +69,7 @@ insert or replace into notes values (?,?,?,?,?,?,?,?,?,?,?)""",
return joinFields(self.fields) return joinFields(self.fields)
def cards(self): def cards(self):
return [self.col.getCard(id) for id in self.col.db.list( return [self.col.getCard(id, log=False) for id in self.col.db.list(
"select id from cards where nid = ? order by ord", self.id)] "select id from cards where nid = ? order by ord", self.id)]
def model(self): def model(self):

View file

@ -3,9 +3,12 @@
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from __future__ import division from __future__ import division
import time, random, itertools import time
import random
import itertools
from operator import itemgetter from operator import itemgetter
from heapq import * from heapq import *
#from anki.cards import Card #from anki.cards import Card
from anki.utils import ids2str, intTime, fmtTimeSpan from anki.utils import ids2str, intTime, fmtTimeSpan
from anki.lang import _ from anki.lang import _
@ -52,6 +55,7 @@ class Scheduler(object):
self._haveQueues = True self._haveQueues = True
def answerCard(self, card, ease): def answerCard(self, card, ease):
self.col.log()
assert ease >= 1 and ease <= 4 assert ease >= 1 and ease <= 4
self.col.markReview(card) self.col.markReview(card)
if self._burySiblingsOnAnswer: if self._burySiblingsOnAnswer:
@ -139,14 +143,19 @@ order by due""" % self._deckLimit(),
def unburyCards(self): def unburyCards(self):
"Unbury cards." "Unbury cards."
self.col.conf['lastUnburied'] = self.today self.col.conf['lastUnburied'] = self.today
self.col.log(
self.col.db.list("select id from cards where queue = -2"))
self.col.db.execute( self.col.db.execute(
"update cards set mod=?,usn=?,queue=type where queue = -2", "update cards set queue=type where queue = -2")
intTime(), self.col.usn())
def unburyCardsForDeck(self): def unburyCardsForDeck(self):
sids = ids2str(self.col.decks.active())
self.col.log(
self.col.db.list("select id from cards where queue = -2 and did in %s"
% sids))
self.col.db.execute( self.col.db.execute(
"update cards set mod=?,usn=?,queue=type where queue = -2 and did in %s" "update cards set mod=?,usn=?,queue=type where queue = -2 and did in %s"
% ids2str(self.col.decks.active()), intTime(), self.col.usn()) % sids, intTime(), self.col.usn())
# Rev/lrn/time daily stats # Rev/lrn/time daily stats
########################################################################## ##########################################################################
@ -943,12 +952,14 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
ids = [] ids = []
return ids return ids
# move the cards over # move the cards over
self.col.log(deck['id'], ids)
self._moveToDyn(deck['id'], ids) self._moveToDyn(deck['id'], ids)
return ids return ids
def emptyDyn(self, did, lim=None): def emptyDyn(self, did, lim=None):
if not lim: if not lim:
lim = "did = %s" % did lim = "did = %s" % did
self.col.log(self.col.db.list("select id from cards where %s" % lim))
# move out of cram queue # move out of cram queue
self.col.db.execute(""" self.col.db.execute("""
update cards set did = odid, queue = (case when type = 1 then 0 update cards set did = odid, queue = (case when type = 1 then 0
@ -1111,6 +1122,7 @@ did = ?, queue = %s, due = ?, mod = ?, usn = ? where id = ?""" % queue, data)
self.today = int((time.time() - self.col.crt) // 86400) self.today = int((time.time() - self.col.crt) // 86400)
# end of day cutoff # end of day cutoff
self.dayCutoff = self.col.crt + (self.today+1)*86400 self.dayCutoff = self.col.crt + (self.today+1)*86400
self.col.log(self.today, self.dayCutoff)
# update all daily counts, but don't save decks to prevent needless # update all daily counts, but don't save decks to prevent needless
# conflicts. we'll save on card answer instead # conflicts. we'll save on card answer instead
def update(g): def update(g):
@ -1239,6 +1251,7 @@ To study outside of the normal schedule, click the Custom Study button below."""
def suspendCards(self, ids): def suspendCards(self, ids):
"Suspend cards." "Suspend cards."
self.col.log(ids)
self.remFromDyn(ids) self.remFromDyn(ids)
self.removeLrn(ids) self.removeLrn(ids)
self.col.db.execute( self.col.db.execute(
@ -1247,6 +1260,7 @@ To study outside of the normal schedule, click the Custom Study button below."""
def unsuspendCards(self, ids): def unsuspendCards(self, ids):
"Unsuspend cards." "Unsuspend cards."
self.col.log(ids)
self.col.db.execute( self.col.db.execute(
"update cards set queue=type,mod=?,usn=? " "update cards set queue=type,mod=?,usn=? "
"where queue = -1 and id in "+ ids2str(ids), "where queue = -1 and id in "+ ids2str(ids),
@ -1256,6 +1270,7 @@ To study outside of the normal schedule, click the Custom Study button below."""
"Bury all cards for note until next session." "Bury all cards for note until next session."
cids = self.col.db.list( cids = self.col.db.list(
"select id from cards where nid = ? and queue >= 0", nid) "select id from cards where nid = ? and queue >= 0", nid)
self.col.log(cids)
self.removeLrn(cids) self.removeLrn(cids)
self.col.db.execute(""" self.col.db.execute("""
update cards set queue=-2,mod=?,usn=? where id in """+ids2str(cids), update cards set queue=-2,mod=?,usn=? where id in """+ids2str(cids),
@ -1266,15 +1281,19 @@ update cards set queue=-2,mod=?,usn=? where id in """+ids2str(cids),
def _burySiblings(self, card): def _burySiblings(self, card):
toBury = [] toBury = []
conf = self._newConf(card) nconf = self._newConf(card)
buryNew = conf.get("bury", True) buryNew = nconf.get("bury", True)
rconf = self._revConf(card)
buryRev = rconf.get("bury", True)
# loop through and remove from queues # loop through and remove from queues
for cid,queue in self.col.db.execute(""" for cid,queue in self.col.db.execute("""
select id, queue from cards where nid=? and id!=? select id, queue from cards where nid=? and id!=?
and (queue=0 or (queue=2 and due<=?))""", and (queue=0 or (queue=2 and due<=?))""",
card.nid, card.id, self.today): card.nid, card.id, self.today):
if queue == 2: if queue == 2:
toBury.append(cid) if buryRev:
toBury.append(cid)
# if bury disabled, we still discard to give same-day spacing
try: try:
self._revQueue.remove(cid) self._revQueue.remove(cid)
except ValueError: except ValueError:
@ -1288,9 +1307,11 @@ and (queue=0 or (queue=2 and due<=?))""",
except ValueError: except ValueError:
pass pass
# then bury # then bury
self.col.db.execute( if toBury:
"update cards set queue=-2,mod=?,usn=? where id in "+ids2str(toBury), self.col.db.execute(
intTime(), self.col.usn()) "update cards set queue=-2,mod=?,usn=? where id in "+ids2str(toBury),
intTime(), self.col.usn())
self.col.log(toBury)
# Resetting # Resetting
########################################################################## ##########################################################################
@ -1304,6 +1325,7 @@ and (queue=0 or (queue=2 and due<=?))""",
"select max(due) from cards where type=0") or 0 "select max(due) from cards where type=0") or 0
# takes care of mod + usn # takes care of mod + usn
self.sortCards(ids, start=pmax+1) self.sortCards(ids, start=pmax+1)
self.col.log(ids)
def reschedCards(self, ids, imin, imax): def reschedCards(self, ids, imin, imax):
"Put cards in review queue with a new interval in days (min, max)." "Put cards in review queue with a new interval in days (min, max)."
@ -1319,6 +1341,7 @@ and (queue=0 or (queue=2 and due<=?))""",
update cards set type=2,queue=2,ivl=:ivl,due=:due, update cards set type=2,queue=2,ivl=:ivl,due=:due,
usn=:usn, mod=:mod, factor=:fact where id=:id and odid=0 and queue >=0""", usn=:usn, mod=:mod, factor=:fact where id=:id and odid=0 and queue >=0""",
d) d)
self.col.log(ids)
def resetCards(self, ids): def resetCards(self, ids):
"Completely reset cards for export." "Completely reset cards for export."
@ -1328,6 +1351,7 @@ usn=:usn, mod=:mod, factor=:fact where id=:id and odid=0 and queue >=0""",
self.col.db.execute( self.col.db.execute(
"update cards set reps=0, lapses=0 where id in " + ids2str(nonNew)) "update cards set reps=0, lapses=0 where id in " + ids2str(nonNew))
self.forgetCards(nonNew) self.forgetCards(nonNew)
self.col.log(ids)
# Repositioning new cards # Repositioning new cards
########################################################################## ##########################################################################
@ -1370,6 +1394,7 @@ and due >= ? and queue = 0""" % scids, now, self.col.usn(), shiftby, low)
d.append(dict(now=now, due=due[nid], usn=self.col.usn(), cid=id)) d.append(dict(now=now, due=due[nid], usn=self.col.usn(), cid=id))
self.col.db.executemany( self.col.db.executemany(
"update cards set due=:due,mod=:now,usn=:usn where id = :cid", d) "update cards set due=:due,mod=:now,usn=:usn where id = :cid", d)
self.col.log(cids)
def randomizeCards(self, did): def randomizeCards(self, did):
cids = self.col.db.list("select id from cards where did = ?", did) cids = self.col.db.list("select id from cards where did = ?", did)

View file

@ -3,11 +3,15 @@
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from __future__ import division from __future__ import division
import time, datetime, json import time
import datetime
import json
import anki.js import anki.js
from anki.utils import fmtTimeSpan, ids2str from anki.utils import fmtTimeSpan, ids2str
from anki.lang import _, ngettext from anki.lang import _, ngettext
# Card stats # Card stats
########################################################################## ##########################################################################
@ -56,6 +60,8 @@ class CardStats(object):
self.addLine(_("Card Type"), c.template()['name']) self.addLine(_("Card Type"), c.template()['name'])
self.addLine(_("Note Type"), c.model()['name']) self.addLine(_("Note Type"), c.model()['name'])
self.addLine(_("Deck"), self.col.decks.name(c.did)) self.addLine(_("Deck"), self.col.decks.name(c.did))
self.addLine(_("Note ID"), c.nid)
self.addLine(_("Card ID"), c.id)
self.txt += "</table>" self.txt += "</table>"
return self.txt return self.txt

View file

@ -17,9 +17,12 @@ from hooks import runHook
import anki import anki
# syncing vars # syncing vars
HTTP_TIMEOUT = 30 HTTP_TIMEOUT = 90
HTTP_PROXY = None HTTP_PROXY = None
# badly named; means no retries, and doesn't affect ssl connections
httplib2.RETRIES = 1
try: try:
# httplib2 >=0.7.7 # httplib2 >=0.7.7
_proxy_info_from_environment = httplib2.proxy_info_from_environment _proxy_info_from_environment = httplib2.proxy_info_from_environment
@ -105,6 +108,7 @@ class Syncer(object):
# step 1: login & metadata # step 1: login & metadata
runHook("sync", "login") runHook("sync", "login")
meta = self.server.meta() meta = self.server.meta()
self.col.log("rmeta", meta)
if not meta: if not meta:
return "badAuth" return "badAuth"
rscm = meta['scm'] rscm = meta['scm']
@ -125,19 +129,24 @@ class Syncer(object):
# and require confirmation if it's non-empty # and require confirmation if it's non-empty
pass pass
meta = self.meta() meta = self.meta()
self.col.log("lmeta", meta)
self.lmod = meta['mod'] self.lmod = meta['mod']
self.minUsn = meta['usn'] self.minUsn = meta['usn']
lscm = meta['scm'] lscm = meta['scm']
lts = meta['ts'] lts = meta['ts']
if abs(rts - lts) > 300: if abs(rts - lts) > 300:
self.col.log("clock off")
return "clockOff" return "clockOff"
if self.lmod == self.rmod: if self.lmod == self.rmod:
self.col.log("no changes")
return "noChanges" return "noChanges"
elif lscm != rscm: elif lscm != rscm:
self.col.log("schema diff")
return "fullSync" return "fullSync"
self.lnewer = self.lmod > self.rmod self.lnewer = self.lmod > self.rmod
# step 1.5: check collection is valid # step 1.5: check collection is valid
if not self.col.basicCheck(): if not self.col.basicCheck():
self.col.log("basic check")
return "basicCheckFailed" return "basicCheckFailed"
# step 2: deletions # step 2: deletions
runHook("sync", "meta") runHook("sync", "meta")
@ -154,6 +163,7 @@ class Syncer(object):
while 1: while 1:
runHook("sync", "stream") runHook("sync", "stream")
chunk = self.server.chunk() chunk = self.server.chunk()
self.col.log("server chunk", chunk)
self.applyChunk(chunk=chunk) self.applyChunk(chunk=chunk)
if chunk['done']: if chunk['done']:
break break
@ -162,6 +172,7 @@ class Syncer(object):
while 1: while 1:
runHook("sync", "stream") runHook("sync", "stream")
chunk = self.chunk() chunk = self.chunk()
self.col.log("client chunk", chunk)
self.server.applyChunk(chunk=chunk) self.server.applyChunk(chunk=chunk)
if chunk['done']: if chunk['done']:
break break
@ -478,6 +489,7 @@ from notes where %s""" % d)
for r in data: for r in data:
if r[0] not in lmods or lmods[r[0]] < r[modIdx]: if r[0] not in lmods or lmods[r[0]] < r[modIdx]:
update.append(r) update.append(r)
self.col.log(table, data)
return update return update
def mergeCards(self, cards): def mergeCards(self, cards):

View file

@ -45,7 +45,7 @@ class DataModel(QAbstractTableModel):
def getCard(self, index): def getCard(self, index):
id = self.cards[index.row()] id = self.cards[index.row()]
if not id in self.cardObjs: if not id in self.cardObjs:
self.cardObjs[id] = self.col.getCard(id) self.cardObjs[id] = self.col.getCard(id, log=False)
return self.cardObjs[id] return self.cardObjs[id]
def refreshNote(self, note): def refreshNote(self, note):

View file

@ -1,13 +1,13 @@
# Copyright: Damien Elmes <anki@ichi2.net> # Copyright: Damien Elmes <anki@ichi2.net>
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from anki.consts import NEW_CARDS_RANDOM from operator import itemgetter
from anki.consts import NEW_CARDS_RANDOM
from aqt.qt import * from aqt.qt import *
import aqt import aqt
from aqt.utils import showInfo, showWarning, openHelp, getOnlyText, askUser, \ from aqt.utils import showInfo, showWarning, openHelp, getOnlyText, askUser, \
tooltip tooltip
from operator import itemgetter
class DeckConf(QDialog): class DeckConf(QDialog):
def __init__(self, mw, deck): def __init__(self, mw, deck):
@ -189,6 +189,7 @@ class DeckConf(QDialog):
f.fi1.setValue(c['ivlFct']*100) f.fi1.setValue(c['ivlFct']*100)
f.maxIvl.setValue(c['maxIvl']) f.maxIvl.setValue(c['maxIvl'])
f.revplim.setText(self.parentLimText('rev')) f.revplim.setText(self.parentLimText('rev'))
f.buryRev.setChecked(c.get("bury", True))
# lapse # lapse
c = self.conf['lapse'] c = self.conf['lapse']
f.lapSteps.setText(self.listToUser(c['delays'])) f.lapSteps.setText(self.listToUser(c['delays']))
@ -270,6 +271,7 @@ class DeckConf(QDialog):
c['ease4'] = f.easyBonus.value()/100.0 c['ease4'] = f.easyBonus.value()/100.0
c['ivlFct'] = f.fi1.value()/100.0 c['ivlFct'] = f.fi1.value()/100.0
c['maxIvl'] = f.maxIvl.value() c['maxIvl'] = f.maxIvl.value()
c['bury'] = f.buryRev.isChecked()
# lapse # lapse
c = self.conf['lapse'] c = self.conf['lapse']
self.updateList(c, 'delays', f.lapSteps, minSize=0) self.updateList(c, 'delays', f.lapSteps, minSize=0)

View file

@ -2,20 +2,30 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import os, sys, re, traceback, signal import os
import pprint
import sys
import re
import traceback
import signal
import zipfile import zipfile
from send2trash import send2trash from send2trash import send2trash
from aqt.qt import * from aqt.qt import *
from anki import Collection from anki import Collection
from anki.utils import isWin, isMac, intTime, splitFields, ids2str from anki.utils import isWin, isMac, intTime, splitFields, ids2str
from anki.hooks import runHook, addHook
import aqt, aqt.progress, aqt.webview, aqt.toolbar, aqt.stats from anki.hooks import runHook, addHook
import aqt
import aqt.progress
import aqt.webview
import aqt.toolbar
import aqt.stats
from aqt.utils import restoreGeom, showInfo, showWarning,\ from aqt.utils import restoreGeom, showInfo, showWarning,\
restoreState, getOnlyText, askUser, applyStyles, showText, tooltip, \ restoreState, getOnlyText, askUser, applyStyles, showText, tooltip, \
openHelp, openLink, checkInvalidFilename openHelp, openLink, checkInvalidFilename
class AnkiQt(QMainWindow): class AnkiQt(QMainWindow):
def __init__(self, app, profileManager, args): def __init__(self, app, profileManager, args):
QMainWindow.__init__(self) QMainWindow.__init__(self)
@ -159,7 +169,6 @@ class AnkiQt(QMainWindow):
return True return True
def profileNameOk(self, str): def profileNameOk(self, str):
from anki.utils import invalidFilename, invalidFilenameChars
return not checkInvalidFilename(str) return not checkInvalidFilename(str)
def onAddProfile(self): def onAddProfile(self):
@ -261,7 +270,8 @@ To import into a password protected profile, please open the profile before atte
showWarning("""\ showWarning("""\
Your collection is corrupt. Please see the manual for \ Your collection is corrupt. Please see the manual for \
how to restore from a backup.""") how to restore from a backup.""")
return self.unloadProfile() self.unloadProfile()
raise
self.hideSchemaMsg = False self.hideSchemaMsg = False
self.progress.setupDB(self.col.db) self.progress.setupDB(self.col.db)
self.maybeEnableUndo() self.maybeEnableUndo()
@ -829,6 +839,7 @@ the problem and restart Anki.""")
def setupHooks(self): def setupHooks(self):
addHook("modSchema", self.onSchemaMod) addHook("modSchema", self.onSchemaMod)
addHook("remNotes", self.onRemNotes) addHook("remNotes", self.onRemNotes)
addHook("log", self.onLog)
# Log note deletion # Log note deletion
########################################################################## ##########################################################################
@ -846,6 +857,23 @@ the problem and restart Anki.""")
f.write(("\t".join([str(id), str(mid)] + fields)).encode("utf8")) f.write(("\t".join([str(id), str(mid)] + fields)).encode("utf8"))
f.write("\n") f.write("\n")
# Debug logging
##########################################################################
def onLog(self, args, kwargs):
def customRepr(x):
if isinstance(x, basestring):
return x
return pprint.pformat(x)
path, num, fn, y = traceback.extract_stack(
limit=4+kwargs.get("stack", 0))[0]
buf = u"[%s] %s:%s(): %s" % (intTime(), os.path.basename(path), fn,
", ".join([customRepr(x) for x in args]))
lpath = re.sub("\.anki2$", ".log", self.pm.collectionPath())
open(lpath, "ab").write(buf.encode("utf8") + "\n")
if os.environ.get("LOG"):
print buf
# Schema modifications # Schema modifications
########################################################################## ##########################################################################
@ -1051,6 +1079,8 @@ will be lost. Continue?"""))
elif isWin: elif isWin:
# make sure ctypes is bundled # make sure ctypes is bundled
from ctypes import windll, wintypes from ctypes import windll, wintypes
_dummy = windll
_dummy = wintypes
def maybeHideAccelerators(self, tgt=None): def maybeHideAccelerators(self, tgt=None):
if not self.hideMenuAccels: if not self.hideMenuAccels:
@ -1076,6 +1106,10 @@ will be lost. Continue?"""))
self.connect(self.app, SIGNAL("appMsg"), self.onAppMsg) self.connect(self.app, SIGNAL("appMsg"), self.onAppMsg)
def onAppMsg(self, buf): def onAppMsg(self, buf):
if not isinstance(buf, unicode):
# even though we're sending this as unicode up above,
# a bug report still came in that we were receiving a qbytearray
buf = unicode(buf, "utf8", "ignore")
if self.state == "startup": if self.state == "startup":
# try again in a second # try again in a second
return self.progress.timer(1000, lambda: self.onAppMsg(buf), False) return self.progress.timer(1000, lambda: self.onAppMsg(buf), False)

View file

@ -271,13 +271,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="2">
<widget class="QLabel" name="revplim">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QLabel" name="label_33"> <widget class="QLabel" name="label_33">
<property name="text"> <property name="text">
@ -292,25 +285,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="fi1">
<property name="decimals">
<number>0</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>999.000000000000000</double>
</property>
<property name="singleStep">
<double>1.000000000000000</double>
</property>
<property name="value">
<double>100.000000000000000</double>
</property>
</widget>
</item>
<item row="3" column="0"> <item row="3" column="0">
<widget class="QLabel" name="label_3"> <widget class="QLabel" name="label_3">
<property name="text"> <property name="text">
@ -335,6 +309,39 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="2">
<widget class="QLabel" name="revplim">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="fi1">
<property name="decimals">
<number>0</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>999.000000000000000</double>
</property>
<property name="singleStep">
<double>1.000000000000000</double>
</property>
<property name="value">
<double>100.000000000000000</double>
</property>
</widget>
</item>
<item row="4" column="0" colspan="3">
<widget class="QCheckBox" name="buryRev">
<property name="text">
<string>Bury related reviews until the next day</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>
@ -616,6 +623,7 @@
<tabstop>easyBonus</tabstop> <tabstop>easyBonus</tabstop>
<tabstop>fi1</tabstop> <tabstop>fi1</tabstop>
<tabstop>maxIvl</tabstop> <tabstop>maxIvl</tabstop>
<tabstop>buryRev</tabstop>
<tabstop>lapSteps</tabstop> <tabstop>lapSteps</tabstop>
<tabstop>lapMult</tabstop> <tabstop>lapMult</tabstop>
<tabstop>lapMinInt</tabstop> <tabstop>lapMinInt</tabstop>