class PanelInputLine(QLineEdit):
    down_pressed = pyqtSignal()
    up_pressed = pyqtSignal()
    im_changed = pyqtSignal(str)

    def __init__(self, parent=None):
        super().__init__(parent)
        if not self.testAttribute(Qt.WidgetAttribute.WA_InputMethodEnabled):
            self.setAttribute(Qt.WidgetAttribute.WA_InputMethodEnabled)

    def inputMethodEvent(self, event):
        super().inputMethodEvent(event)
        self.im_changed.emit(self.text() + event.preeditString())

    def keyPressEvent(self, event):
        super().keyPressEvent(event)
        mod = mw.app.keyboardModifiers() & Qt.KeyboardModifier.ControlModifier
        key = event.key()
        if key == Qt.Key.Key_Down:
            self.down_pressed.emit()
        elif key == Qt.Key.Key_Up:
            self.up_pressed.emit()
        elif mod and (key == Qt.Key.Key_N):
            self.down_pressed.emit()
        elif mod and (key == Qt.Key.Key_P):
            self.up_pressed.emit()
        elif mod and (key == Qt.Key.Key_H):
            self.up_pressed.emit()
class LemmasRequester(QThread):
    """Auxiliary class for receiving lemmas in seperated thread"""
    lemmasReceived = pyqtSignal(list)
    suggestionsReceived = pyqtSignal(list)
    exceptionReceived = pyqtSignal()

    def __init__(self, lemma_query, query_kargs):
        QThread.__init__(self)
        self.lemma_query = lemma_query
        self.query_kargs = query_kargs

    def run(self):
        """
        If lemma list correctly received then emit lemmasReceived
        If suggestions received then emit suggestionsReceived
        if exception Exception raised then emit exceptionReceived
        """
        try:
            responce_type, responce = query.query(str(self.lemma_query),
                                                  **self.query_kargs)
            #aqt.utils.showInfo('responce_type "{}"; responce "{}"'.format(responce_type, responce))
            #aqt.utils.showInfo('{}'.format(str(self.lemma_query)))
            if responce_type == 'word_id':
                lemmas = query.query_lemma(responce, **self.query_kargs)
                self.lemmasReceived.emit(lemmas)
            else:
                suggestions = query.query_suggestions(responce,
                                                      **self.query_kargs)
                self.suggestionsReceived.emit(suggestions)
        except Exception as exception:
            sys.stderr.write(traceback.format_exc())
            self.exceptionReceived.emit()
Esempio n. 3
0
class SyncDialog(qt.QDialog):
    syncClicked = qt.pyqtSignal()
    confirmClicked = qt.pyqtSignal(list, qt.QDialog)
    
    def __init__(self, parent):
        super().__init__(parent)
        self.initUI()

    def initUI(self):
        self.layout = qt.QFormLayout()
        self.layout.setFieldGrowthPolicy(qt.QFormLayout.ExpandingFieldsGrow)
        self.setLayout(self.layout)

        self.extendedQueryEdit = qt.QLineEdit()
        self.layout.addRow('Extended query', self.extendedQueryEdit)

        self.skipTaggedCheckBox = qt.QCheckBox()
        self.skipTaggedCheckBox.setChecked(True)
        self.layout.addRow('Skip tagged', self.skipTaggedCheckBox)

        self.resolveManuallyCheckBox = qt.QCheckBox()
        self.layout.addRow('Resolve manually', self.resolveManuallyCheckBox)

        self.syncButton = qt.QPushButton('Sync')
        self.syncButton.clicked.connect(self.syncClicked)
        self.layout.addRow(self.syncButton)

        self.confirmDialog = ConfirmDialog(self)
        self.confirmDialog.confirmClicked.connect(self.onConfirmClicked)

    def exec_(self):
        self.syncButton.setFocus()
        super().exec_()

    def showConfirmDialog(self, changeOperations):
        self.close()
        self.confirmDialog.exec_(changeOperations)

    def onConfirmClicked(self, changeOperations):
        self.confirmClicked.emit(changeOperations, self.confirmDialog)
        self.confirmDialog.close()

    def skipTagged(self):
        return self.skipTaggedCheckBox.isChecked()

    def resolveManually(self):
        return self.resolveManuallyCheckBox.isChecked()

    def extendedQuery(self):
        return self.extendedQueryEdit.text()
Esempio n. 4
0
class LoginStateCheckWorker(QObject):
    start = pyqtSignal()
    logSuccess = pyqtSignal(str)
    logFailed = pyqtSignal()

    def __init__(self, checkFn, cookie):
        super().__init__()
        self.checkFn = checkFn
        self.cookie = cookie

    def run(self):
        loginState = self.checkFn(self.cookie)
        if loginState:
            self.logSuccess.emit(json.dumps(self.cookie))
        else:
            self.logFailed.emit()
Esempio n. 5
0
class ImportThread(QThread):
    """
    Background thread to export a wiki and parse questions out of it.
    """
    progress_update = pyqtSignal(int, int)

    def __init__(self, conf: dict, wiki_name: str,
                 wiki_conf: Dict[str, str]) -> None:
        super().__init__()
        self.conf = conf
        self.wiki_name = wiki_name
        self.wiki_conf = wiki_conf
        self.notes: Optional[Set[TwNote]] = None
        self.exception: Optional[Exception] = None
        self.warnings: List[str] = []

    def run(self) -> None:
        "Find notes, updating owner on progress periodically."
        try:
            self.notes = twimport.find_notes(
                tw_binary=self.conf['tiddlywikiBinary'],
                wiki_path=self.wiki_conf['path'],
                wiki_type=self.wiki_conf['type'],
                wiki_name=self.wiki_name,
                filter_=self.wiki_conf['contentFilter'],
                password=self.wiki_conf.get('password', ''),
                callback=self.progress_update.emit,
                warnings=self.warnings,
            )
            for n in self.notes:
                wiki_url = self.wiki_conf.get('permalink', '')
                if wiki_url:
                    n.set_permalink(wiki_url)
        except Exception as e:
            self.exception = e
Esempio n. 6
0
class QueryWorker(QThread):

    result_ready = pyqtSignal(dict)
    progress_update = pyqtSignal(dict)

    def __init__(self, service_unique):
        super(QueryWorker, self).__init__()
        self.service_unique = service_unique
        self.index = 0
        self.service = service_manager.get_service(service_unique)
        self.completed_counts = 0
        self.queue = Queue()
        self.result_ready.connect(handle_results)
        self.progress_update.connect(progress.update_labels)

    def target(self, index, service_field, word):
        self.queue.put((index, service_field, word))

    def run(self):
        # self.completed_counts = 0
        while True:
            if progress.abort():
                break
            try:
                index, service_field, word = self.queue.get(timeout=0.1)
                # self.progress_update.emit({
                #     'service_name': self.service.title,
                #     'word': word,
                #     'field_name': service_field
                # })
                result = self.query(service_field, word)
                self.result_ready.emit({index: result})
                self.completed_counts += 1
                # rest a moment
                self.rest()
            except Empty:
                break

    def rest(self):
        time.sleep(self.service.query_interval)

    def query(self, service_field, word):
        self.service.set_notifier(self.progress_update, self.index)
        return self.service.active(service_field, word)
Esempio n. 7
0
class Worker(qt.QObject):
    """Worker to get the IPA transcriptions of the selected Anki notes."""

    finished = qt.pyqtSignal()
    progress_changed = qt.pyqtSignal(int)
    result = qt.pyqtSignal(dict)

    def __init__(self, notes: Dict[int, anki.notes.Note], lang: str, base_field: str) -> None:
        """ Initialize Worker.

        :param notes: Anki notes we want to use
        :param lang: language of base field content
        :param base_field: field for which we want to get IPA transcriptions
        """
        super().__init__()
        self.notes = notes
        self.lang = lang
        self.base_field = base_field
        self._isRunning = True

    @qt.pyqtSlot()
    def run(self) -> None:
        """Get IPA transcription for each note and save it into a dictionary."""
        new_dict = dict()
        for index, key in enumerate(self.notes.keys()):
            try:
                if self.lang == "english":
                    new_dict[key] = get_english_ipa_transcription(self.notes[key][self.base_field])
                else:
                    words = utils.get_words_from_field(field_text=self.notes[key][self.base_field])
                    new_dict[key] = parse_ipa_transcription.transcript(words=words, language=self.lang)
            # IPA transcription not found
            except (urllib.error.HTTPError, IndexError):
                continue

            self.progress_changed.emit(index)

        self.result.emit(new_dict)
        self.finished.emit()

    def stop(self) -> None:
        """Stop worker."""
        self._isRunning = False
Esempio n. 8
0
class LatestVersionFinder(QThread):

    newVerAvail = pyqtSignal(str)
    newMsg = pyqtSignal(dict)
    clockIsOff = pyqtSignal(float)

    def __init__(self, main):
        QThread.__init__(self)
        self.main = main
        self.config = main.pm.meta

    def _data(self):
        d = {
            "ver": versionWithBuild(),
            "os": platDesc(),
            "id": self.config['id'],
            "lm": self.config['lastMsg'],
            "crt": self.config['created']
        }
        return d

    def run(self):
        if not self.config['updates']:
            return
        d = self._data()
        d['proto'] = 1

        try:
            r = requests.post(aqt.appUpdate, data=d)
            r.raise_for_status()
            resp = r.json()
        except:
            # behind proxy, corrupt message, etc
            print("update check failed")
            return
        if resp['msg']:
            self.newMsg.emit(resp)
        if resp['ver']:
            self.newVerAvail.emit(resp['ver'])
        diff = resp['time'] - time.time()
        if abs(diff) > 300:
            self.clockIsOff.emit(diff)
Esempio n. 9
0
class WordDownloadWorker(QObject):
    start = pyqtSignal()
    tick = pyqtSignal()
    done = pyqtSignal()
    logger = logging.getLogger(__name__ + '.WordDownloadWorker')

    def __init__(self, api):
        super().__init__()
        self.api = api

    def run(self):
        currentThread = QThread.currentThread()

        for word in self.api.getAllWords():
            if currentThread.isInterruptionRequested():
                return
            self.api.insertWord(word)
            self.tick.emit()

        self.done.emit()
Esempio n. 10
0
class WordExampleDownloadWorker(QObject):
    start = pyqtSignal()
    tick = pyqtSignal()
    done = pyqtSignal()
    logger = logging.getLogger(__name__ + '.WordExampleDownloadWorker')

    def __init__(self, api):
        super().__init__()
        self.api = api

    def run(self):
        currentThread = QThread.currentThread()

        for row in self.api.getWordsWithoutExample():
            if currentThread.isInterruptionRequested():
                return
            self.api.insertWordExamples(row['id'])
            self.tick.emit()

        self.done.emit()
Esempio n. 11
0
class SentenceTranslateDownloadWorker(QObject):
    start = pyqtSignal()
    tick = pyqtSignal()
    done = pyqtSignal()
    logger = logging.getLogger(__name__ + '.SentenceTranslateDownloadWorker')

    def __init__(self, api):
        super().__init__()
        self.api = api

    def run(self):
        currentThread = QThread.currentThread()

        for row in self.api.getSentencesWithoutTranslate():
            if currentThread.isInterruptionRequested():
                return
            self.api.insertSentenceTranslates(row)
            self.tick.emit()

        self.done.emit()
Esempio n. 12
0
class QueryWorker(QThread):

    result_ready = pyqtSignal(dict)
    progress_update = pyqtSignal(dict)

    def __init__(self, service_name, type):
        super(QueryWorker, self).__init__()
        self.service_name = service_name
        self.queue = Queue()
        if type == 'web':
            self.service = web_service_manager.get_service(service_name)
        if type == 'mdx':
            self.service = mdx_service_manager.get_service(service_name)
        self.result_ready.connect(handle_results)
        self.progress_update.connect(update_progress_label)

    def target(self, index, service_field, word):
        self.queue.put((index, service_field, word))

    def run(self):
        # try:
        while True:
            try:
                index, service_field, word = self.queue.get(timeout=0.1)
                name_info = os.path.basename(
                    self.service_name) if os.path.isabs(
                        self.service_name) else self.service_name
                self.progress_update.emit({
                    'service_name': name_info,
                    'word': word,
                    'field_name': service_field
                })
                result = self.query(service_field,
                                    word) if self.service else ""
                # showInfo('%d, %s' % (index, str(result)))
                self.result_ready.emit({index: result})
            except Empty:
                break

    def query(self, service_field, word):
        return self.service.instance.active(service_field, word)
Esempio n. 13
0
class ConfigSignals(QObject):
    initialized = pyqtSignal()
    saved = pyqtSignal()
    loaded = pyqtSignal()
    reset = pyqtSignal()
    deleted = pyqtSignal()
    unloaded = pyqtSignal()
Esempio n. 14
0
class AudioDownloadWorker(QObject):
    start = pyqtSignal()
    tick = pyqtSignal()
    done = pyqtSignal()
    logger = logging.getLogger(__name__ + '.AudioDownloadWorker')
    retries = Retry(total=5, backoff_factor=3, status_forcelist=[500, 502, 503, 504])
    session = requests.Session()
    session.mount('http://', HTTPAdapter(max_retries=retries))
    session.mount('https://', HTTPAdapter(max_retries=retries))

    def __init__(self, audios: [tuple]):
        super().__init__()
        self.audios = audios

    def run(self):
        currentThread = QThread.currentThread()

        def __download(fileName, url):
            try:
                if currentThread.isInterruptionRequested():
                    return
                r = self.session.get(url, stream=True)

                if not path.exists(fileName) or path.getsize(fileName) != int(r.headers['Content-Length']):
                    with open(fileName, 'wb') as f:
                        for chunk in r.iter_content(chunk_size=1024):
                            if chunk:
                                f.write(chunk)
                    self.logger.info(f'{fileName} 下载完成')
                else:
                    self.logger.info(f'{fileName} 跳过下载')
            except Exception as e:
                self.logger.warning(f'下载{fileName}:{url}异常: {e}')
            finally:
                self.tick.emit()

        with ThreadPool(max_workers=3) as executor:
            for fileName, url in self.audios:
                executor.submit(__download, fileName, url)
        self.done.emit()
Esempio n. 15
0
    class ProfileManager(QMainWindow):
        onClose = pyqtSignal()
        closeFires = True

        def closeEvent(self, evt):
            if self.closeFires:
                self.onClose.emit()
            evt.accept()

        def closeWithoutQuitting(self):
            self.closeFires = False
            self.close()
            self.closeFires = True
Esempio n. 16
0
class ConfirmDialog(qt.QDialog):
    confirmClicked = qt.pyqtSignal(list)
    
    def __init__(self, parent):
        super().__init__(parent)
        self.initUI()
        self.resetChangeOperations()

    def initUI(self):
        self.layout = qt.QVBoxLayout()
        self.setLayout(self.layout)

        self.table = ConfirmTable()
        self.layout.addWidget(self.table)

        self.hideUnchangedCheckBox = qt.QCheckBox('Hide unchanged')
        self.hideUnchangedCheckBox.stateChanged.connect(self.updateTable)
        self.layout.addWidget(self.hideUnchangedCheckBox)

        self.confirmButton = qt.QPushButton('Confirm')
        self.confirmButton.clicked.connect(self.onConfirmClicked)
        self.layout.addWidget(self.confirmButton)

    def resetChangeOperations(self):
        self.setChangeOperations([])

    def updateTable(self):
        changeOperations = self.changeOperations

        if self.hideUnchangedCheckBox.isChecked():
            changeOperations = list(filter(ChangeOperation.hasAnyChanges, changeOperations))
        
        self.table.setChangeOperations(changeOperations)

    def setChangeOperations(self, changeOperations):
        self.changeOperations = changeOperations
        self.updateTable()

    def onConfirmClicked(self):
        self.confirmClicked.emit(self.changeOperations)

    def exec_(self, changeOperations):
        self.setChangeOperations(changeOperations)
        self.setWindowState(qt.Qt.WindowMaximized)
        self.confirmButton.setFocus()
        super().exec_()

    def close(self):
        super().close()
        self.resetChangeOperations()
class MigakuServerThread(QThread):

    alertUser = pyqtSignal(str)
    exportingCondensed = pyqtSignal()
    notExportingCondensed = pyqtSignal()

    def __init__(self, mw):
        self.mw = mw
        QThread.__init__(self)
        self.server = MigakuHTTPServer(self, mw)
        self.start()

    def run(self):
        asyncio.set_event_loop(asyncio.new_event_loop())
        self.server.run()

    def alert(self, message):
        self.alertUser.emit(message)

    def addCondensedAudioInProgressMessage(self):
        self.exportingCondensed.emit()

    def removeCondensedAudioInProgressMessage(self):
        self.notExportingCondensed.emit()
Esempio n. 18
0
class Handler(QObject, logging.Handler):
    newRecord = pyqtSignal(object)

    def __init__(self, parent):
        super().__init__(parent)
        super(logging.Handler).__init__()

        formatter = logging.Formatter(
            '[%(asctime)s][%(name)s][%(levelname)s]\n%(message)s\n',
            '%d/%m/%Y %H:%M:%S')
        self.setFormatter(formatter)
        self.setLevel(logging.DEBUG)

    def emit(self, record):
        msg = self.format(record)
        self.newRecord.emit(msg)
Esempio n. 19
0
class Downloader(QThread):

    recv = pyqtSignal()

    def __init__(self, code):
        QThread.__init__(self)
        self.code = code
        self.error = None

    def run(self):
        # setup progress handler
        self.byteUpdate = time.time()
        self.recvTotal = 0

        def recvEvent(bytes):
            self.recvTotal += bytes
            self.recv.emit()

        addHook("httpRecv", recvEvent)
        client = AnkiRequestsClient()
        try:
            resp = client.get(appShared + "download/%s?v=2.1" % self.code)
            if resp.status_code == 200:
                data = client.streamContent(resp)
            elif resp.status_code in (403, 404):
                self.error = _(
                    "Invalid code, or add-on not available for your version of Anki."
                )
                return
            else:
                self.error = _("Unexpected response code: %s" %
                               resp.status_code)
                return
        except Exception as e:
            self.error = _(
                "Please check your internet connection.") + "\n\n" + str(e)
            return
        finally:
            remHook("httpRecv", recvEvent)

        self.fname = re.match("attachment; filename=(.+)",
                              resp.headers['content-disposition']).group(1)
        self.data = data
Esempio n. 20
0
class StatusListeningHttpClient(QObject):
    status_occurred = pyqtSignal(object)

    def __init__(self, http_client, status, on_status, parent=None):
        super().__init__(parent)
        self._http_client = http_client
        self._status = status

        self.status_occurred.connect(on_status)

    def _do_request(self, method, **kwargs):
        response = getattr(self._http_client, method)(**kwargs)

        if response.status_code == self._status:
            self.status_occurred.emit(response)

        return response

    get = partialmethod(_do_request, 'get')
    post = partialmethod(_do_request, 'post')
    put = partialmethod(_do_request, 'put')
    delete = partialmethod(_do_request, 'delete')
Esempio n. 21
0
class SyncThread(QThread):

    event = pyqtSignal(str, str)

    def __init__(self, path, hkey, auth=None, media=True, hostNum=None):
        QThread.__init__(self)
        self.path = path
        self.hkey = hkey
        self.auth = auth
        self.media = media
        self.hostNum = hostNum
        self._abort = 0  # 1=flagged, 2=aborting

    def flagAbort(self):
        self._abort = 1

    def run(self):
        # init this first so an early crash doesn't cause an error
        # in the main thread
        self.syncMsg = ""
        self.uname = ""
        try:
            self.col = Collection(self.path, log=True)
        except:
            self.fireEvent("corrupt")
            return
        self.server = RemoteServer(self.hkey, hostNum=self.hostNum)
        self.client = Syncer(self.col, self.server)
        self.sentTotal = 0
        self.recvTotal = 0

        def syncEvent(type):
            self.fireEvent("sync", type)

        def syncMsg(msg):
            self.fireEvent("syncMsg", msg)

        def sendEvent(bytes):
            if not self._abort:
                self.sentTotal += bytes
                self.fireEvent("send", str(self.sentTotal))
            elif self._abort == 1:
                self._abort = 2
                raise Exception("sync cancelled")

        def recvEvent(bytes):
            if not self._abort:
                self.recvTotal += bytes
                self.fireEvent("recv", str(self.recvTotal))
            elif self._abort == 1:
                self._abort = 2
                raise Exception("sync cancelled")

        addHook("sync", syncEvent)
        addHook("syncMsg", syncMsg)
        addHook("httpSend", sendEvent)
        addHook("httpRecv", recvEvent)
        # run sync and catch any errors
        try:
            self._sync()
        except:
            err = traceback.format_exc()
            self.fireEvent("error", err)
        finally:
            # don't bump mod time unless we explicitly save
            self.col.close(save=False)
            remHook("sync", syncEvent)
            remHook("syncMsg", syncMsg)
            remHook("httpSend", sendEvent)
            remHook("httpRecv", recvEvent)

    def _abortingSync(self):
        try:
            return self.client.sync()
        except Exception as e:
            if "sync cancelled" in str(e):
                self.server.abort()
                raise
            else:
                raise

    def _sync(self):
        if self.auth:
            # need to authenticate and obtain host key
            self.hkey = self.server.hostKey(*self.auth)
            if not self.hkey:
                # provided details were invalid
                return self.fireEvent("badAuth")
            else:
                # write new details and tell calling thread to save
                self.fireEvent("newKey", self.hkey)
        # run sync and check state
        try:
            ret = self._abortingSync()
        except Exception as e:
            log = traceback.format_exc()
            err = repr(str(e))
            if ("Unable to find the server" in err or "Errno 2" in err
                    or "getaddrinfo" in err):
                self.fireEvent("offline")
            elif "sync cancelled" in err:
                pass
            else:
                self.fireEvent("error", log)
            return
        if ret == "badAuth":
            return self.fireEvent("badAuth")
        elif ret == "clockOff":
            return self.fireEvent("clockOff")
        elif ret == "basicCheckFailed" or ret == "sanityCheckFailed":
            return self.fireEvent("checkFailed")
        # full sync?
        if ret == "fullSync":
            return self._fullSync()
        # save and note success state
        if ret == "noChanges":
            self.fireEvent("noChanges")
        elif ret == "success":
            self.fireEvent("success")
        elif ret == "serverAbort":
            pass
        else:
            self.fireEvent("error", "Unknown sync return code.")
        self.syncMsg = self.client.syncMsg
        self.uname = self.client.uname
        self.hostNum = self.client.hostNum
        # then move on to media sync
        self._syncMedia()

    def _fullSync(self):
        # tell the calling thread we need a decision on sync direction, and
        # wait for a reply
        self.fullSyncChoice = False
        self.localIsEmpty = self.col.isEmpty()
        self.fireEvent("fullSync")
        while not self.fullSyncChoice:
            time.sleep(0.1)
        f = self.fullSyncChoice
        if f == "cancel":
            return
        self.client = FullSyncer(self.col,
                                 self.hkey,
                                 self.server.client,
                                 hostNum=self.hostNum)
        try:
            if f == "upload":
                if not self.client.upload():
                    self.fireEvent("upbad")
            else:
                ret = self.client.download()
                if ret == "downloadClobber":
                    self.fireEvent(ret)
                    return
        except Exception as e:
            if "sync cancelled" in str(e):
                return
            raise
        # reopen db and move on to media sync
        self.col.reopen()
        self._syncMedia()

    def _syncMedia(self):
        if not self.media:
            return
        self.server = RemoteMediaServer(self.col,
                                        self.hkey,
                                        self.server.client,
                                        hostNum=self.hostNum)
        self.client = MediaSyncer(self.col, self.server)
        try:
            ret = self.client.sync()
        except Exception as e:
            if "sync cancelled" in str(e):
                return
            raise
        if ret == "noChanges":
            self.fireEvent("noMediaChanges")
        elif ret == "sanityCheckFailed" or ret == "corruptMediaDB":
            self.fireEvent("mediaSanity")
        else:
            self.fireEvent("mediaSuccess")

    def fireEvent(self, cmd, arg=""):
        self.event.emit(cmd, arg)
Esempio n. 22
0
class Forvo(QThread):

    resultsFound = pyqtSignal(list)

    def __init__(self, language):
        QThread.__init__(self)
        self.selLang = language
        self.term = False
        self.langShortCut = languages[self.selLang]
        self.GOOGLE_SEARCH_URL = "https://forvo.com/word/◳t/#" + self.langShortCut #◳r
        self.session = requests.session()
        self.session.headers.update(
            {
                "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:10.0) \
                    Gecko/20100101 Firefox/10.0"
            }
        )

    def setTermIdName(self, term, idName):
        self.term = term
        self.idName = idName

    def run(self):
        if self.term:
            resultList = [self.attemptFetchForvoLinks(self.term), self.idName]
            self.resultsFound.emit(resultList)

    def search(self, term, lang = False):
        if lang and self.selLang != lang:
            self.selLang = lang
            self.langShortCut = languages[self.selLang]
            self.GOOGLE_SEARCH_URL = "https://forvo.com/word/◳t/#" + self.langShortCut
        query = self.GOOGLE_SEARCH_URL.replace('◳t', re.sub(r'[\/\'".,&*@!#()\[\]\{\}]', '', term))
        return self.image_search(query)

    def decodeURL(self, url1, url2, protocol, audiohost, server):
        url2 = protocol + "//" + server + "/player-mp3-highHandler.php?path=" + url2;
        url1 = protocol + "//" + audiohost + "/mp3/" + base64.b64decode(url1).decode("utf-8", "strict")
        return url1, url2

    def attemptFetchForvoLinks(self,term):
        urls = self.search(term)
        if len(urls) > 0:
            return json.dumps(urls)
        else:
            return False

    def generateURLS(self, results):
        audio = re.findall(r'var pronunciations = \[([\w\W\n]*?)\];', results)
        if not audio:
            return []
        audio = audio[0]
        data = re.findall(self.selLang + r'.*?Pronunciation by (?:<a.*?>)?(\w+).*?class="lang_xx"\>(.*?)\<.*?,.*?,.*?,.*?,\'(.+?)\',.*?,.*?,.*?\'(.+?)\'', audio)     
        if data:
            server = re.search(r"var _SERVER_HOST=\'(.+?)\';", results).group(1)
            audiohost = re.search(r'var _AUDIO_HTTP_HOST=\'(.+?)\';', results).group(1)
            protocol = 'https:'
            urls = []
            for datum in data:
                url1, url2 = self.decodeURL(datum[2],datum[3],protocol, audiohost, server)
                urls.append([datum[0],datum[1], url1, url2])
            return urls
        else:
            return []

    def setSearchRegion(self, region):
        self.region = region

    def image_search(self, query_gen):
        try:
            html = self.session.get(query_gen).text
        except:
            showInfo('The Forvo Dictionary could not be loaded, please confirm that your are connected to the internet and try again. ')
            return []
        results = html
            
        return self.generateURLS(results)
Esempio n. 23
0
class LineEditWithFocusedSignal(QLineEdit):
    focused = pyqtSignal()

    def focusInEvent(self, e):
        self.focused.emit()
Esempio n. 24
0
class ProfileSettingsDialog(QDialog):
    loginPageIndex = 0
    logoutPageIndex = 1

    logged_in = pyqtSignal(dict)
    unauthorized_login = pyqtSignal(dict)

    logged_out = pyqtSignal()
    logout_error = pyqtSignal(dict)

    token_invalidated = pyqtSignal(dict)

    connection_error = pyqtSignal()

    def __init__(self, parent, network_thread, user_repo, user_is_logged_in, achievements_repo):
        super().__init__(parent)
        self.ui = Ui_ProfileSettingsDialog()
        self.ui.setupUi(self)

        self._network_thread = network_thread
        self._user_repo = user_repo
        self._achievements_repo = achievements_repo

        self._connect_login_signals()
        self._connect_logout_signals()
        self._connect_token_validation_signals()
        self._connect_connection_error_signal()

        self._connect_login_button()
        self._connect_logout_button()
        self._connect_signup_button()

        self._show_correct_auth_form(user_is_logged_in)
        self._validate_token_if_logged_in(user_is_logged_in)

    def keyPressEvent(self, event) -> None:
        key = event.key()
        if key == Qt.Key_Return or key == Qt.Key_Enter:
            event.ignore()
        else:
            super().keyPressEvent(event)

    def _connect_login_signals(self):
        self.logged_in.connect(self.on_successful_login)
        self.logged_in.connect(self._start_sync_job)
        self.unauthorized_login.connect(self.on_unauthorized)

    def _connect_logout_signals(self):
        self.logged_out.connect(self.on_logout)
        self.logout_error.connect(self.on_logout_error)

    def _connect_token_validation_signals(self):
        self.token_invalidated.connect(self.on_token_invalidated)

    def _connect_connection_error_signal(self):
        self.connection_error.connect(self.on_connection_error)

    def _connect_login_button(self):
        self.ui.loginButton.clicked.connect(self._login)

    def _login(self):
        email = self.ui.emailLineEdit.text()
        password = self.ui.passwordLineEdit.text()

        login_job = partial(
            accounts.login,
            email,
            password,
            listener=self,
            user_repo=self._user_repo,
        )
        self._network_thread.put(login_job)

    def on_successful_login(self, user_attrs):
        self._switchToLogoutPage(user_attrs)
        self._clear_login_form()

    def _switchToLogoutPage(self, user_attrs):
        self.ui.userEmailLabel.setText(user_attrs["uid"])
        self.ui.stackedWidget.setCurrentIndex(self.logoutPageIndex)

    def _clear_login_form(self):
        email = self.ui.emailLineEdit.setText("")
        password = self.ui.passwordLineEdit.setText("")

    def _start_sync_job(self):
        leaderboards.sync_if_logged_in(
            user_repo=self._user_repo,
            achievements_repo=self._achievements_repo,
            network_thread=self._network_thread,
            http_client=TokenAuthHttpClient(self._user_repo),
        )

    def on_unauthorized(self, response):
        self.ui.statusLabel.setText(response["errors"][0])

    def on_connection_error(self):
        self.ui.statusLabel.setText("Error connecting to server. Ensure you are connected to the internet.\nIf you are and the error persists, the server may be down. Try again later.")

    def _connect_logout_button(self):
        self.ui.logoutButton.clicked.connect(self._logout)

    def _logout(self):
        logout_job = partial(
            accounts.logout,
            self._user_repo,
             listener=self
        )
        self._network_thread.put(logout_job)

    def on_logout(self):
        self.ui.statusLabel.setText("User logged out successfully.")
        self._switchToLoginPage()

    def on_logout_error(self, response):
        self.ui.statusLabel.setText(response["errors"][0])
        self._switchToLoginPage()

    def _switchToLoginPage(self):
        self.ui.stackedWidget.setCurrentIndex(self.loginPageIndex)

    def _connect_signup_button(self):
        signup_url = urljoin(sra_base_url, "users/sign_up")
        self.ui.signupLabel.linkActivated.connect(lambda: webbrowser.open(signup_url))

    def _show_correct_auth_form(self, user_is_logged_in):
        if user_is_logged_in:
            user = self._user_repo.load()
            self._switchToLogoutPage(user_attrs=attr.asdict(user))
        else:
            self._switchToLoginPage()

    def _validate_token_if_logged_in(self, user_is_logged_in):
        if user_is_logged_in:
            job = partial(
                accounts.validate_token,
                self._user_repo,
                listener=self,
            )
            self._network_thread.put(job)

    def on_token_invalidated(self, response):
        self.ui.statusLabel.setText(response["errors"][0])
        self._switchToLoginPage()
Esempio n. 25
0
class AnkiApp(QApplication):

    # Single instance support on Win32/Linux
    ##################################################

    appMsg = pyqtSignal(str)

    KEY = "anki" + checksum(getpass.getuser())
    TMOUT = 30000

    def __init__(self, argv):
        QApplication.__init__(self, argv)
        self._argv = argv

    def secondInstance(self):
        # we accept only one command line argument. if it's missing, send
        # a blank screen to just raise the existing window
        opts, args = parseArgs(self._argv)
        buf = "raise"
        if args and args[0]:
            buf = os.path.abspath(args[0])
        if self.sendMsg(buf):
            print("Already running; reusing existing instance.")
            return True
        else:
            # send failed, so we're the first instance or the
            # previous instance died
            QLocalServer.removeServer(self.KEY)
            self._srv = QLocalServer(self)
            self._srv.newConnection.connect(self.onRecv)
            self._srv.listen(self.KEY)
            return False

    def sendMsg(self, txt):
        sock = QLocalSocket(self)
        sock.connectToServer(self.KEY, QIODevice.WriteOnly)
        if not sock.waitForConnected(self.TMOUT):
            # first instance or previous instance dead
            return False
        sock.write(txt.encode("utf8"))
        if not sock.waitForBytesWritten(self.TMOUT):
            # existing instance running but hung
            QMessageBox.warning(
                None, "Anki Already Running",
                "If the existing instance of Anki is not responding, please close it using your task manager, or restart your computer."
            )

            sys.exit(1)
        sock.disconnectFromServer()
        return True

    def onRecv(self):
        sock = self._srv.nextPendingConnection()
        if not sock.waitForReadyRead(self.TMOUT):
            sys.stderr.write(sock.errorString())
            return
        path = bytes(sock.readAll()).decode("utf8")
        self.appMsg.emit(path)
        sock.disconnectFromServer()

    # OS X file/url handler
    ##################################################

    def event(self, evt):
        if evt.type() == QEvent.FileOpen:
            self.appMsg.emit(evt.file() or "raise")
            return True
        return QApplication.event(self, evt)
Esempio n. 26
0
class Notification(QLabel):

    # Anki dialog manager support
    silentlyClose = True

    closed = pyqtSignal()

    def __init__(
        self,
        text: str,
        settings: NotificationSettings = NotificationSettings(),
        parent: Optional[QWidget] = None,
        **kwargs,
    ):
        super().__init__(text, parent=parent, **kwargs)
        self._settings = settings

        self.setFrameStyle(QFrame.Shape.Panel)
        self.setLineWidth(2)
        self.setWindowFlags(Qt.WindowType.ToolTip)
        self.setContentsMargins(10, 10, 10, 10)

        palette = QPalette()
        palette.setColor(QPalette.ColorRole.Window,
                         QColor(self._settings.bg_color))
        palette.setColor(QPalette.ColorRole.WindowText,
                         QColor(self._settings.fg_color))
        self.setPalette(palette)

        if parent and self._settings.focus_behavior != FocusBehavior.always_on_top:
            app: "AnkiApp" = QApplication.instance(
            )  # type: ignore[assignment]
            app.focusChanged.connect(self._on_app_focus_changed)

    def _on_app_focus_changed(self, old_widget: Optional[QWidget],
                              new_widget: Optional[QWidget]):
        focus_behavior = self._settings.focus_behavior
        focus_exceptions = self._settings.focus_behavior_exceptions
        parent_window = self.parent().window()
        old_window = old_widget.window() if old_widget else None
        new_window = new_widget.window() if new_widget else None

        if focus_exceptions and any(
                isinstance(old_window, wtype) for wtype in focus_exceptions):
            # switching back from an excluded window should not cause notif closing
            pass
        elif new_window is None and QApplication.widgetAt(
                QCursor.pos()) == self:
            # clicking on self should not dismiss notification when not configured as
            # such (Windows bug)
            pass
        elif new_window is None:
            # switched focus away from application
            self.close()
        elif new_window != parent_window and (not focus_exceptions or (all(
                not isinstance(new_window, wtype)
                for wtype in focus_exceptions))):
            # switched to other window within same application that's not excluded
            if focus_behavior == FocusBehavior.close_on_window_focus_lost:
                self.close()
            elif focus_behavior == FocusBehavior.lower_on_window_focus_lost:
                self.setWindowFlag(Qt.WindowType.ToolTip, on=False)
        elif (new_window == parent_window
              and focus_behavior == FocusBehavior.lower_on_window_focus_lost):
            self.setWindowFlag(Qt.WindowType.ToolTip, on=True)
            self.show()

    def mousePressEvent(self, event: QMouseEvent):
        if (not self._settings.dismiss_on_click
                or self.cursor().shape() == Qt.CursorShape.PointingHandCursor):
            # Do not ignore mouse press event if configured that way and/or
            # currently hovering link (as signaled by cursor shape)
            return super().mousePressEvent(event)
        event.accept()
        self.close()

    def closeEvent(self, event: QCloseEvent):
        self.closed.emit()
        return super().closeEvent(event)

    def resizeEvent(self, event: QResizeEvent) -> None:
        # true geometry is only known once resizeEvent fires
        self.update_position()
        super().resizeEvent(event)

    def update_position(self):
        align_horizontal = self._settings.align_horizontal
        align_vertical = self._settings.align_vertical

        if align_horizontal == NotificationHAlignment.left:
            x = 0 + self._settings.space_horizontal
        elif align_horizontal == NotificationHAlignment.right:
            x = self.parent().width() - self.width(
            ) - self._settings.space_horizontal
        elif align_horizontal == NotificationHAlignment.center:
            x = (self.parent().width() - self.width()) / 2
        else:
            raise ValueError(
                f"Alignment value {align_horizontal} is not supported")

        if align_vertical == NotificationVAlignment.top:
            y = 0 + self._settings.space_vertical
        elif align_vertical == NotificationVAlignment.bottom:
            y = self.parent().height() - self.height(
            ) - self._settings.space_vertical
        elif align_vertical == NotificationVAlignment.center:
            y = (self.parent().height() - self.height()) / 2
        else:
            raise ValueError(
                f"Alignment value {align_vertical} is not supported")

        self.move(self.parent().mapToGlobal(QPoint(x, y))  # type:ignore
                  )
Esempio n. 27
0
class TagEdit(QLineEdit):

    lostFocus = pyqtSignal()

    # 0 = tags, 1 = decks
    def __init__(self, parent, type=0):
        QLineEdit.__init__(self, parent)
        self.col = None
        self.model = QStringListModel()
        self.type = type
        if type == 0:
            self.completer = TagCompleter(self.model, parent, self)
        else:
            self.completer = QCompleter(self.model, parent)
        self.completer.setCompletionMode(QCompleter.PopupCompletion)
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)
        self.setCompleter(self.completer)

    def setCol(self, col):
        "Set the current col, updating list of available tags."
        self.col = col
        if self.type == 0:
            l = sorted(self.col.tags.all())
        else:
            l = sorted(self.col.decks.allNames())
        self.model.setStringList(l)

    def focusInEvent(self, evt):
        QLineEdit.focusInEvent(self, evt)

    def keyPressEvent(self, evt):
        if evt.key() in (Qt.Key_Up, Qt.Key_Down):
            # show completer on arrow key up/down
            if not self.completer.popup().isVisible():
                self.showCompleter()
            return
        if (evt.key() == Qt.Key_Tab and evt.modifiers() & Qt.ControlModifier):
            # select next completion
            if not self.completer.popup().isVisible():
                self.showCompleter()
            index = self.completer.currentIndex()
            self.completer.popup().setCurrentIndex(index)
            cur_row = index.row()
            if not self.completer.setCurrentRow(cur_row + 1):
                self.completer.setCurrentRow(0)
            return
        if evt.key() in (Qt.Key_Enter, Qt.Key_Return):
            # apply first completion if no suggestion selected
            selected_row = self.completer.popup().currentIndex().row()
            if selected_row == -1:
                self.completer.setCurrentRow(0)
                index = self.completer.currentIndex()
                self.completer.popup().setCurrentIndex(index)
            self.hideCompleter()
            QWidget.keyPressEvent(self, evt)
            return
        QLineEdit.keyPressEvent(self, evt)
        if not evt.text():
            # if it's a modifier, don't show
            return
        if evt.key() not in (
            Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Space,
            Qt.Key_Tab, Qt.Key_Backspace, Qt.Key_Delete):
            self.showCompleter()

    def showCompleter(self):
        self.completer.setCompletionPrefix(self.text())
        self.completer.complete()

    def focusOutEvent(self, evt):
        QLineEdit.focusOutEvent(self, evt)
        self.lostFocus.emit()
        self.completer.popup().hide()

    def hideCompleter(self):
        if sip.isdeleted(self.completer):
            return
        self.completer.popup().hide()
Esempio n. 28
0
class DockableWithClose(QDockWidget):
    closed = pyqtSignal()
    def closeEvent(self, evt):
        self.closed.emit()
        QDockWidget.closeEvent(self, evt)
Esempio n. 29
0
class ErrorHandler(QObject):
    "Catch stderr and write into buffer."
    ivl = 100

    errorTimer = pyqtSignal()

    def __init__(self, mw):
        QObject.__init__(self, mw)
        self.mw = mw
        self.timer = None
        self.errorTimer.connect(self._setTimer)
        self.pool = ""
        self._oldstderr = sys.stderr
        sys.stderr = self

    def unload(self):
        sys.stderr = self._oldstderr
        sys.excepthook = None

    def write(self, data):
        # dump to stdout
        sys.stdout.write(data)
        # save in buffer
        self.pool += data
        # and update timer
        self.setTimer()

    def setTimer(self):
        # we can't create a timer from a different thread, so we post a
        # message to the object on the main thread
        self.errorTimer.emit()

    def _setTimer(self):
        if not self.timer:
            self.timer = QTimer(self.mw)
            self.timer.timeout.connect(self.onTimeout)
        self.timer.setInterval(self.ivl)
        self.timer.setSingleShot(True)
        self.timer.start()

    def tempFolderMsg(self):
        return _("""Unable to access Anki media folder. The permissions on \
your system's temporary folder may be incorrect.""")

    def onTimeout(self):
        error = html.escape(self.pool)
        self.pool = ""
        self.mw.progress.clear()
        if "abortSchemaMod" in error:
            return
        if "10013" in error:
            return showWarning(
                _("Your firewall or antivirus program is preventing Anki from creating a connection to itself. Please add an exception for Anki."
                  ))
        if "Pyaudio not" in error:
            return showWarning(_("Please install PyAudio"))
        if "install mplayer" in error:
            return showWarning(
                _("Sound and video on cards will not function until mpv or mplayer is installed."
                  ))
        if "no default input" in error.lower():
            return showWarning(
                _("Please connect a microphone, and ensure "
                  "other programs are not using the audio device."))
        if "invalidTempFolder" in error:
            return showWarning(self.tempFolderMsg())
        if "Beautiful Soup is not an HTTP client" in error:
            return
        if "database or disk is full" in error:
            return showWarning(
                _("Your computer's storage may be full. Please delete some unneeded files, then try again."
                  ))
        if "disk I/O error" in error:
            return showWarning(
                _("""\
An error occurred while accessing the database.

Possible causes:

- Antivirus, firewall, backup, or synchronization software may be \
  interfering with Anki. Try disabling such software and see if the \
  problem goes away.
- Your disk may be full.
- The Documents/Anki folder may be on a network drive.
- Files in the Documents/Anki folder may not be writeable.
- Your hard disk may have errors.

It's a good idea to run Tools>Check Database to ensure your collection \
is not corrupt.
"""))

        stdText = _("""\
<h1>Error</h1>

<p>An error occurred. Please use <b>Tools &gt; Check Database</b> to see if \
that fixes the problem.</p>

<p>If problems persist, please report the problem on our \
<a href="https://help.ankiweb.net">support site</a>. Please copy and paste \
 the information below into your report.</p>""")

        pluginText = _("""\
<h1>Error</h1>

<p>An error occurred. Please start Anki while holding down the shift \
key, which will temporarily disable the add-ons you have installed.</p>

<p>If the issue only occurs when add-ons are enabled, please use the \
Tools&gt;Add-ons menu item to disable some add-ons and restart Anki, \
repeating until you discover the add-on that is causing the problem.</p>

<p>When you've discovered the add-on that is causing the problem, please \
report the issue on the <a href="https://help.ankiweb.net/discussions/add-ons/">\
add-ons section</a> of our support site.

<p>Debug info:</p>
""")
        if self.mw.addonManager.dirty:
            txt = pluginText
            error = supportText() + self._addonText(error) + "\n" + error
        else:
            txt = stdText
            error = supportText() + "\n" + error

        # show dialog
        txt = txt + "<div style='white-space: pre-wrap'>" + error + "</div>"
        showText(txt, type="html", copyBtn=True)

    def _addonText(self, error):
        matches = re.findall(r"addons21/(.*?)/", error)
        if not matches:
            return ""
        # reverse to list most likely suspect first, dict to deduplicate:
        addons = [
            aqt_mw.addonManager.addonName(i)
            for i in dict.fromkeys(reversed(matches))
        ]
        txt = _("""Add-ons possibly involved: {}\n""")
        # highlight importance of first add-on:
        addons[0] = "<b>{}</b>".format(addons[0])
        return txt.format(", ".join(addons))
class Google(QThread):

    resultsFound = pyqtSignal(list)
    noResults = pyqtSignal(str)

    def __init__(self):
        QThread.__init__(self)
        self.GOOGLE_SEARCH_URL = "https://www.google.com/search"
        self.term = False
        self.initSession()

    def initSession(self):
        self.session = requests.session()
        self.session.headers.update({
            "User-Agent":
            "Mozilla/5.0 (X11; Linux x86_64; rv:10.0) \
                    Gecko/20100101 Firefox/10.0"
        })

    def setTermIdName(self, term, idName):
        self.term = term
        self.idName = idName

    def run(self):
        if self.term:
            resultList = self.getPreparedResults(self.term, self.idName)
            self.resultsFound.emit(resultList)

    def search(self, keyword, maximum, region=False):
        query = self.query_gen(keyword)

        return self.image_search(query, maximum, region)

    def query_gen(self, keyword):
        page = 0
        while True:
            params = urllib.parse.urlencode({"q": keyword, "tbm": "isch"})
            if self.region == 'Japan':
                url = 'https://www.google.co.jp/search'
            else:
                url = self.GOOGLE_SEARCH_URL
            yield url + "?" + params
            page += 1

    def setSearchRegion(self, region):
        self.region = region

    def getResultsFromRawHtml(self, html):
        pattern = r"AF_initDataCallback[\s\S]+AF_initDataCallback\({key: '[\s\S]+?',[\s\S]+?return (\[[\s\S]+\])[\s\S]+?<\/body><\/html>"
        matches = re.findall(pattern, html)
        results = []
        try:
            if len(matches) > 0:
                decoded = json.loads(matches[0])[31][0][12][2]
                for d in decoded:
                    d1 = d[1]
                    if d1:
                        results.append(str(d1[3][0]))
            return results
        except:
            return []

    def getHtml(self, term):
        images = self.search(term, 80)
        if not images or len(images) < 1:
            return 'No Images Found. This is likely due to a connectivity error.'
        firstImages = []
        tempImages = []
        for idx, image in enumerate(images):
            tempImages.append(image)
            if len(tempImages) > 2 and len(firstImages) < 1:
                firstImages += tempImages
                tempImages = []
            if len(tempImages) > 2 and len(firstImages) > 1:
                break
        html = '<div class="googleCont">'
        for img in firstImages:
            html += '<div class="imgBox"><div onclick="toggleImageSelect(this)" data-url="' + img + '" class="googleHighlight"></div><img class="googleImage"  src="' + img + '"></div>'
        html += '</div><div class="googleCont">'
        for img in tempImages:
            html += '<div class="imgBox"><div onclick="toggleImageSelect(this)" data-url="' + img + '" class="googleHighlight"></div><img class="googleImage"  src="' + img + '"></div>'
        html += '</div><button class="imageLoader" onclick="loadMoreImages(this, \\\'' + '\\\' , \\\''.join(
            self.getCleanedUrls(images)) + '\\\')">Load More</button>'
        return html

    def getPreparedResults(self, term, idName):
        html = self.getHtml(term)
        return [html, idName]

    def getCleanedUrls(self, urls):
        return [x.replace('\\', '\\\\') for x in urls]

    def image_search(self, query_gen, maximum, region=False):
        results = []
        if not region:
            region = countryCodes[self.region]
        total = 0
        finished = False
        while True:
            try:
                count = 0
                while not finished:
                    count += 1
                    hr = self.session.get(
                        next(query_gen) + '&ijn=0&cr=' + region)
                    html = hr.text
                    if not html and not '<!doctype html>' in html:
                        if count > 5:
                            finished = True
                            break
                        self.initSession()
                        time.sleep(.1)
                    else:
                        finished = True
                        break
            except:
                self.noResults.emit(
                    'The Google Image Dictionary could not establish a connection. Please ensure you are connected to the internet and try again. If you will be without internet for some time, consider using a template that does not include the Google Images Dictionary in order to prevent this message appearing everytime a search is performed. '
                )
                return False
            results = self.getResultsFromRawHtml(html)
            if len(results) == 0:
                soup = BeautifulSoup(html, "html.parser")
                elements = soup.select(".rg_meta.notranslate")
                jsons = [json.loads(e.get_text()) for e in elements]

                image_url_list = [js["ou"] for js in jsons]
                if not len(image_url_list):

                    break
                elif len(image_url_list) > maximum - total:
                    results += image_url_list[:maximum - total]
                    break
                else:
                    results += image_url_list
                    total += len(image_url_list)
            else:
                break
        return results