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()
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)
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)
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)
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)
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()