Ejemplo n.º 1
0
class Quiz(QFrame):
    def __init__(self, parent=None):
        super(Quiz, self).__init__(parent)
        """Session Info"""
        self.status = QFrame()
        ##session message
        self.status.message = QLabel(u'')
        self.status.layout = QHBoxLayout()
        self.status.layout.addWidget(self.status.message)
        self.status.setLayout(self.status.layout)
        ##mouse event filter
        #self.status.filter = StatusFilter()
        self.status.setAttribute(Qt.WA_Hover, True)
        #self.status.installEventFilter(self.status.filter)
        """Items Info"""
        self.info = QFrame()
        self.info.reading = QLabel(u'')
        self.info.item = QLabel(u'')
        self.info.components = QLabel(u'')

        self.info.translation = QLabel(u'')
        self.info.translation.setAlignment(Qt.AlignCenter)
        self.info.translation.setWordWrap(True)

        #        separator_one = QFrame()
        #        separator_one.setFrameShape(QFrame.HLine)
        #        separator_one.setFrameShadow(QFrame.Sunken)
        #
        #        separator_two = QFrame()
        #        separator_two.setFrameShape(QFrame.HLine)
        #        separator_two.setFrameShadow(QFrame.Sunken)

        self.info.layout = QVBoxLayout()
        self.info.layout.addWidget(self.info.translation)
        #        self.info.layout.addWidget(self.info.reading)
        #        self.info.layout.addWidget(separator_one)
        #        self.info.layout.addWidget(self.info.item)
        #        self.info.layout.addWidget(separator_two)
        #        self.info.layout.addWidget(self.info.components)
        self.info.setLayout(self.info.layout)
        """Verbose Info"""
        self.allInfo = QFrame()
        self.allInfo.layout = QGridLayout()
        self.allInfo.setLayout(self.allInfo.layout)
        #the rest is (should be) generated on the fly
        """Global Flags"""
        #self.correct = False
        """Quiz Dialog"""
        self.filter = Filter()
        ####    visual components    ###
        self.countdown = QProgressBar()
        self.sentence = QLabel(u'')

        #        self.var_1st = QPushButton(u'')
        #        self.var_2nd = QPushButton(u'')
        #        self.var_3rd = QPushButton(u'')
        #        self.var_4th = QPushButton(u'')

        self.isKnown = QPushButton(u'Good')
        self.isNotKnown = QPushButton(u'Again')

        self.answered = QPushButton(u'')
        self.answered.hide()

        ###    layouts    ####
        self.layout_vertical = QVBoxLayout()  #main
        self.layout_horizontal = QHBoxLayout()  #buttons

        #        self.layout_horizontal.addWidget(self.var_1st)
        #        self.layout_horizontal.addWidget(self.var_2nd)
        #        self.layout_horizontal.addWidget(self.var_3rd)
        #        self.layout_horizontal.addWidget(self.var_4th)

        self.layout_horizontal.addWidget(self.isKnown)
        self.layout_horizontal.addWidget(self.isNotKnown)

        self.layout_vertical.addWidget(self.countdown)
        self.layout_vertical.addWidget(self.sentence)
        self.layout_vertical.addLayout(self.layout_horizontal)

        self.layout_horizontal.addWidget(self.answered)

        self.setLayout(self.layout_vertical)

        ###    utility components    ###
        self.trayIcon = QSystemTrayIcon(self)
        self.trayMenu = QMenu()

        self.gifLoading = QMovie('../res/cube.gif')
        self.gifLoading.frameChanged.connect(self.updateTrayIcon)

        self.nextQuizTimer = QTimer()
        self.nextQuizTimer.setSingleShot(True)
        self.nextQuizTimer.timeout.connect(self.showQuiz)

        self.countdownTimer = QTimer()
        self.countdownTimer.setSingleShot(True)
        self.countdownTimer.timeout.connect(self.timeIsOut)

        ### initializing ###
        self.initializeResources()
        """Start!"""
        if self.options.isQuizStartingAtLaunch():
            self.waitUntilNextTimeslot()
            self.trayIcon.setToolTip('Quiz has started automatically!')
            self.pauseAction.setText('&Pause')
            self.trayIcon.showMessage(
                'Loading complete! (took ~' + str(self.loadingTime.seconds) +
                ' seconds) Quiz underway.', 'Lo! Quiz already in progress!',
                QSystemTrayIcon.MessageIcon.Warning, 10000)
        else:
            self.trayIcon.setToolTip('Quiz is not initiated!')
            self.trayIcon.showMessage(
                'Loading complete! (took ~' + str(self.loadingTime.seconds) +
                ' seconds) Standing by.',
                'Quiz has not started yet! If you wish, you could start it manually or enable autostart by default.',
                QSystemTrayIcon.MessageIcon.Information, 10000)
        """Test calls here:"""
        ###    ...    ###
        #self.connect(self.hooker, SIGNAL('noQdict'), self.noQdict)

    def noQdict(self):
        self.showSessionMessage(
            'Nope, cannot show quick dictionary during actual quiz.')

####################################
#    Initialization procedures     #
####################################

    def initializeResources(self):
        """Pre-initialization"""
        self.animationTimer = ()
        self.progressTimer = ()
        self.grid_layout = ()
        """Initialize Options"""
        self.options = Options()
        """Initialize Statistics"""
        self.stats = Stats()
        """Config Here"""
        self.initializeComposition()
        self.initializeComponents()
        self.setMenus()
        self.trayIcon.show()
        #self.startTrayLoading()
        """"Initialize Dictionaries    (will take a some time!)"""
        time_start = datetime.now()
        self.dict = EdictParser()
        self.dict.loadDict()

        self.morphy = get_morph(PATH_TO_RES + DICT_EN)

        self.trayIcon.showMessage(
            'Loading...', 'Initializing dictionaries',
            QSystemTrayIcon.MessageIcon.Information,
            20000)  #TODO: change into loading dialog... or not
        """Initializing srs system"""
        self.trayIcon.showMessage('Loading...', 'Initializing databases',
                                  QSystemTrayIcon.MessageIcon.Information,
                                  20000)
        self.srs = srsScheduler()
        self.srs.initializeAll()
        self.srs.initializeCurrentSession(self.options.getSessionSize())
        """Global hotkeys hook"""
        #TODO: add multiple hotkeys and fix stop()
        #self.hooker = GlobalHotkeyManager(toggleQDictFlag, 'Q')
        #        self.hooker = GlobalHotkeyManager(toggleWidgetFlag(self.qdict), 'Q')
        #        self.hooker.setDaemon(True) #temporarily, should work using stop()
        #        self.hooker.start()

        time_end = datetime.now()
        self.loadingTime = time_end - time_start

####################################
#    Composition and appearance    #
####################################

    def initializeComposition(self):
        """Main Dialog"""
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.setFocusPolicy(Qt.StrongFocus)
        self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        #Font will appear in buttons
        self.setFont(
            QFont(self.options.getQuizFont(), self.options.getQuizFontSize()))

        desktop = QApplication.desktop().screenGeometry()
        self.setGeometry(
            QRect(desktop.width() - H_INDENT,
                  desktop.height() - V_INDENT, D_WIDTH, D_HEIGHT))

        self.setStyleSheet("QWidget { background-color: rgb(255, 255, 255); }")
        """Info dialog"""
        self.info.setWindowFlags(Qt.FramelessWindowHint
                                 | Qt.WindowStaysOnTopHint)
        self.info.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        self.info.setGeometry(
            QRect(desktop.width() - H_INDENT - I_WIDTH - I_INDENT,
                  desktop.height() - V_INDENT, I_WIDTH, I_HEIGHT))
        self.info.setFixedSize(I_WIDTH, I_HEIGHT)

        self.info.setStyleSheet(
            "QWidget { background-color: rgb(255, 255, 255); }")
        """Verbose info dialog"""
        self.allInfo.setWindowFlags(Qt.FramelessWindowHint
                                    | Qt.WindowStaysOnTopHint)
        self.allInfo.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        self.allInfo.setGeometry(
            QRect(desktop.width() - H_INDENT - I_WIDTH - I_INDENT,
                  desktop.height() - V_INDENT, I_WIDTH, I_HEIGHT))

        self.allInfo.setStyleSheet(
            "QWidget { background-color: rgb(255, 255, 255); }")
        """Session message"""
        self.status.setWindowFlags(Qt.FramelessWindowHint
                                   | Qt.WindowStaysOnTopHint)
        self.status.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        self.status.setGeometry(
            QRect(
                desktop.width() - H_INDENT,
                desktop.height() - V_INDENT - S_HEIGHT - S_INDENT -
                S_CORRECTION, S_WIDTH, S_HEIGHT))

        self.status.setStyleSheet(
            "QWidget { background-color: rgb(255, 255, 255); }")

        self.setMask(roundCorners(self.rect(), 5))
        self.status.setMask(roundCorners(self.status.rect(), 5))
        #self.info.setMask(roundCorners(self.info.rect(),5))
        #self.allInfo.setMask(roundCorners(self.allInfo.rect(),5))

    def initializeComponents(self):
        self.countdown.setMaximumHeight(6)
        self.countdown.setRange(0, self.options.getCountdownInterval() * 100)
        self.countdown.setTextVisible(False)
        self.countdown.setStyleSheet(
            "QProgressbar { background-color: rgb(255, 255, 255); }")

        #self.setFont(QFont(Fonts.SyoutyouProEl, 40))#self.options.getQuizFontSize()))

        self.sentence.setAlignment(Qt.AlignmentFlag.AlignCenter)
        #self.sentence.setFont(QFont(Fonts.HiragiNoMarugotoProW4, self.options.getSentenceFontSize()))
        self.sentence.setFont(
            QFont(self.options.getSentenceFont(),
                  self.options.getSentenceFontSize()))

        self.sentence.setWordWrap(True)
        self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'active.png'))

        self.status.message.setFont(
            QFont('Cambria', self.options.getMessageFontSize()))
        self.status.layout.setAlignment(Qt.AlignCenter)
        self.status.message.setWordWrap(False)
        self.status.layout.setMargin(0)

        self.info.item.setFont(QFont(Fonts.HiragiNoMyoutyouProW3, 36))
        self.info.reading.setFont(QFont(Fonts.HiragiNoMyoutyouProW3, 16))
        self.info.components.setFont((QFont(Fonts.HiragiNoMyoutyouProW3, 14)))
        #self.info.item.setWordWrap(True)
        self.info.components.setWordWrap(True)
        #self.info.layout.setAlignment(Qt.AlignCenter)
        self.info.layout.setMargin(0)
        #self.info.layout.setSizeConstraint(self.info.layout.SetFixedSize)       #NB: would work nice, if the anchor point was in right corner

        self.info.reading.setAlignment(Qt.AlignCenter)
        self.info.item.setAlignment(Qt.AlignCenter)
        self.info.components.setAlignment(Qt.AlignCenter)

        #self.info.setLayoutDirection(Qt.RightToLeft)

        self.info.reading.setStyleSheet(
            "QLabel { color: rgb(155, 155, 155); }")
        self.info.components.setStyleSheet(
            "QLabel { color: rgb(100, 100, 100); }")

        self.info.gem = self.info.saveGeometry()

####################################
#        Updating content          #
####################################

    def updateContent(self):
        """Resetting multi-label sentence"""
        if self.grid_layout != ():
            for i in range(0, self.grid_layout.count()):
                self.grid_layout.itemAt(i).widget().hide()
            self.layout_vertical.removeItem(self.grid_layout)
            self.grid_layout.setParent(None)
            self.update()
        if self.sentence.isHidden():
            self.sentence.show()
        self.showButtonsQuiz()
        """Getting actual content"""
        self.srs.getNextItem()

        start = datetime.now()  #testing
        #example = self.srs.getCurrentExample().replace(self.srs.getWordFromExample(), u"<font color='blue'>" + self.srs.getWordFromExample() + u"</font>")
        example = self.srs.getCurrentExample().replace(
            self.srs.getCurrentItem(),
            u"<font color='blue'>" + self.srs.getCurrentItem() + u"</font>")
        print datetime.now() - start  #testing
        self.sentence.setText(example)

        #        start = datetime.now()  #testing
        #        readings = self.srs.getQuizVariants()
        #        print datetime.now() - start    #testing
        '''
        changeFont = False
        for item in readings:
            if len(item) > 5 : changeFont = True
            
        if changeFont: self.setStyleSheet('QWidget { font-size: 11pt; }')
        else:   self.setStyleSheet('QWidget { font-size: %spt; }' % self.options.getQuizFontSize())
        '''
        '''
        if len(readings) == 4:                  #NB: HERE LIES THE GREAT ERROR
            self.var_1st.setText(readings[0])
            self.var_2nd.setText(readings[1])
            self.var_3rd.setText(readings[2])
            self.var_4th.setText(readings[3])
        '''

#        try:
#            for i in range(0, self.layout_horizontal.count()):
#                    if i > 3: break
#                    self.layout_horizontal.itemAt(i).widget().setText(u'')
#                    #self.layout_horizontal.itemAt(i).setStyleSheet('QPushButton { font-size: 11pt; }')
#                    self.layout_horizontal.itemAt(i).widget().setText(readings[i])
#        except:
#            print 'Not enough quiz variants'
#TODO: log this

    def getReadyPostLayout(self):
        self.sentence.hide()
        self.update()

        self.grid_layout = QGridLayout()
        self.grid_layout.setSpacing(0)
        self.labels = []

        columns_mod = 0

        if len(self.srs.currentExample['eng']) > SENTENCE_MAX:
            font = QFont(self.options.getSentenceFont(), MIN_FONT_SIZE)
            columns_mod = 6
        else:
            font = QFont(self.options.getSentenceFont(),
                         self.options.getSentenceFontSize())

        #row, column, rows span, columns span, max columns
        i = 0
        j = 0
        r = 1
        c = 1
        n = COLUMNS_MAX + columns_mod
        for word in self.srs.parseCurrentExample():
            label = QLabel(word)
            #            label.setFont(QFont(self.options.getSentenceFont(), self.options.getSentenceFontSize()))
            label.setFont(font)

            label.setAttribute(Qt.WA_Hover, True)
            label.installEventFilter(self.filter)
            self.labels.append(label)

            if len(label.text()) > 1: c = len(label.text())
            else: c = 1
            #Don't ask, really
            if j + c > n:
                i = i + 1
                j = 0

            self.grid_layout.addWidget(self.labels.pop(), i, j, r, c)

            if j <= n: j = j + c
            else:
                j = 0
                i = i + 1

        self.grid_layout.setAlignment(Qt.AlignCenter)
        self.layout_vertical.insertLayout(1, self.grid_layout)

        self.update()

    def hideButtonsQuiz(self):
        self.isKnown.hide()
        self.isNotKnown.hide()

        self.answered.clicked.connect(self.hideQuizAndWaitForNext)
        self.answered.show()

    def showButtonsQuiz(self):
        self.isKnown.show()
        self.isNotKnown.show()

        self.answered.hide()
        self.answered.disconnect()

####################################
#        Timers and animations     #
####################################

    def waitUntilNextTimeslot(self):
        #if self.nextQuizTimer.isActive():   self.nextQuizTimer.stop()
        self.nextQuizTimer.start(
            self.options.getRepetitionInterval() * 60 * 1000
        )  #options are in minutes    NB: how do neatly I convert minutes to ms?

    def beginCountdown(self):
        self.trayIcon.setToolTip('Quiz in progress!')
        self.pauseAction.setText('&Pause')
        self.pauseAction.setShortcut('P')

        self.countdownTimer.start(self.options.getCountdownInterval() * 1000)

        self.progressTimer = RepeatTimer(
            0.01, self.updateCountdownBar,
            self.options.getCountdownInterval() * 100)
        self.progressTimer.start()

    def updateCountdownBar(self):
        self.countdown.setValue(self.countdown.value() - 1)
        #print self.countdown.value()
        self.countdown.update(
        )  #NB: without .update() recursive repaint crushes qt

    def fade(self):
        if self.windowOpacity() == 1:
            self.animationTimer = RepeatTimer(0.025, self.fadeOut, 40)
            self.animationTimer.start()
        else:
            self.animationTimer = RepeatTimer(0.025, self.fadeIn, 40)
            self.animationTimer.start()

    def fadeIn(self):
        self.setWindowOpacity(self.windowOpacity() + 0.1)

    def fadeOut(self):
        self.setWindowOpacity(self.windowOpacity() - 0.1)

    def stopCountdown(self):
        self.progressTimer.cancel()
        self.countdownTimer.stop()
        self.countdown.setValue(0)

####################################
#        Actions and events        #
####################################

    def setMenus(self):
        self.trayMenu.addAction(
            QAction('&Quiz me now!',
                    self,
                    shortcut="Q",
                    triggered=self.showQuiz))
        self.pauseAction = QAction('&Start quiz!',
                                   self,
                                   shortcut="S",
                                   triggered=self.pauseQuiz)
        self.trayMenu.addAction(self.pauseAction)
        self.trayMenu.addSeparator()
        self.trayMenu.addAction(
            QAction('Quick &dictionary',
                    self,
                    shortcut="D",
                    triggered=self.showQuickDict))
        self.trayMenu.addAction(
            QAction('&Global &statistics',
                    self,
                    shortcut="G",
                    triggered=self.showGlobalStatistics))
        self.trayMenu.addAction(
            QAction('&Options', self, shortcut="O",
                    triggered=self.showOptions))
        self.trayMenu.addAction(
            QAction('&About', self, shortcut="A", triggered=self.showAbout))
        self.trayMenu.addSeparator()
        self.trayMenu.addAction(
            QAction('&Exit', self, shortcut="E", triggered=self.saveAndExit))

        self.trayIcon.setContextMenu(self.trayMenu)
        self.trayIcon.activated.connect(self.onTrayIconActivated)

    #TODO: show session statistics
    def onTrayIconActivated(self, reason):
        '''
        if reason == QSystemTrayIcon.DoubleClick:
            print 'tray icon double clicked'
        '''
        if reason == QSystemTrayIcon.Trigger:
            if self.isHidden():
                self.trayIcon.showMessage(
                    'Current session statistics:',
                    'Running time:\t\t' + self.stats.getRunningTime() +
                    '\nItems seen:\t\t' + str(self.stats.totalItemSeen) +
                    '\nCorrect answers:\t\t' +
                    str(self.stats.answeredCorrect) + '\nWrong answers:\t\t' +
                    self.stats.getIncorrectAnswersCount() +
                    '\nCorrect ratio:\t\t' +
                    self.stats.getCorrectRatioPercent() +
                    #'\nQuiz total time:\t\t' + self.stats.getQuizActive() +
                    '\nQuiz paused time:\t\t' + self.stats.getPausedTime() +
                    '\nTotal pondering time:\t' + self.stats.getMusingsTime() +
                    '\nTotal post-quiz time:\t' + self.stats.getQuizTime() +
                    '\nAverage pondering:\t' +
                    self.stats.getAverageMusingTime() +
                    '\nAverage post-quiz:\t' +
                    self.stats.getAveragePostQuizTime(),
                    QSystemTrayIcon.MessageIcon.Information,
                    20000)

    def setButtonsActions(self):
        self.isKnown.clicked.connect(self.correctAnswer)
        self.isNotKnown.clicked.connect(self.wrongAnswer)

    def resetButtonsActions(self):
        self.isKnown.disconnect()
        self.isNotKnown.disconnect()

    def postAnswerActions(self):
        self.stats.musingsStopped()
        self.stats.postQuizStarted()

        self.stopCountdown()
        self.hideButtonsQuiz()

        self.getReadyPostLayout()

    def checkTranslationSize(self, translation):
        if len(translation) > TRANSLATION_CHARS_LIMIT:
            self.answered.setStyleSheet('QPushButton { font-size: 9pt; }')

            space_indices = [
                i for i, value in enumerate(translation) if value == ' '
            ]
            find_nearest_index = lambda value, list: min(
                list, key=lambda x: abs(x - value))
            nearest_index = find_nearest_index(TRANSLATION_CHARS_LIMIT,
                                               space_indices)
            translation = translation[:nearest_index] + '\n' + translation[
                nearest_index + 1:]
        else:
            self.answered.setStyleSheet('QPushButton { font-size: 11pt; }')

        self.answered.setText(translation)

    def correctAnswer(self):
        '''
        self.stats.musingsStopped()
        self.stats.postQuizStarted()
        
        self.stopCountdown()
        self.hideButtonsQuiz()
        
        self.getReadyPostLayout()
        '''
        self.postAnswerActions()

        self.srs.answeredCorrect()
        self.stats.quizAnsweredCorrect()
        #self.answered.setText(u"<font='Cambria'>" + self.srs.getCurrentSentenceTranslation() + "</font>")
        #        self.answered.setText(self.srs.getCurrentSentenceTranslation())

        self.checkTranslationSize(self.srs.getCurrentSentenceTranslation())

        #self.answered.setFont(QFont('Calibri', 11))
        self.showSessionMessage(
            u'<font color=green>Correct: OK</font>\t|\tNext quiz: ' +
            self.srs.getNextQuizTime() + '\t|\t<font color=' +
            self.srs.getLeitnerGradeAndColor()['color'] + '>Grade: ' +
            self.srs.getLeitnerGradeAndColor()['grade'] + ' (' +
            self.srs.getLeitnerGradeAndColor()['name'] + ')<font>')

        #self.answered.setShortcut('5')
        #self.setFocus()

    def wrongAnswer(self):
        '''
        self.stats.musingsStopped()
        self.stats.postQuizStarted()
        
        self.stopCountdown()
        self.hideButtonsQuiz()
        
        self.getReadyPostLayout()
        '''
        self.postAnswerActions()

        self.srs.answeredWrong()
        self.stats.quizAnsweredWrong()

        #        self.answered.setText(self.srs.getCurrentSentenceTranslation())

        self.checkTranslationSize(self.srs.getCurrentSentenceTranslation())

        #self.answered.setFont(QFont('Calibri', 11))
        #self.showSessionMessage(u"Wrong! Should be: <font style='font-family:" + Fonts.MSMyoutyou + "'>"
        #+ self.srs.getCorrectAnswer() + "</font> - Next quiz: " + self.srs.getNextQuizTime())
        self.showSessionMessage(
            u'<font color=tomato>Bad</font>\t|\tNext quiz: ' +
            self.srs.getNextQuizTime() + '\t|\t<font color=' +
            self.srs.getLeitnerGradeAndColor()['color'] + '>Grade: ' +
            self.srs.getLeitnerGradeAndColor()['grade'] + ' (' +
            self.srs.getLeitnerGradeAndColor()['name'] + ')<font>')

    def timeIsOut(self):
        self.stats.musingsStopped()
        self.stats.postQuizStarted()

        QTimer.singleShot(
            50, self.hideButtonsQuiz
        )  #NB: slight artificial lag to prevent recursive repaint crush, when mouse is suddenly over appearing button
        self.getReadyPostLayout()

        self.srs.answeredWrong()
        self.stats.quizAnsweredWrong()

        #self.showSessionMessage(u'Time is out! Correct answer is:' + self.srs.getCorrectAnswer())
        #        self.answered.setFont(QFont('Calibri', 11))
        #        self.answered.setText(self.srs.getCurrentSentenceTranslation())

        self.checkTranslationSize(self.srs.getCurrentSentenceTranslation())

        self.showSessionMessage(
            u'<font color=tomato>Timeout!</font>\t|\tNext quiz: ' +
            self.srs.getNextQuizTime() + '\t|\t<font color=' +
            self.srs.getLeitnerGradeAndColor()['color'] + '>Grade: ' +
            self.srs.getLeitnerGradeAndColor()['grade'] + ' (' +
            self.srs.getLeitnerGradeAndColor()['name'] + ')<font>')

    def hideQuizAndWaitForNext(self):
        self.stats.postQuizEnded()

        self.status.hide()
        self.info.hide()
        self.allInfo.hide()
        self.resetButtonsActions()

        self.setWindowOpacity(1)
        self.fade()
        QTimer.singleShot(1000, self.hide)
        self.waitUntilNextTimeslot()
        #self.updater.mayUpdate = True

    def pauseQuiz(self):
        if self.isHidden():
            if self.pauseAction.text() == '&Pause':
                self.nextQuizTimer.stop()
                self.pauseAction.setText('&Unpause')
                self.pauseAction.setShortcut('U')
                self.trayIcon.setToolTip('Quiz paused!')

                self.trayIcon.setIcon(
                    QIcon(PATH_TO_RES + TRAY + 'inactive.png'))
                self.stats.pauseStarted()

                #self.updater.mayUpdate = True

            elif self.pauseAction.text() == '&Start quiz!':
                self.waitUntilNextTimeslot()
                self.pauseAction.setText('&Pause')
                self.pauseAction.setShortcut('P')
                self.trayIcon.setToolTip('Quiz in progress!')

                self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'active.png'))
            else:
                self.waitUntilNextTimeslot()
                self.pauseAction.setText('&Pause')
                self.pauseAction.setShortcut('P')
                self.trayIcon.setToolTip('Quiz in progress!')

                self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'active.png'))
                self.stats.pauseEnded()

                #self.updater.mayUpdate = False
        else:
            self.showSessionMessage(
                u'Sorry, cannot pause while quiz in progress!')

    def showQuiz(self):
        if self.isHidden():
            self.updateContent()
            self.setButtonsActions()

            self.show()
            self.setWindowOpacity(0)
            self.fade()

            self.countdown.setValue(self.options.getCountdownInterval() * 100)
            self.beginCountdown()
            self.stats.musingsStarted()

            if self.nextQuizTimer.isActive(): self.nextQuizTimer.stop()
            #self.updater.mayUpdate = False
        else:
            self.showSessionMessage(u'Quiz is already underway!')

    def showOptions(self):
        self.optionsDialog.show()

    def showAbout(self):
        self.about.show()

    def showQuickDict(self):
        self.qdict.showQDict = True

    def showGlobalStatistics(self):
        print '...'

    def startTrayLoading(self):
        self.gifLoading.start()
        #self.iconTimer = QTimer()
        #self.iconTimer.timeout.connect(self.updateTrayIcon)
        #self.iconTimer.start(100)

    def stopTrayLoading(self):
        self.gifLoading.stop()

    def updateTrayIcon(self):
        self.trayIcon.setIcon(self.gifLoading.currentPixmap())

    def showSessionMessage(self, message):
        """Shows info message"""
        self.status.message.setText(message)
        self.status.show()
        #self.setFocus() #NB: does not work

    def saveAndExit(self):
        self.hide()
        self.status.hide()
        self.allInfo.hide()
        self.trayIcon.showMessage('Shutting down...', 'Saving session',
                                  QSystemTrayIcon.MessageIcon.Information,
                                  20000)
        #self.startTrayLoading()

        if self.countdownTimer.isActive():
            self.countdownTimer.stop()
        if self.nextQuizTimer.isActive():
            self.nextQuizTimer.stop()
        if self.progressTimer != () and self.progressTimer.isAlive():
            self.progressTimer.cancel()

        self.srs.endCurrentSession()
        self.trayIcon.hide()

        #        self.hooker.stop()

        #self.updater.stop()
        self.optionsDialog.close()
        self.about.close()
        #self.qdict.close()
        self.close()

    def addReferences(self, about, options, qdict, updater):
        self.about = about
        self.optionsDialog = options
        self.qdict = qdict
        self.updater = updater

    def initGlobalHotkeys(self):
        def toggleWidgetFlag():
            self.qdict.showQDict = True

        self.hooker = GlobalHotkeyManager(toggleWidgetFlag, 'Q')
        self.hooker.setDaemon(True)
        self.hooker.start()
Ejemplo n.º 2
0
class Quiz(QFrame):
     
    def __init__(self, options, parent=None):
        super(Quiz, self).__init__(parent)
        
        self.options = options
        
        """Session Info"""
        self.status = QFrame()
        #session message
        self.status.message = QLabel(u'')
        #achievements
        self.status.achievements = Achievements()
        self.status.info = QLabel(u'')
        self.status.progress = QProgressBar()
        self.status.layout = QVBoxLayout()
        #layout
        self.status.layout.addWidget(self.status.info)
        self.status.layout.addWidget(self.status.progress)
        self.status.layout.addWidget(self.status.message)
        self.status.setLayout(self.status.layout)
        #mouse event filter
        self.status.filter = StatusFilter(self.status)
        self.status.setAttribute(Qt.WA_Hover, True)
        self.status.installEventFilter(self.status.filter)

        """Items Info"""
        self.info = QFrame()
        self.info.reading = QLabel(u'')
        self.info.item = QLabel(u'')
        self.info.components = QLabel(u'')

        separator_one = QFrame()
        separator_one.setFrameShape(QFrame.HLine)
        separator_one.setFrameShadow(QFrame.Sunken)
        
        separator_two = QFrame()
        separator_two.setFrameShape(QFrame.HLine)
        separator_two.setFrameShadow(QFrame.Sunken)
        
        self.info.layout = QVBoxLayout()
        self.info.layout.addWidget(self.info.reading)
        self.info.layout.addWidget(separator_one)
        self.info.layout.addWidget(self.info.item)
        self.info.layout.addWidget(separator_two)
        self.info.layout.addWidget(self.info.components)
        self.info.setLayout(self.info.layout)
        
        """Verbose Info"""
        self.allInfo = QFrame()
        self.allInfo.layout = QGridLayout()
        self.allInfo.setLayout(self.allInfo.layout)
        #the rest is (should be) generated on the fly
        
        """Kanji info"""
        self.kanjiInfo = QFrame()
        self.kanjiInfo.layout = QVBoxLayout()
        self.kanjiInfo.info = QLabel(u'')
        self.kanjiInfo.layout.addWidget(self.kanjiInfo.info)
        self.kanjiInfo.setLayout(self.kanjiInfo.layout)
        
        """Kanji groups"""
        self.kanjiGroups = QFrame()
        self.kanjiGroups.layout = QVBoxLayout()
        self.kanjiGroups.info = QLabel(u'')
        self.kanjiGroups.layout.addWidget(self.kanjiGroups.info)
        self.kanjiGroups.setLayout(self.kanjiGroups.layout)
        
        """Global Flags"""
        #self.correct = False
        
        """Quiz Dialog"""
        self.filter = Filter()
        ####    visual components    ###
        self.countdown = QProgressBar()
        self.sentence = QLabel(u'')
        
        self.var_1st = QPushButton(u'')
        self.var_2nd = QPushButton(u'')
        self.var_3rd = QPushButton(u'')
        self.var_4th = QPushButton(u'')

        self.answered = QPushButton(u'')
        self.answered.hide()
                
        ###    layouts    ####
        self.layout_vertical = QVBoxLayout()        #main
        self.layout_horizontal = QHBoxLayout()      #buttons
        
        self.layout_horizontal.addWidget(self.var_1st)
        self.layout_horizontal.addWidget(self.var_2nd)
        self.layout_horizontal.addWidget(self.var_3rd)
        self.layout_horizontal.addWidget(self.var_4th)
        
        self.layout_vertical.addWidget(self.countdown)
        self.layout_vertical.addWidget(self.sentence)
        self.layout_vertical.addLayout(self.layout_horizontal)
        
        self.layout_horizontal.addWidget(self.answered)

        self.setLayout(self.layout_vertical)

        ###    utility components    ###
        self.trayIcon = QSystemTrayIcon(self)
        self.trayMenu = QMenu()
        
        self.gifLoading = QMovie('../res/cube.gif')
        self.gifLoading.frameChanged.connect(self.updateTrayIcon)
        
        ### initializing ###
        self.initializeResources()
        
        ### timers ###
        self.nextQuizTimer = QTimer()
        self.nextQuizTimer.setSingleShot(True)
        self.nextQuizTimer.timeout.connect(self.showQuiz)
        
        self.countdownTimer = QTimer()
        self.countdownTimer.setSingleShot(True)
        self.countdownTimer.timeout.connect(self.timeIsOut)
        
        self.trayUpdater = None
        #self.trayUpdater = RepeatTimer(1.0, self.updateTrayTooltip, self.options.getRepetitionInterval() * 60)
        self.remaining = 0
        
        """Start!"""
        if self.options.isQuizStartingAtLaunch():
            self.waitUntilNextTimeslot()
            self.trayIcon.setToolTip('Quiz has started automatically!')
            self.pauseAction.setText('&Pause')
            self.trayIcon.showMessage('Loading complete! (took ~'+ str(self.loadingTime.seconds) + ' seconds) Quiz underway.', 
                                      'Lo! Quiz already in progress!'  + self.loadingStatus, QSystemTrayIcon.MessageIcon.Warning, 10000)
        else:
            self.trayIcon.setToolTip('Quiz is not initiated!')
            self.trayIcon.showMessage('Loading complete! (took ~' + str(self.loadingTime.seconds) + ' seconds) Standing by.', 
                                      'Quiz has not started yet! If you wish, you could start it manually or enable autostart by default.'  + self.loadingStatus, 
                                      QSystemTrayIcon.MessageIcon.Information, 10000 )
            
        self.setWindowIcon(QIcon(PATH_TO_RES + ICONS + 'suzu.png'))
        """Test calls here:"""
        ###    ...    ###
        #self.connect(self.hooker, SIGNAL('noQdict'), self.noQdict)
        self.gem = self.saveGeometry()
        
    def startUpdatingTrayTooltip(self):
        self.remaining = self.nextQuizTimer.interval()
        
        if self.trayUpdater is not None and self.trayUpdater.isAlive():
            self.trayUpdater.cancel()
            
        self.trayUpdater = RepeatTimer(1.0, self.updateTrayTooltip, self.options.getRepetitionInterval() * 60)
        self.trayUpdater.start()
        
    def updateTrayTooltip(self):
        self.remaining -= UPDATE_FREQ
        self.trayIcon.setToolTip('Next quiz in ' + (str(self.remaining/UPDATE_FREQ) + ' seconds'))

#    def noQdict(self):
#        self.showSessionMessage('Nope, cannot show quick dictionary during actual quiz.')
        
####################################
#    Initialization procedures     #
####################################

    def initializeResources(self):
        
        """Initialize Options"""
#        self.options = Options()
        self.loadingStatus = u''
        
        self.qload = QuickLoad(self.options)
        if self.options.isLoadingOnStart():
            self.qload.exec_()
        
        """Pre-initialization"""
        self.animationTimer = ()
        self.progressTimer = ()
        self.grid_layout =()
        
        """Initialize Statistics"""
        self.stats = Stats()
        
        """Config Here"""
        self.initializeComposition()
        self.initializeComponents()
        self.setMenus()
        self.trayIcon.show()
        #self.startTrayLoading()
        
        """"Initialize Dictionaries    (will take a some time!)"""
        time_start = datetime.now()
        
        self.trayIcon.showMessage('Loading...', 'Initializing dictionaries', QSystemTrayIcon.MessageIcon.Information, 20000 )
        # kanji composition #
        if self.options.isLoadingRadk(): self.rdk = RadkDict()
        else: self.loadingStatus += '--> Radikt disabled!\n'
        # edict dictionary
        if self.options.isLoadingEdict():
            edict_file = resource_filename('cjktools_data', 'dict/je_edict')
            self.edict = auto_format.load_dictionary(edict_file)
        else: 
            self.edict = None
            self.loadingStatus += '--> Edict disabled!\n'
        # kanjidict dictionary #
        if self.options.isLoadingKdict(): self.kjd = kanjidic.Kanjidic()
        else: 
            self.kjd = None
            self.loadingStatus += '--> Kanjidict disabled!\n'
        # Kanji.Odyssey groups #
        self.groups = KanjiGrouper()
        if self.options.isLoadingGroups(): self.groups.loadgroupsFromDump()
        else: self.loadingStatus += '--> Kanji.Odyssey disabled!\n'
        
        """Initializing srs system"""
        self.trayIcon.showMessage('Loading...', 'Initializing databases', QSystemTrayIcon.MessageIcon.Information, 20000 )
        self.srs = srsScheduler()
        if self.options.isLoadingDb(): self.srs.initializeCurrentSession(self.options.getQuizMode(), self.options.getSessionSize())
        else: self.loadingStatus += '--> Database disabled!\n'
        
        """Jmdict lookup"""
        self.jmdict = DictionaryLookup()
        if self.options.isLoadingJmdict(): 
            self.jmdict.loadJmdictFromDumpRegex()
            self.jmdict.joinTables()
        else: self.loadingStatus += '--> Jmdict disabled!\n'
                
        """Manual add dialog"""
        self.manualAddDialog = ManualAdd(self.srs.db)
        
        if self.loadingStatus != '': self.loadingStatus = '\n\n' + self.loadingStatus
        
        time_end = datetime.now()
        self.loadingTime =  time_end - time_start

####################################
#    Composition and appearance    #
####################################

    def initializeComposition(self):
        
        """Main Dialog"""
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.setFocusPolicy(Qt.StrongFocus)
        self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        #NB: This font will be used in buttons
        self.setFont(QFont(Fonts.TukusiMyoutyouProLB, self.options.getQuizFontSize()))

        desktop = QApplication.desktop().screenGeometry()
        self.setGeometry(QRect(desktop.width() - H_INDENT, desktop.height() - V_INDENT, D_WIDTH, D_HEIGHT))
        
        self.setStyleSheet("QWidget { background-color: rgb(252, 252, 252); }")
        
        """Info dialog"""
        self.info.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.info.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        self.info.setGeometry(QRect(desktop.width() - H_INDENT - I_WIDTH - I_INDENT, desktop.height() - V_INDENT, I_WIDTH, I_HEIGHT))
        
        self.info.setStyleSheet("QWidget { background-color: rgb(252, 252, 252); }")
        
        """Verbose info dialog"""
        self.allInfo.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.allInfo.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        self.allInfo.setGeometry(QRect(desktop.width() - H_INDENT - I_WIDTH - I_INDENT, desktop.height() - V_INDENT, I_WIDTH, I_HEIGHT))
        
        self.allInfo.setStyleSheet("QWidget { background-color: rgb(252, 252, 252); }")
        
        """Session message"""
        self.status.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.status.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        self.status.setGeometry(QRect(desktop.width() - H_INDENT, desktop.height() - V_INDENT - S_HEIGHT - S_INDENT - S_CORRECTION, S_WIDTH, S_HEIGHT))
        self.status.setMinimumSize(S_WIDTH, S_HEIGHT)
#        self.status.setMinimumWidth(S_WIDTH)
        
        self.status.setStyleSheet("QWidget { background-color: rgb(252, 252, 252); }")
        
        self.setMask(roundCorners(self.rect(),5))
#        self.status.setMask(roundCorners(self.status.rect(),5))
        #self.info.setMask(roundCorners(self.info.rect(),5))
        #self.allInfo.setMask(roundCorners(self.allInfo.rect(),5))
        
        """Kanji info"""
        self.kanjiInfo.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.kanjiInfo.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        self.kanjiInfo.setGeometry(QRect(desktop.width() - H_INDENT - K_WIDTH - K_INDENT, desktop.height() - V_INDENT, K_WIDTH, K_HEIGHT))
        
        self.kanjiInfo.setStyleSheet("QWidget { background-color: rgb(252, 252, 252); }")
        
        """Kanji groups"""
        self.kanjiGroups.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.kanjiGroups.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        self.kanjiGroups.setGeometry(QRect(desktop.width() - H_INDENT - G_WIDTH - G_INDENT, desktop.height() - V_INDENT, G_WIDTH, G_HEIGHT))
        
        self.kanjiGroups.setStyleSheet("QWidget { background-color: rgb(250, 250, 250); }")
        
#        self.setMask(roundCorners(self.rect(),5))
#        self.status.setMask(roundCorners(self.status.rect(),5))

    def initializeComponents(self):
        self.countdown.setMaximumHeight(6)
        self.countdown.setRange(0, self.options.getCountdownInterval() * 100)
        self.countdown.setTextVisible(False)
        self.countdown.setStyleSheet("QProgressbar { background-color: rgb(255, 255, 255); }")
        
        #self.setFont(QFont(Fonts.SyoutyouProEl, 40))#self.options.getQuizFontSize()))
        
        self.sentence.setAlignment(Qt.AlignmentFlag.AlignCenter)
        #self.sentence.setFont(QFont(Fonts.HiragiNoMarugotoProW4, self.options.getSentenceFontSize()))
        self.sentence.setFont(QFont(self.options.getSentenceFont(), self.options.getSentenceFontSize()))
        
        self.sentence.setWordWrap(True)
        self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'active.png'))
        
        self.status.message.setFont(QFont('Cambria', self.options.getMessageFontSize()))
        self.status.layout.setAlignment(Qt.AlignCenter)
        self.status.message.setWordWrap(False)
        self.status.message.setAlignment(Qt.AlignCenter)
        self.status.layout.setMargin(0)
        
        self.status.info.setHidden(True)
        self.status.progress.setHidden(True)
        self.status.progress.setMaximumHeight(10)
        self.status.progress.setRange(0, self.status.achievements.threshold)
        self.status.layout.setAlignment(Qt.AlignCenter)
        self.status.info.setAlignment(Qt.AlignCenter)
        self.status.info.setFont(QFont(Fonts.RyuminPr5, 13))
        self.status.info.setWordWrap(False)
        
        self.status.gem = self.status.saveGeometry()
        
        self.info.item.setFont(QFont(Fonts.HiragiNoMyoutyouProW3, 36))
        self.info.reading.setFont(QFont(Fonts.HiragiNoMyoutyouProW3, 16))
        self.info.components.setFont((QFont(Fonts.HiragiNoMyoutyouProW3, 14)))
        #self.info.item.setWordWrap(True)
        self.info.components.setWordWrap(True)
        #self.info.layout.setAlignment(Qt.AlignCenter)
        self.info.layout.setMargin(0)
        #self.info.layout.setSizeConstraint(self.info.layout.SetFixedSize)       #NB: would work nice, if the anchor point was in right corner
        
        self.info.reading.setAlignment(Qt.AlignCenter)
        self.info.item.setAlignment(Qt.AlignCenter)
        self.info.components.setAlignment(Qt.AlignCenter)
        
        #self.info.setLayoutDirection(Qt.RightToLeft)
        
        self.info.reading.setStyleSheet("QLabel { color: rgb(155, 155, 155); }")
        self.info.components.setStyleSheet("QLabel { color: rgb(100, 100, 100); }")
        
        self.kanjiInfo.info.setFont(QFont(Fonts.MSMyoutyou, 14.5))
        self.kanjiInfo.info.setAlignment(Qt.AlignCenter)
        self.kanjiInfo.info.setWordWrap(True)
        self.kanjiInfo.layout.setMargin(0)
        
        self.kanjiGroups.info.setFont(QFont(Fonts.MSMyoutyou, 18.5))
        self.kanjiGroups.info.setAlignment(Qt.AlignCenter)
        self.kanjiGroups.info.setWordWrap(True)
        self.kanjiGroups.layout.setMargin(0)
        
        #NB: ...
        self.answered.setMaximumWidth(D_WIDTH)
        self.answered.setFont(QFont('Calibri', 11))


####################################
#        Updating content          #
####################################        
    def updateContent(self):
        
        """Resetting multi-label sentence"""
        if self.grid_layout != ():
            for i in range(0, self.grid_layout.count()):
                    self.grid_layout.itemAt(i).widget().hide()
            self.layout_vertical.removeItem(self.grid_layout)
            self.grid_layout.setParent(None)
            self.update()
        if self.sentence.isHidden():    
            self.sentence.show()
        self.showButtonsQuiz()
        
        """Getting actual content"""
        self.srs.getNextItem()
              
        example = self.srs.getCurrentExample()
        
        # checking for no example case
        if example is None:
            self.manualAddDialog.setProblemKanji(self.srs.getCurrentItemKanji())
            done = self.manualAddDialog.exec_()
            
            if done == 0:
                self.updateContent()
            elif done == 1:
                self.updateContent()
            else:
                pass
        else:
            example = example.replace(self.srs.getWordFromExample(), u"<font color='blue'>" + self.srs.getWordFromExample() + u"</font>")
            
            # checking sentence length
            if len(self.srs.currentExample.sentence) > SENTENCE_MAX: self.sentence.setFont(QFont(self.options.getSentenceFont(), MIN_FONT_SIZE))
            else: self.sentence.setFont(QFont(self.options.getSentenceFont(), self.options.getSentenceFontSize()))
            
            #temporary debug info:
#            print len(example), self.sentence.font()
            
            self.sentence.setText(example)
            
            readings = self.srs.getQuizVariants()

            changeFont = False
            for item in readings:
                if len(item) > BUTTON_KANA_MAX : changeFont = True
                
            try:
                for i in range(0, self.layout_horizontal.count()):
                        if i > 3: break
                        self.layout_horizontal.itemAt(i).widget().setText(u'')
                        
                        if changeFont:
                            self.layout_horizontal.itemAt(i).widget().setStyleSheet('QPushButton { font-family: ' + self.options.getQuizFont() + '; font-size: 11pt; }')
                        else:
                            self.layout_horizontal.itemAt(i).widget().setStyleSheet('QPushButton { font-family: ' + self.options.getQuizFont() + '; font-size: %spt; }' % self.options.getQuizFontSize())
                            
                        self.layout_horizontal.itemAt(i).widget().setText(readings[i])
            except:
                log.debug(u'Not enough quiz variants for ' + self.srs.getCurrentItem())
        
    def getReadyPostLayout(self):
        self.sentence.hide()
        self.update()
        
        self.grid_layout = QGridLayout()
        self.grid_layout.setSpacing(0)
        self.labels = []
        
        columns_mod = 0
        #font size depending on sentence length
        if len(self.srs.currentExample.sentence) > SENTENCE_MAX: font =  QFont(self.options.getSentenceFont(), MIN_FONT_SIZE); columns_mod = 6
        else: font = QFont(self.options.getSentenceFont(), self.options.getSentenceFontSize())
        
        #row, column, rows span, columns span, max columns
        i = 0; j = 0; r = 1; c = 1; n = COLUMNS_MAX + columns_mod
        for word in self.srs.parseCurrentExample():
            label = QLabel(word)
            label.setFont(font)
            
            label.setAttribute(Qt.WA_Hover, True)
            label.installEventFilter(self.filter)
            self.labels.append(label)
            
            if len(label.text()) > 1: c = len(label.text())
            else: c = 1
            #Don't ask, really
            if j + c > n: i = i + 1; j = 0
            
            self.grid_layout.addWidget(self.labels.pop(), i, j, r, c)       #NB: Ehh, pop should remove label from list, shouldn't it?
            
            if j <= n: j = j + c
            else: j = 0; i = i + 1
        
        self.grid_layout.setAlignment(Qt.AlignCenter)
        self.layout_vertical.insertLayout(1, self.grid_layout)

        self.update()
        
    def hideButtonsQuiz(self):
        self.var_1st.hide()
        self.var_2nd.hide()
        self.var_3rd.hide()
        self.var_4th.hide()
        
        self.answered.clicked.connect(self.hideQuizAndWaitForNext)
        self.answered.show()
        
    def showButtonsQuiz(self):
        self.var_1st.show()
        self.var_2nd.show()
        self.var_3rd.show()
        self.var_4th.show()
        
        self.answered.hide()
        self.answered.disconnect()
        
####################################
#        Timers and animations     #
####################################

    def waitUntilNextTimeslot(self):
        #if self.nextQuizTimer.isActive():   self.nextQuizTimer.stop()
        self.nextQuizTimer.start(self.options.getRepetitionInterval() * 60 * 1000)  #options are in minutes    NB: how do neatly I convert minutes to ms?
        self.startUpdatingTrayTooltip()
        
    def beginCountdown(self):
        self.trayIcon.setToolTip('Quiz in progress!')
        self.pauseAction.setText('&Pause')
#        self.pauseAction.setShortcut('P')
        
        self.countdownTimer.start(self.options.getCountdownInterval() * 1000)

        self.progressTimer = RepeatTimer(0.01, self.updateCountdownBar, self.options.getCountdownInterval() * 100)
        self.progressTimer.start()
        
    def updateCountdownBar(self):
        self.countdown.setValue(self.countdown.value() - 1)
        #print self.countdown.value()
        self.countdown.update()         #NB: without .update() recursive repaint crushes qt

    def fade(self):
        if self.windowOpacity() == 1:
            self.animationTimer = RepeatTimer(0.025, self.fadeOut, 40)
            self.animationTimer.start()
        else:
            self.animationTimer = RepeatTimer(0.025, self.fadeIn, 40)
            self.animationTimer.start()
    
    def fadeIn(self):
        self.setWindowOpacity(self.windowOpacity() + 0.1)
        
    def fadeOut(self):
        self.setWindowOpacity(self.windowOpacity() - 0.1)
        
    def stopCountdown(self):
        self.progressTimer.cancel()
        self.countdownTimer.stop()
        self.countdown.setValue(0)
        
####################################
#        Actions and events        #
####################################    
    
    def setMenus(self):
        
        self.showQuizAction = QAction('&Quiz me now!', self, triggered=self.showQuiz)
        self.showQuizAction.setIcon(QIcon(PATH_TO_RES + TRAY + NOW_ICON))
        self.trayMenu.addAction(self.showQuizAction)
        
        self.pauseAction = QAction('&Start quiz!', self, triggered=self.pauseQuiz)
        self.pauseAction.setIcon(QIcon(PATH_TO_RES + TRAY + START_ICON))
        self.trayMenu.addAction(self.pauseAction)
        
        self.trayMenu.addSeparator()
        
        self.quickDictAction = QAction('Quick &dictionary', self, triggered=self.showQuickDict)
        self.quickDictAction.setIcon(QIcon(PATH_TO_RES + TRAY + DICT_ICON))
        self.trayMenu.addAction(self.quickDictAction)
        
        self.optionsAction = QAction('&Options', self, triggered=self.showOptions)
        self.optionsAction.setIcon(QIcon(PATH_TO_RES + TRAY + OPTIONS_ICON))
        self.trayMenu.addAction(self.optionsAction)
        
        self.quickLoadAction = QAction('Quick &load', self, triggered=self.showQuickLoad)
        self.quickLoadAction.setIcon(QIcon(PATH_TO_RES + TRAY + LOAD_ICON))
        self.trayMenu.addAction(self.quickLoadAction)
        
        self.trayMenu.addSeparator()
        
        self.aboutAction = QAction('&About', self, triggered=self.showAbout)
        self.aboutAction.setIcon(QIcon(PATH_TO_RES + TRAY + ABOUT_ICON))
        self.trayMenu.addAction(self.aboutAction)
        
        self.globalStatsAction = QAction('&Global statistics', self, triggered=self.showGlobalStatistics)
        self.globalStatsAction.setIcon(QIcon(PATH_TO_RES + TRAY + STAT_ICON))
        self.trayMenu.addAction(self.globalStatsAction)
        
        self.utilAction = QAction('U&tilities', self, triggered=self.showToolsDialog)
        self.utilAction.setIcon(QIcon(PATH_TO_RES + TRAY + UTILS_ICON))
        self.trayMenu.addAction(self.utilAction)
        
        self.trayMenu.addSeparator()
        
        self.quitAction = QAction('&Exit', self, triggered=self.saveAndExit)
        self.quitAction.setIcon(QIcon(PATH_TO_RES + TRAY + CLOSE_ICON))
        self.trayMenu.addAction(self.quitAction)
        

        self.trayIcon.setContextMenu(self.trayMenu)
        self.trayIcon.activated.connect(self.onTrayIconActivated)

    def onTrayIconActivated(self, reason):
        '''
        if reason == QSystemTrayIcon.DoubleClick:
            print 'tray icon double clicked'
        '''
        if reason == QSystemTrayIcon.Trigger:
            if self.isHidden():
                self.trayIcon.showMessage('Current session statistics:', 'Running time:\t\t' + self.stats.getRunningTime() + 
                                          '\nItems seen:\t\t' + str(self.stats.totalItemSeen) + 
                                          '\nCorrect answers:\t\t' + str(self.stats.answeredCorrect) +
                                          '\nWrong answers:\t\t' + self.stats.getIncorrectAnswersCount() +
                                          '\nCorrect ratio:\t\t' + self.stats.getCorrectRatioPercent() +
                                          #'\nQuiz total time:\t\t' + self.stats.getQuizActive() +
                                          '\nQuiz paused time:\t\t' + self.stats.getPausedTime() +
                                          '\nTotal pondering time:\t' + self.stats.getMusingsTime() +
                                          '\nTotal post-quiz time:\t' + self.stats.getQuizTime() +
                                          '\nAverage pondering:\t' + self.stats.getAverageMusingTime() +
                                          '\nAverage post-quiz:\t' + self.stats.getAveragePostQuizTime(), 
                                          QSystemTrayIcon.MessageIcon.Information, 20000)
    
    def setButtonsActions(self):

        if self.var_1st.text() == self.srs.getCorrectAnswer():
                self.var_1st.clicked.connect(self.correctAnswer)
        else:
                self.var_1st.clicked.connect(self.wrongAnswer)
               
        if self.var_2nd.text() == self.srs.getCorrectAnswer():
                self.var_2nd.clicked.connect(self.correctAnswer)
        else:
                self.var_2nd.clicked.connect(self.wrongAnswer)
                
        if self.var_3rd.text() == self.srs.getCorrectAnswer():
                self.var_3rd.clicked.connect(self.correctAnswer)
        else:
                self.var_3rd.clicked.connect(self.wrongAnswer)
                
        if self.var_4th.text() == self.srs.getCorrectAnswer():
                self.var_4th.clicked.connect(self.correctAnswer)
        else:
                self.var_4th.clicked.connect(self.wrongAnswer)
                
        self.var_1st.setShortcut('1')
        self.var_2nd.setShortcut('2')
        self.var_3rd.setShortcut('3')
        self.var_4th.setShortcut('4')
                
    def resetButtonsActions(self):
        self.var_1st.disconnect()
        self.var_2nd.disconnect()
        self.var_3rd.disconnect()
        self.var_4th.disconnect()
        
    def postAnswerActions(self):
        self.stats.musingsStopped()
        self.stats.postQuizStarted()
        
        self.stopCountdown()
        self.hideButtonsQuiz()
        
        self.getReadyPostLayout()
        
    def refocusQuiz(self):
        self.answered.setShortcut('Space')
        self.activateWindow()
        self.answered.setFocus()
        
    def correctAnswer(self):
        self.postAnswerActions()
        
        self.srs.answeredCorrect()
        self.stats.quizAnsweredCorrect()
        
        self.checkTranslationSize(self.srs.getCurrentSentenceTranslation())

        self.status.achievements.correctAnswer()
        self.showSessionMessage(u'<font color=green>Correct: ' + self.srs.getCorrectAnswer() + '</font>\t|\tNext quiz: ' + self.srs.getNextQuizTime() 
                                + '\t|\t<font color=' + self.srs.getLeitnerGradeAndColor()['color'] +  '>Grade: ' + self.srs.getLeitnerGradeAndColor()['grade'] 
                                + ' (' + self.srs.getLeitnerGradeAndColor()['name'] + ')<font>')
        self.refocusQuiz()
        
    def wrongAnswer(self):
        self.postAnswerActions()
        
        self.srs.answeredWrong()
        self.stats.quizAnsweredWrong()
        
        self.checkTranslationSize(self.srs.getCurrentSentenceTranslation())
        
        self.status.achievements.wrongAnswer()
        self.showSessionMessage(u'<font color=tomato>Wrong! Should be: '+ self.srs.getCorrectAnswer() + '</font>\t|\tNext quiz: ' + self.srs.getNextQuizTime()
                                + '\t|\t<font color=' + self.srs.getLeitnerGradeAndColor()['color'] +  '>Grade: ' + self.srs.getLeitnerGradeAndColor()['grade'] 
                                + ' (' + self.srs.getLeitnerGradeAndColor()['name'] + ')<font>')
        self.refocusQuiz()
            
    def timeIsOut(self):
        self.stats.musingsStopped()
        self.stats.postQuizStarted()
        
        QTimer.singleShot(50, self.hideButtonsQuiz)     #NB: slight artificial lag to prevent recursive repaint crush (when mouse is suddenly over repainted button)
        self.getReadyPostLayout()
        
        self.srs.answeredWrong()
        self.stats.quizAnsweredWrong()

        self.checkTranslationSize(self.srs.getCurrentSentenceTranslation())
        
        self.status.achievements.wrongAnswer()
        self.showSessionMessage(u'<font color=tomato>Timeout! Should be: ' + self.srs.getCorrectAnswer() + '</font>\t|\tNext quiz: ' + self.srs.getNextQuizTime()
                                + '\t|\t<font color=' + self.srs.getLeitnerGradeAndColor()['color'] +  '>Grade: ' + self.srs.getLeitnerGradeAndColor()['grade'] 
                                + ' (' + self.srs.getLeitnerGradeAndColor()['name'] + ')<font>')    
        self.refocusQuiz()
        
    def checkTranslationSize(self, translation):
        if len(translation) > TRANSLATION_CHARS_LIMIT:
            self.answered.setStyleSheet('QPushButton { font-size: 9pt; }')
            
            space_indices = [i for i, value in enumerate(translation) if value == ' ']
            find_nearest_index = lambda value,list : min(list, key = lambda x:abs(x - value))
            nearest_index = find_nearest_index(TRANSLATION_CHARS_LIMIT, space_indices)
            translation = translation[:nearest_index] + '\n' + translation[nearest_index + 1:]
        else:
            self.answered.setStyleSheet('QPushButton { font-size: 11pt; }')
        
        self.answered.setText(translation)
    
    def hideQuizAndWaitForNext(self):
        self.stats.postQuizEnded()
        
        self.status.hide()
        self.info.hide()
        self.allInfo.hide()
        self.resetButtonsActions()
        
        self.setWindowOpacity(1)
        self.fade()
        QTimer.singleShot(1000, self.hide)
        self.waitUntilNextTimeslot()
        self.updater.mayUpdate = True
 
    def pauseQuiz(self):
        if self.isHidden():
            if self.pauseAction.text() == '&Pause':
                self.nextQuizTimer.stop()
                self.pauseAction.setText('&Unpause')
#                self.pauseAction.setShortcut('U')
                self.trayIcon.setToolTip('Quiz paused!')
                
                self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'inactive.png'))
                self.trayUpdater.cancel()
                self.pauseAction.setIcon(QIcon(PATH_TO_RES + TRAY + START_ICON))
                self.stats.pauseStarted()
                
                self.updater.mayUpdate = True
                
            elif self.pauseAction.text() == '&Start quiz!':
                self.waitUntilNextTimeslot()
                self.pauseAction.setText('&Pause')
#                self.pauseAction.setShortcut('P')
                self.trayIcon.setToolTip('Quiz in progress!')
                
                self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'active.png'))
                self.pauseAction.setIcon(QIcon(PATH_TO_RES + TRAY + PAUSE_ICON))
            else:
                self.waitUntilNextTimeslot()
                self.pauseAction.setText('&Pause')
#                self.pauseAction.setShortcut('P')
                self.trayIcon.setToolTip('Quiz in progress!')
                
                self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'active.png'))
                self.pauseAction.setIcon(QIcon(PATH_TO_RES + TRAY + PAUSE_ICON))
                self.stats.pauseEnded()
                
                self.updater.mayUpdate = False
        else:
            self.showSessionMessage(u'Sorry, cannot pause while quiz in progress!')
 
    def showQuiz(self):
        if self.isHidden():
            self.updateContent()
            self.setButtonsActions()
            
            #self.restoreGeometry(self.gem)
            self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'active.png'))
            
            self.show()
            self.setWindowOpacity(0)
            self.fade()

            self.countdown.setValue(self.options.getCountdownInterval() * 100)
            self.beginCountdown()
            self.stats.musingsStarted()
            
            if self.nextQuizTimer.isActive():   self.nextQuizTimer.stop()
            self.updater.mayUpdate = False
        else:
            self.showSessionMessage(u'Quiz is already underway!')
         
    def showOptions(self):
        self.optionsDialog.show()
        
    def showAbout(self):
        self.about.show()
        
    def showQuickDict(self):
        self.qdict.showQDict = True
        
    def showGlobalStatistics(self):
        self.statistics.show()
        
    def showToolsDialog(self):
        self.tools.show()
        
    def showQuickLoad(self):
        self.qload.show()
        
    def startTrayLoading(self):
        self.gifLoading.start()
        #self.iconTimer = QTimer()
        #self.iconTimer.timeout.connect(self.updateTrayIcon)
        #self.iconTimer.start(100)
        
    def stopTrayLoading(self):
        self.gifLoading.stop()
        
    def updateTrayIcon(self):
        self.trayIcon.setIcon(self.gifLoading.currentPixmap())
        
    def showSessionMessage(self, message):
        """Shows info message"""
        self.status.message.setText(message)
        if self.status.achievements.achieved is not None:
            self.status.info.setText(self.status.achievements.achieved[1] + '\t( ' + self.status.achievements.achieved[0] + ' )')
            self.status.progress.hide()
            self.status.move(self.status.x(), self.status.y() - 15)
            self.status.info.show()
#            self.status.setMask(roundCorners(self.status.rect(),5))
        else:
            self.status.info.setText(u'')
            self.status.info.hide()
            self.status.restoreGeometry(self.status.gem)
#            self.status.setMask(roundCorners(self.status.rect(),5))
#        print self.status.y()
        self.status.adjustSize()
        self.status.show()
 
    def saveAndExit(self):
        self.hide()
        self.status.hide()
        self.allInfo.hide()
        self.kanjiInfo.hide()
        
        self.trayIcon.showMessage('Shutting down...', 'Saving session', QSystemTrayIcon.MessageIcon.Information, 20000 )
        
        if self.countdownTimer.isActive():
                self.countdownTimer.stop()
        if self.nextQuizTimer.isActive():
                self.nextQuizTimer.stop()
        if self.progressTimer != () and self.progressTimer.isAlive():
                self.progressTimer.cancel()
        if self.trayUpdater is not None and self.trayUpdater.isAlive() :
                self.trayUpdater.cancel()    
            
        self.rehash.checkSessionResults()
        
        self.srs.endCurrentSession(self.stats)
        self.trayIcon.hide()

        self.hooker.stop()

        self.updater.stop()
        self.optionsDialog.close()
        self.about.close()
        self.qdict.close()
        self.close()        
        
    def addReferences(self, about, options, qdict, updater, tools, statistics, web, rehash):
        self.about = about
        self.optionsDialog = options
        self.qdict = qdict
        self.updater = updater
        self.tools = tools
        self.statistics = statistics
        self.status.web = web
        self.rehash = rehash
        
    def initGlobalHotkeys(self):
        def toggleWidgetFlag(): self.qdict.showQDict = True
        self.hooker = GlobalHotkeyManager(toggleWidgetFlag , 'Q')
        self.hooker.setDaemon(True)
        self.hooker.start()
        
    def showEvent(self, event):
        self.restoreGeometry(self.gem)
Ejemplo n.º 3
0
class OptionsDialog(QFrame):
    def __init__(self, db, freq, options, parent=None):
        super(OptionsDialog, self).__init__(parent)
        # db & options handlers
        self.db = db
        self.freq = freq
        self.options = options
        self.dbItems = self.db.countTotalItemsInDb()
        self.totalItemsInRange = 0

        # status info
        self.status = QFrame()
        self.status.info = QLabel(u"")
        self.status.layout = QHBoxLayout()
        self.status.layout.addWidget(self.status.info)
        self.status.setLayout(self.status.layout)

        # all items info
        self.items = QDialog()
        self.items.layout = QGridLayout()
        self.items.infoLayout = QHBoxLayout()
        # filter
        self.filter = StatusFilter()

        ### runtime group ###
        self.appOptionsGroup = QGroupBox("Runtime")
        self.appOptionsGroup.setAlignment(Qt.AlignCenter)
        self.checkAutostart = QCheckBox("Begin quiz on application start")
        self.checkEnableLog = QCheckBox("Enable errors logging")
        self.checkEnableFade = QCheckBox("Enable fade effect")
        self.checkAlwaysOnTop = QCheckBox("Always on top")
        self.checkSoundSignal = QCheckBox("Announce quiz with sound")
        self.checkSplashScreen = QCheckBox("Splash screen on launch")
        self.checkGlobalHotkeys = QCheckBox("Enable global hotkeys")
        self.checkItemsBackground = QCheckBox("Show colorful background")
        self.checkPlastiqueTheme = QCheckBox("Use 'plastique' theme")
        self.checkPreloadDictionary = QCheckBox("Load jmdict into memory on start")

        self.appLayout = QVBoxLayout()
        self.appLayout.addWidget(self.checkAutostart)
        self.appLayout.addWidget(self.checkSplashScreen)
        self.appLayout.addWidget(self.checkAlwaysOnTop)
        self.appLayout.addWidget(self.checkSoundSignal)
        self.appLayout.addWidget(self.checkEnableFade)
        self.appLayout.addWidget(self.checkEnableLog)
        self.appLayout.addWidget(self.checkGlobalHotkeys)
        self.appLayout.addWidget(self.checkItemsBackground)
        self.appLayout.addWidget(self.checkPlastiqueTheme)
        self.appLayout.addWidget(self.checkPreloadDictionary)

        self.appOptionsGroup.setLayout(self.appLayout)

        ### fonts group ###
        self.appFontsGroup = QGroupBox("Fonts")
        self.appFontsGroup.setAlignment(Qt.AlignCenter)
        self.sentenceFontLabel = QLabel(u"Sentence:")
        self.selectSentenceFont = QFontComboBox()
        self.quizFontLabel = QLabel(u"Quiz:")
        self.selectQuizFont = QFontComboBox()
        self.infoFontLabel = QLabel(u"Kanji info:")
        self.selectInfoFont = QFontComboBox()
        self.statusFontLabel = QLabel(u"Status message:")
        self.selectStatusFont = QFontComboBox()

        self.fontsLayout = QVBoxLayout()
        self.fontsLayout.addWidget(self.sentenceFontLabel)
        self.fontsLayout.addWidget(self.selectSentenceFont)
        self.fontsLayout.addWidget(self.quizFontLabel)
        self.fontsLayout.addWidget(self.selectQuizFont)
        self.fontsLayout.addWidget(self.infoFontLabel)
        self.fontsLayout.addWidget(self.selectInfoFont)
        self.fontsLayout.addWidget(self.statusFontLabel)
        self.fontsLayout.addWidget(self.selectStatusFont)

        self.appFontsGroup.setLayout(self.fontsLayout)

        ### srs group ###
        self.srsGroup = QGroupBox("SRS Tweaks")
        self.srsGroup.setAlignment(Qt.AlignCenter)

        self.intervalLabel = QLabel(u"Interval between quizzes (minutes):")
        self.countdownlLabel = QLabel(u"Time for answer (seconds):")
        self.intervalSpin = QSpinBox()
        self.intervalDial = QDial()

        self.countdownSpin = QSpinBox()
        self.countdownDial = QDial()

        self.sessionItemsLabel = QLabel(u"Sample size:")
        self.sessionItemsLabel.setToolTip(
            u"Maximum number of unique items, selected for current session from active database."
        )
        self.sessionItemsSpin = QSpinBox()
        self.sessionLengthLabel = QLabel(u"Session limit:")
        self.sessionLengthLabel.setToolTip(u"Maximum allowed repetitions/day, including non-unique items.")
        self.sessionLengthSpin = QSpinBox()

        #        self.sessionModeLabel = QLabel(u'Quiz mode:')
        #        self.sessionModeCombo = QComboBox()

        # TODO: add leitner coefficient tweak (0.1 ~ 2.0)

        self.srsLayout = QGridLayout()
        self.srsLayout.addWidget(self.intervalLabel, 0, 0, 1, 2)
        self.srsLayout.addWidget(self.intervalDial, 1, 0)
        self.srsLayout.addWidget(self.intervalSpin, 1, 1)
        self.srsLayout.addWidget(self.countdownlLabel, 2, 0, 1, 2)
        self.srsLayout.addWidget(self.countdownDial, 3, 0)
        self.srsLayout.addWidget(self.countdownSpin, 3, 1)
        self.srsLayout.addWidget(self.sessionItemsLabel, 4, 0)
        self.srsLayout.addWidget(self.sessionItemsSpin, 4, 1)
        self.srsLayout.addWidget(self.sessionLengthLabel, 5, 0)
        self.srsLayout.addWidget(self.sessionLengthSpin, 5, 1)
        #        self.srsLayout.addWidget(self.sessionModeLabel, 6, 0)
        #        self.srsLayout.addWidget(self.sessionModeCombo, 6, 1)

        self.srsGroup.setLayout(self.srsLayout)

        ### dictionary group ###
        self.dictGroup = QGroupBox("JMdict")
        self.dictGroup.setAlignment(Qt.AlignCenter)

        self.languageLabel = QLabel(u"Translation lookup in:")
        self.languageCombo = QComboBox()

        self.shortcutLabel = QLabel(u"Hotkey: Ctrl + Alt + ")
        self.shortcutCombo = QComboBox()

        self.dictLayout = QGridLayout()
        self.dictLayout.addWidget(self.languageLabel, 0, 0)
        self.dictLayout.addWidget(self.languageCombo, 0, 1)
        self.dictLayout.addWidget(self.shortcutLabel, 1, 0)
        self.dictLayout.addWidget(self.shortcutCombo, 1, 1)

        self.dictGroup.setLayout(self.dictLayout)

        ### database group ###
        self.dbGroup = QGroupBox("Database")
        self.dbGroup.setAlignment(Qt.AlignCenter)

        #        self.totalLabel = QLabel(u'Kanji: <b>' + str(self.dbItems['kanji']) + '</b>\tWords: <b>' + str(self.dbItems['words']) + '</b>\tTotal: <b>%s</b>'
        #                                 % (self.dbItems['kanji'] + self.dbItems['words'])  )
        self.totalLabel = QLabel(u"Items: " + str(self.dbItems["items"]))
        self.totalLabel.setWordWrap(True)
        self.viewAll = QToolButton()
        separator_one = QFrame()
        separator_one.setFrameShape(QFrame.HLine)
        separator_one.setFrameShadow(QFrame.Sunken)
        self.viewAll.setText(u"View all")
        self.addLabel = QLabel(u"Batch-add items to studying list:")
        #        self.comboTag = QComboBox()
        #        self.comboLevel = QComboBox()
        #        self.comboCompare = QComboBox()
        #        self.inputFrequency = QLineEdit()
        #        self.inputFrequency.setToolTip(u'Enter character frequency (from 1 to 6265)')
        #        self.addButton = QPushButton(u'Add by grade')
        #        self.addButton.setToolTip(u'Update db according to specified criteria (duplicates will be ignored)')
        #        self.addButtonFrequency = QPushButton(u'Add by frequency')

        self.frequencyRange = RangeSlider(Qt.Horizontal)
        self.valueLow = QDoubleSpinBox()
        self.valueHigh = QDoubleSpinBox()
        self.addItemInRange = QPushButton(u"Add items in db")
        self.totalItemsLabel = QLabel(u"")

        self.purgeButton = QPushButton(u"Purge all data from database")
        self.purgeButtonCustom = QPushButton(u"Partial purge")
        # self.progressDb = QProgressBar()

        self.tagsView = QTableWidget()

        self.dbLayout = QGridLayout()
        self.dbLayout.addWidget(self.totalLabel, 0, 0, 1, 3)
        self.dbLayout.addWidget(self.viewAll, 0, 3, 1, 1)
        self.dbLayout.addWidget(separator_one, 1, 0, 1, 4)
        self.dbLayout.addWidget(self.addLabel, 2, 0, 1, 4)

        self.dbLayout.addWidget(self.valueLow, 3, 0, 1, 2)
        self.dbLayout.addWidget(self.valueHigh, 3, 2, 1, 2)
        self.dbLayout.addWidget(self.frequencyRange, 4, 0, 1, 4)
        self.dbLayout.addWidget(self.totalItemsLabel, 5, 0, 1, 4)
        self.dbLayout.addWidget(self.addItemInRange, 6, 0, 1, 4)

        #        self.dbLayout.addWidget(self.comboTag, 3, 0)
        #        self.dbLayout.addWidget(self.comboLevel, 3, 1)
        #        self.dbLayout.addWidget(self.comboCompare, 4, 0)
        #        self.dbLayout.addWidget(self.inputFrequency, 4, 1)
        #        self.dbLayout.addWidget(self.addButton, 3, 2, 1, 2)
        #        self.dbLayout.addWidget(self.addButtonFrequency, 4, 2, 1, 2)

        #        self.dbLayout.addWidget(self.tagsView, 5, 0, 1, 4)
        self.dbLayout.addWidget(self.purgeButton, 7, 0, 1, 4)
        self.dbLayout.addWidget(self.purgeButtonCustom, 8, 0, 1, 4)

        self.dbGroup.setLayout(self.dbLayout)

        ### toolbox ###
        self.toolbox = QToolBox()
        self.toolbox.addItem(self.appOptionsGroup, "Application")
        self.toolbox.addItem(self.appFontsGroup, "Fonts")
        self.toolbox.addItem(self.srsGroup, "Spaced Repetition System")
        self.toolbox.addItem(self.dictGroup, "Dictionaries")
        self.toolbox.addItem(self.dbGroup, "Studying items")

        ### main layout ###
        self.mainLayout = QVBoxLayout()

        self.bBox = QDialogButtonBox(
            QDialogButtonBox.Save | QDialogButtonBox.Reset | QDialogButtonBox.Close
        )  # | QDialogButtonBox.Help)
        self.saveRestart = QPushButton(u"Save/Restart")
        self.bBox.addButton(self.saveRestart, QDialogButtonBox.ResetRole)

        self.mainLayout.addWidget(self.toolbox)
        self.mainLayout.addWidget(self.bBox)
        # self.mainLayout.addWidget(self.progressDb)

        self.setLayout(self.mainLayout)

        self.initializeComposition()
        self.initializeComponents()
        self.initializeActions()

        ### additional preparations ###
        self.updateComboLevel()
        self.updateDbTable()
        self.animationTimer = ()

        self.roundCorners()
        self.items.backgroundFlag = False

        self.updateFrequencyRange()

    def roundCorners(self):
        self.status.setMask(roundCorners(self.status.rect(), 5))
        self.setMask(roundCorners(self.rect(), 5))

    def initializeComposition(self):

        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        desktop = QApplication.desktop().screenGeometry()

        self.setGeometry(QRect((desktop.width() - O_WIDTH) / 2, (desktop.height() - O_HEIGHT) / 2, O_WIDTH, O_HEIGHT))

        self.setStyleSheet("QWidget { background-color: rgb(255, 255, 255) }")

        self.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Maximum))

        self.status.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.status.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)

        self.status.setGeometry(
            (desktop.width() - O_WIDTH) / 2, (desktop.height() + O_HEIGHT) / 2 + OS_INDENT, O_WIDTH, OS_HEIGTH
        )
        self.status.setStyleSheet("QWidget { background-color: rgb(255, 255, 255) }")

        self.status.layout.setAlignment(Qt.AlignCenter)
        self.status.layout.setMargin(0)

        self.items.setWindowFlags(Qt.WindowStaysOnTopHint)
        self.items.setStyleSheet("QDialog { background-color: rgb(255, 255, 255) }")

    def initializeComponents(self):

        self.status.info.setFont(QFont("Cambria", self.options.getMessageFontSize()))
        #        self.progressDb.setMaximumHeight(6)
        #        self.progressDb.setRange(0,0)
        #        self.progressDb.hide()

        self.appLayout.setAlignment(Qt.AlignCenter)
        self.mainLayout.setAlignment(Qt.AlignCenter)

        self.intervalDial.setNotchesVisible(True)
        self.intervalDial.setRange(1, 30)
        self.intervalDial.setSingleStep(1)

        self.intervalSpin.setRange(1, 30)

        self.countdownDial.setNotchesVisible(True)
        self.countdownDial.setRange(5, 45)
        self.countdownDial.setSingleStep(1)

        self.countdownSpin.setRange(5, 45)

        self.countdownSpin.setValue(self.options.getCountdownInterval())
        self.intervalSpin.setValue(self.options.getRepetitionInterval())

        #        self.sessionItemsSpin.setRange(1, self.dbItems['kanji'] + self.dbItems['words'])
        #        self.sessionLengthSpin.setRange(1, 4 * (self.dbItems['kanji'] + self.dbItems['words']))

        self.sessionItemsSpin.setRange(1, self.dbItems["items"])
        self.sessionLengthSpin.setRange(1, 4 * self.dbItems["items"])

        self.intervalDial.setValue(self.options.getRepetitionInterval())
        self.countdownDial.setValue(self.options.getCountdownInterval())

        self.sessionItemsSpin.setValue(self.options.getSessionSize())
        self.sessionLengthSpin.setValue(self.options.getSessionLength())

        self.checkAutostart.setChecked(self.options.isQuizStartingAtLaunch())
        self.checkSplashScreen.setChecked(self.options.isSplashEnabled())
        self.checkAlwaysOnTop.setChecked(self.options.isAlwaysOnTop())
        self.checkGlobalHotkeys.setChecked(self.options.isGlobalHotkeyOn())
        self.checkEnableLog.setChecked(self.options.isLoggingOn())
        self.checkEnableFade.setChecked(self.options.isFadeEffectOn())
        self.checkSoundSignal.setChecked(self.options.isSoundOn())
        self.checkItemsBackground.setChecked(self.options.isBackgroundOn())
        self.checkPlastiqueTheme.setChecked(self.options.isPlastique())
        self.checkPreloadDictionary.setChecked(self.options.isPreloading())

        # self.sessionModeCombo.addItems(['kanji', 'compounds', 'all'])
        self.languageCombo.addItems(["eng", "rus"])
        self.shortcutCombo.addItems(["Q", "D", "J"])
        # self.comboTag.addItems(['jlpt', 'grade'])
        # self.comboCompare.addItems(['=', '>', '<', '>=', '<='])

        # self.tagsView.setColumnCount(4) #jlpt/grade level count active
        # self.tagsView.setHorizontalHeaderLabels(['Grade', 'Level', 'Items', 'Active'])
        # self.tagsView.setFixedSize(310,130)

        self.selectSentenceFont.setCurrentFont(QFont(self.options.getSentenceFont()))
        self.selectQuizFont.setCurrentFont(QFont(self.options.getQuizFont()))
        self.selectStatusFont.setCurrentFont(QFont(self.options.getMessageFont()))
        self.selectInfoFont.setCurrentFont(QFont(self.options.getInfoFont()))

        # self.intervalDial.setStyleSheet("QDial { background-color: rgb(255, 170, 0) ; }")

        # self.frequencyRange.setTickPosition(QSlider.TicksBelow)
        fRange = self.freq.getFrequencyRangeLimits()
        self.frequencyRange.setRange(fRange["min"], fRange["max"])
        self.frequencyRange.setLow(fRange["min"] * 2)
        self.frequencyRange.setHigh(fRange["max"] / 2)
        # self.frequencyRange.setValue(fRange['max']/2)

    def initializeActions(self):
        self.bBox.accepted.connect(self.saveOptions)
        self.bBox.rejected.connect(self.discardOptions)

        self.bBox.button(QDialogButtonBox.Reset).clicked.connect(self.resetOptions)

        self.intervalDial.valueChanged.connect(self.updateCountdown)
        self.intervalSpin.valueChanged.connect(self.updateInterval)

        self.countdownDial.valueChanged.connect(self.updateCountdownSpin)
        self.countdownSpin.valueChanged.connect(self.updateCountdownDial)

        # self.comboTag.currentIndexChanged.connect(self.updateComboLevel)

        self.viewAll.clicked.connect(self.showAll)
        # self.addButton.clicked.connect(self.updateDB)
        self.purgeButton.clicked.connect(self.purgeDB)

        self.frequencyRange.sliderReleased.connect(self.updateFrequencyRange)
        self.valueLow.valueChanged.connect(self.updateLow)
        self.valueHigh.valueChanged.connect(self.updateHigh)

        self.addItemInRange.clicked.connect(self.addItemsToDb)

        # self.frequencyRange.valueChanged.connect(self.updateFrequencyRange)

        # self.tagsView.itemChanged.connect(self.updateActiveItems)

    def saveOptions(self):
        """Saving all options"""
        ### flags ###
        self.options.setAlwaysOnTop(self.checkAlwaysOnTop.isChecked())
        self.options.setSplashEnabled(self.checkSplashScreen.isChecked())
        self.options.setQuizStartingAtLaunch(self.checkAutostart.isChecked())
        self.options.setSoundOn(self.checkSoundSignal.isChecked())
        self.options.setGlobalHotkeyOn(self.checkGlobalHotkeys.isChecked())
        self.options.setLoggingOn(self.checkEnableLog.isChecked())
        self.options.setFadeEffectOn(self.checkEnableFade.isChecked())
        self.options.setBackgroundOn(self.checkItemsBackground.isChecked())
        self.options.setPlastique(self.checkPlastiqueTheme.isChecked())
        ### session ###
        self.options.setRepetitionInterval(self.intervalDial.value())
        self.options.setCountdownInterval(self.countdownDial.value())
        self.options.setSessionSize(self.sessionItemsSpin.value())
        self.options.setSessionLength(self.sessionLengthSpin.value())
        ### dictionary ###
        self.options.setLookupLang(self.languageCombo.currentText())
        self.options.setPreloading(self.checkPreloadDictionary.isChecked())

        self.showInfo(u"All options saved!")

    def resetOptions(self):
        # TODO: ...
        print "reset"

    def discardOptions(self):
        if self.status.isVisible():
            self.status.hide()
        self.hide()

    def showInfo(self, message):
        self.status.info.setText(message)
        self.status.show()
        self.status.setWindowOpacity(0)
        self.fadeStatus()
        QTimer.singleShot(3000, self.fadeStatus)

    def updateCountdown(self):
        self.intervalSpin.setValue(self.intervalDial.value())
        self.update()

    def updateCountdownSpin(self):
        self.countdownSpin.setValue(self.countdownDial.value())
        self.update()

    def updateInterval(self):
        self.intervalDial.setValue(self.intervalSpin.value())
        self.update()

    def updateCountdownDial(self):
        self.countdownDial.setValue(self.countdownSpin.value())
        self.update()

    def updateComboLevel(self):
        pass

    #        if self.comboTag.currentText()=='jlpt':         #TODO: move to constants
    #            self.comboLevel.clear()
    #            self.comboLevel.addItems(['1','2','3','4'])
    #        elif self.comboTag.currentText()=='grade':
    #            self.comboLevel.clear()
    #            self.comboLevel.addItems(['1','2','3','4','5','6','7','8','9','10'])

    def updateDbTable(self):
        self.tagsView.clearContents()
        self.tagsView.setRowCount(0)
        # TODO: scan db for all possible tags, not just predefined
        #        dbStats = self.db.countItemsByGrades()
        #        i = 0
        #        for item in dbStats:
        #            if dbStats[item] != 0:
        #                self.tagsView.insertRow(i)
        #                grade = item[-1:];  level = u''
        #                if grade == 0:
        #                    grade = 10; level = item[:-2]
        #                else:
        #                    level = item[:-1]
        #
        #                self.tagsView.setItem(i, 0, QTableWidgetItem(level));
        #                self.tagsView.setItem(i, 1, QTableWidgetItem(str(grade)))
        #                self.tagsView.setItem(i, 2, QTableWidgetItem(str(dbStats[item])))
        #
        #                checkedItem = QTableWidgetItem();  checkedItem.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsSelectable);
        #
        #                if self.db.checkIfActive(item):  checkedItem.setCheckState(Qt.Checked)
        #                else: checkedItem.setCheckState(Qt.Unchecked)
        #
        #                self.tagsView.setItem(i, 3, checkedItem)
        #                i = i + 1
        #
        #        for row in range(0, self.tagsView.rowCount()):
        #            column = 0
        #            for column in range(0, self.tagsView.columnCount()):
        #                self.tagsView.item(row, column).setTextAlignment(Qt.AlignCenter)

        self.tagsView.resizeColumnsToContents()
        self.tagsView.resizeRowsToContents()

    def updateTotalItemsLabel(self):
        self.totalLabel.setText(
            u"Kanji: <b>"
            + str(self.dbItems["kanji"])
            + "</b>\tWords: <b>"
            + str(self.dbItems["words"])
            + "</b>\tTotal: <b>%s</b>" % (self.dbItems["kanji"] + self.dbItems["words"])
        )

    def updateSessionLimits(self):
        self.sessionItemsSpin.setRange(1, self.dbItems["kanji"] + self.dbItems["words"])
        self.sessionLengthSpin.setRange(1, 4 * (self.dbItems["kanji"] + self.dbItems["words"]))

    def updateTotalItems(self):
        self.dbItems = self.db.countTotalItemsInDb()
        self.updateTotalItemsLabel()
        self.updateSessionLimits()

    def updateDB(self):
        # self.progressDb.show()
        # self.showInfo('Updating database, please wait...')
        if self.comboTag.currentText() == "jlpt":
            if self.db.addItemsToDbJlpt(int(self.comboLevel.currentText())):
                self.showInfo("Successfully updated database.")
        elif self.comboTag.currentText() == "grade":
            print "unimplemented, yet"

        self.updateDbTable()
        self.updateTotalItems()
        # self.progressDb.hide()

    def updateActiveItems(self):
        for i in range(0, self.tagsView.rowCount()):
            try:
                self.db.updateActive(
                    self.tagsView.item(i, 0).text() + self.tagsView.item(i, 1).text(),
                    self.tagsView.item(i, 3).checkState() == Qt.CheckState.Checked,
                )
            except:
                # pass
                sys.exc_clear()

    def updateDbByTags(self):
        print "..."

    def purgeDB(self):
        self.db.clearDB()
        self.updateDbTable()
        self.updateTotalItems()
        self.showInfo("All data purged, database compacted.")

    def showAll(self):

        unfillLayout(self.items.infoLayout)
        unfillLayout(self.items.layout)
        self.items.infoLayout = QHBoxLayout()

        self.items.backgroundFlag = self.options.isBackgroundOn()

        # self.items.scrollArea = QScrollArea(self.items)
        # self.items.groupItems = QGroupBox()
        # TODO: implement using group box: QScrollArea <- QGroupBox <- a layout () <- widgets ( created with the group box as parent and added to the layout )

        studyItems = self.db.getAllItemsInFull()

        if len(studyItems) == 0:
            QMessageBox.information(self, "No items", "Currently no items in db")
        else:
            # progress = QProgressDialog('Loading items list...', 'Cancel', 0, len(studyItems), self)
            progress = QProgressDialog("Loading items list...", None, 0, len(studyItems), self)
            progress.setWindowModality(Qt.WindowModal)
            progress.show()
            count = 0

            i = 0
            j = 0
            max_c = 20  # ; max_r =50
            for item in studyItems:
                element = QLabel(item.item)
                element.setFont(QFont(self.options.getInfoFont(), 13))

                # if item.active:  element.setStyleSheet("QLabel { color:" + Leitner.correspondingColor(item.leitner_grade) + "}")
                # else: element.setStyleSheet("QLabel { color:gray }")
                if item.active:
                    color = Leitner.correspondingColor(item.leitner_grade)
                else:
                    color = "gray"

                if self.options.isBackgroundOn():
                    element.setStyleSheet(
                        "QLabel { color: black; background-color:" + color + "; border: 1px solid white; }"
                    )
                    # element.setStyleSheet("QLabel { color: blue; }")
                else:
                    element.setStyleSheet("QLabel { color:" + color + "}")

                element.setAttribute(Qt.WA_Hover, True)
                element.installEventFilter(self.filter)

                #                words = []; examples = {}
                #                for el in item.word:
                #                    words.append(el.word)
                #                for el in item.example:
                #                    examples[el.sentence] = el.translation

                element.params = {
                    "color": color,
                    "next": item.next_quiz.strftime("%d %b %H:%M:%S"),
                    "inSession": item.been_in_session,
                    "leitner": Leitner.grades[item.leitner_grade].key,
                }
                #'words': words, 'examples': examples,

                self.items.layout.addWidget(element, i, j)

                count = count + 1
                progress.setValue(count)
                if progress.wasCanceled():
                    break

                if j > max_c:
                    i = i + 1
                    j = 0
                else:
                    j = j + 1

            separator_two = QFrame()
            separator_two.setFrameShape(QFrame.HLine)
            separator_two.setFrameShadow(QFrame.Sunken)
            self.items.layout.addWidget(separator_two, i + 1, 0, 1, self.items.layout.columnCount())

            self.items.kanjiLarge = QLabel(u"")
            self.items.kanjiLarge.setFont(QFont(self.options.getSentenceFont(), 56))
            self.items.words = QLabel(u"")
            self.items.words.setWordWrap(True)
            self.items.words.setFont(QFont(Fonts.MSMyoutyou, 14))
            self.items.next = QLabel(u"")
            self.items.leitner = QLabel(u"")

            self.items.infoLayout.addWidget(self.items.kanjiLarge)
            self.items.infoLayout.addWidget(self.items.next)
            self.items.infoLayout.addWidget(self.items.leitner)
            self.items.infoLayout.addWidget(self.items.words)

            # self.items.infoLayout.setAlignment(Qt.AlignCenter)

            """
            #NB: or, may be, add horizontal layout?
            self.items.layout.addWidget(self.items.kanjiLarge, i + 2, 0, 2, 2)
            self.items.layout.addWidget(self.items.next, i + 2, 3, 1, 1)
            self.items.layout.addWidget(self.items.leitner, i + 3, 3, 1, 1)
            
            #self.items.layout.addWidget(self.items.kanjiLarge, i + 2, 9, 1, 4)
            """

            if self.options.isBackgroundOn():
                self.items.layout.setSpacing(0)
            else:
                self.items.layout.setSpacing(6)

            # self.items.layout.addLayout(self.items.infoLayout, i + 2, 0, 1, self.items.layout.columnCount())
            # self.items.layout.addLayout(self.items.infoLayout, i + 2, 0, 1, 8)
            self.items.layout.addLayout(self.items.infoLayout, i + 2, self.items.layout.columnCount() / 2, 1, 8)

            # self.items.scrollArea.setLayout(self.items.layout)

            # self.groupItems.setLayout(self.items.layout)
            # self.scrollArea.setWidget(self.groupItems)

            self.items.setLayout(self.items.layout)
            self.items.show()

    def updateFrequencyRange(self):
        self.totalItemsInRange = self.freq.getItemsCountInRange(self.frequencyRange.low(), self.frequencyRange.high())
        self.totalItemsLabel.setText(u"Total items in range: " + str(self.totalItemsInRange))
        self.valueLow.setValue(self.frequencyRange.low())
        self.valueHigh.setValue(self.frequencyRange.high())

    def updateLow(self):
        self.frequencyRange.setLow(self.valueLow.value())

    def updateHigh(self):
        self.frequencyRange.setHigh(self.valueHigh.value())

    def addItemsToDb(self):
        if self.db.addItemsToDb(self.frequencyRange.low(), self.frequencyRange.high()):
            # self.showInfo('Added ' + str(self.totalItemsInRange) + ' items to db')
            self.showInfo("Added " + str(self.db.count) + " items to db")

    # ------------------- Fading methods ---------------------#
    def fadeStatus(self):
        if self.status.windowOpacity() == 1:
            self.animationTimer = RepeatTimer(0.025, self.fadeOut, 40)
            self.animationTimer.start()
        else:
            self.animationTimer = RepeatTimer(0.025, self.fadeIn, 40)
            self.animationTimer.start()

    def fadeIn(self):
        self.status.setWindowOpacity(self.status.windowOpacity() + 0.1)

    def fadeOut(self):
        self.status.setWindowOpacity(self.status.windowOpacity() - 0.1)
Ejemplo n.º 4
0
Archivo: guiOpt.py Proyecto: Xifax/suzu
class OptionsDialog(QFrame):
    
    def __init__(self, db, options, parent=None):
        super(OptionsDialog, self).__init__(parent)
        # db & options handlers
        self.db = db
        self.options = options
        self.dbItems = self.db.countTotalItemsInDb()
    
        # status info
        self.status = QFrame()
        self.status.info = QLabel(u'')
        self.status.layout = QHBoxLayout()
        self.status.layout.addWidget(self.status.info)
        self.status.setLayout(self.status.layout)
        
        # all items info
        self.items = QDialog()
        self.items.db = self.db
#        self.items = QFrame(self)
#        self.items.setWindowFlags(Qt.ToolTip)
        self.items.layout = QGridLayout()
        self.items.infoLayout = QHBoxLayout()
        # filter 
        self.filter = StatusFilter()
        
        ### runtime group ###
        self.appOptionsGroup = QGroupBox('Runtime')
        self.appOptionsGroup.setAlignment(Qt.AlignCenter)
        self.checkAutostart = QCheckBox('Begin quiz on application start')
        self.checkEnableLog = QCheckBox('Enable errors logging')
        self.checkEnableFade = QCheckBox('Enable fade effect')
        self.checkAlwaysOnTop = QCheckBox('Always on top')
        self.checkSoundSignal = QCheckBox('Announce quiz with sound')
        self.checkSplashScreen = QCheckBox('Splash screen on launch')
        self.checkGlobalHotkeys = QCheckBox('Enable global hotkeys')
        self.checkItemsBackground = QCheckBox('Show colorful background')
        self.checkPlastiqueTheme = QCheckBox("Use 'plastique' theme")
        
        self.appLayout = QVBoxLayout()
        self.appLayout.addWidget(self.checkAutostart)
        self.appLayout.addWidget(self.checkSplashScreen)
        self.appLayout.addWidget(self.checkAlwaysOnTop)
        self.appLayout.addWidget(self.checkSoundSignal)
        self.appLayout.addWidget(self.checkEnableFade)
        self.appLayout.addWidget(self.checkEnableLog)
        self.appLayout.addWidget(self.checkGlobalHotkeys)
        self.appLayout.addWidget(self.checkItemsBackground)
        self.appLayout.addWidget(self.checkPlastiqueTheme)
#        self.appLayout.addWidget(self.checkPreloadDictionary)
        
        self.appOptionsGroup.setLayout(self.appLayout)
        
        ### fonts group ###
        self.appFontsGroup = QGroupBox('Fonts')
        self.appFontsGroup.setAlignment(Qt.AlignCenter)
        self.sentenceFontLabel = QLabel(u'Sentence:')
        self.selectSentenceFont = QFontComboBox()
        self.quizFontLabel = QLabel(u'Quiz:')
        self.selectQuizFont = QFontComboBox()
        self.infoFontLabel = QLabel(u'Kanji info:')
        self.selectInfoFont = QFontComboBox()
        self.statusFontLabel = QLabel(u'Status message:')
        self.selectStatusFont = QFontComboBox()
        
        self.fontsLayout = QVBoxLayout()
        self.fontsLayout.addWidget(self.sentenceFontLabel)
        self.fontsLayout.addWidget(self.selectSentenceFont)
        self.fontsLayout.addWidget(self.quizFontLabel)
        self.fontsLayout.addWidget(self.selectQuizFont)
        self.fontsLayout.addWidget(self.infoFontLabel)
        self.fontsLayout.addWidget(self.selectInfoFont)
        self.fontsLayout.addWidget(self.statusFontLabel)
        self.fontsLayout.addWidget(self.selectStatusFont)      
        
        self.appFontsGroup.setLayout(self.fontsLayout)
        
        ### srs group ###
        self.srsGroup = QGroupBox('SRS Tweaks')
        self.srsGroup.setAlignment(Qt.AlignCenter)
        
        self.intervalLabel = QLabel(u'Interval between quizzes (minutes):')
        self.countdownlLabel = QLabel(u'Time for answer (seconds):')
        self.intervalSpin = QSpinBox()
        self.intervalDial = QDial()

        self.countdownSpin = QSpinBox()
        self.countdownDial = QDial()
        
        self.sessionItemsLabel = QLabel(u'Sample size:')
        self.sessionItemsLabel.setToolTip(u'Maximum number of unique items, selected for current session from active database.')
        self.sessionItemsSpin = QSpinBox()
        self.sessionLengthLabel = QLabel(u'Session limit:')
        self.sessionLengthLabel.setToolTip(u'Maximum allowed repetitions/day, including non-unique items.')
        self.sessionLengthSpin = QSpinBox()
        
        self.sessionModeLabel = QLabel(u'Quiz mode:')
        self.sessionModeCombo = QComboBox()
        
        #TODO: add leitner coefficient tweak (0.1 ~ 2.0)
        
        self.srsLayout = QGridLayout()
        self.srsLayout.addWidget(self.intervalLabel, 0, 0, 1, 2)
        self.srsLayout.addWidget(self.intervalDial, 1, 0)
        self.srsLayout.addWidget(self.intervalSpin, 1, 1)
        self.srsLayout.addWidget(self.countdownlLabel, 2, 0, 1, 2)
        self.srsLayout.addWidget(self.countdownDial, 3, 0)
        self.srsLayout.addWidget(self.countdownSpin, 3, 1)
        self.srsLayout.addWidget(self.sessionItemsLabel, 4, 0)
        self.srsLayout.addWidget(self.sessionItemsSpin, 4, 1)
        self.srsLayout.addWidget(self.sessionLengthLabel, 5, 0)
        self.srsLayout.addWidget(self.sessionLengthSpin, 5, 1)
        self.srsLayout.addWidget(self.sessionModeLabel, 6, 0)
        self.srsLayout.addWidget(self.sessionModeCombo, 6, 1)
        
        self.srsGroup.setLayout(self.srsLayout)
        
        ### dictionary group ###
        self.dictGroup = QGroupBox('JMdict')
        self.dictGroup.setAlignment(Qt.AlignCenter)
        
        self.languageLabel = QLabel(u'Translation lookup in:')
        self.languageCombo = QComboBox()
        
        self.shortcutLabel = QLabel(u'Hotkey: Ctrl + Alt + ')
        self.shortcutCombo = QComboBox()
        
        self.dictLayout = QGridLayout()
        self.dictLayout.addWidget(self.languageLabel, 0, 0)
        self.dictLayout.addWidget(self.languageCombo, 0, 1)
        self.dictLayout.addWidget(self.shortcutLabel, 1, 0)
        self.dictLayout.addWidget(self.shortcutCombo, 1, 1)
        
        self.dictGroup.setLayout(self.dictLayout)
        
        ### database group ###
        self.dbGroup = QGroupBox('Database')
        self.dbGroup.setAlignment(Qt.AlignCenter)
        
        self.totalLabel = QLabel(u'Kanji: <b>' + str(self.dbItems['kanji']) + '</b>\tWords: <b>' + str(self.dbItems['words']) + '</b>\tTotal: <b>%s</b>' 
                                 % (self.dbItems['kanji'] + self.dbItems['words'])  )
        self.totalLabel.setWordWrap(True)
        self.viewAll = QToolButton()
        separator_one = QFrame();   separator_one.setFrameShape(QFrame.HLine);    separator_one.setFrameShadow(QFrame.Sunken)
        self.viewAll.setText(u'View all')
        self.addLabel = QLabel(u'Batch-add kanji to studying list:')
        self.comboTag = QComboBox()
        self.comboLevel = QComboBox()
        self.comboCompare = QComboBox()
        self.inputFrequency = QLineEdit()
        self.inputFrequency.setToolTip(u'Enter character frequency (from 1 to 6265)')
        self.addButton = QPushButton(u'Add by grade')
        self.addButton.setToolTip(u'Update db according to specified criteria (duplicates will be ignored)')
        self.addButtonFrequency = QPushButton(u'Add by frequency')
        self.purgeButton = QPushButton(u'Purge all data from database')
        self.purgeButtonCustom = QPushButton(u'Partial purge')
        self.progressDb = QProgressBar()
        
        self.tagsView = QTableWidget()
        
        self.dbLayout = QGridLayout()
        self.dbLayout.addWidget(self.totalLabel, 0, 0, 1, 3)
        self.dbLayout.addWidget(self.viewAll, 0, 3, 1, 1)
        self.dbLayout.addWidget(separator_one, 1, 0, 1, 4)
        self.dbLayout.addWidget(self.addLabel, 2, 0, 1, 4)
        self.dbLayout.addWidget(self.comboTag, 3, 0)
        self.dbLayout.addWidget(self.comboLevel, 3, 1)
        self.dbLayout.addWidget(self.comboCompare, 4, 0)
        self.dbLayout.addWidget(self.inputFrequency, 4, 1)
        self.dbLayout.addWidget(self.addButton, 3, 2, 1, 2)
        self.dbLayout.addWidget(self.addButtonFrequency, 4, 2, 1, 2)

        self.dbLayout.addWidget(self.tagsView, 5, 0, 1, 4)
        self.dbLayout.addWidget(self.purgeButton, 6, 0, 1, 4)
        self.dbLayout.addWidget(self.purgeButtonCustom, 7, 0, 1, 4)
        
        self.dbGroup.setLayout(self.dbLayout)
                
        ### toolbox ###
        self.toolbox = QToolBox()
        self.toolbox.addItem(self.appOptionsGroup, 'Application')
        self.toolbox.addItem(self.appFontsGroup, 'Fonts')
        self.toolbox.addItem(self.srsGroup, 'Spaced Repetition System')
        self.toolbox.addItem(self.dictGroup, 'Dictionaries')
        self.toolbox.addItem(self.dbGroup, 'Studying items')
#        self.toolbox.setCurrentIndex(-1)
        
        ### main layout ###
        self.mainLayout = QVBoxLayout()

        self.bBox = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Reset | QDialogButtonBox.Close )#| QDialogButtonBox.Help)
        self.saveRestart = QPushButton(u'Save/Restart')
        self.bBox.addButton(self.saveRestart, QDialogButtonBox.ResetRole)

        self.mainLayout.addWidget(self.toolbox)
        self.mainLayout.addWidget(self.bBox)
        self.mainLayout.addWidget(self.progressDb)
        
        self.setLayout(self.mainLayout)
        
        self.initializeComposition()
        self.initializeComponents()
        self.initializeActions()
        
        ### additional preparations ###
        self.updateComboLevel()
        self.updateDbTable()
        self.animationTimer = ()
        
        self.roundCorners()
        self.items.backgroundFlag = False
        
    def roundCorners(self):
        self.status.setMask(roundCorners(self.status.rect(),5))
        self.setMask(roundCorners(self.rect(),5))
    
    def initializeComposition(self):
        
#        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        desktop = QApplication.desktop().screenGeometry()

        self.setGeometry(QRect((desktop.width() - O_WIDTH)/2, (desktop.height() - O_HEIGHT)/2, O_WIDTH, O_HEIGHT))

        self.setStyleSheet("QWidget { background-color: rgb(255, 255, 255) }")
        
        self.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Maximum))
        
#        self.status.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.status.setWindowFlags(Qt.FramelessWindowHint)
        self.status.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        
        self.status.setGeometry((desktop.width() - O_WIDTH)/2, (desktop.height() + O_HEIGHT)/2 + OS_INDENT, O_WIDTH, OS_HEIGTH )
        self.status.setStyleSheet("QWidget { background-color: rgb(255, 255, 255) }")
        
        self.status.layout.setAlignment(Qt.AlignCenter)
        self.status.layout.setMargin(0)
        
#        self.items.setWindowFlags(Qt.WindowStaysOnTopHint)
        self.items.setStyleSheet("QDialog { background-color: rgb(255, 255, 255) }")
            
    def initializeComponents(self):
        
        self.status.info.setFont(QFont('Cambria',self.options.getMessageFontSize()))
        self.progressDb.setMaximumHeight(6)
        self.progressDb.setRange(0,0)
        self.progressDb.hide()

        self.appLayout.setAlignment(Qt.AlignCenter)
        self.mainLayout.setAlignment(Qt.AlignCenter)

        self.intervalDial.setNotchesVisible(True)
        self.intervalDial.setRange(1, 30)
        self.intervalDial.setSingleStep(1)
        
        self.intervalSpin.setRange(1,30)
        
        self.countdownDial.setNotchesVisible(True)
        self.countdownDial.setRange(5, 45)
        self.countdownDial.setSingleStep(1)
        
        self.countdownSpin.setRange(5,45)
        
        self.countdownSpin.setValue(self.options.getCountdownInterval())
        self.intervalSpin.setValue(self.options.getRepetitionInterval())
        
        self.sessionItemsSpin.setRange(1, self.dbItems['kanji'] + self.dbItems['words'])
        self.sessionLengthSpin.setRange(1, 4 * (self.dbItems['kanji'] + self.dbItems['words']))
        
        self.intervalDial.setValue(self.options.getRepetitionInterval())
        self.countdownDial.setValue(self.options.getCountdownInterval())
        
        self.sessionItemsSpin.setValue(self.options.getSessionSize())
        self.sessionLengthSpin.setValue(self.options.getSessionLength())
        
        self.checkAutostart.setChecked(self.options.isQuizStartingAtLaunch())
        self.checkSplashScreen.setChecked(self.options.isSplashEnabled())
        self.checkAlwaysOnTop.setChecked(self.options.isAlwaysOnTop())
        self.checkGlobalHotkeys.setChecked(self.options.isGlobalHotkeyOn())
        self.checkEnableLog.setChecked(self.options.isLoggingOn())
        self.checkEnableFade.setChecked(self.options.isFadeEffectOn())
        self.checkSoundSignal.setChecked(self.options.isSoundOn())
        self.checkItemsBackground.setChecked(self.options.isBackgroundOn())
        self.checkPlastiqueTheme.setChecked(self.options.isPlastique())
        
        self.sessionModeCombo.addItems([modes.kanji.key, modes.words.key, modes.all.key])
        self.languageCombo.addItems(['eng','rus'])
        self.shortcutCombo.addItems(['Q', 'D', 'J'])
        self.comboTag.addItems(['jlpt', 'grade'])
        self.comboCompare.addItems(['=', '>', '<', '>=', '<='])
        
        self.tagsView.setColumnCount(4) #jlpt/grade level count active
        self.tagsView.setHorizontalHeaderLabels(['Grade', 'Level', 'Items', 'Active'])
        self.tagsView.setFixedSize(310,130)
        
        self.selectSentenceFont.setCurrentFont(QFont(self.options.getSentenceFont()))
        self.selectQuizFont.setCurrentFont(QFont(self.options.getQuizFont()))
        self.selectStatusFont.setCurrentFont(QFont(self.options.getMessageFont()))
        self.selectInfoFont.setCurrentFont(QFont(self.options.getInfoFont()))
        
        #self.intervalDial.setStyleSheet("QDial { background-color: rgb(255, 170, 0) ; }")
        
    def initializeActions(self):
        self.bBox.accepted.connect(self.saveOptions)
        self.bBox.rejected.connect(self.discardOptions)
        
        self.bBox.button(QDialogButtonBox.Reset).clicked.connect(self.resetOptions)
        
        self.intervalDial.valueChanged.connect(self.updateCountdown)
        self.intervalSpin.valueChanged.connect(self.updateInterval)
        
        self.countdownDial.valueChanged.connect(self.updateCountdownSpin)
        self.countdownSpin.valueChanged.connect(self.updateCountdownDial)
        
        self.sessionModeCombo.currentIndexChanged.connect(self.updateTooltips)
        
        self.comboTag.currentIndexChanged.connect(self.updateComboLevel)
        
        self.viewAll.clicked.connect(self.showAll)
        self.addButton.clicked.connect(self.updateDB)
        self.purgeButton.clicked.connect(self.purgeDB)
        
        self.tagsView.itemChanged.connect(self.updateActiveItems)
    
    def saveOptions(self):
        """Saving all options"""
        ### flags ###
        self.options.setAlwaysOnTop(self.checkAlwaysOnTop.isChecked())
        self.options.setSplashEnabled(self.checkSplashScreen.isChecked())
        self.options.setQuizStartingAtLaunch(self.checkAutostart.isChecked())
        self.options.setSoundOn(self.checkSoundSignal.isChecked())
        self.options.setGlobalHotkeyOn(self.checkGlobalHotkeys.isChecked())
        self.options.setLoggingOn(self.checkEnableLog.isChecked())
        self.options.setFadeEffectOn(self.checkEnableFade.isChecked())
        self.options.setBackgroundOn(self.checkItemsBackground.isChecked())
        self.options.setPlastique(self.checkPlastiqueTheme.isChecked())
        ### session ###
        self.options.setRepetitionInterval(self.intervalDial.value())
        self.options.setCountdownInterval(self.countdownDial.value())
        self.options.setSessionSize(self.sessionItemsSpin.value())
        self.options.setSessionLength(self.sessionLengthSpin.value())
        ### dictionary ###
        self.options.setLookupLang(self.languageCombo.currentText())
        
        self.showInfo(u'All options saved!')
    
    def resetOptions(self):
        #TODO: ...
        print 'reset'
        
    def discardOptions(self):
        if self.status.isVisible(): self.status.hide()
        self.hide()
        
    def showInfo(self, message):
        self.status.info.setText(message)
        self.status.show()
        self.status.setWindowOpacity(0)
        self.fadeStatus()
        QTimer.singleShot(3000, self.fadeStatus)
    
    def updateCountdown(self):
        self.intervalSpin.setValue(self.intervalDial.value())
        self.update()
        
    def updateCountdownSpin(self):
        self.countdownSpin.setValue(self.countdownDial.value())
        self.update()
        
    def updateInterval(self):
        self.intervalDial.setValue(self.intervalSpin.value())
        self.update()
        
    def updateCountdownDial(self):
        self.countdownDial.setValue(self.countdownSpin.value())
        self.update()
        
    def updateComboLevel(self):
        if self.comboTag.currentText()=='jlpt':         #TODO: move to constants
            self.comboLevel.clear()
            self.comboLevel.addItems(['1','2','3','4'])
        elif self.comboTag.currentText()=='grade':
            self.comboLevel.clear()
            self.comboLevel.addItems(['1','2','3','4','5','6','7','8','9','10'])
            
    def updateDbTable(self):
        self.tagsView.clearContents()
        self.tagsView.setRowCount(0)
        
        dbStats = self.db.countItemsByGrades()
        i = 0
        for item in dbStats:
            if dbStats[item] != 0:
                self.tagsView.insertRow(i)
                if item != u'user':
                    grade = item[-1:];  level = u''
                    if grade == 0: 
                        grade = 10; level = item[:-2]
                    else:
                        level = item[:-1]
                else:
                    grade = u'*'
                    level = u'user'
                
                self.tagsView.setItem(i, 0, QTableWidgetItem(level));
                self.tagsView.setItem(i, 1, QTableWidgetItem(str(grade)))
                self.tagsView.setItem(i, 2, QTableWidgetItem(str(dbStats[item])))
                   
                checkedItem = QTableWidgetItem();  checkedItem.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsSelectable);

                if self.db.checkIfActive(item):  checkedItem.setCheckState(Qt.Checked)
                else: checkedItem.setCheckState(Qt.Unchecked)

                self.tagsView.setItem(i, 3, checkedItem)
                i = i + 1

#        else:
#        dbStats = self.db.countItemsByTags()
#        i = 0
#        if dbStats != 0:
#            self.tagsView.setItem(i, 0, QTableWidgetItem('user'));
#            self.tagsView.setItem(i, 1, QTableWidgetItem('-'))
#            self.tagsView.setItem(i, 2, QTableWidgetItem(str(dbStats)))
#            
#            checkedItem = QTableWidgetItem();  checkedItem.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsSelectable);
#
#            if self.db.checkIfActive('user'):  checkedItem.setCheckState(Qt.Checked)
#            else: checkedItem.setCheckState(Qt.Unchecked)
#
#            self.tagsView.setItem(i, 3, checkedItem)
#                
#        for row in range(0, self.tagsView.rowCount()):
#            column = 0
#            for column in range(0, self.tagsView.columnCount()):
#                self.tagsView.item(row, column).setTextAlignment(Qt.AlignCenter)

        self.tagsView.resizeColumnsToContents()
        self.tagsView.resizeRowsToContents()
        
    def updateTotalItemsLabel(self):
        self.totalLabel.setText(u'Kanji: <b>' + str(self.dbItems[modes.kanji.key]) + '</b>\tWords: <b>' + str(self.dbItems[modes.words.key]) + '</b>\tTotal: <b>%s</b>' 
                        % (self.dbItems[modes.kanji.key] + self.dbItems[modes.words.key])  )
        
    def updateSessionLimits(self):
#        self.sessionItemsSpin.setRange(1, self.dbItems[modes.kanji.key] + self.dbItems[modes.words.key])
        if self.sessionModeCombo.currentText() == modes.kanji.key:
            self.sessionItemsSpin.setRange(1, self.dbItems[modes.kanji.key])
        elif self.sessionModeCombo.currentText() == modes.words.key:
            self.sessionItemsSpin.setRange(1, self.dbItems[modes.words.key])
        elif self.sessionModeCombo.currentText() == modes.all.key:
            self.sessionItemsSpin.setRange(1, self.dbItems[modes.kanji.key] + self.dbItems[modes.words.key])
    
        self.sessionLengthSpin.setRange(1, 4 * (self.dbItems[modes.kanji.key] + self.dbItems[modes.words.key]))
        
    def updateTotalItems(self):
        self.dbItems = self.db.countTotalItemsInDb()
        self.updateTotalItemsLabel()
        self.updateSessionLimits()
        self.updateTooltips()
        
        self.updateDbTable()
    
    def updateTooltips(self):
        if self.sessionModeCombo.currentText() == modes.kanji.key:
            self.sessionItemsSpin.setToolTip('Max items: <b>' + str(self.dbItems[modes.kanji.key]) + '<b/>')
        elif self.sessionModeCombo.currentText() == modes.words.key:
            self.sessionItemsSpin.setToolTip('Max items: <b>' + str(self.dbItems[modes.words.key]) + '<b/>')
        elif self.sessionModeCombo.currentText() == modes.all.key:
            self.sessionItemsSpin.setToolTip('Max items: <b>' + str(self.dbItems[modes.kanji.key] + self.dbItems[modes.words.key]) + '<b/>')
    
    def updateDB(self):
        if self.comboTag.currentText() == 'jlpt':
#            if self.db.addItemsToDbJlpt(int(self.comboLevel.currentText())):
            if self.db.addItemsToDbJlpt(int(self.comboLevel.currentText())):
                self.showInfo('Successfully updated database.')
        elif self.comboTag.currentText() == 'grade':
            if self.db.addItemsToDbGrade(int(self.comboLevel.currentText())):
                self.showInfo('Successfully updated database.')
        self.updateDbTable()
        self.updateTotalItems()
        
    def updateActiveItems(self):
        for i in range(0, self.tagsView.rowCount()):
            try:
                self.db.updateActive(self.tagsView.item(i, 0).text() + self.tagsView.item(i, 1).text(), self.tagsView.item(i, 3).checkState() == Qt.CheckState.Checked)
            except:
                sys.exc_clear()
            
    def updateDbByTags(self):
        print '...'
    
    def purgeDB(self):
        self.db.clearDB()
        self.updateDbTable()
        self.updateTotalItems()
        self.showInfo('All data purged, database compacted.')
        
    def showAll(self):
        self.items.setWindowTitle('Items in db')
        unfillLayout(self.items.infoLayout)
        unfillLayout(self.items.layout)
        self.items.infoLayout = QHBoxLayout()
        
        self.items.backgroundFlag = self.options.isBackgroundOn()
        
        #self.items.scrollArea = QScrollArea(self.items)
        #self.items.groupItems = QGroupBox()
        #TODO: implement using group box: QScrollArea <- QGroupBox <- a layout () <- widgets ( created with the group box as parent and added to the layout )
                
        studyItems = self.db.getAllItemsInFull()
        
        if len(studyItems) == 0:
            QMessageBox.information(self, 'No items', 'Currently no items in db')
        else:
            #progress = QProgressDialog('Loading items list...', 'Cancel', 0, len(studyItems), self)
            progress = QProgressDialog('Loading items list...', None, 0, len(studyItems), self)
            progress.setWindowModality(Qt.WindowModal)
            progress.setWindowTitle('Reading db')
            progress.show()
            count = 0;
            
            i=0; j=0; max_c = 40#; max_r =50
            for item in studyItems:
                element = QLabel(item.character)
                element.setFont(QFont(self.options.getInfoFont(), 20))
                
                #if item.active:  element.setStyleSheet("QLabel { color:" + Leitner.correspondingColor(item.leitner_grade) + "}")
                #else: element.setStyleSheet("QLabel { color:gray }")
                if item.active:  color = Leitner.correspondingColor(item.leitner_grade)
                else: color = 'gray'
                
                if self.options.isBackgroundOn():
                    element.setStyleSheet("QLabel { color: black; background-color:" + color + "; border: 1px solid white; }")
                    #element.setStyleSheet("QLabel { color: blue; }")
                else:
                    element.setStyleSheet("QLabel { color:" + color + "}")
                
                element.setAttribute(Qt.WA_Hover, True)
                element.installEventFilter(self.filter)
                
                words = []; examples = {}
                for el in item.word:
                    words.append(el.word)
                for el in item.example:
                    examples[el.sentence] = el.translation
    
                element.params = {'color' : color, 'next': item.next_quiz.strftime('%d %b %H:%M:%S'), 'inSession': item.been_in_session, 
                                  'words': words, 'examples': examples, 'leitner': Leitner.grades[item.leitner_grade].key, 'item' : item}
                
                self.items.layout.addWidget(element, i, j)
                
                count = count + 1
                progress.setValue(count)
                if progress.wasCanceled():
                    break
                
                if j > max_c:
                    i = i + 1; j = 0
                else:
                    j = j + 1
            
            separator_two = QFrame();   separator_two.setFrameShape(QFrame.HLine);    separator_two.setFrameShadow(QFrame.Sunken)
            self.items.layout.addWidget(separator_two, i + 1, 0, 1, self.items.layout.columnCount())
            
            self.items.kanjiLarge = QLabel(u'')
            self.items.kanjiLarge.setFont(QFont(self.options.getSentenceFont(), 56))
            self.items.words = QLabel(u'')
            self.items.words.setWordWrap(True)
            self.items.words.setFont(QFont(Fonts.MSMyoutyou, 18))
            self.items.next = QLabel(u'')
            self.items.leitner = QLabel(u'')
            
            self.items.infoLayout.addWidget(self.items.kanjiLarge)
            self.items.infoLayout.addWidget(self.items.next)
            self.items.infoLayout.addWidget(self.items.leitner)
            self.items.infoLayout.addWidget(self.items.words)
            
            #self.items.infoLayout.setAlignment(Qt.AlignCenter)
            
            '''
            #NB: or, may be, add horizontal layout?
            self.items.layout.addWidget(self.items.kanjiLarge, i + 2, 0, 2, 2)
            self.items.layout.addWidget(self.items.next, i + 2, 3, 1, 1)
            self.items.layout.addWidget(self.items.leitner, i + 3, 3, 1, 1)
            
            #self.items.layout.addWidget(self.items.kanjiLarge, i + 2, 9, 1, 4)
            '''
            
            if self.options.isBackgroundOn():  self.items.layout.setSpacing(0)
            else:   self.items.layout.setSpacing(6)
    
            
            #self.items.layout.addLayout(self.items.infoLayout, i + 2, 0, 1, self.items.layout.columnCount())
            #self.items.layout.addLayout(self.items.infoLayout, i + 2, 0, 1, 8)
            self.items.layout.addLayout(self.items.infoLayout, i + 2, self.items.layout.columnCount()/2, 1, 8)
            
            #self.items.scrollArea.setLayout(self.items.layout)
            
            #self.groupItems.setLayout(self.items.layout)
            #self.scrollArea.setWidget(self.groupItems)
            
            self.items.setLayout(self.items.layout)
            self.items.show()
            
    def showEvent(self, event):
        self.updateTotalItems()
        
#------------------- Fading methods ---------------------#
    def fadeStatus(self):
        if self.status.windowOpacity() == 1:
            self.animationTimer = RepeatTimer(0.025, self.fadeOut, 40)
            self.animationTimer.start()
        else:
            self.animationTimer = RepeatTimer(0.025, self.fadeIn, 40)
            self.animationTimer.start()
    
    def fadeIn(self):
        self.status.setWindowOpacity(self.status.windowOpacity() + 0.1)
        
    def fadeOut(self):
        self.status.setWindowOpacity(self.status.windowOpacity() - 0.1)
Ejemplo n.º 5
0
class OptionsDialog(QFrame):
    def __init__(self, db, freq, options, parent=None):
        super(OptionsDialog, self).__init__(parent)
        # db & options handlers
        self.db = db
        self.freq = freq
        self.options = options
        self.dbItems = self.db.countTotalItemsInDb()
        self.totalItemsInRange = 0

        # status info
        self.status = QFrame()
        self.status.info = QLabel(u'')
        self.status.layout = QHBoxLayout()
        self.status.layout.addWidget(self.status.info)
        self.status.setLayout(self.status.layout)

        # all items info
        self.items = QDialog()
        self.items.layout = QGridLayout()
        self.items.infoLayout = QHBoxLayout()
        # filter
        self.filter = StatusFilter()

        ### runtime group ###
        self.appOptionsGroup = QGroupBox('Runtime')
        self.appOptionsGroup.setAlignment(Qt.AlignCenter)
        self.checkAutostart = QCheckBox('Begin quiz on application start')
        self.checkEnableLog = QCheckBox('Enable errors logging')
        self.checkEnableFade = QCheckBox('Enable fade effect')
        self.checkAlwaysOnTop = QCheckBox('Always on top')
        self.checkSoundSignal = QCheckBox('Announce quiz with sound')
        self.checkSplashScreen = QCheckBox('Splash screen on launch')
        self.checkGlobalHotkeys = QCheckBox('Enable global hotkeys')
        self.checkItemsBackground = QCheckBox('Show colorful background')
        self.checkPlastiqueTheme = QCheckBox("Use 'plastique' theme")
        self.checkPreloadDictionary = QCheckBox(
            "Load jmdict into memory on start")

        self.appLayout = QVBoxLayout()
        self.appLayout.addWidget(self.checkAutostart)
        self.appLayout.addWidget(self.checkSplashScreen)
        self.appLayout.addWidget(self.checkAlwaysOnTop)
        self.appLayout.addWidget(self.checkSoundSignal)
        self.appLayout.addWidget(self.checkEnableFade)
        self.appLayout.addWidget(self.checkEnableLog)
        self.appLayout.addWidget(self.checkGlobalHotkeys)
        self.appLayout.addWidget(self.checkItemsBackground)
        self.appLayout.addWidget(self.checkPlastiqueTheme)
        self.appLayout.addWidget(self.checkPreloadDictionary)

        self.appOptionsGroup.setLayout(self.appLayout)

        ### fonts group ###
        self.appFontsGroup = QGroupBox('Fonts')
        self.appFontsGroup.setAlignment(Qt.AlignCenter)
        self.sentenceFontLabel = QLabel(u'Sentence:')
        self.selectSentenceFont = QFontComboBox()
        self.quizFontLabel = QLabel(u'Quiz:')
        self.selectQuizFont = QFontComboBox()
        self.infoFontLabel = QLabel(u'Kanji info:')
        self.selectInfoFont = QFontComboBox()
        self.statusFontLabel = QLabel(u'Status message:')
        self.selectStatusFont = QFontComboBox()

        self.fontsLayout = QVBoxLayout()
        self.fontsLayout.addWidget(self.sentenceFontLabel)
        self.fontsLayout.addWidget(self.selectSentenceFont)
        self.fontsLayout.addWidget(self.quizFontLabel)
        self.fontsLayout.addWidget(self.selectQuizFont)
        self.fontsLayout.addWidget(self.infoFontLabel)
        self.fontsLayout.addWidget(self.selectInfoFont)
        self.fontsLayout.addWidget(self.statusFontLabel)
        self.fontsLayout.addWidget(self.selectStatusFont)

        self.appFontsGroup.setLayout(self.fontsLayout)

        ### srs group ###
        self.srsGroup = QGroupBox('SRS Tweaks')
        self.srsGroup.setAlignment(Qt.AlignCenter)

        self.intervalLabel = QLabel(u'Interval between quizzes (minutes):')
        self.countdownlLabel = QLabel(u'Time for answer (seconds):')
        self.intervalSpin = QSpinBox()
        self.intervalDial = QDial()

        self.countdownSpin = QSpinBox()
        self.countdownDial = QDial()

        self.sessionItemsLabel = QLabel(u'Sample size:')
        self.sessionItemsLabel.setToolTip(
            u'Maximum number of unique items, selected for current session from active database.'
        )
        self.sessionItemsSpin = QSpinBox()
        self.sessionLengthLabel = QLabel(u'Session limit:')
        self.sessionLengthLabel.setToolTip(
            u'Maximum allowed repetitions/day, including non-unique items.')
        self.sessionLengthSpin = QSpinBox()

        #        self.sessionModeLabel = QLabel(u'Quiz mode:')
        #        self.sessionModeCombo = QComboBox()

        #TODO: add leitner coefficient tweak (0.1 ~ 2.0)

        self.srsLayout = QGridLayout()
        self.srsLayout.addWidget(self.intervalLabel, 0, 0, 1, 2)
        self.srsLayout.addWidget(self.intervalDial, 1, 0)
        self.srsLayout.addWidget(self.intervalSpin, 1, 1)
        self.srsLayout.addWidget(self.countdownlLabel, 2, 0, 1, 2)
        self.srsLayout.addWidget(self.countdownDial, 3, 0)
        self.srsLayout.addWidget(self.countdownSpin, 3, 1)
        self.srsLayout.addWidget(self.sessionItemsLabel, 4, 0)
        self.srsLayout.addWidget(self.sessionItemsSpin, 4, 1)
        self.srsLayout.addWidget(self.sessionLengthLabel, 5, 0)
        self.srsLayout.addWidget(self.sessionLengthSpin, 5, 1)
        #        self.srsLayout.addWidget(self.sessionModeLabel, 6, 0)
        #        self.srsLayout.addWidget(self.sessionModeCombo, 6, 1)

        self.srsGroup.setLayout(self.srsLayout)

        ### dictionary group ###
        self.dictGroup = QGroupBox('JMdict')
        self.dictGroup.setAlignment(Qt.AlignCenter)

        self.languageLabel = QLabel(u'Translation lookup in:')
        self.languageCombo = QComboBox()

        self.shortcutLabel = QLabel(u'Hotkey: Ctrl + Alt + ')
        self.shortcutCombo = QComboBox()

        self.dictLayout = QGridLayout()
        self.dictLayout.addWidget(self.languageLabel, 0, 0)
        self.dictLayout.addWidget(self.languageCombo, 0, 1)
        self.dictLayout.addWidget(self.shortcutLabel, 1, 0)
        self.dictLayout.addWidget(self.shortcutCombo, 1, 1)

        self.dictGroup.setLayout(self.dictLayout)

        ### database group ###
        self.dbGroup = QGroupBox('Database')
        self.dbGroup.setAlignment(Qt.AlignCenter)

        #        self.totalLabel = QLabel(u'Kanji: <b>' + str(self.dbItems['kanji']) + '</b>\tWords: <b>' + str(self.dbItems['words']) + '</b>\tTotal: <b>%s</b>'
        #                                 % (self.dbItems['kanji'] + self.dbItems['words'])  )
        self.totalLabel = QLabel(u'Items: ' + str(self.dbItems['items']))
        self.totalLabel.setWordWrap(True)
        self.viewAll = QToolButton()
        separator_one = QFrame()
        separator_one.setFrameShape(QFrame.HLine)
        separator_one.setFrameShadow(QFrame.Sunken)
        self.viewAll.setText(u'View all')
        self.addLabel = QLabel(u'Batch-add items to studying list:')
        #        self.comboTag = QComboBox()
        #        self.comboLevel = QComboBox()
        #        self.comboCompare = QComboBox()
        #        self.inputFrequency = QLineEdit()
        #        self.inputFrequency.setToolTip(u'Enter character frequency (from 1 to 6265)')
        #        self.addButton = QPushButton(u'Add by grade')
        #        self.addButton.setToolTip(u'Update db according to specified criteria (duplicates will be ignored)')
        #        self.addButtonFrequency = QPushButton(u'Add by frequency')

        self.frequencyRange = RangeSlider(Qt.Horizontal)
        self.valueLow = QDoubleSpinBox()
        self.valueHigh = QDoubleSpinBox()
        self.addItemInRange = QPushButton(u'Add items in db')
        self.totalItemsLabel = QLabel(u'')

        self.purgeButton = QPushButton(u'Purge all data from database')
        self.purgeButtonCustom = QPushButton(u'Partial purge')
        #self.progressDb = QProgressBar()

        self.tagsView = QTableWidget()

        self.dbLayout = QGridLayout()
        self.dbLayout.addWidget(self.totalLabel, 0, 0, 1, 3)
        self.dbLayout.addWidget(self.viewAll, 0, 3, 1, 1)
        self.dbLayout.addWidget(separator_one, 1, 0, 1, 4)
        self.dbLayout.addWidget(self.addLabel, 2, 0, 1, 4)

        self.dbLayout.addWidget(self.valueLow, 3, 0, 1, 2)
        self.dbLayout.addWidget(self.valueHigh, 3, 2, 1, 2)
        self.dbLayout.addWidget(self.frequencyRange, 4, 0, 1, 4)
        self.dbLayout.addWidget(self.totalItemsLabel, 5, 0, 1, 4)
        self.dbLayout.addWidget(self.addItemInRange, 6, 0, 1, 4)

        #        self.dbLayout.addWidget(self.comboTag, 3, 0)
        #        self.dbLayout.addWidget(self.comboLevel, 3, 1)
        #        self.dbLayout.addWidget(self.comboCompare, 4, 0)
        #        self.dbLayout.addWidget(self.inputFrequency, 4, 1)
        #        self.dbLayout.addWidget(self.addButton, 3, 2, 1, 2)
        #        self.dbLayout.addWidget(self.addButtonFrequency, 4, 2, 1, 2)

        #        self.dbLayout.addWidget(self.tagsView, 5, 0, 1, 4)
        self.dbLayout.addWidget(self.purgeButton, 7, 0, 1, 4)
        self.dbLayout.addWidget(self.purgeButtonCustom, 8, 0, 1, 4)

        self.dbGroup.setLayout(self.dbLayout)

        ### toolbox ###
        self.toolbox = QToolBox()
        self.toolbox.addItem(self.appOptionsGroup, 'Application')
        self.toolbox.addItem(self.appFontsGroup, 'Fonts')
        self.toolbox.addItem(self.srsGroup, 'Spaced Repetition System')
        self.toolbox.addItem(self.dictGroup, 'Dictionaries')
        self.toolbox.addItem(self.dbGroup, 'Studying items')

        ### main layout ###
        self.mainLayout = QVBoxLayout()

        self.bBox = QDialogButtonBox(
            QDialogButtonBox.Save | QDialogButtonBox.Reset
            | QDialogButtonBox.Close)  #| QDialogButtonBox.Help)
        self.saveRestart = QPushButton(u'Save/Restart')
        self.bBox.addButton(self.saveRestart, QDialogButtonBox.ResetRole)

        self.mainLayout.addWidget(self.toolbox)
        self.mainLayout.addWidget(self.bBox)
        #self.mainLayout.addWidget(self.progressDb)

        self.setLayout(self.mainLayout)

        self.initializeComposition()
        self.initializeComponents()
        self.initializeActions()

        ### additional preparations ###
        self.updateComboLevel()
        self.updateDbTable()
        self.animationTimer = ()

        self.roundCorners()
        self.items.backgroundFlag = False

        self.updateFrequencyRange()

    def roundCorners(self):
        self.status.setMask(roundCorners(self.status.rect(), 5))
        self.setMask(roundCorners(self.rect(), 5))

    def initializeComposition(self):

        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        desktop = QApplication.desktop().screenGeometry()

        self.setGeometry(
            QRect((desktop.width() - O_WIDTH) / 2,
                  (desktop.height() - O_HEIGHT) / 2, O_WIDTH, O_HEIGHT))

        self.setStyleSheet("QWidget { background-color: rgb(255, 255, 255) }")

        self.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Maximum))

        self.status.setWindowFlags(Qt.FramelessWindowHint
                                   | Qt.WindowStaysOnTopHint)
        self.status.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)

        self.status.setGeometry((desktop.width() - O_WIDTH) / 2,
                                (desktop.height() + O_HEIGHT) / 2 + OS_INDENT,
                                O_WIDTH, OS_HEIGTH)
        self.status.setStyleSheet(
            "QWidget { background-color: rgb(255, 255, 255) }")

        self.status.layout.setAlignment(Qt.AlignCenter)
        self.status.layout.setMargin(0)

        self.items.setWindowFlags(Qt.WindowStaysOnTopHint)
        self.items.setStyleSheet(
            "QDialog { background-color: rgb(255, 255, 255) }")

    def initializeComponents(self):

        self.status.info.setFont(
            QFont('Cambria', self.options.getMessageFontSize()))
        #        self.progressDb.setMaximumHeight(6)
        #        self.progressDb.setRange(0,0)
        #        self.progressDb.hide()

        self.appLayout.setAlignment(Qt.AlignCenter)
        self.mainLayout.setAlignment(Qt.AlignCenter)

        self.intervalDial.setNotchesVisible(True)
        self.intervalDial.setRange(1, 30)
        self.intervalDial.setSingleStep(1)

        self.intervalSpin.setRange(1, 30)

        self.countdownDial.setNotchesVisible(True)
        self.countdownDial.setRange(5, 45)
        self.countdownDial.setSingleStep(1)

        self.countdownSpin.setRange(5, 45)

        self.countdownSpin.setValue(self.options.getCountdownInterval())
        self.intervalSpin.setValue(self.options.getRepetitionInterval())

        #        self.sessionItemsSpin.setRange(1, self.dbItems['kanji'] + self.dbItems['words'])
        #        self.sessionLengthSpin.setRange(1, 4 * (self.dbItems['kanji'] + self.dbItems['words']))

        self.sessionItemsSpin.setRange(1, self.dbItems['items'])
        self.sessionLengthSpin.setRange(1, 4 * self.dbItems['items'])

        self.intervalDial.setValue(self.options.getRepetitionInterval())
        self.countdownDial.setValue(self.options.getCountdownInterval())

        self.sessionItemsSpin.setValue(self.options.getSessionSize())
        self.sessionLengthSpin.setValue(self.options.getSessionLength())

        self.checkAutostart.setChecked(self.options.isQuizStartingAtLaunch())
        self.checkSplashScreen.setChecked(self.options.isSplashEnabled())
        self.checkAlwaysOnTop.setChecked(self.options.isAlwaysOnTop())
        self.checkGlobalHotkeys.setChecked(self.options.isGlobalHotkeyOn())
        self.checkEnableLog.setChecked(self.options.isLoggingOn())
        self.checkEnableFade.setChecked(self.options.isFadeEffectOn())
        self.checkSoundSignal.setChecked(self.options.isSoundOn())
        self.checkItemsBackground.setChecked(self.options.isBackgroundOn())
        self.checkPlastiqueTheme.setChecked(self.options.isPlastique())
        self.checkPreloadDictionary.setChecked(self.options.isPreloading())

        #self.sessionModeCombo.addItems(['kanji', 'compounds', 'all'])
        self.languageCombo.addItems(['eng', 'rus'])
        self.shortcutCombo.addItems(['Q', 'D', 'J'])
        #self.comboTag.addItems(['jlpt', 'grade'])
        #self.comboCompare.addItems(['=', '>', '<', '>=', '<='])

        #self.tagsView.setColumnCount(4) #jlpt/grade level count active
        #self.tagsView.setHorizontalHeaderLabels(['Grade', 'Level', 'Items', 'Active'])
        #self.tagsView.setFixedSize(310,130)

        self.selectSentenceFont.setCurrentFont(
            QFont(self.options.getSentenceFont()))
        self.selectQuizFont.setCurrentFont(QFont(self.options.getQuizFont()))
        self.selectStatusFont.setCurrentFont(
            QFont(self.options.getMessageFont()))
        self.selectInfoFont.setCurrentFont(QFont(self.options.getInfoFont()))

        #self.intervalDial.setStyleSheet("QDial { background-color: rgb(255, 170, 0) ; }")

        #self.frequencyRange.setTickPosition(QSlider.TicksBelow)
        fRange = self.freq.getFrequencyRangeLimits()
        self.frequencyRange.setRange(fRange['min'], fRange['max'])
        self.frequencyRange.setLow(fRange['min'] * 2)
        self.frequencyRange.setHigh(fRange['max'] / 2)
        #self.frequencyRange.setValue(fRange['max']/2)

    def initializeActions(self):
        self.bBox.accepted.connect(self.saveOptions)
        self.bBox.rejected.connect(self.discardOptions)

        self.bBox.button(QDialogButtonBox.Reset).clicked.connect(
            self.resetOptions)

        self.intervalDial.valueChanged.connect(self.updateCountdown)
        self.intervalSpin.valueChanged.connect(self.updateInterval)

        self.countdownDial.valueChanged.connect(self.updateCountdownSpin)
        self.countdownSpin.valueChanged.connect(self.updateCountdownDial)

        #self.comboTag.currentIndexChanged.connect(self.updateComboLevel)

        self.viewAll.clicked.connect(self.showAll)
        #self.addButton.clicked.connect(self.updateDB)
        self.purgeButton.clicked.connect(self.purgeDB)

        self.frequencyRange.sliderReleased.connect(self.updateFrequencyRange)
        self.valueLow.valueChanged.connect(self.updateLow)
        self.valueHigh.valueChanged.connect(self.updateHigh)

        self.addItemInRange.clicked.connect(self.addItemsToDb)

        #self.frequencyRange.valueChanged.connect(self.updateFrequencyRange)

        #self.tagsView.itemChanged.connect(self.updateActiveItems)

    def saveOptions(self):
        """Saving all options"""
        ### flags ###
        self.options.setAlwaysOnTop(self.checkAlwaysOnTop.isChecked())
        self.options.setSplashEnabled(self.checkSplashScreen.isChecked())
        self.options.setQuizStartingAtLaunch(self.checkAutostart.isChecked())
        self.options.setSoundOn(self.checkSoundSignal.isChecked())
        self.options.setGlobalHotkeyOn(self.checkGlobalHotkeys.isChecked())
        self.options.setLoggingOn(self.checkEnableLog.isChecked())
        self.options.setFadeEffectOn(self.checkEnableFade.isChecked())
        self.options.setBackgroundOn(self.checkItemsBackground.isChecked())
        self.options.setPlastique(self.checkPlastiqueTheme.isChecked())
        ### session ###
        self.options.setRepetitionInterval(self.intervalDial.value())
        self.options.setCountdownInterval(self.countdownDial.value())
        self.options.setSessionSize(self.sessionItemsSpin.value())
        self.options.setSessionLength(self.sessionLengthSpin.value())
        ### dictionary ###
        self.options.setLookupLang(self.languageCombo.currentText())
        self.options.setPreloading(self.checkPreloadDictionary.isChecked())

        self.showInfo(u'All options saved!')

    def resetOptions(self):
        #TODO: ...
        print 'reset'

    def discardOptions(self):
        if self.status.isVisible(): self.status.hide()
        self.hide()

    def showInfo(self, message):
        self.status.info.setText(message)
        self.status.show()
        self.status.setWindowOpacity(0)
        self.fadeStatus()
        QTimer.singleShot(3000, self.fadeStatus)

    def updateCountdown(self):
        self.intervalSpin.setValue(self.intervalDial.value())
        self.update()

    def updateCountdownSpin(self):
        self.countdownSpin.setValue(self.countdownDial.value())
        self.update()

    def updateInterval(self):
        self.intervalDial.setValue(self.intervalSpin.value())
        self.update()

    def updateCountdownDial(self):
        self.countdownDial.setValue(self.countdownSpin.value())
        self.update()

    def updateComboLevel(self):
        pass
#        if self.comboTag.currentText()=='jlpt':         #TODO: move to constants
#            self.comboLevel.clear()
#            self.comboLevel.addItems(['1','2','3','4'])
#        elif self.comboTag.currentText()=='grade':
#            self.comboLevel.clear()
#            self.comboLevel.addItems(['1','2','3','4','5','6','7','8','9','10'])

    def updateDbTable(self):
        self.tagsView.clearContents()
        self.tagsView.setRowCount(0)
        #TODO: scan db for all possible tags, not just predefined
        #        dbStats = self.db.countItemsByGrades()
        #        i = 0
        #        for item in dbStats:
        #            if dbStats[item] != 0:
        #                self.tagsView.insertRow(i)
        #                grade = item[-1:];  level = u''
        #                if grade == 0:
        #                    grade = 10; level = item[:-2]
        #                else:
        #                    level = item[:-1]
        #
        #                self.tagsView.setItem(i, 0, QTableWidgetItem(level));
        #                self.tagsView.setItem(i, 1, QTableWidgetItem(str(grade)))
        #                self.tagsView.setItem(i, 2, QTableWidgetItem(str(dbStats[item])))
        #
        #                checkedItem = QTableWidgetItem();  checkedItem.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsSelectable);
        #
        #                if self.db.checkIfActive(item):  checkedItem.setCheckState(Qt.Checked)
        #                else: checkedItem.setCheckState(Qt.Unchecked)
        #
        #                self.tagsView.setItem(i, 3, checkedItem)
        #                i = i + 1
        #
        #        for row in range(0, self.tagsView.rowCount()):
        #            column = 0
        #            for column in range(0, self.tagsView.columnCount()):
        #                self.tagsView.item(row, column).setTextAlignment(Qt.AlignCenter)

        self.tagsView.resizeColumnsToContents()
        self.tagsView.resizeRowsToContents()

    def updateTotalItemsLabel(self):
        self.totalLabel.setText(
            u'Kanji: <b>' + str(self.dbItems['kanji']) + '</b>\tWords: <b>' +
            str(self.dbItems['words']) + '</b>\tTotal: <b>%s</b>' %
            (self.dbItems['kanji'] + self.dbItems['words']))

    def updateSessionLimits(self):
        self.sessionItemsSpin.setRange(
            1, self.dbItems['kanji'] + self.dbItems['words'])
        self.sessionLengthSpin.setRange(
            1, 4 * (self.dbItems['kanji'] + self.dbItems['words']))

    def updateTotalItems(self):
        self.dbItems = self.db.countTotalItemsInDb()
        self.updateTotalItemsLabel()
        self.updateSessionLimits()

    def updateDB(self):
        #self.progressDb.show()
        #self.showInfo('Updating database, please wait...')
        if self.comboTag.currentText() == 'jlpt':
            if self.db.addItemsToDbJlpt(int(self.comboLevel.currentText())):
                self.showInfo('Successfully updated database.')
        elif self.comboTag.currentText() == 'grade':
            print 'unimplemented, yet'

        self.updateDbTable()
        self.updateTotalItems()
        #self.progressDb.hide()

    def updateActiveItems(self):
        for i in range(0, self.tagsView.rowCount()):
            try:
                self.db.updateActive(
                    self.tagsView.item(i, 0).text() +
                    self.tagsView.item(i, 1).text(),
                    self.tagsView.item(
                        i, 3).checkState() == Qt.CheckState.Checked)
            except:
                #pass
                sys.exc_clear()

    def updateDbByTags(self):
        print '...'

    def purgeDB(self):
        self.db.clearDB()
        self.updateDbTable()
        self.updateTotalItems()
        self.showInfo('All data purged, database compacted.')

    def showAll(self):

        unfillLayout(self.items.infoLayout)
        unfillLayout(self.items.layout)
        self.items.infoLayout = QHBoxLayout()

        self.items.backgroundFlag = self.options.isBackgroundOn()

        #self.items.scrollArea = QScrollArea(self.items)
        #self.items.groupItems = QGroupBox()
        #TODO: implement using group box: QScrollArea <- QGroupBox <- a layout () <- widgets ( created with the group box as parent and added to the layout )

        studyItems = self.db.getAllItemsInFull()

        if len(studyItems) == 0:
            QMessageBox.information(self, 'No items',
                                    'Currently no items in db')
        else:
            #progress = QProgressDialog('Loading items list...', 'Cancel', 0, len(studyItems), self)
            progress = QProgressDialog('Loading items list...', None, 0,
                                       len(studyItems), self)
            progress.setWindowModality(Qt.WindowModal)
            progress.show()
            count = 0

            i = 0
            j = 0
            max_c = 20  #; max_r =50
            for item in studyItems:
                element = QLabel(item.item)
                element.setFont(QFont(self.options.getInfoFont(), 13))

                #if item.active:  element.setStyleSheet("QLabel { color:" + Leitner.correspondingColor(item.leitner_grade) + "}")
                #else: element.setStyleSheet("QLabel { color:gray }")
                if item.active:
                    color = Leitner.correspondingColor(item.leitner_grade)
                else:
                    color = 'gray'

                if self.options.isBackgroundOn():
                    element.setStyleSheet(
                        "QLabel { color: black; background-color:" + color +
                        "; border: 1px solid white; }")
                    #element.setStyleSheet("QLabel { color: blue; }")
                else:
                    element.setStyleSheet("QLabel { color:" + color + "}")

                element.setAttribute(Qt.WA_Hover, True)
                element.installEventFilter(self.filter)

                #                words = []; examples = {}
                #                for el in item.word:
                #                    words.append(el.word)
                #                for el in item.example:
                #                    examples[el.sentence] = el.translation

                element.params = {
                    'color': color,
                    'next': item.next_quiz.strftime('%d %b %H:%M:%S'),
                    'inSession': item.been_in_session,
                    'leitner': Leitner.grades[item.leitner_grade].key
                }
                #'words': words, 'examples': examples,

                self.items.layout.addWidget(element, i, j)

                count = count + 1
                progress.setValue(count)
                if progress.wasCanceled():
                    break

                if j > max_c:
                    i = i + 1
                    j = 0
                else:
                    j = j + 1

            separator_two = QFrame()
            separator_two.setFrameShape(QFrame.HLine)
            separator_two.setFrameShadow(QFrame.Sunken)
            self.items.layout.addWidget(separator_two, i + 1, 0, 1,
                                        self.items.layout.columnCount())

            self.items.kanjiLarge = QLabel(u'')
            self.items.kanjiLarge.setFont(
                QFont(self.options.getSentenceFont(), 56))
            self.items.words = QLabel(u'')
            self.items.words.setWordWrap(True)
            self.items.words.setFont(QFont(Fonts.MSMyoutyou, 14))
            self.items.next = QLabel(u'')
            self.items.leitner = QLabel(u'')

            self.items.infoLayout.addWidget(self.items.kanjiLarge)
            self.items.infoLayout.addWidget(self.items.next)
            self.items.infoLayout.addWidget(self.items.leitner)
            self.items.infoLayout.addWidget(self.items.words)

            #self.items.infoLayout.setAlignment(Qt.AlignCenter)
            '''
            #NB: or, may be, add horizontal layout?
            self.items.layout.addWidget(self.items.kanjiLarge, i + 2, 0, 2, 2)
            self.items.layout.addWidget(self.items.next, i + 2, 3, 1, 1)
            self.items.layout.addWidget(self.items.leitner, i + 3, 3, 1, 1)
            
            #self.items.layout.addWidget(self.items.kanjiLarge, i + 2, 9, 1, 4)
            '''

            if self.options.isBackgroundOn(): self.items.layout.setSpacing(0)
            else: self.items.layout.setSpacing(6)

            #self.items.layout.addLayout(self.items.infoLayout, i + 2, 0, 1, self.items.layout.columnCount())
            #self.items.layout.addLayout(self.items.infoLayout, i + 2, 0, 1, 8)
            self.items.layout.addLayout(self.items.infoLayout, i + 2,
                                        self.items.layout.columnCount() / 2, 1,
                                        8)

            #self.items.scrollArea.setLayout(self.items.layout)

            #self.groupItems.setLayout(self.items.layout)
            #self.scrollArea.setWidget(self.groupItems)

            self.items.setLayout(self.items.layout)
            self.items.show()

    def updateFrequencyRange(self):
        self.totalItemsInRange = self.freq.getItemsCountInRange(
            self.frequencyRange.low(), self.frequencyRange.high())
        self.totalItemsLabel.setText(u'Total items in range: ' +
                                     str(self.totalItemsInRange))
        self.valueLow.setValue(self.frequencyRange.low())
        self.valueHigh.setValue(self.frequencyRange.high())

    def updateLow(self):
        self.frequencyRange.setLow(self.valueLow.value())

    def updateHigh(self):
        self.frequencyRange.setHigh(self.valueHigh.value())

    def addItemsToDb(self):
        if self.db.addItemsToDb(self.frequencyRange.low(),
                                self.frequencyRange.high()):
            #self.showInfo('Added ' + str(self.totalItemsInRange) + ' items to db')
            self.showInfo('Added ' + str(self.db.count) + ' items to db')

#------------------- Fading methods ---------------------#

    def fadeStatus(self):
        if self.status.windowOpacity() == 1:
            self.animationTimer = RepeatTimer(0.025, self.fadeOut, 40)
            self.animationTimer.start()
        else:
            self.animationTimer = RepeatTimer(0.025, self.fadeIn, 40)
            self.animationTimer.start()

    def fadeIn(self):
        self.status.setWindowOpacity(self.status.windowOpacity() + 0.1)

    def fadeOut(self):
        self.status.setWindowOpacity(self.status.windowOpacity() - 0.1)
Ejemplo n.º 6
0
class Quiz(QFrame):
     
    def __init__(self, parent=None):
        super(Quiz, self).__init__(parent)
        
        """Session Info"""
        self.status = QFrame()
        ##session message
        self.status.message = QLabel(u'')
        self.status.layout = QHBoxLayout()
        self.status.layout.addWidget(self.status.message)
        self.status.setLayout(self.status.layout)
        ##mouse event filter
        #self.status.filter = StatusFilter()
        self.status.setAttribute(Qt.WA_Hover, True)
        #self.status.installEventFilter(self.status.filter)

        """Items Info"""
        self.info = QFrame()
        self.info.reading = QLabel(u'')
        self.info.item = QLabel(u'')
        self.info.components = QLabel(u'')
        
        self.info.translation = QLabel(u'')
        self.info.translation.setAlignment(Qt.AlignCenter)
        self.info.translation.setWordWrap(True)

#        separator_one = QFrame()
#        separator_one.setFrameShape(QFrame.HLine)
#        separator_one.setFrameShadow(QFrame.Sunken)
#        
#        separator_two = QFrame()
#        separator_two.setFrameShape(QFrame.HLine)
#        separator_two.setFrameShadow(QFrame.Sunken)
        
        self.info.layout = QVBoxLayout()
        self.info.layout.addWidget(self.info.translation)
#        self.info.layout.addWidget(self.info.reading)
#        self.info.layout.addWidget(separator_one)
#        self.info.layout.addWidget(self.info.item)
#        self.info.layout.addWidget(separator_two)
#        self.info.layout.addWidget(self.info.components)
        self.info.setLayout(self.info.layout)
        
        """Verbose Info"""
        self.allInfo = QFrame()
        self.allInfo.layout = QGridLayout()
        self.allInfo.setLayout(self.allInfo.layout)
        #the rest is (should be) generated on the fly
        
        """Global Flags"""
        #self.correct = False
        
        """Quiz Dialog"""
        self.filter = Filter()
        ####    visual components    ###
        self.countdown = QProgressBar()
        self.sentence = QLabel(u'')
        
#        self.var_1st = QPushButton(u'')
#        self.var_2nd = QPushButton(u'')
#        self.var_3rd = QPushButton(u'')
#        self.var_4th = QPushButton(u'')

        self.isKnown = QPushButton(u'Good')
        self.isNotKnown = QPushButton(u'Again')

        self.answered = QPushButton(u'')
        self.answered.hide()
        
        ###    layouts    ####
        self.layout_vertical = QVBoxLayout()        #main
        self.layout_horizontal = QHBoxLayout()      #buttons
        
#        self.layout_horizontal.addWidget(self.var_1st)
#        self.layout_horizontal.addWidget(self.var_2nd)
#        self.layout_horizontal.addWidget(self.var_3rd)
#        self.layout_horizontal.addWidget(self.var_4th)

        self.layout_horizontal.addWidget(self.isKnown)
        self.layout_horizontal.addWidget(self.isNotKnown)
        
        self.layout_vertical.addWidget(self.countdown)
        self.layout_vertical.addWidget(self.sentence)
        self.layout_vertical.addLayout(self.layout_horizontal)
        
        self.layout_horizontal.addWidget(self.answered)

        self.setLayout(self.layout_vertical)

        ###    utility components    ###
        self.trayIcon = QSystemTrayIcon(self)
        self.trayMenu = QMenu()
        
        self.gifLoading = QMovie('../res/cube.gif')
        self.gifLoading.frameChanged.connect(self.updateTrayIcon)
        
        self.nextQuizTimer = QTimer()
        self.nextQuizTimer.setSingleShot(True)
        self.nextQuizTimer.timeout.connect(self.showQuiz)
        
        self.countdownTimer = QTimer()
        self.countdownTimer.setSingleShot(True)
        self.countdownTimer.timeout.connect(self.timeIsOut)
        
        ### initializing ###
        self.initializeResources()
        
        
        """Start!"""
        if self.options.isQuizStartingAtLaunch():
            self.waitUntilNextTimeslot()
            self.trayIcon.setToolTip('Quiz has started automatically!')
            self.pauseAction.setText('&Pause')
            self.trayIcon.showMessage('Loading complete! (took ~'+ str(self.loadingTime.seconds) + ' seconds) Quiz underway.', 'Lo! Quiz already in progress!', QSystemTrayIcon.MessageIcon.Warning, 10000)
        else:
            self.trayIcon.setToolTip('Quiz is not initiated!')
            self.trayIcon.showMessage('Loading complete! (took ~' + str(self.loadingTime.seconds) + ' seconds) Standing by.', 'Quiz has not started yet! If you wish, you could start it manually or enable autostart by default.', 
                                      QSystemTrayIcon.MessageIcon.Information, 10000 )
            
        """Test calls here:"""
        ###    ...    ###
        #self.connect(self.hooker, SIGNAL('noQdict'), self.noQdict)

    def noQdict(self):
        self.showSessionMessage('Nope, cannot show quick dictionary during actual quiz.')

####################################
#    Initialization procedures     #
####################################

    def initializeResources(self):
        
        """Pre-initialization"""
        self.animationTimer = ()
        self.progressTimer = ()
        self.grid_layout =()
                       
        """Initialize Options"""
        self.options = Options()
        
        """Initialize Statistics"""
        self.stats = Stats()
        
        """Config Here"""
        self.initializeComposition()
        self.initializeComponents()
        self.setMenus()
        self.trayIcon.show()
        #self.startTrayLoading()
        
        """"Initialize Dictionaries    (will take a some time!)"""
        time_start = datetime.now()
        self.dict = EdictParser()
        self.dict.loadDict()
        
        self.morphy = get_morph(PATH_TO_RES + DICT_EN)
        
        self.trayIcon.showMessage('Loading...', 'Initializing dictionaries', QSystemTrayIcon.MessageIcon.Information, 20000 )     #TODO: change into loading dialog... or not
        
        """Initializing srs system"""
        self.trayIcon.showMessage('Loading...', 'Initializing databases', QSystemTrayIcon.MessageIcon.Information, 20000 )
        self.srs = srsScheduler()
        self.srs.initializeAll()
        self.srs.initializeCurrentSession(self.options.getSessionSize())
        
        """Global hotkeys hook"""
        #TODO: add multiple hotkeys and fix stop()
        #self.hooker = GlobalHotkeyManager(toggleQDictFlag, 'Q')
#        self.hooker = GlobalHotkeyManager(toggleWidgetFlag(self.qdict), 'Q')
#        self.hooker.setDaemon(True) #temporarily, should work using stop()
#        self.hooker.start()
        
        time_end = datetime.now()
        self.loadingTime =  time_end - time_start

####################################
#    Composition and appearance    #
####################################

    def initializeComposition(self):
        
        """Main Dialog"""
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.setFocusPolicy(Qt.StrongFocus)
        self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        #Font will appear in buttons
        self.setFont(QFont(self.options.getQuizFont(), self.options.getQuizFontSize()))

        desktop = QApplication.desktop().screenGeometry()
        self.setGeometry(QRect(desktop.width() - H_INDENT, desktop.height() - V_INDENT, D_WIDTH, D_HEIGHT))
        
        self.setStyleSheet("QWidget { background-color: rgb(255, 255, 255); }")
        
        """Info dialog"""
        self.info.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.info.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        self.info.setGeometry(QRect(desktop.width() - H_INDENT - I_WIDTH - I_INDENT, desktop.height() - V_INDENT, I_WIDTH, I_HEIGHT))
        self.info.setFixedSize(I_WIDTH, I_HEIGHT)
        
        self.info.setStyleSheet("QWidget { background-color: rgb(255, 255, 255); }")
        
        """Verbose info dialog"""
        self.allInfo.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.allInfo.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        self.allInfo.setGeometry(QRect(desktop.width() - H_INDENT - I_WIDTH - I_INDENT, desktop.height() - V_INDENT, I_WIDTH, I_HEIGHT))
        
        self.allInfo.setStyleSheet("QWidget { background-color: rgb(255, 255, 255); }")
        
        """Session message"""
        self.status.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.status.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        self.status.setGeometry(QRect(desktop.width() - H_INDENT, desktop.height() - V_INDENT - S_HEIGHT - S_INDENT - S_CORRECTION, S_WIDTH, S_HEIGHT))
        
        self.status.setStyleSheet("QWidget { background-color: rgb(255, 255, 255); }")
        
        self.setMask(roundCorners(self.rect(),5))
        self.status.setMask(roundCorners(self.status.rect(),5))
        #self.info.setMask(roundCorners(self.info.rect(),5))
        #self.allInfo.setMask(roundCorners(self.allInfo.rect(),5))

    def initializeComponents(self):
        self.countdown.setMaximumHeight(6)
        self.countdown.setRange(0, self.options.getCountdownInterval() * 100)
        self.countdown.setTextVisible(False)
        self.countdown.setStyleSheet("QProgressbar { background-color: rgb(255, 255, 255); }")
        
        #self.setFont(QFont(Fonts.SyoutyouProEl, 40))#self.options.getQuizFontSize()))
        
        self.sentence.setAlignment(Qt.AlignmentFlag.AlignCenter)
        #self.sentence.setFont(QFont(Fonts.HiragiNoMarugotoProW4, self.options.getSentenceFontSize()))
        self.sentence.setFont(QFont(self.options.getSentenceFont(), self.options.getSentenceFontSize()))
        
        self.sentence.setWordWrap(True)
        self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'active.png'))
        
        self.status.message.setFont(QFont('Cambria', self.options.getMessageFontSize()))
        self.status.layout.setAlignment(Qt.AlignCenter)
        self.status.message.setWordWrap(False)
        self.status.layout.setMargin(0)
        
        self.info.item.setFont(QFont(Fonts.HiragiNoMyoutyouProW3, 36))
        self.info.reading.setFont(QFont(Fonts.HiragiNoMyoutyouProW3, 16))
        self.info.components.setFont((QFont(Fonts.HiragiNoMyoutyouProW3, 14)))
        #self.info.item.setWordWrap(True)
        self.info.components.setWordWrap(True)
        #self.info.layout.setAlignment(Qt.AlignCenter)
        self.info.layout.setMargin(0)
        #self.info.layout.setSizeConstraint(self.info.layout.SetFixedSize)       #NB: would work nice, if the anchor point was in right corner
        
        self.info.reading.setAlignment(Qt.AlignCenter)
        self.info.item.setAlignment(Qt.AlignCenter)
        self.info.components.setAlignment(Qt.AlignCenter)
        
        #self.info.setLayoutDirection(Qt.RightToLeft)
        
        self.info.reading.setStyleSheet("QLabel { color: rgb(155, 155, 155); }")
        self.info.components.setStyleSheet("QLabel { color: rgb(100, 100, 100); }")
        
        self.info.gem = self.info.saveGeometry()


####################################
#        Updating content          #
####################################        

    def updateContent(self):
        
        """Resetting multi-label sentence"""
        if self.grid_layout != ():
            for i in range(0, self.grid_layout.count()):
                    self.grid_layout.itemAt(i).widget().hide()
            self.layout_vertical.removeItem(self.grid_layout)
            self.grid_layout.setParent(None)
            self.update()
        if self.sentence.isHidden():    
            self.sentence.show()
        self.showButtonsQuiz()
        
        """Getting actual content"""
        self.srs.getNextItem()
              
        start = datetime.now()  #testing
        #example = self.srs.getCurrentExample().replace(self.srs.getWordFromExample(), u"<font color='blue'>" + self.srs.getWordFromExample() + u"</font>")
        example = self.srs.getCurrentExample().replace(self.srs.getCurrentItem(), u"<font color='blue'>" + self.srs.getCurrentItem() + u"</font>")
        print datetime.now() - start    #testing
        self.sentence.setText(example)
        
#        start = datetime.now()  #testing
#        readings = self.srs.getQuizVariants()
#        print datetime.now() - start    #testing
        
        '''
        changeFont = False
        for item in readings:
            if len(item) > 5 : changeFont = True
            
        if changeFont: self.setStyleSheet('QWidget { font-size: 11pt; }')
        else:   self.setStyleSheet('QWidget { font-size: %spt; }' % self.options.getQuizFontSize())
        '''
        '''
        if len(readings) == 4:                  #NB: HERE LIES THE GREAT ERROR
            self.var_1st.setText(readings[0])
            self.var_2nd.setText(readings[1])
            self.var_3rd.setText(readings[2])
            self.var_4th.setText(readings[3])
        '''
        
#        try:
#            for i in range(0, self.layout_horizontal.count()):
#                    if i > 3: break
#                    self.layout_horizontal.itemAt(i).widget().setText(u'')
#                    #self.layout_horizontal.itemAt(i).setStyleSheet('QPushButton { font-size: 11pt; }')
#                    self.layout_horizontal.itemAt(i).widget().setText(readings[i])
#        except:
#            print 'Not enough quiz variants'
            #TODO: log this
        
    def getReadyPostLayout(self):
        self.sentence.hide()
        self.update()
        
        self.grid_layout = QGridLayout()
        self.grid_layout.setSpacing(0)
        self.labels = []
        
        columns_mod = 0
        
        if len(self.srs.currentExample['eng']) > SENTENCE_MAX: font =  QFont(self.options.getSentenceFont(), MIN_FONT_SIZE); columns_mod = 6
        else: font = QFont(self.options.getSentenceFont(), self.options.getSentenceFontSize())
        
        #row, column, rows span, columns span, max columns
        i = 0; j = 0; r = 1; c = 1; n = COLUMNS_MAX + columns_mod
        for word in self.srs.parseCurrentExample():
            label = QLabel(word)
#            label.setFont(QFont(self.options.getSentenceFont(), self.options.getSentenceFontSize()))
            label.setFont(font)
            
            label.setAttribute(Qt.WA_Hover, True)
            label.installEventFilter(self.filter)
            self.labels.append(label)
            
            if len(label.text()) > 1: c = len(label.text())
            else: c = 1
            #Don't ask, really
            if j + c > n: i = i + 1; j = 0
            
            self.grid_layout.addWidget(self.labels.pop(), i, j, r, c)
            
            if j <= n: j = j + c
            else: j = 0; i = i + 1
        
        self.grid_layout.setAlignment(Qt.AlignCenter)
        self.layout_vertical.insertLayout(1, self.grid_layout)

        self.update()
        
    def hideButtonsQuiz(self):
        self.isKnown.hide()
        self.isNotKnown.hide()
        
        self.answered.clicked.connect(self.hideQuizAndWaitForNext)
        self.answered.show()
        
    def showButtonsQuiz(self):
        self.isKnown.show()
        self.isNotKnown.show()
        
        self.answered.hide()
        self.answered.disconnect()
        
####################################
#        Timers and animations     #
####################################

    def waitUntilNextTimeslot(self):
        #if self.nextQuizTimer.isActive():   self.nextQuizTimer.stop()
        self.nextQuizTimer.start(self.options.getRepetitionInterval() * 60 * 1000)  #options are in minutes    NB: how do neatly I convert minutes to ms?
        
    def beginCountdown(self):
        self.trayIcon.setToolTip('Quiz in progress!')
        self.pauseAction.setText('&Pause')
        self.pauseAction.setShortcut('P')
        
        self.countdownTimer.start(self.options.getCountdownInterval() * 1000)

        self.progressTimer = RepeatTimer(0.01, self.updateCountdownBar, self.options.getCountdownInterval() * 100)
        self.progressTimer.start()
        
    def updateCountdownBar(self):
        self.countdown.setValue(self.countdown.value() - 1)
        #print self.countdown.value()
        self.countdown.update()         #NB: without .update() recursive repaint crushes qt

    def fade(self):
        if self.windowOpacity() == 1:
            self.animationTimer = RepeatTimer(0.025, self.fadeOut, 40)
            self.animationTimer.start()
        else:
            self.animationTimer = RepeatTimer(0.025, self.fadeIn, 40)
            self.animationTimer.start()
    
    def fadeIn(self):
        self.setWindowOpacity(self.windowOpacity() + 0.1)
        
    def fadeOut(self):
        self.setWindowOpacity(self.windowOpacity() - 0.1)
        
    def stopCountdown(self):
        self.progressTimer.cancel()
        self.countdownTimer.stop()
        self.countdown.setValue(0)
        
####################################
#        Actions and events        #
####################################    
    
    def setMenus(self):
        self.trayMenu.addAction(QAction('&Quiz me now!', self, shortcut="Q", triggered=self.showQuiz))
        self.pauseAction = QAction('&Start quiz!', self, shortcut="S", triggered=self.pauseQuiz)
        self.trayMenu.addAction(self.pauseAction)
        self.trayMenu.addSeparator()
        self.trayMenu.addAction(QAction('Quick &dictionary', self, shortcut="D", triggered=self.showQuickDict))
        self.trayMenu.addAction(QAction('&Global &statistics', self, shortcut="G", triggered=self.showGlobalStatistics))
        self.trayMenu.addAction(QAction('&Options', self, shortcut="O", triggered=self.showOptions))
        self.trayMenu.addAction(QAction('&About', self, shortcut="A", triggered=self.showAbout))
        self.trayMenu.addSeparator()
        self.trayMenu.addAction(QAction('&Exit', self, shortcut="E", triggered=self.saveAndExit))

        self.trayIcon.setContextMenu(self.trayMenu)
        self.trayIcon.activated.connect(self.onTrayIconActivated)

    #TODO: show session statistics
    def onTrayIconActivated(self, reason):
        '''
        if reason == QSystemTrayIcon.DoubleClick:
            print 'tray icon double clicked'
        '''
        if reason == QSystemTrayIcon.Trigger:
            if self.isHidden():
                self.trayIcon.showMessage('Current session statistics:', 'Running time:\t\t' + self.stats.getRunningTime() + 
                                          '\nItems seen:\t\t' + str(self.stats.totalItemSeen) + 
                                          '\nCorrect answers:\t\t' + str(self.stats.answeredCorrect) +
                                          '\nWrong answers:\t\t' + self.stats.getIncorrectAnswersCount() +
                                          '\nCorrect ratio:\t\t' + self.stats.getCorrectRatioPercent() +
                                          #'\nQuiz total time:\t\t' + self.stats.getQuizActive() +
                                          '\nQuiz paused time:\t\t' + self.stats.getPausedTime() +
                                          '\nTotal pondering time:\t' + self.stats.getMusingsTime() +
                                          '\nTotal post-quiz time:\t' + self.stats.getQuizTime() +
                                          '\nAverage pondering:\t' + self.stats.getAverageMusingTime() +
                                          '\nAverage post-quiz:\t' + self.stats.getAveragePostQuizTime(), 
                                          QSystemTrayIcon.MessageIcon.Information, 20000)
    
    def setButtonsActions(self):
        self.isKnown.clicked.connect(self.correctAnswer)
        self.isNotKnown.clicked.connect(self.wrongAnswer)
        
    def resetButtonsActions(self):
        self.isKnown.disconnect()
        self.isNotKnown.disconnect()
        
    def postAnswerActions(self):
        self.stats.musingsStopped()
        self.stats.postQuizStarted()
        
        self.stopCountdown()
        self.hideButtonsQuiz()
        
        self.getReadyPostLayout()
        
    def checkTranslationSize(self, translation):
        if len(translation) > TRANSLATION_CHARS_LIMIT:
            self.answered.setStyleSheet('QPushButton { font-size: 9pt; }')
            
            space_indices = [i for i, value in enumerate(translation) if value == ' ']
            find_nearest_index = lambda value,list : min(list, key = lambda x:abs(x - value))
            nearest_index = find_nearest_index(TRANSLATION_CHARS_LIMIT, space_indices)
            translation = translation[:nearest_index] + '\n' + translation[nearest_index + 1:]
        else:
            self.answered.setStyleSheet('QPushButton { font-size: 11pt; }')
        
        self.answered.setText(translation)
        
    def correctAnswer(self):
        '''
        self.stats.musingsStopped()
        self.stats.postQuizStarted()
        
        self.stopCountdown()
        self.hideButtonsQuiz()
        
        self.getReadyPostLayout()
        '''
        self.postAnswerActions()
        
        self.srs.answeredCorrect()
        self.stats.quizAnsweredCorrect()
        #self.answered.setText(u"<font='Cambria'>" + self.srs.getCurrentSentenceTranslation() + "</font>")
#        self.answered.setText(self.srs.getCurrentSentenceTranslation())

        self.checkTranslationSize(self.srs.getCurrentSentenceTranslation())

        #self.answered.setFont(QFont('Calibri', 11))
        self.showSessionMessage(u'<font color=green>Correct: OK</font>\t|\tNext quiz: ' + self.srs.getNextQuizTime() 
                                + '\t|\t<font color=' + self.srs.getLeitnerGradeAndColor()['color'] +  '>Grade: ' + self.srs.getLeitnerGradeAndColor()['grade'] 
                                + ' (' + self.srs.getLeitnerGradeAndColor()['name'] + ')<font>')
        
        #self.answered.setShortcut('5')
        #self.setFocus()
        
    def wrongAnswer(self):
        '''
        self.stats.musingsStopped()
        self.stats.postQuizStarted()
        
        self.stopCountdown()
        self.hideButtonsQuiz()
        
        self.getReadyPostLayout()
        '''
        self.postAnswerActions()
        
        self.srs.answeredWrong()
        self.stats.quizAnsweredWrong()
        
#        self.answered.setText(self.srs.getCurrentSentenceTranslation())

        self.checkTranslationSize(self.srs.getCurrentSentenceTranslation())

        #self.answered.setFont(QFont('Calibri', 11))
        #self.showSessionMessage(u"Wrong! Should be: <font style='font-family:" + Fonts.MSMyoutyou + "'>" 
                                #+ self.srs.getCorrectAnswer() + "</font> - Next quiz: " + self.srs.getNextQuizTime())
        self.showSessionMessage(u'<font color=tomato>Bad</font>\t|\tNext quiz: ' + self.srs.getNextQuizTime()
                                + '\t|\t<font color=' + self.srs.getLeitnerGradeAndColor()['color'] +  '>Grade: ' + self.srs.getLeitnerGradeAndColor()['grade'] 
                                + ' (' + self.srs.getLeitnerGradeAndColor()['name'] + ')<font>')
            
    def timeIsOut(self):
        self.stats.musingsStopped()
        self.stats.postQuizStarted()
        
        QTimer.singleShot(50, self.hideButtonsQuiz)     #NB: slight artificial lag to prevent recursive repaint crush, when mouse is suddenly over appearing button
        self.getReadyPostLayout()
        
        self.srs.answeredWrong()
        self.stats.quizAnsweredWrong()

        #self.showSessionMessage(u'Time is out! Correct answer is:' + self.srs.getCorrectAnswer())
#        self.answered.setFont(QFont('Calibri', 11))
#        self.answered.setText(self.srs.getCurrentSentenceTranslation())

        self.checkTranslationSize(self.srs.getCurrentSentenceTranslation())

        self.showSessionMessage(u'<font color=tomato>Timeout!</font>\t|\tNext quiz: ' + self.srs.getNextQuizTime()
                                + '\t|\t<font color=' + self.srs.getLeitnerGradeAndColor()['color'] +  '>Grade: ' + self.srs.getLeitnerGradeAndColor()['grade'] 
                                + ' (' + self.srs.getLeitnerGradeAndColor()['name'] + ')<font>')        
    
    def hideQuizAndWaitForNext(self):
        self.stats.postQuizEnded()
        
        self.status.hide()
        self.info.hide()
        self.allInfo.hide()
        self.resetButtonsActions()
        
        self.setWindowOpacity(1)
        self.fade()
        QTimer.singleShot(1000, self.hide)
        self.waitUntilNextTimeslot()
        #self.updater.mayUpdate = True
 
    def pauseQuiz(self):
        if self.isHidden():
            if self.pauseAction.text() == '&Pause':
                self.nextQuizTimer.stop()
                self.pauseAction.setText('&Unpause')
                self.pauseAction.setShortcut('U')
                self.trayIcon.setToolTip('Quiz paused!')
                
                self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'inactive.png'))
                self.stats.pauseStarted()
                
                #self.updater.mayUpdate = True
                
            elif self.pauseAction.text() == '&Start quiz!':
                self.waitUntilNextTimeslot()
                self.pauseAction.setText('&Pause')
                self.pauseAction.setShortcut('P')
                self.trayIcon.setToolTip('Quiz in progress!')
                
                self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'active.png'))
            else:
                self.waitUntilNextTimeslot()
                self.pauseAction.setText('&Pause')
                self.pauseAction.setShortcut('P')
                self.trayIcon.setToolTip('Quiz in progress!')
                
                self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'active.png'))
                self.stats.pauseEnded()
                
                #self.updater.mayUpdate = False
        else:
            self.showSessionMessage(u'Sorry, cannot pause while quiz in progress!')
 
    def showQuiz(self):
        if self.isHidden():
            self.updateContent()
            self.setButtonsActions()
            
            self.show()
            self.setWindowOpacity(0)
            self.fade()

            self.countdown.setValue(self.options.getCountdownInterval() * 100)
            self.beginCountdown()
            self.stats.musingsStarted()
            
            if self.nextQuizTimer.isActive():   self.nextQuizTimer.stop()
            #self.updater.mayUpdate = False
        else:
            self.showSessionMessage(u'Quiz is already underway!')
         
    def showOptions(self):
        self.optionsDialog.show()
        
    def showAbout(self):
        self.about.show()
        
    def showQuickDict(self):
        self.qdict.showQDict = True
        
    def showGlobalStatistics(self):
        print '...'
        
    def startTrayLoading(self):
        self.gifLoading.start()
        #self.iconTimer = QTimer()
        #self.iconTimer.timeout.connect(self.updateTrayIcon)
        #self.iconTimer.start(100)
        
    def stopTrayLoading(self):
        self.gifLoading.stop()
        
    def updateTrayIcon(self):
        self.trayIcon.setIcon(self.gifLoading.currentPixmap())
        
    def showSessionMessage(self, message):
        """Shows info message"""
        self.status.message.setText(message)
        self.status.show()
        #self.setFocus() #NB: does not work
 
    def saveAndExit(self):
        self.hide()
        self.status.hide()
        self.allInfo.hide()
        self.trayIcon.showMessage('Shutting down...', 'Saving session', QSystemTrayIcon.MessageIcon.Information, 20000 )
        #self.startTrayLoading()
        
        if self.countdownTimer.isActive():
                self.countdownTimer.stop()
        if self.nextQuizTimer.isActive():
                self.nextQuizTimer.stop()
        if self.progressTimer != () and self.progressTimer.isAlive():
                self.progressTimer.cancel()      
            
        self.srs.endCurrentSession()
        self.trayIcon.hide()

#        self.hooker.stop()

        #self.updater.stop()
        self.optionsDialog.close()
        self.about.close()
        #self.qdict.close()
        self.close()        
        
    def addReferences(self, about, options, qdict, updater):
        self.about = about
        self.optionsDialog = options
        self.qdict = qdict
        self.updater = updater
        
    def initGlobalHotkeys(self):
        def toggleWidgetFlag(): self.qdict.showQDict = True
        self.hooker = GlobalHotkeyManager(toggleWidgetFlag , 'Q')
        self.hooker.setDaemon(True)
        self.hooker.start()