Ejemplo n.º 1
0
        class Render(QWebEngineView):
            def __init__(self, url):
                QWebEngineView.__init__(self)
                self.loop = QEventLoop(
                )  #Queda en un loop hasta que acaba la carga de todas las páginas
                self.loadFinished.connect(self._loadFinished)
                self.pages = []
                self.page().profile().cookieStore().deleteAllCookies()
                self.page().profile().setPersistentCookiesPolicy(
                    QWebEngineProfile.NoPersistentCookies)
                self.load(QUrl(url))
                self.loop.exec()

            def numPages(self):
                return len(self.pages)

            def _loadFinished(self, result):
                """
                   This is an async call, you need to wait for this to be called before closing the app
                """
                self.page().toHtml(self._callable)

            def _callable(self, data):
                """
                   Se llama cada vez que una página es cargada con el html en data
                """
                time.sleep(0.25)
                self.pages.append(data)
                self.loop.quit()  #CUIDADO DEBE ESTAR EN EL ULTIMO
Ejemplo n.º 2
0
class QSignalWait(QObject):
    """
    Class that waits for a QTSignal and returns its value
    Works only with signals with no type or type int,str,bool
    """
    def __init__(self, signal, parent=None):
        super(QSignalWait, self).__init__(parent)
        self.state = None
        self.signal = signal
        self.loop = QEventLoop()

    @pyqtSlot()
    @pyqtSlot(bool)
    @pyqtSlot(str)
    @pyqtSlot(int)
    def _quit(self, state=None):
        self.state = state
        self.loop.quit()

    def wait(self):
        """Waits for a signal to be emitted.
        """
        self.signal.connect(self._quit)
        self.loop.exec()
        self.signal.disconnect(self._quit)
        return self.state
Ejemplo n.º 3
0
class WidgetPicker(QWidget):
    """Widget for letting user point at another widget."""

    selected = Signal()

    def __init__(self):
        super(WidgetPicker, self).__init__()
        self.band = QRubberBand(QRubberBand.Rectangle)
        self.setMouseTracking(True)
        self.el = QEventLoop()

    def mousePressEvent(self, ev):
        self.el.quit()
        self.widget = QApplication.widgetAt(ev.globalPos())
        self.band.hide()

    def mouseMoveEvent(self, ev):
        widget = QApplication.widgetAt(ev.globalPos())
        if widget:
            rect = widget.frameGeometry()
            if widget.parent():
                rect.moveTo(widget.parent().mapToGlobal(rect.topLeft()))
            self.band.setGeometry(rect)
            self.band.show()
        else:
            self.band.hide()

    def run(self):
        self.grabMouse()
        try:
            self.el.exec_()
        finally:
            self.releaseMouse()
        return self.widget
Ejemplo n.º 4
0
class Render(QWebPage):
    def __init__(self, url):
        self.url = url
        self.app = QEventLoop()
        QWebPage.__init__(self)
        self.urls = []
        self.frame = self.mainFrame()
        self.viewport = self.setViewportSize(QSize(1600, 9000))
        self.network = NetworkAccessManager()
        self.setNetworkAccessManager(self.network)
        self.loadFinished.connect(self._loadFinished)
        self.linkClicked.connect(self.linkClick)
        self.settings().setAttribute(QWebSettings.AutoLoadImages, False)
        self.settings().setAttribute(QWebSettings.JavascriptCanOpenWindows,
                                     False)
        self.setLinkDelegationPolicy(2)
        #self.action(QWebPage.OpenLinkInNewWindow).setEnabled(True)
        #self.settings().clearMemoryCaches()
        self.mainFrame().load(QUrl(self.url))
        self.network.requestSignal.connect(self.networkRequest)
        self.app.exec_()

    allFinished = pyqtSignal()

    def _loadFinished(self, res):
        iList = self.frame.findAllElements('input')

        for i in iList:
            i.evaluateJavaScript('this.click()')

        aList = self.frame.findAllElements('a')
        for a in aList:
            a.evaluateJavaScript('this.click()')

        if self.frame.blockSignals(True) == True:
            self.isFinished = True
            self.allFinished.emit()
            self.app.quit()
            self.deleteLater()

    def javaScriptAlert(browser, frame, message):
        """Notifies session for alert, then pass."""
        print "signal for javaScriptAlert (class) fired"

    def javaScriptConfirm(browser, frame, message):
        #"""Notifies session for alert, then pass."""
        print "signal for javaScriptConfirm (class) fired"

    def javaScriptPrompt(browser, frame, message):
        #"""Notifies session for alert, then pass."""
        print "signal for javaScriptPromt (class) fired"

    def networkRequest(self):
        self.urls = self.network.request_urls
        print self.network.request_urls
        print 'allfinished'

    def linkClick(self, url):
        self.urls.append(url.url())
Ejemplo n.º 5
0
class Render(QWebPage):
    def __init__(self, url):
        self.url = url
        self.app = QEventLoop()
        QWebPage.__init__(self)
        self.urls = [url]
        self.frame = self.mainFrame()
        self.viewport = self.setViewportSize(QSize(1600, 9000))
        self.network = NetworkAccessManager()
        self.setNetworkAccessManager(self.network)
        self.loadFinished.connect(self._loadFinished)
        self.linkClicked.connect(self.change)
        self.settings().setAttribute(QWebSettings.AutoLoadImages, False)
        self.settings().setAttribute(QWebSettings.JavascriptCanOpenWindows,
                                     True)
        self.setLinkDelegationPolicy(1)
        self.action(QWebPage.OpenLinkInNewWindow).setEnabled(True)
        #self.settings().clearMemoryCaches()
        #self.mainFrame().load(QUrl(self.url))
        self.network.requestSignal.connect(self.networkRequest)
        self.thread = QThread()
        self.moveToThread(self.thread)
        self.thread.started.connect(self.run)
        self.thread.run()

    def run(self):
        self.mainFrame().load(QUrl(self.urls.pop(0)))
        self.app.exec_()

    def _loadFinished(self, res):
        ilist = self.frame.findAllElements('input')
        for i in ilist:
            i.evaluateJavaScript('this.click()')
            print i.attribute("onClick")
            #self.setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
        #self.setLinkDelegationPolicy(0)
        alist = self.frame.findAllElements('a')
        for a in alist:
            a.evaluateJavaScript("this.click()")

        if self.frame.blockSignals(True) == False:
            print 'yes'
            self.app.quit()

    def javaScriptAlert(browser, frame, message):
        """Notifies session for alert, then pass."""
        print "signal for javaScriptAlert (class) fired"

    def networkRequest(self):
        self.urls = self.network.request_Urls
        print 'allfinished'

    def change(self, url):
        self.urls.append(url.url())
        print url.url()
        #self.setLinkDelegationPolicy(0)
        print 'change'
Ejemplo n.º 6
0
def waitForSignal(signal, message="", timeout=0):
    """Waits (max timeout msecs if given) for a signal to be emitted.

    It the waiting lasts more than 2 seconds, a progress dialog is displayed
    with the message.

    Returns True if the signal was emitted.
    Return False if the wait timed out or the dialog was canceled by the user.

    """
    loop = QEventLoop()
    dlg = QProgressDialog(minimum=0, maximum=0, labelText=message)
    dlg.setWindowTitle(appinfo.appname)
    dlg.setWindowModality(Qt.ApplicationModal)
    QTimer.singleShot(2000, dlg.show)
    dlg.canceled.connect(loop.quit)
    if timeout:
        QTimer.singleShot(timeout, dlg.cancel)
    stop = lambda: loop.quit()
    signal.connect(stop)
    loop.exec_()
    signal.disconnect(stop)
    dlg.hide()
    dlg.deleteLater()
    return not dlg.wasCanceled()
Ejemplo n.º 7
0
def waitForSignal(signal, message="", timeout=0):
    """Waits (max timeout msecs if given) for a signal to be emitted.

    It the waiting lasts more than 2 seconds, a progress dialog is displayed
    with the message.

    Returns True if the signal was emitted.
    Return False if the wait timed out or the dialog was canceled by the user.

    """
    loop = QEventLoop()
    dlg = QProgressDialog(minimum=0, maximum=0, labelText=message)
    dlg.setWindowTitle(appinfo.appname)
    dlg.setWindowModality(Qt.ApplicationModal)
    QTimer.singleShot(2000, dlg.show)
    dlg.canceled.connect(loop.quit)
    if timeout:
        QTimer.singleShot(timeout, dlg.cancel)
    stop = lambda: loop.quit()
    signal.connect(stop)
    loop.exec_()
    signal.disconnect(stop)
    dlg.hide()
    dlg.deleteLater()
    return not dlg.wasCanceled()
Ejemplo n.º 8
0
 def checkAnswer(self):
     if not self.List.L[self.ITER].checkTranslate(str(self.form_widjet.line.text())):
         self.form_widjet.result.setText(self.List.L[self.ITER].translate)
         loop = QEventLoop()
         QTimer.singleShot(1500, lambda: loop.quit())
         loop.exec_()
     self.form_widjet.result.setText('')
     self.form_widjet.line.setText('')
class BrowserPage(QWebEnginePage):
    def __init__(self):
        QWebEnginePage.__init__(self)

    def execute_javascript(self, script_src):
        self.loop = QEventLoop()
        self.result = QVariant()
        QTimer.singleShot(250, self.loop.quit)

        self.runJavaScript(script_src, self.callback_js)
        self.loop.exec_()
        self.loop = None
        return self.result

    def callback_js(self, res):
        if self.loop is not None and self.loop.isRunning():
            self.result = res
            self.loop.quit()
Ejemplo n.º 10
0
class BrowserPage(QWebEnginePage):
    def __init__(self):
        QWebEnginePage.__init__(self)

    def hitTestContent(self, pos):
        return WebHitTestResult(self, pos)

    def mapToViewport(self, pos):
        return QPointF(pos.x(), pos.y())

    def executeJavaScript(self, scriptSrc):
        self.loop = QEventLoop()
        self.result = QVariant()
        QTimer.singleShot(250, self.loop.quit)

        self.runJavaScript(scriptSrc, self.callbackJS)
        self.loop.exec_()
        self.loop = None
        return self.result

    def callbackJS(self, res):
        if self.loop is not None and self.loop.isRunning():
            self.result = res
            self.loop.quit()
Ejemplo n.º 11
0
class Ace(QWebView):
    """ Embbeded Ace javascript web editor """

    isReady = pyqtSignal(name='isReady')
    modificationChanged = pyqtSignal(bool)
    cursorPositionChanged = pyqtSignal(int, int, name='cursorPositionChanged')

    def __init__(self, file_info, parent=None):
        super(Ace, self).__init__(parent)
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.parent = parent
        self.file_info = file_info
        self.language = EditorHelper.lang_from_file_info(file_info)
        self.waitForReady = False
        self.loop = QEventLoop()

        settings = self.settings()
        settings.setAttribute(QWebSettings.JavascriptCanAccessClipboard, True)
        settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True)
        self.inspector = QWebInspector(self)
        showInspectorAction = QAction('showInspector', self)
        showInspectorAction.triggered.connect(self.showInspector)
        self.addAction(showInspectorAction)

        self.modificationChanged.connect(self.modification_changed)
        self.main_frame().javaScriptWindowObjectCleared.connect(self.__self_js)
        pckg, file_name = 'ace_editor', 'ace_editor.html'
        resource = pkg_resources.resource_string(pckg, file_name)
        html_template = str(resource, 'utf-8')
        #insert file content
        with open(self.file_info.absoluteFilePath(), 'r') as f:
            text = f.read()
            text = html.escape(text)
            html_template = html_template.replace('{{ content }}', text)
        base_url = QUrl.fromLocalFile(os.path.dirname(__file__))

        self.setHtml(html_template, base_url)
        self.modified = False

        if not self.waitForReady:
            self.loop.exec()

    @pyqtSlot(str, name='test', result=str)
    @EditorHelper.json_dumps
    def test(self, prefix):
        print(prefix)
        return ['plop', 'cool', 42, {'plop': 23}]

    def modification_changed(self, b):
        self.modified = b

    def save(self, parent, action=None):
        Alter.invoke_all('editor_presave', self)
        if self.modified:
            with open(self.file_info.absoluteFilePath(), 'w') as f:
                f.write(self.get_value())
            self.original_to_current_doc()
            self.modificationChanged.emit(False)
            parent.status_bar.showMessage(self.tr("Saved file."))
            Alter.invoke_all('editor_save', self)
        else:
            parent.status_bar.showMessage(self.tr("Nothing to save."))

    def toggle_hidden(self, parent, action=None):
        self.set_show_invisibles(action.isChecked())

    def toggle_soft_tabs(self, parent, action=None):
        self.set_use_soft_tabs(action.isChecked())

    def main_frame(self):
        """ Convinient function to get main QWebFrame """
        return self.page().mainFrame()

    def send_js(self, script):
        """ Convinient function to send javascript to ace editor """
        return self.main_frame().evaluateJavaScript(script)

    def __self_js(self):
        self.main_frame().addToJavaScriptWindowObject('AceEditor', self)

    @pyqtSlot(name='isReady')
    def editor_ready(self):
        if self.language != None:
            self.set_mode(self.language.lower())
        self.set_focus()
        if self.loop.isRunning():
            self.loop.quit()
        self.waitForReady = True

    def showInspector(self):
        self.dialogInspector = QDialog(self)
        self.dialogInspector.setLayout(QVBoxLayout())
        self.dialogInspector.layout().addWidget(self.inspector)
        self.dialogInspector.setModal(False)
        self.dialogInspector.show()

    def original_to_current_doc(self):
        self.send_js('editor.orignalToCurrentDoc()')

    def get_value(self):
        return self.send_js('editor.getValue()')

    def set_value(self, content):
        self.send_js('editor.setValue("{0}")'.format(content))

    def set_focus(self):
        self.send_js('editor.focus()')

    def insert(self, text):
        self.send_js('editor.insert("{0}")'.format(text))

    def set_mode(self, language):
        cmd = 'editor.getSession().setMode("ace/mode/{0}")'
        self.send_js(cmd.format(language))

    def set_theme(self, theme):
        self.send_js('editor.setTheme("ace/theme/{0}")'.format(theme))

    def get_selected_text(self):
        cmd = 'editor.session.getTextRange(editor.getSelectionRange())'
        return self.send_js(cmd)

    def get_cursor(self):
        cmd = 'editor.selection.getCursor()'
        return self.send_js(cmd)

    def got_to_line(self, line):
        cmd = 'editor.gotoLine({0})'
        self.send_js(cmd.format(line))

    def get_length(self):
        return self.send_js('editor.session.getLength()')

    def set_tab_size(self, tab_size):
        self.send_js('editor.getSession().setTabSize({0})'.format(tab_size))

    def get_tab_size(self):
        return self.send_js('editor.getSession().getTabSize()')

    def set_use_soft_tabs(self, b):
        b = 'true' if b else 'false'
        self.send_js('editor.getSession().setUseSoftTabs({0})'.format(b))

    def set_font_size(self, font_size):
        cmd = "document.getElementById('editor').style.fontSize='{0}px'"
        self.send_js(cmd.format(font_size))

    def set_use_wrap_mode(self, b):
        b = 'true' if b else 'false'
        cmd = "editor.getSession().setUseWrapMode({0})"
        self.send_js(cmd.format(b))

    def set_highlight_active_line(self, b):
        b = 'true' if b else 'false'
        cmd = "editor.setHighlightActiveLine({0})"
        self.send_js(cmd.format(b))

    def set_show_print_margin(self, b):
        b = 'true' if b else 'false'
        cmd = "editor.setShowPrintMargin({0})"
        self.send_js(cmd.format(b))

    def set_read_only(self, b):
        b = 'true' if b else 'false'
        self.send_js('editor.setReadOnly({0})'.format(b))

    def set_show_invisibles(self, b):
        b = 'true' if b else 'false'
        self.send_js('editor.setShowInvisibles({0})'.format(b))
Ejemplo n.º 12
0
class IntegerInputDialog(QMainWindow):
    """
        How can I show a PyQt modal dialog and get data out of its controls once its closed?
        https://stackoverflow.com/questions/18196799/how-can-i-show-a-pyqt-modal-dialog-and-get-data-out-of-its-controls-once-its-clo

        how to clear child window reference stored in parent application when child window is closed?
        https://stackoverflow.com/questions/27420338/how-to-clear-child-window-reference-stored-in-parent-application-when-child-wind
    """
    def __init__(self, parent, settings, fontOptions):
        super().__init__(parent)
        self.result = 0

        self.settings = settings
        self.setAttribute(Qt.WA_DeleteOnClose)

        # QWidget::setLayout: Attempting to set QLayout “” on ProgramWindow “”, which already has a layout
        # https://stackoverflow.com/questions/50176661/qwidgetsetlayout-attempting-to-set-qlayout-on-programwindow-which-alre
        self.centralwidget = QWidget()
        self.setCentralWidget(self.centralwidget)
        self.setWindowTitle("Input a Integer value")

        # Initial window size/pos last saved. Use default values for first time
        windowScreenGeometry = self.settings.value(
            "integerInputWindowScreenGeometry")
        windowScreenState = self.settings.value(
            "integerInputWindowScreenState")

        if windowScreenGeometry:
            self.restoreGeometry(windowScreenGeometry)

        else:
            self.resize(600, 400)
            # self.move( get_screen_center( self ) )

        if windowScreenState:
            self.restoreState(windowScreenState)

        # nice widget for editing the date
        self.textEditWidget = QPlainTextEdit(self)
        self.textEditWidget.setLineWrapMode(QPlainTextEdit.NoWrap)

        # Detect Ctrl+S ion QTextedit?
        # https://stackoverflow.com/questions/43010630/detect-ctrls-ion-qtextedit
        enterShortcut = QShortcut(QKeySequence("Ctrl+Enter"),
                                  self.textEditWidget)
        returnShortcut = QShortcut(QKeySequence("Ctrl+Return"),
                                   self.textEditWidget)
        enterShortcut.activated.connect(self.accept)
        returnShortcut.activated.connect(self.accept)

        # Change font, colour of text entry box
        self.textEditWidget.setStyleSheet(fontOptions)
        self.textEditWidget.installEventFilter(self)

        # Set initial value of text
        self.textEditWidget.document().setPlainText(
            "# Write here bellow, an integer with the number of steps\n\n5")
        self.textEditWidget.selectAll()

        # OK and Cancel buttons
        self.standardButtons = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self)
        self.standardButtons.accepted.connect(self.accept)
        self.standardButtons.rejected.connect(self.reject)

        # Setup the main layout
        self.verticalLayout = QVBoxLayout(self.centralwidget)
        self.horizontalLayout = QHBoxLayout()

        self.verticalLayout.addWidget(self.textEditWidget)
        self.verticalLayout.addLayout(self.horizontalLayout)

        self.horizontalLayout.addWidget(self.standardButtons)

    def accept(self):
        # log( 1, "Print done" )
        self.result = QDialog.Accepted
        self.close()

    def reject(self):
        # log( 1, "Print reject" )
        self.result = QDialog.Rejected
        self.close()

    def exec_(self):
        """
            Problems with connect in pyqt5
            https://stackoverflow.com/questions/33236358/problems-with-connect-in-pyqt5
        """
        # log( 1, "Blocking main ui" )
        self.event = QEventLoop()
        self.setWindowModality(Qt.ApplicationModal)

        self.show()
        self.event.exec()
        return self.result

    def closeEvent(self, event=None):
        # log( 1, "closeEvent " )
        self.settings.setValue("integerInputWindowScreenGeometry",
                               self.saveGeometry())
        self.settings.setValue("integerInputWindowScreenState",
                               self.saveState())

        # super().closeEvent( event )
        self.event.quit()

    def keyPressEvent(self, event):
        """
            Closes the application when the escape key is pressed.
        """
        # log( 1, "Key pressing: %s", event.key() )

        if event.key() == PyQt5.QtCore.Qt.Key_Escape:
            self.close()

    def eventFilter(self, obj, event):
        """
            eventFilter on a QWidget with PyQt4
            https://stackoverflow.com/questions/28050397/eventfilter-on-a-qwidget-with-pyqt4
        """

        if event.type() == PyQt5.QtCore.QEvent.KeyPress:
            key = event.key()
            # log( 1, "Key pressing: %s", event.key() )

            if key == Qt.Key_Return or key == Qt.Key_Enter:
                self.clearTextSelection()
                self.accept()

        return super().eventFilter(obj, event)

    def clearTextSelection(self):
        textCursor = self.textEditWidget.textCursor()
        textCursor.clearSelection()
        self.textEditWidget.setTextCursor(textCursor)

    # static method to create the dialog and return ( date, time, accepted )
    @classmethod
    def getNewUserInput(cls, parent, settings, fontOptions):
        result = 1
        integer = 5

        while result:
            dialog = IntegerInputDialog(parent, settings, fontOptions)
            result = dialog.exec_()

            if result:
                # log( 1, "dialog.textEditWidget.toPlainText(): %s", dialog.textEditWidget.toPlainText() )
                integer = cls.convertToInteger(
                    parent, dialog.textEditWidget.toPlainText())

                if integer is not None:
                    break

            else:
                break

        # log( 1, "result: %s, integer: %s", result, integer )
        return (integer, result == QDialog.Accepted)

    @staticmethod
    @ignore_exceptions
    def convertToInteger(parent, inputString):
        # log( 1, "inputString: %s", inputString )
        inputString = "".join(getCleanSpaces(inputString))
        inputInteger = int(inputString)

        if inputInteger < 0:
            raise RuntimeError("You cannot enter negative values!")

        return inputInteger
Ejemplo n.º 13
0
class CFFEXSpider(QObject):
    spider_finished = pyqtSignal(str, bool)

    def __init__(self, *args, **kwargs):
        super(CFFEXSpider, self).__init__(*args, **kwargs)
        self.date = None
        self.event_loop = QEventLoop(self)  # 用于网络请求同步事件阻塞

    def set_date(self, date):
        self.date = datetime.strptime(date, '%Y-%m-%d')

    def get_daily_source_file(self):
        """ 获取每日行情数据源文件保存至本地 """
        if self.date is None:
            raise DateValueError("请先使用`set_date`设置`CZCESpider`日期.")
        url = "http://www.cffex.com.cn/sj/hqsj/rtj/{}/{}/{}_1.csv".format(
            self.date.strftime('%Y%m'), self.date.strftime('%d'),
            self.date.strftime('%Y%m%d'))

        network_manager = getattr(qApp, "_network")
        request = QNetworkRequest(QUrl(url))
        request.setHeader(QNetworkRequest.UserAgentHeader,
                          random.choice(USER_AGENTS))
        reply = network_manager.get(request)
        reply.finished.connect(self.daily_source_file_reply)

    def daily_source_file_reply(self):
        """ 获取日统计数据请求返回 """
        reply = self.sender()
        if reply.error():
            reply.deleteLater()
            self.spider_finished.emit("失败:" + str(reply.error()), True)
            return
        save_path = os.path.join(
            LOCAL_SPIDER_SRC,
            'cffex/daily/{}.csv'.format(self.date.strftime("%Y-%m-%d")))
        file_data = reply.readAll()
        file_obj = QFile(save_path)
        is_open = file_obj.open(QFile.WriteOnly)
        if is_open:
            file_obj.write(file_data)
            file_obj.close()
        reply.deleteLater()
        self.spider_finished.emit(
            "获取中金所{}日交易数据源文件成功!".format(self.date.strftime("%Y-%m-%d")), True)

    def get_rank_source_file(self):
        """ 获取日排名数据源文件 """
        base_url = "http://www.cffex.com.cn/sj/ccpm/{}/{}/{}_1.csv"
        network_manager = getattr(qApp, "_network")

        for variety in VARIETY_LIST:
            url = base_url.format(self.date.strftime("%Y%m"),
                                  self.date.strftime("%d"), variety)
            self.spider_finished.emit("准备获取{}的日排名数据文件...".format(variety),
                                      False)
            request = QNetworkRequest(QUrl(url))
            request.setHeader(QNetworkRequest.UserAgentHeader,
                              random.choice(USER_AGENTS))
            reply = network_manager.get(request)
            reply.finished.connect(self.rank_source_file_reply)
            time.sleep(1)
            self.event_loop.exec_()

    def rank_source_file_reply(self):
        """ 获取日排名请求返回 """
        reply = self.sender()
        request_url = reply.request().url().url()
        # 解析出请求的品种
        request_filename = request_url.rsplit("/", 1)[1]
        request_variety = request_filename.split("_")[0]
        if reply.error():
            reply.deleteLater()
            self.spider_finished.emit(
                "获取{}排名数据文件。\n失败:{}".format(request_variety[:2],
                                            str(reply.error())), True)
            logger.error("获取{}排名数据文件失败了!".format(request_url[:2]))
            return
        save_path = os.path.join(
            LOCAL_SPIDER_SRC,
            'cffex/rank/{}_{}.csv'.format(request_variety,
                                          self.date.strftime("%Y-%m-%d")))
        file_data = reply.readAll()
        file_obj = QFile(save_path)
        is_open = file_obj.open(QFile.WriteOnly)
        if is_open:
            file_obj.write(file_data)
            file_obj.close()
        reply.deleteLater()
        tip = "获取中金所{}_{}日持仓排名数据保存到文件成功!".format(
            request_variety, self.date.strftime("%Y-%m-%d"))
        if request_variety == "T":
            tip = "获取中金所{}日所有品种持仓排名数据保存到文件成功!".format(
                self.date.strftime("%Y-%m-%d"))
        self.spider_finished.emit(tip, True)
        self.event_loop.quit()
Ejemplo n.º 14
0
class Prompt(QLabel):
    """Blocking prompt widget asking the user a question.

    The prompt is initialized with a question and displays the question, its title and
    the valid keybindings. Calling ``run`` blocks the UI until a valid keybinding to
    answer/abort the question was given.

    Class Attributes:
        BINDINGS: Valid keybindings to answer/abort the question.

    Attributes:
        question: Question object defining title, question and answer.
        loop: Event loop used to block the UI.
    """

    STYLESHEET = """
    QLabel {
        font: {prompt.font};
        color: {prompt.fg};
        background-color: {prompt.bg};
        padding: {prompt.padding};
        border-top-right-radius: {prompt.border_radius};
        border-top: {prompt.border} {prompt.border.color};
        border-right: {prompt.border} {prompt.border.color};
    }
    """

    BINDINGS = (
        ("y", "Yes"),
        ("n", "No"),
        ("<return>", "No"),
        ("<escape>", "Abort"),
    )

    def __init__(self, question: api.prompt.Question, *, parent):
        super().__init__(parent=parent)
        self.question = question
        self.loop = QEventLoop()

        styles.apply(self)
        header = f"<h3>{question.title}</h3>{question.body}"
        self.setText(header + self.bindings_table())
        _logger.debug("Initialized %s", self)

        self.setFocus()
        self.adjustSize()
        self.raise_()
        self.show()

    def __str__(self):
        return f"prompt for '{self.question.title}'"

    @classmethod
    def bindings_table(cls):
        """Return a formatted html table with the valid keybindings."""
        return utils.format_html_table(
            (f"<b>{utils.escape_html(binding)}</b>", command)
            for binding, command in cls.BINDINGS
        )

    def run(self):
        """Run the blocking event loop."""
        _logger.debug("Running blocking %s", self)
        self.loop.exec_()

    def update_geometry(self, _width: int, bottom: int):
        y = bottom - self.height()
        self.setGeometry(0, y, self.width(), self.height())

    def leave(self, *, answer=None):
        """Leave the prompt by answering the question and quitting the loop."""
        _logger.debug("Leaving %s with '%s'", self, answer)
        self.question.answer = answer
        self.loop.quit()
        self.loop.deleteLater()
        self.deleteLater()
        api.modes.current().widget.setFocus()

    def keyPressEvent(self, event):
        """Leave the prompt on a valid key binding."""
        if event.key() == Qt.Key_Y:
            self.leave(answer=True)
        elif event.key() in (Qt.Key_N, Qt.Key_Return):
            self.leave(answer=False)
        elif event.key() == Qt.Key_Escape:
            self.leave()

    def focusOutEvent(self, event):
        """Leave the prompt without answering when unfocused."""
        if self.loop.isRunning():
            self.leave()
        super().focusOutEvent(event)
Ejemplo n.º 15
0
class Page(QWebEnginePage):
    def __init__(self, view):
        super(Page, self).__init__()
        self.parent = view.parent
        self.view = view
        self.result = QVariant()
        self.fullView = QWebEngineView()
        self.exitFSAction = QAction(self.fullView)
        self.loop = None

    def javaScriptConsoleMessage(self, level, msg, line, sourceID):
        """Override javaScriptConsoleMessage to use debug log."""
        if level == QWebEnginePage.InfoMessageLevel:
            print("JS - INFO - Ligne {} : {}".format(line, msg))
        elif level == QWebEnginePage.WarningMessageLevel:
            print("JS - WARNING - Ligne {} : {}".format(line, msg))
        else:
            print("JS - ERROR - Ligne {} : {}".format(line, msg))

    def hittestcontent(self, pos):
        return WebHitTestResult(self, pos)

    def maptoviewport(self, pos):
        return QPointF(pos.x(), pos.y())

    def executejavascript(self, scriptsrc):
        self.loop = QEventLoop()
        self.result = QVariant()
        QTimer.singleShot(250, self.loop.quit)

        self.runJavaScript(scriptsrc, self.callbackjs)
        self.loop.exec_()
        self.loop = None
        return self.result

    def callbackjs(self, res):
        if self.loop is not None and self.loop.isRunning():
            self.result = res
            self.loop.quit()

    def vsource(self):
        if "view-source:http" in self.url().toString():
            self.load(QUrl(self.url().toString().split("view-source:")[1]))
        else:
            self.triggerAction(self.ViewSource)

    def cutaction(self):
        self.triggerAction(self.Cut)

    def copyaction(self):
        self.triggerAction(self.Copy)

    def pasteaction(self):
        self.triggerAction(self.Paste)

    def exitfs(self):
        self.triggerAction(self.ExitFullScreen)

    def makefullscreen(self, request):
        if request.toggleOn():
            self.fullView = QWebEngineView()
            self.exitFSAction = QAction(self.fullView)
            self.exitFSAction.setShortcut(Qt.Key_Escape)
            self.exitFSAction.triggered.connect(self.exitfs)

            self.fullView.addAction(self.exitFSAction)
            self.setView(self.fullView)
            self.fullView.showFullScreen()
            self.fullView.raise_()
        else:
            del self.fullView
            self.setView(self.view)
        request.accept()
Ejemplo n.º 16
0
class Homepage(HomepageUI):
    """ 首页业务 """
    SkipPage = pyqtSignal(str, str)

    def __init__(self, *args, **kwargs):
        super(Homepage, self).__init__(*args, **kwargs)
        self.event_loop = QEventLoop(self)
        # 滚动条的滚动移动相对于父窗口的控制button位置
        self.horizontalScrollBar().valueChanged.connect(self.horizontal_scroll_value_changed)
        self.verticalScrollBar().valueChanged.connect(self.vertical_scroll_value_changed)

        """ 左侧菜单及显示业务 """
        self.add_left_menus()
        self.left_menu.currentRowChanged.connect(self.left_menu_selected)

        """ 右侧广告及其他相关业务 """
        self.control_buttons = list()
        self.get_all_advertisement()
        # self.slide_stacked.autoStart(msec=4000)  # 自动开启得放置于填充图片之后,否则闪退
        self.slide_stacked.clicked_release.connect(self.image_widget_clicked)
        self.slide_stacked.currentChanged.connect(self.change_button_icon)  # 图片变化设置icon的背景

        # 获取即时资讯板块初始化信息
        self.get_instant_message()
        # 即时通讯内容表格的点击事件
        self.instant_message_widget.content_table.cellClicked.connect(self.view_detail_instant_message)
        # 点击更多短信通
        self.instant_message_widget.more_button.clicked.connect(self.view_more_instant_message)

        # 获取现货报价板块初始化信息
        self.get_latest_spot_price()
        # 点击了现货报价的更多
        self.spot_price_widget.more_button.clicked.connect(self.more_sport_price_popup)

        # 获取每日收盘报告
        self.get_latest_daily_report()
        # 每日报告内容表格的点击事件
        self.daily_report_widget.content_table.cellClicked.connect(self.view_detail_daily_report)
        # 每日收盘评论点击更多
        self.daily_report_widget.more_button.clicked.connect(self.view_more_daily_report)

        # 获取周度报告
        self.get_latest_weekly_report()
        # 周度报告的内容表格点击事件
        self.weekly_report_widget.content_table.cellClicked.connect(self.view_detail_weekly_report)
        # 周度报告评论点击更多
        self.weekly_report_widget.more_button.clicked.connect(self.view_more_weekly_report)

        # 获取月季报告
        self.get_latest_monthly_report()
        # 月季报告的内容表格点击事件
        self.monthly_report_widget.content_table.cellClicked.connect(self.view_detail_monthly_report)
        # 周度报告评论点击更多
        self.monthly_report_widget.more_button.clicked.connect(self.view_more_monthly_report)

        # 获取年度报告
        self.get_latest_annual_report()
        # 月季报告的内容表格点击事件
        self.annual_report_widget.content_table.cellClicked.connect(self.view_annual_monthly_report)
        # 周度报告评论点击更多
        self.annual_report_widget.more_button.clicked.connect(self.view_more_annual_report)

    def add_left_menus(self):
        """ 添加左侧菜单列表 """
        # 遍历菜单,增加QListWidgetItem和新增stackedWidget
        for list_menu in HOMEPAGE_MENUS:
            menu_item = QListWidgetItem(list_menu["name"])
            self.left_menu.addItem(menu_item)
            left_widget = LeftChildrenMenuWidget(list_menu["children"], self)
            left_widget.SelectedMenu.connect(self.left_children_menu_selected)
            self.left_stacked.addWidget(left_widget)
        item = self.left_menu.item(0)
        item.setSelected(True)

    def left_menu_selected(self, row):
        """ 点击了左侧的listItem """
        self.left_stacked.setCurrentIndex(row)

    def left_children_menu_selected(self, menu_id, menu_text):
        """ 点击左侧的子菜单 """
        print(menu_id, menu_text)
        # 处理菜单能在本页面完成的在本页面,如不能在本页面完成的,传出信号到主窗口去跳转

        self.SkipPage.emit(menu_id, menu_text)

    def horizontal_scroll_value_changed(self, value):
        """ 横向滚动条滚动事件 """
        self.control_widget.move(self.CONTROL_LEFT_DISTANCE - value, 0 - self.verticalScrollBar().value())  # 移动控制控件

    def vertical_scroll_value_changed(self, value):
        """ 纵向滚动条滚动事件 """
        self.control_widget.move(self.CONTROL_LEFT_DISTANCE - self.horizontalScrollBar().value(), 0 - value)

    def get_all_advertisement(self):
        """ 获取所有的广告信息 """
        url = SERVER_API + 'advertisement/'
        network_manager = getattr(qApp, "_network")
        reply = network_manager.get(QNetworkRequest(QUrl(url)))
        reply.finished.connect(self.get_advertisement_reply)
        self.event_loop.exec_()

    def get_advertisement_reply(self):
        reply = self.sender()
        if reply.error():
            pass
        else:
            data = json.loads(reply.readAll().data().decode("utf-8"))
            self.show_advertisement(data["advertisements"])
            self.event_loop.quit()  # 没使用同步无法加载出控制的按钮
        reply.deleteLater()

    def show_advertisement(self, advertisements):
        """ 显示所有的广告"""
        self.control_buttons.clear()
        for index, ad_item in enumerate(advertisements):
            label = PixMapLabel(ad_item, self.slide_stacked)  # 内置QThread访问图片
            self.slide_stacked.addWidget(label)
            button = ControlButton("media/icons/empty_circle.png", "media/icons/full_circle.png", self.control_widget)
            if index == 0:
                button.setIcon(QIcon(button.hover_icon_path))
            setattr(button, "image_index", index)
            button.clicked.connect(self.skip_to_image)
            self.control_buttons.append(button)
            self.control_widget.layout().addWidget(button)
        if advertisements:
            self.slide_stacked.autoStart(msec=4000)

    # def add_images(self):
    #     """ 添加轮播的图片信息(开发GUI测试代码) """
    #     # 获取广告信息
    #     self.control_buttons.clear()
    #     for index, name in enumerate(os.listdir('Data/Images')):
    #         label = QLabel(self.slide_stacked)
    #         label.setScaledContents(True)
    #         label.setPixmap(QPixmap('Data/Images/' + name))
    #         self.slide_stacked.addWidget(label)
    #         button = ControlButton("media/icons/empty_circle.png", "media/icons/full_circle.png", self.control_widget)
    #         if index == 0:
    #             button.setIcon(QIcon(button.hover_icon_path))
    #         setattr(button, "image_index", index)
    #         button.clicked.connect(self.skip_to_image)
    #         self.control_buttons.append(button)
    #         self.control_widget.layout().addWidget(button)

    def change_button_icon(self, current_index):
        """ 改变button的背景icon """
        for button in self.control_buttons:
            if getattr(button, "image_index") == current_index:
                button.setIcon(QIcon(button.hover_icon_path))
            else:
                button.setIcon(QIcon(button.icon_path))

    def image_widget_clicked(self):
        """ 点击了广告 """
        current_ad = self.slide_stacked.currentWidget()
        ad_data = current_ad.get_ad_data()
        # 根据ad_data显示出来广告响应
        ad_type = ad_data["ad_type"]
        if ad_type == "file":
            file_url = STATIC_URL + ad_data["filepath"]
            p = PDFContentPopup(file=file_url, title=ad_data["title"])
            p.exec_()
        elif ad_type == "web":
            webbrowser.open_new_tab(ad_data["web_url"])
        elif ad_type == "content":
            p = TextPopup(message=ad_data["content"])
            p.setWindowTitle(ad_data["title"])
            p.exec_()
        else:
            pass

    def skip_to_image(self):
        """ 跳到指定广告 """
        target_index = getattr(self.sender(), "image_index")
        if target_index is not None:
            self.slide_stacked.setCurrentIndex(target_index)
            self.change_button_icon(target_index)

    def get_instant_message(self):
        """ 获取即时资讯(短信通) """
        network_manager = getattr(qApp, "_network")
        url = SERVER_API + "instant-message/?count=8"
        reply = network_manager.get(QNetworkRequest(QUrl(url)))
        reply.finished.connect(self.instant_message_reply)

    def instant_message_reply(self):
        """ 即时资讯返回 """
        reply = self.sender()
        if reply.error():
            pass
        else:
            data = json.loads(reply.readAll().data().decode("utf-8"))
            # 将数据进行展示
            self.instant_message_widget.set_contents(
                content_values=data["short_messages"], content_keys=["content", "time_str"],
                data_keys=["create_time"], resize_cols=[1], column_text_color={1: QColor(100,100,100)},
                zero_text_color=[], center_alignment_columns=[]
            )
        reply.deleteLater()

    def view_detail_instant_message(self, row, col):
        """ 查看详细的即时信息 """
        if col == 0:
            item = self.instant_message_widget.content_table.item(row, col)
            create_time = item.data(Qt.UserRole).get("create_time", "未知时间")
            create_time = create_time.replace("T", " ")[:16]
            text = "<div style='text-indent:30px;line-height:25px;font-size:13px;'>" \
                    "<span style='font-size:15px;font-weight:bold;color:rgb(233,20,20);'>{}</span>" \
                    "{}</div>".format(create_time, item.text())
            p = TextPopup(text, self)
            p.setWindowTitle("即时资讯")
            p.resize(550, 180)
            p.exec_()

    def get_latest_spot_price(self):
        """ 获取最新现货报价 """
        network_manager = getattr(qApp, "_network")
        url = SERVER_API + "latest-spotprice/?count=8"
        reply = network_manager.get(QNetworkRequest(QUrl(url)))
        reply.finished.connect(self.latest_spot_price_reply)

    def latest_spot_price_reply(self):
        """ 最新现货报价数据返回 """
        reply = self.sender()
        if reply.error():
            pass
        else:
            data = json.loads(reply.readAll().data().decode("utf-8"))
            # 将数据进行展示
            self.spot_price_widget.set_contents(
                content_values=data["sport_prices"], content_keys=["variety_zh", "spot_price", "price_increase", "date"],
                data_keys=[], resize_cols=[3], column_text_color={3: QColor(100, 100, 100)},
                zero_text_color=[2], center_alignment_columns=[1, 2]
            )
        reply.deleteLater()

    def more_sport_price_popup(self):
        """ 显示更多现货报价的信息 """
        p = SpotPricePopup(self)
        p.exec_()

    def get_latest_daily_report(self):
        """ 获取最新日报信息 """
        network_manager = getattr(qApp, "_network")
        url = SERVER_API + "latest-report/?report_type=daily&count=8"
        reply = network_manager.get(QNetworkRequest(QUrl(url)))
        reply.finished.connect(self.latest_daily_report_reply)

    def latest_daily_report_reply(self):
        """ 最新日常报告数据返回 """
        reply = self.sender()
        if reply.error():
            pass
        else:
            data = json.loads(reply.readAll().data().decode("utf-8"))
            # 将数据进行展示
            self.daily_report_widget.set_contents(
                content_values=data["reports"],
                content_keys=["title", "date"],
                data_keys=["filepath"], resize_cols=[1], column_text_color={1: QColor(100, 100, 100)},
                zero_text_color=[], center_alignment_columns=[]
            )
        reply.deleteLater()

    def view_detail_daily_report(self, row, col):
        """ 查看报告的详细内容 """
        if col == 0:
            item = self.daily_report_widget.content_table.item(row, col)
            title = item.text()
            file_url = STATIC_URL + item.data(Qt.UserRole).get("filepath", 'no-found.pdf')
            p = PDFContentPopup(file=file_url, title=title)
            p.exec_()

    def view_more_daily_report(self):
        """ 查看更多的日常报告 """
        self.SkipPage.emit("l_0_0", "收盘日评")

    def view_more_weekly_report(self):
        """ 查看更多的周报 """
        self.SkipPage.emit("l_0_1", "周度报告")

    def view_more_monthly_report(self):
        """ 查看更多的周报 """
        self.SkipPage.emit("l_0_2", "月季报告")

    def view_more_annual_report(self):
        """ 查看更多的年度报 """
        self.SkipPage.emit("l_0_3", "年度报告")

    def view_more_instant_message(self):
        """ 查看更多的年度报 """
        self.SkipPage.emit("l_1_0", "短信通")

    def get_latest_weekly_report(self):
        """ 获取最新周报信息 """
        network_manager = getattr(qApp, "_network")
        url = SERVER_API + "latest-report/?report_type=weekly&count=8"
        reply = network_manager.get(QNetworkRequest(QUrl(url)))
        reply.finished.connect(self.latest_weekly_report_reply)

    def latest_weekly_report_reply(self):
        """ 最新周度报告数据返回 """
        reply = self.sender()
        if reply.error():
            pass
        else:
            data = json.loads(reply.readAll().data().decode("utf-8"))
            # 将数据进行展示
            self.weekly_report_widget.set_contents(
                content_values=data["reports"],
                content_keys=["title", "date"],
                data_keys=["filepath"], resize_cols=[1], column_text_color={1: QColor(100, 100, 100)},
                zero_text_color=[], center_alignment_columns=[]
            )
        reply.deleteLater()

    def view_detail_weekly_report(self, row, col):
        """ 查看周度报告的详细内容 """
        if col == 0:
            item = self.weekly_report_widget.content_table.item(row, col)
            title = item.text()
            file_url = STATIC_URL + item.data(Qt.UserRole).get("filepath", 'no-found.pdf')
            p = PDFContentPopup(file=file_url, title=title)
            p.exec_()

    def get_latest_monthly_report(self):
        """ 获取最新月报信息 """
        network_manager = getattr(qApp, "_network")
        url = SERVER_API + "latest-report/?report_type=monthly&count=8"
        reply = network_manager.get(QNetworkRequest(QUrl(url)))
        reply.finished.connect(self.latest_monthly_report_reply)

    def latest_monthly_report_reply(self):
        """ 最新月季报告数据返回 """
        reply = self.sender()
        if reply.error():
            pass
        else:
            data = json.loads(reply.readAll().data().decode("utf-8"))
            # 将数据进行展示
            self.monthly_report_widget.set_contents(
                content_values=data["reports"],
                content_keys=["title", "date"],
                data_keys=["filepath"], resize_cols=[1], column_text_color={1: QColor(100, 100, 100)},
                zero_text_color=[], center_alignment_columns=[]
            )
        reply.deleteLater()

    def view_detail_monthly_report(self, row, col):
        """ 查看月度报告的详细内容 """
        if col == 0:
            item = self.monthly_report_widget.content_table.item(row, col)
            title = item.text()
            file_url = STATIC_URL + item.data(Qt.UserRole).get("filepath", 'no-found.pdf')
            p = PDFContentPopup(file=file_url, title=title)
            p.exec_()

    def get_latest_annual_report(self):
        """ 获取最新年报半年报信息 """
        network_manager = getattr(qApp, "_network")
        url = SERVER_API + "latest-report/?report_type=annual&count=8"
        reply = network_manager.get(QNetworkRequest(QUrl(url)))
        reply.finished.connect(self.latest_annual_report_reply)

    def latest_annual_report_reply(self):
        """ 最新年报半年报数据返回 """
        reply = self.sender()
        if reply.error():
            pass
        else:
            data = json.loads(reply.readAll().data().decode("utf-8"))
            # 将数据进行展示
            self.annual_report_widget.set_contents(
                content_values=data["reports"],
                content_keys=["title", "date"],
                data_keys=["filepath"], resize_cols=[1], column_text_color={1: QColor(100, 100, 100)},
                zero_text_color=[], center_alignment_columns=[]
            )
        reply.deleteLater()

    def view_annual_monthly_report(self, row, col):
        """ 查看年报半年报的详细内容 """
        if col == 0:
            item = self.annual_report_widget.content_table.item(row, col)
            title = item.text()
            file_url = STATIC_URL + item.data(Qt.UserRole).get("filepath", 'no-found.pdf')
            p = PDFContentPopup(file=file_url, title=title)
            p.exec_()
class SwissLocatorFilter(QgsLocatorFilter):

    HEADERS = {
        b'User-Agent': b'Mozilla/5.0 QGIS Swiss Geoportal Locator Filter'
    }

    message_emitted = pyqtSignal(str, str, Qgis.MessageLevel, QWidget)

    def __init__(self,
                 filter_type: FilterType,
                 iface: QgisInterface = None,
                 crs: str = None):
        """"
        :param filter_type: the type of filter
        :param locale_lang: the language of the locale.
        :param iface: QGIS interface, given when on the main thread (which will display/trigger results), None otherwise
        :param crs: if iface is not given, it shall be provided, see clone()
        """
        super().__init__()
        self.type = filter_type
        self.rubber_band = None
        self.feature_rubber_band = None
        self.iface = iface
        self.map_canvas = None
        self.settings = Settings()
        self.transform_ch = None
        self.transform_4326 = None
        self.map_tip = None
        self.current_timer = None
        self.crs = None
        self.event_loop = None
        self.result_found = False
        self.access_managers = {}
        self.nam_map_tip = None
        self.nam_fetch_feature = None

        if crs:
            self.crs = crs

        self.lang = get_language()

        self.searchable_layers = searchable_layers(self.lang, restrict=True)

        if iface is not None:
            # happens only in main thread
            self.map_canvas = iface.mapCanvas()
            self.map_canvas.destinationCrsChanged.connect(
                self.create_transforms)

            self.rubber_band = QgsRubberBand(self.map_canvas,
                                             QgsWkbTypes.PointGeometry)
            self.rubber_band.setColor(QColor(255, 255, 50, 200))
            self.rubber_band.setIcon(self.rubber_band.ICON_CIRCLE)
            self.rubber_band.setIconSize(15)
            self.rubber_band.setWidth(4)
            self.rubber_band.setBrushStyle(Qt.NoBrush)

            self.feature_rubber_band = QgsRubberBand(
                self.map_canvas, QgsWkbTypes.PolygonGeometry)
            self.feature_rubber_band.setColor(QColor(255, 50, 50, 200))
            self.feature_rubber_band.setFillColor(QColor(255, 255, 50, 160))
            self.feature_rubber_band.setBrushStyle(Qt.SolidPattern)
            self.feature_rubber_band.setLineStyle(Qt.SolidLine)
            self.feature_rubber_band.setWidth(4)

            self.create_transforms()

    def name(self):
        return '{}_{}'.format(self.__class__.__name__,
                              FilterType(self.type).name)

    def clone(self):
        return SwissLocatorFilter(self.type, crs=self.crs)

    def priority(self):
        return self.settings.value(
            '{type}_priority'.format(type=self.type.value))

    def displayName(self):
        if self.type is FilterType.Location:
            return self.tr('Swiss Geoportal locations')
        elif self.type is FilterType.WMS:
            return self.tr('Swiss Geoportal / opendata.swiss WMS layers')
        elif self.type is FilterType.Feature:
            return self.tr('Swiss Geoportal features')
        else:
            raise NameError('Filter type is not valid.')

    def prefix(self):
        if self.type is FilterType.Location:
            return 'chl'
        elif self.type is FilterType.WMS:
            return 'chw'
        elif self.type is FilterType.Feature:
            return 'chf'
        else:
            raise NameError('Filter type is not valid.')

    def clearPreviousResults(self):
        self.rubber_band.reset(QgsWkbTypes.PointGeometry)
        self.feature_rubber_band.reset(QgsWkbTypes.PolygonGeometry)
        if self.map_tip is not None:
            del self.map_tip
            self.map_tip = None
        if self.current_timer is not None:
            self.current_timer.stop()
            self.current_timer.deleteLater()
            self.current_timer = None

    def hasConfigWidget(self):
        return True

    def openConfigWidget(self, parent=None):
        dlg = ConfigDialog(parent)
        wid = dlg.findChild(QTabWidget, "tabWidget", Qt.FindDirectChildrenOnly)
        tab = wid.findChild(QWidget, self.type.value)
        wid.setCurrentWidget(tab)
        dlg.exec_()

    def create_transforms(self):
        # this should happen in the main thread
        self.crs = self.settings.value('crs')
        if self.crs == 'project':
            map_crs = self.map_canvas.mapSettings().destinationCrs()
            if map_crs.isValid():
                self.crs = map_crs.authid().split(':')[1]
            if self.crs not in AVAILABLE_CRS:
                self.crs = '2056'
        assert self.crs in AVAILABLE_CRS
        src_crs_ch = QgsCoordinateReferenceSystem('EPSG:{}'.format(self.crs))
        assert src_crs_ch.isValid()
        dst_crs = self.map_canvas.mapSettings().destinationCrs()
        self.transform_ch = QgsCoordinateTransform(src_crs_ch, dst_crs,
                                                   QgsProject.instance())

        src_crs_4326 = QgsCoordinateReferenceSystem('EPSG:4326')
        self.transform_4326 = QgsCoordinateTransform(src_crs_4326, dst_crs,
                                                     QgsProject.instance())

    def group_info(self, group: str) -> (str, str):
        groups = {
            'zipcode': {
                'name': self.tr('ZIP code'),
                'layer': 'ch.swisstopo-vd.ortschaftenverzeichnis_plz'
            },
            'gg25': {
                'name': self.tr('Municipal boundaries'),
                'layer': 'ch.swisstopo.swissboundaries3d-gemeinde-flaeche.fill'
            },
            'district': {
                'name': self.tr('District'),
                'layer': 'ch.swisstopo.swissboundaries3d-bezirk-flaeche.fill'
            },
            'kantone': {
                'name': self.tr('Cantons'),
                'layer': 'ch.swisstopo.swissboundaries3d-kanton-flaeche.fill'
            },
            'gazetteer': {
                'name': self.tr('Index'),
                'layer': 'ch.swisstopo.swissnames3d'
            },  # there is also: ch.bav.haltestellen-oev ?
            'address': {
                'name': self.tr('Address'),
                'layer': 'ch.bfs.gebaeude_wohnungs_register'
            },
            'parcel': {
                'name': self.tr('Parcel'),
                'layer': None
            }
        }
        if group not in groups:
            self.info('Could not find group {} in dictionary'.format(group))
            return None, None
        return groups[group]['name'], groups[group]['layer']

    @staticmethod
    def rank2priority(rank) -> float:
        """
        Translate the rank from geoportal to the priority of the result
        see https://api3.geo.admin.ch/services/sdiservices.html#search
        :param rank: an integer from 1 to 7
        :return: the priority as a float from 0 to 1, 1 being a perfect match
        """
        return float(-rank / 7 + 1)

    @staticmethod
    def box2geometry(box: str) -> QgsRectangle:
        """
        Creates a rectangle from a Box definition as string
        :param box: the box as a string
        :return: the rectangle
        """
        coords = re.findall(r'\b(\d+(?:\.\d+)?)\b', box)
        if len(coords) != 4:
            raise InvalidBox('Could not parse: {}'.format(box))
        return QgsRectangle(float(coords[0]), float(coords[1]),
                            float(coords[2]), float(coords[3]))

    @staticmethod
    def url_with_param(url, params) -> str:
        url = QUrl(url)
        q = QUrlQuery(url)
        for key, value in params.items():
            q.addQueryItem(key, value)
        url.setQuery(q)
        return url.url()

    def fetchResults(self, search: str, context: QgsLocatorContext,
                     feedback: QgsFeedback):
        try:
            self.dbg_info("start Swiss locator search...")

            if len(search) < 2:
                return

            if len(search) < 4 and self.type is FilterType.Feature:
                return

            self.result_found = False

            swisstopo_base_url = 'https://api3.geo.admin.ch/rest/services/api/SearchServer'
            swisstopo_base_params = {
                'type':
                self.type.value,
                'searchText':
                str(search),
                'returnGeometry':
                'true',
                'lang':
                self.lang,
                'sr':
                self.crs,
                'limit':
                str(
                    self.settings.value(
                        '{type}_limit'.format(type=self.type.value)))
                # bbox Must be provided if the searchText is not.
                # A comma separated list of 4 coordinates representing
                # the bounding box on which features should be filtered (SRID: 21781).
            }
            # Locations, WMS layers
            if self.type is not FilterType.Feature:
                nam = NetworkAccessManager()
                feedback.canceled.connect(nam.abort)

                search_urls = [(swisstopo_base_url, swisstopo_base_params)]

                if self.settings.value('layers_include_opendataswiss'
                                       ) and self.type is FilterType.WMS:
                    search_urls.append(
                        ('https://opendata.swiss/api/3/action/package_search?',
                         {
                             'q': 'q=WMS+%C3' + str(search)
                         }))

                for (swisstopo_base_url, swisstopo_base_params) in search_urls:
                    swisstopo_base_url = self.url_with_param(
                        swisstopo_base_url, swisstopo_base_params)
                    self.dbg_info(swisstopo_base_url)
                    try:
                        (response, content) = nam.request(swisstopo_base_url,
                                                          headers=self.HEADERS,
                                                          blocking=True)
                        self.handle_response(response, search, feedback)
                    except RequestsExceptionUserAbort:
                        pass
                    except RequestsException as err:
                        self.info(err)

            # Feature search
            else:
                # Feature search is split in several requests
                # otherwise URL is too long
                self.access_managers = {}
                try:
                    layers = list(self.searchable_layers.keys())
                    assert len(layers) > 0
                    step = 30
                    for l in range(0, len(layers), step):
                        last = min(l + step - 1, len(layers) - 1)
                        swisstopo_base_params['features'] = ','.join(
                            layers[l:last])
                        self.access_managers[self.url_with_param(
                            swisstopo_base_url, swisstopo_base_params)] = None
                except IOError:
                    self.info(
                        'Layers data file not found. Please report an issue.',
                        Qgis.Critical)

                # init event loop
                # wait for all requests to end
                self.event_loop = QEventLoop()

                def reply_finished(response):
                    self.handle_response(response, search, feedback)
                    if response.url in self.access_managers:
                        self.access_managers[response.url] = None
                    for nam in self.access_managers.values():
                        if nam is not None:
                            return
                        self.event_loop.quit()

                feedback.canceled.connect(self.event_loop.quit)

                # init the network access managers, create the URL
                for swisstopo_base_url in self.access_managers:
                    self.dbg_info(swisstopo_base_url)
                    nam = NetworkAccessManager()
                    self.access_managers[swisstopo_base_url] = nam
                    nam.finished.connect(reply_finished)
                    nam.request(swisstopo_base_url,
                                headers=self.HEADERS,
                                blocking=False)
                    feedback.canceled.connect(nam.abort)

                # Let the requests end and catch all exceptions (and clean up requests)
                if len(self.access_managers) > 0:
                    try:
                        self.event_loop.exec_(
                            QEventLoop.ExcludeUserInputEvents)
                    except RequestsExceptionUserAbort:
                        pass
                    except RequestsException as err:
                        self.info(str(err))

            if not self.result_found:
                result = QgsLocatorResult()
                result.filter = self
                result.displayString = self.tr('No result found.')
                result.userData = NoResult().as_definition()
                self.resultFetched.emit(result)

        except Exception as e:
            self.info(e, Qgis.Critical)
            exc_type, exc_obj, exc_traceback = sys.exc_info()
            filename = os.path.split(
                exc_traceback.tb_frame.f_code.co_filename)[1]
            self.info(
                '{} {} {}'.format(exc_type, filename, exc_traceback.tb_lineno),
                Qgis.Critical)
            self.info(
                traceback.print_exception(exc_type, exc_obj, exc_traceback),
                Qgis.Critical)

    def handle_response(self, response, search: str, feedback: QgsFeedback):
        try:
            if response.status_code != 200:
                if not isinstance(response.exception,
                                  RequestsExceptionUserAbort):
                    self.info(
                        "Error in main response with status code: {} from {}".
                        format(response.status_code, response.url))
                return

            data = json.loads(response.content.decode('utf-8'))
            # self.dbg_info(data)

            if self.is_opendata_swiss_response(data):
                visited_capabilities = []

                for loc in data['result']['results']:
                    display_name = loc['title'].get(self.lang, "")
                    if not display_name:
                        # Fallback to german
                        display_name = loc['title']['de']

                    for res in loc['resources']:

                        url = res['url']
                        url_components = urlparse(url)
                        wms_url = url_components.scheme + '://' + url_components.netloc + '/' + url_components.path + '?'

                        result = QgsLocatorResult()
                        result.filter = self
                        result.group = 'opendata.swiss'
                        result.icon = QgsApplication.getThemeIcon(
                            "/mActionAddWmsLayer.svg")

                        if 'wms' in url.lower():
                            if res['media_type'] == 'WMS':
                                result.displayString = display_name
                                result.description = url

                                if res['title']['de'] == 'GetMap':
                                    layers = parse_qs(
                                        url_components.query)['LAYERS']
                                    result.userData = WMSLayerResult(
                                        layer=layers[0],
                                        title=display_name,
                                        url=wms_url).as_definition()
                                    self.result_found = True
                                    self.resultFetched.emit(result)

                            elif 'request=getcapabilities' in url.lower(
                            ) and url_components.netloc not in visited_capabilities:
                                visited_capabilities.append(
                                    url_components.netloc)

                                def parse_capabilities_result(response):
                                    capabilities = ET.fromstring(
                                        response.content)

                                    # Get xml namespace
                                    match = re.match(r'\{.*\}',
                                                     capabilities.tag)
                                    namespace = match.group(0) if match else ''

                                    # Search for layers containing the search term in the name or title
                                    for layer in capabilities.findall(
                                            './/{}Layer'.format(namespace)):
                                        layername = self.find_text(
                                            layer, '{}Name'.format(namespace))
                                        layertitle = self.find_text(
                                            layer, '{}Title'.format(namespace))
                                        if layername and (
                                                search in layername.lower() or
                                                search in layertitle.lower()):
                                            if not layertitle:
                                                layertitle = layername

                                            result.displayString = layertitle
                                            result.description = '{}?LAYERS={}'.format(
                                                url.replace(
                                                    'GetCapabilities',
                                                    'GetMap'), layername)
                                            result.userData = WMSLayerResult(
                                                layer=layername,
                                                title=layertitle,
                                                url=wms_url).as_definition()
                                            self.result_found = True
                                            self.resultFetched.emit(result)

                                    self.event_loop.quit()

                                # Retrieve Capabilities xml
                                self.event_loop = QEventLoop()
                                nam = NetworkAccessManager()
                                nam.finished.connect(parse_capabilities_result)
                                nam.request(url,
                                            headers=self.HEADERS,
                                            blocking=False)
                                feedback.canceled.connect(self.event_loop.quit)

                                try:
                                    self.event_loop.exec_(
                                        QEventLoop.ExcludeUserInputEvents)
                                except RequestsExceptionUserAbort:
                                    pass
                                except RequestsException as err:
                                    self.info(err)

            else:
                for loc in data['results']:
                    self.dbg_info("keys: {}".format(loc['attrs'].keys()))

                    result = QgsLocatorResult()
                    result.filter = self
                    result.group = 'Swiss Geoportal'
                    if loc['attrs']['origin'] == 'layer':
                        # available keys: ['origin', 'lang', 'layer', 'staging', 'title', 'topics', 'detail', 'label', 'id']
                        for key, val in loc['attrs'].items():
                            self.dbg_info('{}: {}'.format(key, val))
                        result.displayString = loc['attrs']['title']
                        result.description = loc['attrs']['layer']
                        result.userData = WMSLayerResult(
                            layer=loc['attrs']['layer'],
                            title=loc['attrs']['title'],
                            url='http://wms.geo.admin.ch/?VERSION%3D2.0.0'
                        ).as_definition()
                        result.icon = QgsApplication.getThemeIcon(
                            "/mActionAddWmsLayer.svg")
                        self.result_found = True
                        self.resultFetched.emit(result)

                    elif loc['attrs']['origin'] == 'feature':
                        for key, val in loc['attrs'].items():
                            self.dbg_info('{}: {}'.format(key, val))
                        layer = loc['attrs']['layer']
                        point = QgsPointXY(loc['attrs']['lon'],
                                           loc['attrs']['lat'])
                        if layer in self.searchable_layers:
                            layer_display = self.searchable_layers[layer]
                        else:
                            self.info(
                                self.
                                tr('Layer {} is not in the list of searchable layers.'
                                   ' Please report issue.'.format(layer)),
                                Qgis.Warning)
                            layer_display = layer
                        result.group = layer_display
                        result.displayString = loc['attrs']['detail']
                        result.userData = FeatureResult(
                            point=point,
                            layer=layer,
                            feature_id=loc['attrs']
                            ['feature_id']).as_definition()
                        result.icon = QIcon(
                            ":/plugins/swiss_locator/icons/swiss_locator.png")
                        self.result_found = True
                        self.resultFetched.emit(result)

                    else:  # locations
                        for key, val in loc['attrs'].items():
                            self.dbg_info('{}: {}'.format(key, val))
                        group_name, group_layer = self.group_info(
                            loc['attrs']['origin'])
                        if 'layerBodId' in loc['attrs']:
                            self.dbg_info("layer: {}".format(
                                loc['attrs']['layerBodId']))
                        if 'featureId' in loc['attrs']:
                            self.dbg_info("feature: {}".format(
                                loc['attrs']['featureId']))

                        result.displayString = strip_tags(
                            loc['attrs']['label'])
                        # result.description = loc['attrs']['detail']
                        # if 'featureId' in loc['attrs']:
                        #     result.description = loc['attrs']['featureId']
                        result.group = group_name
                        result.userData = LocationResult(
                            point=QgsPointXY(loc['attrs']['y'],
                                             loc['attrs']['x']),
                            bbox=self.box2geometry(
                                loc['attrs']['geom_st_box2d']),
                            layer=group_layer,
                            feature_id=loc['attrs']['featureId']
                            if 'featureId' in loc['attrs'] else None,
                            html_label=loc['attrs']['label']).as_definition()
                        result.icon = QIcon(
                            ":/plugins/swiss_locator/icons/swiss_locator.png")
                        self.result_found = True
                        self.resultFetched.emit(result)

        except Exception as e:
            self.info(str(e), Qgis.Critical)
            exc_type, exc_obj, exc_traceback = sys.exc_info()
            filename = os.path.split(
                exc_traceback.tb_frame.f_code.co_filename)[1]
            self.info(
                '{} {} {}'.format(exc_type, filename, exc_traceback.tb_lineno),
                Qgis.Critical)
            self.info(
                traceback.print_exception(exc_type, exc_obj, exc_traceback),
                Qgis.Critical)

    def triggerResult(self, result: QgsLocatorResult):
        # this should be run in the main thread, i.e. mapCanvas should not be None

        # remove any map tip
        self.clearPreviousResults()

        user_data = NoResult
        try:
            swiss_result = result_from_data(result)
        except SystemError:
            self.message_emitted.emit(
                self.displayName(),
                self.
                tr('QGIS Swiss Locator encountered an error. Please <b>update to QGIS 3.16.2</b> or newer.'
                   ), Qgis.Warning, None)

        if type(swiss_result) == NoResult:
            return

        # WMS
        if type(swiss_result) == WMSLayerResult:
            url_with_params = 'contextualWMSLegend=0' \
                              '&crs=EPSG:{crs}' \
                              '&dpiMode=7' \
                              '&featureCount=10' \
                              '&format=image/png' \
                              '&layers={layer}' \
                              '&styles=' \
                              '&url={url}' \
                .format(crs=self.crs, layer=swiss_result.layer, url=swiss_result.url)
            wms_layer = QgsRasterLayer(url_with_params, result.displayString,
                                       'wms')
            label = QLabel()
            label.setTextFormat(Qt.RichText)
            label.setTextInteractionFlags(Qt.TextBrowserInteraction)
            label.setOpenExternalLinks(True)

            if 'geo.admin.ch' in swiss_result.url.lower():
                label.setText(
                    '<a href="https://map.geo.admin.ch/'
                    '?lang={}&bgLayer=ch.swisstopo.pixelkarte-farbe&layers={}">'
                    'Open layer in map.geo.admin.ch</a>'.format(
                        self.lang, swiss_result.layer))

            if not wms_layer.isValid():
                msg = self.tr('Cannot load WMS layer: {} ({})'.format(
                    swiss_result.title, swiss_result.layer))
                level = Qgis.Warning
                self.info(msg, level)
            else:
                msg = self.tr('WMS layer added to the map: {} ({})'.format(
                    swiss_result.title, swiss_result.layer))
                level = Qgis.Info

                QgsProject.instance().addMapLayer(wms_layer)

            self.message_emitted.emit(self.displayName(), msg, level, label)

        # Feature
        elif type(swiss_result) == FeatureResult:
            point = QgsGeometry.fromPointXY(swiss_result.point)
            point.transform(self.transform_4326)
            self.highlight(point)
            if self.settings.value('show_map_tip'):
                self.show_map_tip(swiss_result.layer, swiss_result.feature_id,
                                  point)
        # Location
        else:
            point = QgsGeometry.fromPointXY(swiss_result.point)
            if swiss_result.bbox.isNull():
                bbox = None
            else:
                bbox = QgsGeometry.fromRect(swiss_result.bbox)
                bbox.transform(self.transform_ch)
            layer = swiss_result.layer
            feature_id = swiss_result.feature_id
            if not point:
                return

            point.transform(self.transform_ch)

            self.highlight(point, bbox)

            if layer and feature_id:
                self.fetch_feature(layer, feature_id)

                if self.settings.value('show_map_tip'):
                    self.show_map_tip(layer, feature_id, point)
            else:
                self.current_timer = QTimer()
                self.current_timer.timeout.connect(self.clearPreviousResults)
                self.current_timer.setSingleShot(True)
                self.current_timer.start(5000)

    def highlight(self, point, bbox=None):
        if bbox is None:
            bbox = point
        self.rubber_band.reset(QgsWkbTypes.PointGeometry)
        self.rubber_band.addGeometry(point, None)
        rect = bbox.boundingBox()
        rect.scale(1.1)
        self.map_canvas.setExtent(rect)
        self.map_canvas.refresh()

    def fetch_feature(self, layer, feature_id):
        # Try to get more info
        self.nam_fetch_feature = NetworkAccessManager()
        url_detail = 'https://api3.geo.admin.ch/rest/services/api/MapServer/{layer}/{feature_id}' \
            .format(layer=layer, feature_id=feature_id)
        params = {'lang': self.lang, 'sr': self.crs}
        url_detail = self.url_with_param(url_detail, params)
        self.dbg_info(url_detail)
        self.nam_fetch_feature.finished.connect(self.parse_feature_response)
        self.nam_fetch_feature.request(url_detail,
                                       headers=self.HEADERS,
                                       blocking=False)

    def parse_feature_response(self, response):
        if response.status_code != 200:
            if not isinstance(response.exception, RequestsExceptionUserAbort):
                self.info(
                    "Error in feature response with status code: {} from {}".
                    format(response.status_code, response.url))
            return

        data = json.loads(response.content.decode('utf-8'))
        self.dbg_info(data)

        if 'feature' not in data or 'geometry' not in data['feature']:
            return

        if 'rings' in data['feature']['geometry']:
            rings = data['feature']['geometry']['rings']
            self.dbg_info(rings)
            for r in range(0, len(rings)):
                for p in range(0, len(rings[r])):
                    rings[r][p] = QgsPointXY(rings[r][p][0], rings[r][p][1])
            geometry = QgsGeometry.fromPolygonXY(rings)
            geometry.transform(self.transform_ch)

            self.feature_rubber_band.reset(QgsWkbTypes.PolygonGeometry)
            self.feature_rubber_band.addGeometry(geometry, None)

    def show_map_tip(self, layer, feature_id, point):
        if layer and feature_id:
            url_html = 'https://api3.geo.admin.ch/rest/services/api/MapServer/{layer}/{feature_id}/htmlPopup' \
                .format(layer=layer, feature_id=feature_id)
            params = {'lang': self.lang, 'sr': self.crs}
            url_html = self.url_with_param(url_html, params)
            self.dbg_info(url_html)

            self.nam_map_tip = NetworkAccessManager()
            self.nam_map_tip.finished.connect(
                lambda response: self.parse_map_tip_response(response, point))
            self.nam_map_tip.request(url_html,
                                     headers=self.HEADERS,
                                     blocking=False)

    def parse_map_tip_response(self, response, point):
        if response.status_code != 200:
            if not isinstance(response.exception, RequestsExceptionUserAbort):
                self.info(
                    "Error in map tip response with status code: {} from {}".
                    format(response.status_code, response.url))
            return

        self.dbg_info(response.content.decode('utf-8'))
        self.map_tip = MapTip(self.iface, response.content.decode('utf-8'),
                              point.asPoint())
        self.map_tip.closed.connect(self.clearPreviousResults)

    def info(self, msg="", level=Qgis.Info):
        self.logMessage(str(msg), level)

    def dbg_info(self, msg=""):
        if DEBUG:
            self.info(msg)

    @staticmethod
    def break_camelcase(identifier):
        matches = re.finditer(
            '.+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)',
            identifier)
        return ' '.join([m.group(0) for m in matches])

    def is_opendata_swiss_response(self, json):
        return 'opendata.swiss' in json.get("help", [])

    def find_text(self, xmlElement, match):
        node = xmlElement.find(match)
        return node.text if node is not None else ''
Ejemplo n.º 18
0
class Ace(QWebView):
    """ Embbeded Ace javascript web editor """
    
    isReady = pyqtSignal(name='isReady')
    modificationChanged = pyqtSignal(bool)
    cursorPositionChanged = pyqtSignal(int, int, name='cursorPositionChanged')
    
    def __init__(self, file_info, parent=None):
        super(Ace, self).__init__(parent)
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.parent = parent
        self.file_info = file_info
        self.language = EditorHelper.lang_from_file_info(file_info)
        self.waitForReady = False
        self.loop = QEventLoop()
        
        settings = self.settings()
        settings.setAttribute(QWebSettings.JavascriptCanAccessClipboard, True)
        settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True)
        self.inspector = QWebInspector(self)
        showInspectorAction = QAction('showInspector', self)
        showInspectorAction.triggered.connect(self.showInspector)
        self.addAction(showInspectorAction)
        
        self.modificationChanged.connect(self.modification_changed)
        self.main_frame().javaScriptWindowObjectCleared.connect(self.__self_js)
        pckg, file_name = 'ace_editor', 'ace_editor.html'
        resource = pkg_resources.resource_string(pckg, file_name)
        html_template = str(resource, 'utf-8')
        #insert file content
        with open(self.file_info.absoluteFilePath(), 'r') as f:
            text = f.read()
            text = html.escape(text)
            html_template = html_template.replace('{{ content }}', text)
        base_url = QUrl.fromLocalFile(os.path.dirname(__file__))
        
        self.setHtml(html_template, base_url)
        self.modified = False
        
        if not self.waitForReady:
            self.loop.exec()
    
    @pyqtSlot(str, name='test', result=str)
    @EditorHelper.json_dumps
    def test(self, prefix):
        print(prefix)
        return ['plop', 'cool', 42, {'plop':23}]
    
    def modification_changed(self, b):
        self.modified = b
    
    def save(self, parent, action=None):
        Alter.invoke_all('editor_presave', self)
        if self.modified:
            with open(self.file_info.absoluteFilePath(), 'w') as f:
                f.write(self.get_value())
            self.original_to_current_doc()
            self.modificationChanged.emit(False)
            parent.status_bar.showMessage(self.tr("Saved file."))
            Alter.invoke_all('editor_save', self)
        else :
            parent.status_bar.showMessage(self.tr("Nothing to save."))
    
    def toggle_hidden(self, parent, action=None):
        self.set_show_invisibles(action.isChecked())
    
    def toggle_soft_tabs(self, parent, action=None):
        self.set_use_soft_tabs(action.isChecked())
    
    def main_frame(self):
        """ Convinient function to get main QWebFrame """
        return self.page().mainFrame()
    
    def send_js(self, script):
        """ Convinient function to send javascript to ace editor """
        return self.main_frame().evaluateJavaScript(script)

    def __self_js(self):
        self.main_frame().addToJavaScriptWindowObject('AceEditor', self)
    
    @pyqtSlot(name='isReady')
    def editor_ready(self):
        if self.language != None:
            self.set_mode(self.language.lower())
        self.set_focus()
        if self.loop.isRunning():
            self.loop.quit()
        self.waitForReady = True
    
    def showInspector(self):
        self.dialogInspector = QDialog(self)
        self.dialogInspector.setLayout(QVBoxLayout())
        self.dialogInspector.layout().addWidget(self.inspector)
        self.dialogInspector.setModal(False)
        self.dialogInspector.show()
    
    def original_to_current_doc(self):
        self.send_js('editor.orignalToCurrentDoc()')
    
    def get_value(self):
        return self.send_js('editor.getValue()')
    
    def set_value(self, content):
        self.send_js('editor.setValue("{0}")'.format(content))
    
    def set_focus(self):
        self.send_js('editor.focus()')
    
    def insert(self, text):
        self.send_js('editor.insert("{0}")'.format(text))
    
    def set_mode(self, language):
        cmd = 'editor.getSession().setMode("ace/mode/{0}")'
        self.send_js(cmd.format(language))
    
    def set_theme(self, theme):
        self.send_js('editor.setTheme("ace/theme/{0}")'.format(theme))
    
    def get_selected_text(self):
        cmd = 'editor.session.getTextRange(editor.getSelectionRange())'
        return self.send_js(cmd)
    
    def get_cursor(self):
        cmd = 'editor.selection.getCursor()'
        return self.send_js(cmd)
    
    def got_to_line(self, line):
        cmd = 'editor.gotoLine({0})'
        self.send_js(cmd.format(line))
    
    def get_length(self):
        return self.send_js('editor.session.getLength()');
    
    def set_tab_size(self, tab_size):
        self.send_js('editor.getSession().setTabSize({0})'.format(tab_size))
    
    def get_tab_size(self):
        return self.send_js('editor.getSession().getTabSize()')
    
    def set_use_soft_tabs(self, b):
        b = 'true' if b else 'false'
        self.send_js('editor.getSession().setUseSoftTabs({0})'.format(b))
    
    def set_font_size(self, font_size):
        cmd = "document.getElementById('editor').style.fontSize='{0}px'"
        self.send_js(cmd.format(font_size))
    
    def set_use_wrap_mode(self, b):
        b = 'true' if b else 'false'
        cmd = "editor.getSession().setUseWrapMode({0})"
        self.send_js(cmd.format(b))
    
    def set_highlight_active_line(self, b):
        b = 'true' if b else 'false'
        cmd = "editor.setHighlightActiveLine({0})"
        self.send_js(cmd.format(b))
    
    def set_show_print_margin(self, b):
        b = 'true' if b else 'false'
        cmd = "editor.setShowPrintMargin({0})"
        self.send_js(cmd.format(b))
    
    def set_read_only(self, b):
        b = 'true' if b else 'false'
        self.send_js('editor.setReadOnly({0})'.format(b))
    
    def set_show_invisibles(self, b):
        b = 'true' if b else 'false'
        self.send_js('editor.setShowInvisibles({0})'.format(b))
Ejemplo n.º 19
0
class Downloader(QObject):

    # error status
    NO_ERROR = 0
    TIMEOUT_ERROR = 4
    UNKNOWN_ERROR = -1

    # PyQt signals
    replyFinished = pyqtSignal(str)
    allRepliesFinished = pyqtSignal()

    def __init__(self,
                 parent=None,
                 maxConnections=2,
                 defaultCacheExpiration=24,
                 userAgent=""):
        QObject.__init__(self, parent)

        self.maxConnections = maxConnections
        self.defaultCacheExpiration = defaultCacheExpiration  # hours
        self.userAgent = userAgent

        # initialize variables
        self.clear()
        self.sync = False

        self.eventLoop = QEventLoop()

        self.timer = QTimer()
        self.timer.setSingleShot(True)
        self.timer.timeout.connect(self.timeOut)

    def clear(self):
        self.queue = []
        self.requestingReplies = {}
        self.fetchedFiles = {}

        self._successes = 0
        self._errors = 0
        self._cacheHits = 0

        self.errorStatus = Downloader.NO_ERROR

    def _replyFinished(self):
        reply = self.sender()
        url = reply.request().url().toString()
        if url not in self.fetchedFiles:
            self.fetchedFiles[url] = None

        if url in self.requestingReplies:
            del self.requestingReplies[url]

        httpStatusCode = reply.attribute(
            QNetworkRequest.HttpStatusCodeAttribute)
        if reply.error() == QNetworkReply.NoError:
            self._successes += 1

            if reply.attribute(QNetworkRequest.SourceIsFromCacheAttribute):
                self._cacheHits += 1

            elif not reply.hasRawHeader(b"Cache-Control"):
                cache = QgsNetworkAccessManager.instance().cache()
                if cache:
                    metadata = cache.metaData(reply.request().url())
                    if metadata.expirationDate().isNull():
                        metadata.setExpirationDate(
                            QDateTime.currentDateTime().addSecs(
                                self.defaultCacheExpiration * 3600))
                        cache.updateMetaData(metadata)
                        self.log(
                            "Default expiration date has been set: %s (%d h)" %
                            (url, self.defaultCacheExpiration))

            if reply.isReadable():
                data = reply.readAll()
                self.fetchedFiles[url] = data
            else:
                qDebug("http status code: " + str(httpStatusCode))

        else:
            self._errors += 1
            if self.errorStatus == self.NO_ERROR:
                self.errorStatus = self.UNKNOWN_ERROR

        self.replyFinished.emit(url)
        reply.deleteLater()

        if len(self.queue) + len(self.requestingReplies) == 0:
            # all replies have been received
            if self.sync:
                self.logT("eventLoop.quit()")
                self.eventLoop.quit()
            else:
                self.timer.stop()

            self.allRepliesFinished.emit()

        elif len(self.queue) > 0:
            # start fetching the next file
            self.fetchNext()

    def timeOut(self):
        self.log("Downloader.timeOut()")
        self.abort()
        self.errorStatus = Downloader.TIMEOUT_ERROR

    @pyqtSlot()
    def abort(self, stopTimer=True):
        # clear queue and abort requests
        self.queue = []

        for reply in self.requestingReplies.values():
            url = reply.url().toString()
            reply.abort()
            reply.deleteLater()
            self.log("request aborted: {0}".format(url))

        self.errorStatus = Downloader.UNKNOWN_ERROR
        self.requestingReplies = {}

        if stopTimer:
            self.timer.stop()

    def fetchNext(self):
        if len(self.queue) == 0:
            return
        url = self.queue.pop(0)
        self.log("fetchNext: %s" % url)

        # create request
        request = QNetworkRequest(QUrl(url))
        if self.userAgent:
            request.setRawHeader(
                b"User-Agent", self.userAgent.encode("ascii", "ignore")
            )  # will be overwritten in QgsNetworkAccessManager::createRequest() since 2.2

        # send request
        reply = QgsNetworkAccessManager.instance().get(request)
        reply.finished.connect(self._replyFinished)
        self.requestingReplies[url] = reply
        return reply

    def fetchFiles(self, urlList, timeoutSec=0):
        self.log("fetchFiles()")
        files = self._fetch(True, urlList, timeoutSec)
        self.log("fetchFiles() End: %d" % self.errorStatus)
        return files

    @pyqtSlot(list, int)
    def fetchFilesAsync(self, urlList, timeoutSec=0):
        self.log("fetchFilesAsync()")
        self._fetch(False, urlList, timeoutSec)

    def _fetch(self, sync, urlList, timeoutSec):
        self.clear()
        self.sync = sync

        if not urlList:
            return {}

        for url in urlList:
            if url not in self.queue:
                self.queue.append(url)

        for i in range(self.maxConnections):
            self.fetchNext()

        if timeoutSec > 0:
            self.timer.setInterval(timeoutSec * 1000)
            self.timer.start()

        if sync:
            self.logT("eventLoop.exec_(): " + str(self.eventLoop))
            self.eventLoop.exec_()

            if timeoutSec > 0:
                self.timer.stop()

            return self.fetchedFiles

    def log(self, msg):
        if DEBUG_MODE:
            qDebug(msg)

    def logT(self, msg):
        if DEBUG_MODE:
            qDebug("%s: %s" % (str(threading.current_thread()), msg))

    def finishedCount(self):
        return len(self.fetchedFiles)

    def unfinishedCount(self):
        return len(self.queue) + len(self.requestingReplies)

    def stats(self):
        finished = self.finishedCount()
        unfinished = self.unfinishedCount()
        return {
            "total": finished + unfinished,
            "finished": finished,
            "unfinished": unfinished,
            "successed": self._successes,
            "errors": self._errors,
            "cacheHits": self._cacheHits,
            "downloaded": self._successes - self._cacheHits
        }