예제 #1
0
 def on_searchPushButton_clicked(self, checked):
     if self.categoryID != '' and self.categoryID != '-1':
         merchandising = Merchandising(warnings = False)
         response = merchandising.execute(
             'getMostWatchedItems',
             {
                 'categoryId': self.categoryID,
                 'maxResults': self.ui.spinBox.value()
             }
         )
         reply = response.reply
         itemRecommendations = reply.itemRecommendations.item
         row = self.ui.tableWidget.rowCount()
         for item in itemRecommendations:
             self.ui.tableWidget.insertRow(row)
             imageUrl = 'http://thumbs3.ebaystatic.com/pict/%s4040.jpg' % item.itemId
             request = QNetworkRequest(QUrl(imageUrl))
             request.setAttribute(QNetworkRequest.CacheLoadControlAttribute, QNetworkRequest.PreferCache)
             reply = self.manager.get(request)
             self.replyMap[reply] = row
             viewItemURL = QLabel()
             viewItemURL.setOpenExternalLinks(True)
             #viewItemURL.setTextInteractionFlags(Qt.TextBrowserInteraction)
             title = '<a href="%s">%s</a>' % (item.viewItemURL, item.title)
             viewItemURL.setText(title)
             self.ui.tableWidget.setCellWidget(row, 1, viewItemURL)
             self.ui.tableWidget.setItem(row, 2, QTableWidgetItem(item.primaryCategoryName))
             self.ui.tableWidget.setItem(row, 3, QTableWidgetItem(item.buyItNowPrice.value))
             self.ui.tableWidget.setItem(row, 4, QTableWidgetItem(item.watchCount))
             row += 1
예제 #2
0
 def _wrap_request(self, request):
     req = QNetworkRequest(request)
     req_id = next(self._request_ids)
     req.setAttribute(self._REQUEST_ID, req_id)
     if hasattr(request, 'timeout'):
         req.timeout = request.timeout
     return req, req_id
예제 #3
0
 def __downloadFile(self, url, filename, doneMethod=None):
     """
     Private slot to download the given file.
     
     @param url URL for the download (string)
     @param filename local name of the file (string)
     @param doneMethod method to be called when done
     """
     self.__updateButton.setEnabled(False)
     self.__downloadButton.setEnabled(False)
     self.__downloadInstallButton.setEnabled(False)
     self.__downloadCancelButton.setEnabled(True)
     
     self.statusLabel.setText(url)
     
     self.__doneMethod = doneMethod
     self.__downloadURL = url
     self.__downloadFileName = filename
     self.__downloadIODevice = QFile(self.__downloadFileName + ".tmp")
     self.__downloadCancelled = False
     
     request = QNetworkRequest(QUrl(url))
     request.setAttribute(QNetworkRequest.CacheLoadControlAttribute,
                          QNetworkRequest.AlwaysNetwork)
     reply = self.__networkManager.get(request)
     reply.finished.connect(self.__downloadFileDone)
     reply.downloadProgress.connect(self.__downloadProgress)
     self.__replies.append(reply)
예제 #4
0
 def checkPluginUpdatesAvailable(self):
     """
     Public method to check the availability of updates of plug-ins.
     """
     period = Preferences.getPluginManager("UpdatesCheckInterval")
     if period == 0:
         return
     elif period in [1, 2, 3]:
         lastModified = QFileInfo(self.pluginRepositoryFile).lastModified()
         if lastModified.isValid() and lastModified.date().isValid():
             lastModifiedDate = lastModified.date()
             now = QDate.currentDate()
             if period == 1 and lastModifiedDate.day() == now.day():
                 # daily
                 return
             elif period == 2 and lastModifiedDate.daysTo(now) < 7:
                 # weekly
                 return
             elif period == 3 and \
                 (lastModifiedDate.daysTo(now) <
                  lastModifiedDate.daysInMonth()):
                 # monthly
                 return
     
     self.__updateAvailable = False
     
     request = QNetworkRequest(
         QUrl(Preferences.getUI("PluginRepositoryUrl6")))
     request.setAttribute(QNetworkRequest.CacheLoadControlAttribute,
                          QNetworkRequest.AlwaysNetwork)
     reply = self.__networkManager.get(request)
     reply.finished.connect(self.__downloadRepositoryFileDone)
     self.__replies.append(reply)
예제 #5
0
 def _wrap_request(self, request):
     req = QNetworkRequest(request)
     req_id = next(self._request_ids)
     req.setAttribute(self._REQUEST_ID, req_id)
     for attr in ['timeout', 'track_response_body']:
         if hasattr(request, attr):
             setattr(req, attr, getattr(request, attr))
     return req, req_id
예제 #6
0
    def download(self):
        grab = None
        for x in range(self._tilesRect.width()):
            for y in range(self._tilesRect.height()):
                tp = Point(self._tilesRect.topLeft() + QPoint(x, y))
                if tp not in self._tilePixmaps:
                    grab = QPoint(tp)
                    break

        if grab is None:
            self._url = QUrl()
            return

        path = 'http://tile.openstreetmap.org/%d/%d/%d.png' % (self.zoom, grab.x(), grab.y())
        self._url = QUrl(path)
        request = QNetworkRequest()
        request.setUrl(self._url)
        request.setRawHeader(b'User-Agent', b'Nokia (PyQt) Graphics Dojo 1.0')
        request.setAttribute(QNetworkRequest.User, grab)
        self._manager.get(request)
    def get(self, url, cache=True, **kwargs):
        """Start a download with a link URL.

        Args:
            url: The URL to get, as QUrl
            cache: If set to False, don't cache the response.
            **kwargs: passed to get_request().

        Return:
            The created DownloadItem.
        """
        if not url.isValid():
            urlutils.invalid_url_error(url, "start download")
            return None

        req = QNetworkRequest(url)
        user_agent = websettings.user_agent(url)
        req.setHeader(QNetworkRequest.UserAgentHeader, user_agent)

        if not cache:
            req.setAttribute(QNetworkRequest.CacheSaveControlAttribute, False)

        return self.get_request(req, **kwargs)
 def __downloadFile(self, url, filename, doneMethod=None):
     """
     Private slot to download the given file.
     
     @param url URL for the download (string)
     @param filename local name of the file (string)
     @param doneMethod method to be called when done
     """
     if self.__networkConfigurationManager.isOnline():
         self.__updateButton.setEnabled(False)
         self.__downloadButton.setEnabled(False)
         self.__downloadInstallButton.setEnabled(False)
         self.__downloadCancelButton.setEnabled(True)
         
         self.statusLabel.setText(url)
         
         self.__doneMethod = doneMethod
         self.__downloadURL = url
         self.__downloadFileName = filename
         self.__downloadIODevice = QFile(self.__downloadFileName + ".tmp")
         self.__downloadCancelled = False
         
         request = QNetworkRequest(QUrl(url))
         request.setAttribute(QNetworkRequest.CacheLoadControlAttribute,
                              QNetworkRequest.AlwaysNetwork)
         reply = self.__networkManager.get(request)
         reply.finished.connect(self.__downloadFileDone)
         reply.downloadProgress.connect(self.__downloadProgress)
         self.__replies.append(reply)
     else:
         E5MessageBox.warning(
             self,
             self.tr("Error downloading file"),
             self.tr(
                 """<p>Could not download the requested file"""
                 """ from {0}.</p><p>Error: {1}</p>"""
             ).format(url, self.tr("Computer is offline.")))
예제 #9
0
    def __getTranslationModels(self):
        """
        Private method to get the translation models supported by IBM Watson
        Language Translator.
        """
        apiKey = self.plugin.getPreferences("IbmKey")
        if not apiKey:
            E5MessageBox.critical(
                self.__ui, self.tr("Error Getting Available Translations"),
                self.tr("A valid IBM Watson Language Translator key is"
                        " required."))
            return
        translatorUrl = self.plugin.getPreferences("IbmUrl")
        if not translatorUrl:
            E5MessageBox.critical(
                self.__ui, self.tr("Error Getting Available Translations"),
                self.tr("A valid IBM Watson Language Translator URL is"
                        " required."))
            return

        params = "?version=2018-05-01"
        url = QUrl(translatorUrl + "/v3/models" + params)

        extraHeaders = [
            (b"Authorization", b"Basic " +
             QByteArray(b"apikey:" + apiKey.encode("utf-8")).toBase64())
        ]

        request = QNetworkRequest(url)
        request.setAttribute(QNetworkRequest.FollowRedirectsAttribute, True)
        if extraHeaders:
            for name, value in extraHeaders:
                request.setRawHeader(name, value)
        reply = self.__networkManager.get(request)
        reply.finished.connect(
            lambda: self.__getTranslationModelsReplyFinished(reply))
        self.__replies.append(reply)
예제 #10
0
    def download(self):
        grab = None
        for x in range(self._tilesRect.width()):
            for y in range(self._tilesRect.height()):
                tp = Point(self._tilesRect.topLeft() + QPoint(x, y))
                if tp not in self._tilePixmaps:
                    grab = QPoint(tp)
                    break

        if grab is None:
            self._url = QUrl()
            return

        path = "http://tile.openstreetmap.org/%d/%d/%d.png" % (
            self.zoom,
            grab.x(),
            grab.y(),
        )
        self._url = QUrl(path)
        request = QNetworkRequest()
        request.setUrl(self._url)
        request.setRawHeader(b"User-Agent", b"Nokia (PyQt) Graphics Dojo 1.0")
        request.setAttribute(QNetworkRequest.User, grab)
        self._manager.get(request)
예제 #11
0
    def requestStarted(self, request):
        # 拦截
        # request.fail(QWebEngineUrlRequestJob.RequestDenied)
        # print('initiator:', request.initiator())
        print('requestMethod:', request.requestMethod())
        print('requestHeaders:', request.requestHeaders())
        url = request.requestUrl()
        if url.scheme().startswith('myurl'):
            url.setScheme(url.scheme().replace('myurl', 'http'))
        print('requestUrl:', url)

        # 构造真实请求
        req = QNetworkRequest(url)
        req.setAttribute(self.AttrType, request)  # 记录
        for headerName, headerValue in request.requestHeaders().items():
            req.setRawHeader(headerName, headerValue)
        method = request.requestMethod()

        # TODO: 这里需要把浏览器内部的cookie获取出来重新设置
        if method == b'GET':
            self._manager.get(req)
        # TODO: 这里貌似没法得到POST的数据,ajax的请求貌似也有问题
        elif method == b'POST':
            self._manager.post(req)
예제 #12
0
 def createRequest(self, enddate, url, path):
     """创建网络请求"""
     req = QNetworkRequest(QUrl(url))
     # 日期
     req.setAttribute(QNetworkRequest.User + 1, enddate)
     # 图片储存路径
     req.setAttribute(QNetworkRequest.User + 2, path)
     # 回调函数用于加载图片显示
     req.setAttribute(QNetworkRequest.User + 3, self._setPixmap)
     return req
예제 #13
0
    def _wrap_request(self, request):
        req = QNetworkRequest(request)
        req_id = next(self._request_ids)
        req.setAttribute(self._REQUEST_ID, req_id)

        if self.disable_browser_caches:
            # disables the network cache
            # see http://doc.qt.io/qt-5/qnetworkrequest.html#CacheLoadControl-enum
            req.setAttribute(QNetworkRequest.CacheLoadControlAttribute, QNetworkRequest.AlwaysNetwork)
            req.setAttribute(QNetworkRequest.CacheSaveControlAttribute, False)

        for attr in ['timeout', 'track_request_body', 'track_response_body']:
            if hasattr(request, attr):
                setattr(req, attr, getattr(request, attr))
        return req, req_id
    def _wrap_request(self, request):
        req = QNetworkRequest(request)
        req_id = next(self._request_ids)
        req.setAttribute(self._REQUEST_ID, req_id)

        if self.disable_browser_caches:
            # disables the network cache
            # see http://doc.qt.io/qt-5/qnetworkrequest.html#CacheLoadControl-enum
            req.setAttribute(QNetworkRequest.CacheLoadControlAttribute,
                             QNetworkRequest.AlwaysNetwork)
            req.setAttribute(QNetworkRequest.CacheSaveControlAttribute, False)

        for attr in ['timeout', 'track_request_body', 'track_response_body']:
            if hasattr(request, attr):
                setattr(req, attr, getattr(request, attr))
        return req, req_id
예제 #15
0
 def _get(self, url):
     """设置图片或者请求网络图片
     :param url:
     """
     if not url:
         self.onError('')
         return
     if url.startswith('http') and not self.loadingTimer.isActive():
         url = QUrl(url)
         request = QNetworkRequest(url)
         request.setHeader(QNetworkRequest.UserAgentHeader, b'CAvatar')
         request.setRawHeader(b'Author', b'Irony')
         request.setAttribute(QNetworkRequest.FollowRedirectsAttribute,
                              True)
         if qApp._network.cache():
             request.setAttribute(QNetworkRequest.CacheLoadControlAttribute,
                                  QNetworkRequest.PreferNetwork)
             request.setAttribute(QNetworkRequest.CacheSaveControlAttribute,
                                  True)
         reply = qApp._network.get(request)
         self.pradius = 0
         self.loadingTimer.start(50)  # 显示进度动画
         reply.finished.connect(self.onFinished)
         reply.error.connect(self.onError)
         return
     self.pradius = 0
     if os.path.exists(url) and os.path.isfile(url):
         if self.isGif:
             self._movie = QMovie(url, parent=self)
             if self._movie.isValid():
                 self._movie.frameChanged.connect(self._resizeGifPixmap)
                 self._movie.start()
         else:
             self._pixmap = QPixmap(url)
             self._resizePixmap()
     else:
         self.onError('')
예제 #16
0
 def get_arrive_time(self, station_id: int):
     self.__url = QUrl(self.time_arrive_pattern.format(station_id))
     request = QNetworkRequest()
     request.setUrl(self.__url)
     request.setAttribute(QNetworkRequest.User, (int, station_id))
     self.__manager.get(request)
예제 #17
0
class NetworkMJPGImage(QQuickPaintedItem):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)

        self._stream_buffer = QByteArray()
        self._stream_buffer_start_index = -1
        self._network_manager = None  # type: QNetworkAccessManager
        self._image_request = None  # type: QNetworkRequest
        self._image_reply = None  # type: QNetworkReply
        self._image = QImage()
        self._image_rect = QRect()

        self._source_url = QUrl()
        self._started = False

        self._mirror = False

        self.setAntialiasing(True)

    ##  Ensure that close gets called when object is destroyed
    def __del__(self) -> None:
        self.stop()

    def paint(self, painter: "QPainter") -> None:
        if self._mirror:
            painter.drawImage(self.contentsBoundingRect(),
                              self._image.mirrored())
            return

        painter.drawImage(self.contentsBoundingRect(), self._image)

    def setSourceURL(self, source_url: "QUrl") -> None:
        self._source_url = source_url
        self.sourceURLChanged.emit()
        if self._started:
            self.start()

    def getSourceURL(self) -> "QUrl":
        return self._source_url

    sourceURLChanged = pyqtSignal()
    source = pyqtProperty(QUrl,
                          fget=getSourceURL,
                          fset=setSourceURL,
                          notify=sourceURLChanged)

    def setMirror(self, mirror: bool) -> None:
        if mirror == self._mirror:
            return
        self._mirror = mirror
        self.mirrorChanged.emit()
        self.update()

    def getMirror(self) -> bool:
        return self._mirror

    mirrorChanged = pyqtSignal()
    mirror = pyqtProperty(bool,
                          fget=getMirror,
                          fset=setMirror,
                          notify=mirrorChanged)

    imageSizeChanged = pyqtSignal()

    @pyqtProperty(int, notify=imageSizeChanged)
    def imageWidth(self) -> int:
        return self._image.width()

    @pyqtProperty(int, notify=imageSizeChanged)
    def imageHeight(self) -> int:
        return self._image.height()

    @pyqtSlot()
    def start(self) -> None:
        self.stop()  # Ensure that previous requests (if any) are stopped.

        if not self._source_url:
            Logger.log("w", "Unable to start camera stream without target!")
            return

        auth_data = ""
        if self._source_url.userInfo():
            # move auth data to basic authorization header
            auth_data = base64.b64encode(
                self._source_url.userInfo().encode()).decode("utf-8")
            authority = self._source_url.authority()
            self._source_url.setAuthority(authority.rsplit("@", 1)[1])

        self._image_request = QNetworkRequest(self._source_url)
        self._image_request.setAttribute(
            QNetworkRequest.FollowRedirectsAttribute, True)

        if auth_data:
            self._image_request.setRawHeader(b"Authorization",
                                             ("basic %s" % auth_data).encode())

        if self._source_url.scheme().lower() == "https":
            # ignore SSL errors (eg for self-signed certificates)
            ssl_configuration = QSslConfiguration.defaultConfiguration()
            ssl_configuration.setPeerVerifyMode(QSslSocket.VerifyNone)
            self._image_request.setSslConfiguration(ssl_configuration)

        if self._network_manager is None:
            self._network_manager = QNetworkAccessManager()

        self._image_reply = self._network_manager.get(self._image_request)
        self._image_reply.downloadProgress.connect(
            self._onStreamDownloadProgress)

        self._started = True

    @pyqtSlot()
    def stop(self) -> None:
        self._stream_buffer = QByteArray()
        self._stream_buffer_start_index = -1

        if self._image_reply:
            try:
                try:
                    self._image_reply.downloadProgress.disconnect(
                        self._onStreamDownloadProgress)
                except Exception:
                    pass

                if not self._image_reply.isFinished():
                    self._image_reply.close()
            except Exception as e:  # RuntimeError
                pass  # It can happen that the wrapped c++ object is already deleted.

            self._image_reply = None
            self._image_request = None

        self._network_manager = None

        self._started = False

    def _onStreamDownloadProgress(self, bytes_received: int,
                                  bytes_total: int) -> None:
        # An MJPG stream is (for our purpose) a stream of concatenated JPG images.
        # JPG images start with the marker 0xFFD8, and end with 0xFFD9
        if self._image_reply is None:
            return
        self._stream_buffer += self._image_reply.readAll()

        if (len(self._stream_buffer) >
                5000000):  # No single camera frame should be 5 MB or larger
            Logger.log(
                "w",
                "MJPEG buffer exceeds reasonable size. Restarting stream...")
            self.stop()  # resets stream buffer and start index
            self.start()
            return

        if self._stream_buffer_start_index == -1:
            self._stream_buffer_start_index = self._stream_buffer.indexOf(
                b"\xff\xd8")
        stream_buffer_end_index = self._stream_buffer.lastIndexOf(b"\xff\xd9")
        # If this happens to be more than a single frame, then so be it; the JPG decoder will
        # ignore the extra data. We do it like this in order not to get a buildup of frames

        if self._stream_buffer_start_index != -1 and stream_buffer_end_index != -1:
            jpg_data = self._stream_buffer[
                self._stream_buffer_start_index:stream_buffer_end_index + 2]
            self._stream_buffer = self._stream_buffer[stream_buffer_end_index +
                                                      2:]
            self._stream_buffer_start_index = -1
            self._image.loadFromData(jpg_data)

            if self._image.rect() != self._image_rect:
                self.imageSizeChanged.emit()

            self.update()
예제 #18
0
class Toolbox(QObject, Extension):

    DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com"
    DEFAULT_CLOUD_API_VERSION = 1

    def __init__(self, parent=None) -> None:
        super().__init__(parent)

        self._application = Application.getInstance()
        self._package_manager = None
        self._plugin_registry = Application.getInstance().getPluginRegistry()

        self._sdk_version = None
        self._cloud_api_version = None
        self._cloud_api_root = None
        self._api_url = None

        # Network:
        self._get_packages_request = None
        self._get_showcase_request = None
        self._download_request = None
        self._download_reply = None
        self._download_progress = 0
        self._is_downloading = False
        self._network_manager = None
        self._request_header = [
            b"User-Agent",
            str.encode("%s/%s (%s %s)" % (
                Application.getInstance().getApplicationName(),
                Application.getInstance().getVersion(),
                platform.system(),
                platform.machine(),
            ))
        ]
        self._request_urls = {}
        self._to_update = []  # Package_ids that are waiting to be updated
        self._old_plugin_ids = []

        # Data:
        self._metadata = {
            "authors": [],
            "packages": [],
            "plugins_showcase": [],
            "plugins_available": [],
            "plugins_installed": [],
            "materials_showcase": [],
            "materials_available": [],
            "materials_installed": []
        }

        # Models:
        self._models = {
            "authors": AuthorsModel(self),
            "packages": PackagesModel(self),
            "plugins_showcase": PackagesModel(self),
            "plugins_available": PackagesModel(self),
            "plugins_installed": PackagesModel(self),
            "materials_showcase": AuthorsModel(self),
            "materials_available": PackagesModel(self),
            "materials_installed": PackagesModel(self)
        }

        # These properties are for keeping track of the UI state:
        # ----------------------------------------------------------------------
        # View category defines which filter to use, and therefore effectively
        # which category is currently being displayed. For example, possible
        # values include "plugin" or "material", but also "installed".
        self._view_category = "plugin"

        # View page defines which type of page layout to use. For example,
        # possible values include "overview", "detail" or "author".
        self._view_page = "loading"

        # Active package refers to which package is currently being downloaded,
        # installed, or otherwise modified.
        self._active_package = None

        self._dialog = None
        self._restart_required = False

        # variables for the license agreement dialog
        self._license_dialog_plugin_name = ""
        self._license_dialog_license_content = ""
        self._license_dialog_plugin_file_location = ""
        self._restart_dialog_message = ""

        Application.getInstance().initializationFinished.connect(
            self._onAppInitialized)

    # Signals:
    # --------------------------------------------------------------------------
    # Downloading changes
    activePackageChanged = pyqtSignal()
    onDownloadProgressChanged = pyqtSignal()
    onIsDownloadingChanged = pyqtSignal()
    restartRequiredChanged = pyqtSignal()
    installChanged = pyqtSignal()
    enabledChanged = pyqtSignal()

    # UI changes
    viewChanged = pyqtSignal()
    detailViewChanged = pyqtSignal()
    filterChanged = pyqtSignal()
    metadataChanged = pyqtSignal()
    showLicenseDialog = pyqtSignal()

    @pyqtSlot(result=str)
    def getLicenseDialogPluginName(self) -> str:
        return self._license_dialog_plugin_name

    @pyqtSlot(result=str)
    def getLicenseDialogPluginFileLocation(self) -> str:
        return self._license_dialog_plugin_file_location

    @pyqtSlot(result=str)
    def getLicenseDialogLicenseContent(self) -> str:
        return self._license_dialog_license_content

    def openLicenseDialog(self, plugin_name: str, license_content: str,
                          plugin_file_location: str) -> None:
        self._license_dialog_plugin_name = plugin_name
        self._license_dialog_license_content = license_content
        self._license_dialog_plugin_file_location = plugin_file_location
        self.showLicenseDialog.emit()

    # This is a plugin, so most of the components required are not ready when
    # this is initialized. Therefore, we wait until the application is ready.
    def _onAppInitialized(self) -> None:
        self._package_manager = Application.getInstance().getPackageManager()
        self._sdk_version = self._getSDKVersion()
        self._cloud_api_version = self._getCloudAPIVersion()
        self._cloud_api_root = self._getCloudAPIRoot()
        self._api_url = "{cloud_api_root}/cura-packages/v{cloud_api_version}/cura/v{sdk_version}".format(
            cloud_api_root=self._cloud_api_root,
            cloud_api_version=self._cloud_api_version,
            sdk_version=self._sdk_version)
        self._request_urls = {
            "authors":
            QUrl("{base_url}/authors".format(base_url=self._api_url)),
            "packages":
            QUrl("{base_url}/packages".format(base_url=self._api_url)),
            "plugins_showcase":
            QUrl("{base_url}/showcase".format(base_url=self._api_url)),
            "plugins_available":
            QUrl("{base_url}/packages?package_type=plugin".format(
                base_url=self._api_url)),
            "materials_showcase":
            QUrl("{base_url}/showcase".format(base_url=self._api_url)),
            "materials_available":
            QUrl("{base_url}/packages?package_type=material".format(
                base_url=self._api_url))
        }

    # Get the API root for the packages API depending on Cura version settings.
    def _getCloudAPIRoot(self) -> str:
        if not hasattr(cura, "CuraVersion"):
            return self.DEFAULT_CLOUD_API_ROOT
        if not hasattr(cura.CuraVersion, "CuraCloudAPIRoot"):
            return self.DEFAULT_CLOUD_API_ROOT
        if not cura.CuraVersion.CuraCloudAPIRoot:
            return self.DEFAULT_CLOUD_API_ROOT
        return cura.CuraVersion.CuraCloudAPIRoot

    # Get the cloud API version from CuraVersion
    def _getCloudAPIVersion(self) -> int:
        if not hasattr(cura, "CuraVersion"):
            return self.DEFAULT_CLOUD_API_VERSION
        if not hasattr(cura.CuraVersion, "CuraCloudAPIVersion"):
            return self.DEFAULT_CLOUD_API_VERSION
        if not cura.CuraVersion.CuraCloudAPIVersion:
            return self.DEFAULT_CLOUD_API_VERSION
        return cura.CuraVersion.CuraCloudAPIVersion

    # Get the packages version depending on Cura version settings.
    def _getSDKVersion(self) -> int:
        if not hasattr(cura, "CuraVersion"):
            return self._plugin_registry.APIVersion
        if not hasattr(cura.CuraVersion, "CuraSDKVersion"):
            return self._plugin_registry.APIVersion
        if not cura.CuraVersion.CuraSDKVersion:
            return self._plugin_registry.APIVersion
        return cura.CuraVersion.CuraSDKVersion

    @pyqtSlot()
    def browsePackages(self) -> None:
        # Create the network manager:
        # This was formerly its own function but really had no reason to be as
        # it was never called more than once ever.
        if self._network_manager:
            self._network_manager.finished.disconnect(self._onRequestFinished)
            self._network_manager.networkAccessibleChanged.disconnect(
                self._onNetworkAccessibleChanged)
        self._network_manager = QNetworkAccessManager()
        self._network_manager.finished.connect(self._onRequestFinished)
        self._network_manager.networkAccessibleChanged.connect(
            self._onNetworkAccessibleChanged)

        # Make remote requests:
        self._makeRequestByType("packages")
        self._makeRequestByType("authors")
        self._makeRequestByType("plugins_showcase")
        self._makeRequestByType("materials_showcase")

        # Gather installed packages:
        self._updateInstalledModels()

        if not self._dialog:
            self._dialog = self._createDialog("Toolbox.qml")
        self._dialog.show()

        # Apply enabled/disabled state to installed plugins
        self.enabledChanged.emit()

    def _createDialog(self, qml_name: str) -> Optional[QObject]:
        Logger.log("d", "Toolbox: Creating dialog [%s].", qml_name)
        path = os.path.join(
            PluginRegistry.getInstance().getPluginPath(self.getPluginId()),
            "resources", "qml", qml_name)
        dialog = Application.getInstance().createQmlComponent(
            path, {"toolbox": self})
        return dialog

    def _convertPluginMetadata(self, plugin: dict) -> dict:
        formatted = {
            "package_id": plugin["id"],
            "package_type": "plugin",
            "display_name": plugin["plugin"]["name"],
            "package_version": plugin["plugin"]["version"],
            "sdk_version": plugin["plugin"]["api"],
            "author": {
                "author_id": plugin["plugin"]["author"],
                "display_name": plugin["plugin"]["author"]
            },
            "is_installed": True,
            "description": plugin["plugin"]["description"]
        }
        return formatted

    @pyqtSlot()
    def _updateInstalledModels(self) -> None:

        # This is moved here to avoid code duplication and so that after installing plugins they get removed from the
        # list of old plugins
        old_plugin_ids = self._plugin_registry.getInstalledPlugins()
        installed_package_ids = self._package_manager.getAllInstalledPackageIDs(
        )
        scheduled_to_remove_package_ids = self._package_manager.getToRemovePackageIDs(
        )

        self._old_plugin_ids = []
        self._old_plugin_metadata = []

        for plugin_id in old_plugin_ids:
            # Neither the installed packages nor the packages that are scheduled to remove are old plugins
            if plugin_id not in installed_package_ids and plugin_id not in scheduled_to_remove_package_ids:
                Logger.log(
                    'i',
                    'Found a plugin that was installed with the old plugin browser: %s',
                    plugin_id)

                old_metadata = self._plugin_registry.getMetaData(plugin_id)
                new_metadata = self._convertPluginMetadata(old_metadata)

                self._old_plugin_ids.append(plugin_id)
                self._old_plugin_metadata.append(new_metadata)

        all_packages = self._package_manager.getAllInstalledPackagesInfo()
        if "plugin" in all_packages:
            self._metadata["plugins_installed"] = all_packages[
                "plugin"] + self._old_plugin_metadata
            self._models["plugins_installed"].setMetadata(
                self._metadata["plugins_installed"])
            self.metadataChanged.emit()
        if "material" in all_packages:
            self._metadata["materials_installed"] = all_packages["material"]
            # TODO: ADD MATERIALS HERE ONCE MATERIALS PORTION OF TOOLBOX IS LIVE
            self._models["materials_installed"].setMetadata(
                self._metadata["materials_installed"])
            self.metadataChanged.emit()

    @pyqtSlot(str)
    def install(self, file_path: str) -> None:
        self._package_manager.installPackage(file_path)
        self.installChanged.emit()
        self._updateInstalledModels()
        self.metadataChanged.emit()
        self._restart_required = True
        self.restartRequiredChanged.emit()

    @pyqtSlot(str)
    def uninstall(self, plugin_id: str) -> None:
        self._package_manager.removePackage(plugin_id, force_add=True)
        self.installChanged.emit()
        self._updateInstalledModels()
        self.metadataChanged.emit()
        self._restart_required = True
        self.restartRequiredChanged.emit()

    ##  Actual update packages that are in self._to_update
    def _update(self) -> None:
        if self._to_update:
            plugin_id = self._to_update.pop(0)
            remote_package = self.getRemotePackage(plugin_id)
            if remote_package:
                download_url = remote_package["download_url"]
                Logger.log("d", "Updating package [%s]..." % plugin_id)
                self.startDownload(download_url)
            else:
                Logger.log(
                    "e",
                    "Could not update package [%s] because there is no remote package info available.",
                    plugin_id)

        if self._to_update:
            self._application.callLater(self._update)

    ##  Update a plugin by plugin_id
    @pyqtSlot(str)
    def update(self, plugin_id: str) -> None:
        self._to_update.append(plugin_id)
        self._application.callLater(self._update)

    @pyqtSlot(str)
    def enable(self, plugin_id: str) -> None:
        self._plugin_registry.enablePlugin(plugin_id)
        self.enabledChanged.emit()
        Logger.log("i", "%s was set as 'active'.", plugin_id)
        self._restart_required = True
        self.restartRequiredChanged.emit()

    @pyqtSlot(str)
    def disable(self, plugin_id: str) -> None:
        self._plugin_registry.disablePlugin(plugin_id)
        self.enabledChanged.emit()
        Logger.log("i", "%s was set as 'deactive'.", plugin_id)
        self._restart_required = True
        self.restartRequiredChanged.emit()

    @pyqtProperty(bool, notify=metadataChanged)
    def dataReady(self) -> bool:
        return self._packages_model is not None

    @pyqtProperty(bool, notify=restartRequiredChanged)
    def restartRequired(self) -> bool:
        return self._restart_required

    @pyqtSlot()
    def restart(self):
        CuraApplication.getInstance().windowClosed()

    def getRemotePackage(self, package_id: str) -> Optional[Dict]:
        # TODO: make the lookup in a dict, not a loop. canUpdate is called for every item.
        remote_package = None
        for package in self._metadata["packages"]:
            if package["package_id"] == package_id:
                remote_package = package
                break
        return remote_package

    # Checks
    # --------------------------------------------------------------------------
    @pyqtSlot(str, result=bool)
    def canUpdate(self, package_id: str) -> bool:
        if self.isOldPlugin(package_id):
            return True

        local_package = self._package_manager.getInstalledPackageInfo(
            package_id)
        if local_package is None:
            return False

        remote_package = self.getRemotePackage(package_id)
        if remote_package is None:
            return False

        local_version = Version(local_package["package_version"])
        remote_version = Version(remote_package["package_version"])
        return remote_version > local_version

    @pyqtSlot(str, result=bool)
    def canDowngrade(self, package_id: str) -> bool:
        # If the currently installed version is higher than the bundled version (if present), the we can downgrade
        # this package.
        local_package = self._package_manager.getInstalledPackageInfo(
            package_id)
        if local_package is None:
            return False

        bundled_package = self._package_manager.getBundledPackageInfo(
            package_id)
        if bundled_package is None:
            return False

        local_version = Version(local_package["package_version"])
        bundled_version = Version(bundled_package["package_version"])
        return bundled_version < local_version

    @pyqtSlot(str, result=bool)
    def isInstalled(self, package_id: str) -> bool:
        return self._package_manager.isPackageInstalled(package_id)

    @pyqtSlot(str, result=bool)
    def isEnabled(self, package_id: str) -> bool:
        if package_id in self._plugin_registry.getActivePlugins():
            return True
        return False

    # Check for plugins that were installed with the old plugin browser
    @pyqtSlot(str, result=bool)
    def isOldPlugin(self, plugin_id: str) -> bool:
        if plugin_id in self._old_plugin_ids:
            return True
        return False

    def loadingComplete(self) -> bool:
        populated = 0
        for list in self._metadata.items():
            if len(list) > 0:
                populated += 1
        if populated == len(self._metadata.items()):
            return True
        return False

    # Make API Calls
    # --------------------------------------------------------------------------
    def _makeRequestByType(self, type: str) -> None:
        Logger.log("i", "Toolbox: Requesting %s metadata from server.", type)
        request = QNetworkRequest(self._request_urls[type])
        request.setRawHeader(*self._request_header)
        self._network_manager.get(request)

    @pyqtSlot(str)
    def startDownload(self, url: str) -> None:
        Logger.log(
            "i", "Toolbox: Attempting to download & install package from %s.",
            url)
        url = QUrl(url)
        self._download_request = QNetworkRequest(url)
        if hasattr(QNetworkRequest, "FollowRedirectsAttribute"):
            # Patch for Qt 5.6-5.8
            self._download_request.setAttribute(
                QNetworkRequest.FollowRedirectsAttribute, True)
        if hasattr(QNetworkRequest, "RedirectPolicyAttribute"):
            # Patch for Qt 5.9+
            self._download_request.setAttribute(
                QNetworkRequest.RedirectPolicyAttribute, True)
        self._download_request.setRawHeader(*self._request_header)
        self._download_reply = self._network_manager.get(
            self._download_request)
        self.setDownloadProgress(0)
        self.setIsDownloading(True)
        self._download_reply.downloadProgress.connect(self._onDownloadProgress)

    @pyqtSlot()
    def cancelDownload(self) -> None:
        Logger.log("i", "Toolbox: User cancelled the download of a plugin.")
        self.resetDownload()

    def resetDownload(self) -> None:
        if self._download_reply:
            try:
                self._download_reply.downloadProgress.disconnect(
                    self._onDownloadProgress)
            except TypeError:  #Raised when the method is not connected to the signal yet.
                pass  #Don't need to disconnect.
            self._download_reply.abort()
        self._download_reply = None
        self._download_request = None
        self.setDownloadProgress(0)
        self.setIsDownloading(False)

    # Handlers for Network Events
    # --------------------------------------------------------------------------
    def _onNetworkAccessibleChanged(
        self, network_accessibility: QNetworkAccessManager.NetworkAccessibility
    ) -> None:
        if network_accessibility == QNetworkAccessManager.NotAccessible:
            self.resetDownload()

    def _onRequestFinished(self, reply: QNetworkReply) -> None:

        if reply.error() == QNetworkReply.TimeoutError:
            Logger.log("w", "Got a timeout.")
            self.setViewPage("errored")
            self.resetDownload()
            return

        if reply.error() == QNetworkReply.HostNotFoundError:
            Logger.log("w", "Unable to reach server.")
            self.setViewPage("errored")
            self.resetDownload()
            return

        if reply.operation() == QNetworkAccessManager.GetOperation:
            for type, url in self._request_urls.items():
                if reply.url() == url:
                    if reply.attribute(
                            QNetworkRequest.HttpStatusCodeAttribute) == 200:
                        try:
                            json_data = json.loads(
                                bytes(reply.readAll()).decode("utf-8"))

                            # Check for errors:
                            if "errors" in json_data:
                                for error in json_data["errors"]:
                                    Logger.log("e", "%s", error["title"])
                                return

                            # Create model and apply metadata:
                            if not self._models[type]:
                                Logger.log("e", "Could not find the %s model.",
                                           type)
                                break

                            # HACK: Eventually get rid of the code from here...
                            if type is "plugins_showcase" or type is "materials_showcase":
                                self._metadata["plugins_showcase"] = json_data[
                                    "data"]["plugin"]["packages"]
                                self._models["plugins_showcase"].setMetadata(
                                    self._metadata["plugins_showcase"])
                                self._metadata[
                                    "materials_showcase"] = json_data["data"][
                                        "material"]["authors"]
                                self._models["materials_showcase"].setMetadata(
                                    self._metadata["materials_showcase"])
                            else:
                                # ...until here.
                                # This hack arises for multiple reasons but the main
                                # one is because there are not separate API calls
                                # for different kinds of showcases.
                                self._metadata[type] = json_data["data"]
                                self._models[type].setMetadata(
                                    self._metadata[type])

                            # Do some auto filtering
                            # TODO: Make multiple API calls in the future to handle this
                            if type is "packages":
                                self._models[type].setFilter(
                                    {"type": "plugin"})
                            if type is "authors":
                                self._models[type].setFilter(
                                    {"package_types": "material"})

                            self.metadataChanged.emit()

                            if self.loadingComplete() is True:
                                self.setViewPage("overview")

                            return
                        except json.decoder.JSONDecodeError:
                            Logger.log(
                                "w", "Toolbox: Received invalid JSON for %s.",
                                type)
                            break
                    else:
                        self.setViewPage("errored")
                        self.resetDownload()
                        return

        else:
            # Ignore any operation that is not a get operation
            pass

    def _onDownloadProgress(self, bytes_sent: int, bytes_total: int) -> None:
        if bytes_total > 0:
            new_progress = bytes_sent / bytes_total * 100
            self.setDownloadProgress(new_progress)
            if bytes_sent == bytes_total:
                self.setIsDownloading(False)
                self._download_reply.downloadProgress.disconnect(
                    self._onDownloadProgress)
                # Must not delete the temporary file on Windows
                self._temp_plugin_file = tempfile.NamedTemporaryFile(
                    mode="w+b", suffix=".curapackage", delete=False)
                file_path = self._temp_plugin_file.name
                # Write first and close, otherwise on Windows, it cannot read the file
                self._temp_plugin_file.write(self._download_reply.readAll())
                self._temp_plugin_file.close()
                self._onDownloadComplete(file_path)

    def _onDownloadComplete(self, file_path: str):
        Logger.log("i", "Toolbox: Download complete.")
        package_info = self._package_manager.getPackageInfo(file_path)
        if not package_info:
            Logger.log(
                "w", "Toolbox: Package file [%s] was not a valid CuraPackage.",
                file_path)
            return

        license_content = self._package_manager.getPackageLicense(file_path)
        if license_content is not None:
            self.openLicenseDialog(package_info["package_id"], license_content,
                                   file_path)
            return

        self.install(file_path)
        return

    # Getter & Setters for Properties:
    # --------------------------------------------------------------------------
    def setDownloadProgress(self, progress: Union[int, float]) -> None:
        if progress != self._download_progress:
            self._download_progress = progress
            self.onDownloadProgressChanged.emit()

    @pyqtProperty(int,
                  fset=setDownloadProgress,
                  notify=onDownloadProgressChanged)
    def downloadProgress(self) -> int:
        return self._download_progress

    def setIsDownloading(self, is_downloading: bool) -> None:
        if self._is_downloading != is_downloading:
            self._is_downloading = is_downloading
            self.onIsDownloadingChanged.emit()

    @pyqtProperty(bool, fset=setIsDownloading, notify=onIsDownloadingChanged)
    def isDownloading(self) -> bool:
        return self._is_downloading

    def setActivePackage(self, package: Dict[str, Any]) -> None:
        self._active_package = package
        self.activePackageChanged.emit()

    @pyqtProperty(QObject, fset=setActivePackage, notify=activePackageChanged)
    def activePackage(self) -> Optional[Dict[str, Any]]:
        return self._active_package

    def setViewCategory(self, category: str = "plugin") -> None:
        self._view_category = category
        self.viewChanged.emit()

    @pyqtProperty(str, fset=setViewCategory, notify=viewChanged)
    def viewCategory(self) -> str:
        return self._view_category

    def setViewPage(self, page: str = "overview") -> None:
        self._view_page = page
        self.viewChanged.emit()

    @pyqtProperty(str, fset=setViewPage, notify=viewChanged)
    def viewPage(self) -> str:
        return self._view_page

    # Expose Models:
    # --------------------------------------------------------------------------
    @pyqtProperty(QObject, notify=metadataChanged)
    def authorsModel(self) -> AuthorsModel:
        return self._models["authors"]

    @pyqtProperty(QObject, notify=metadataChanged)
    def packagesModel(self) -> PackagesModel:
        return self._models["packages"]

    @pyqtProperty(QObject, notify=metadataChanged)
    def pluginsShowcaseModel(self) -> PackagesModel:
        return self._models["plugins_showcase"]

    @pyqtProperty(QObject, notify=metadataChanged)
    def pluginsInstalledModel(self) -> PackagesModel:
        return self._models["plugins_installed"]

    @pyqtProperty(QObject, notify=metadataChanged)
    def materialsShowcaseModel(self) -> PackagesModel:
        return self._models["materials_showcase"]

    @pyqtProperty(QObject, notify=metadataChanged)
    def materialsInstalledModel(self) -> PackagesModel:
        return self._models["materials_installed"]

    # Filter Models:
    # --------------------------------------------------------------------------
    @pyqtSlot(str, str, str)
    def filterModelByProp(self, modelType: str, filterType: str,
                          parameter: str):
        if not self._models[modelType]:
            Logger.log(
                "w",
                "Toolbox: Couldn't filter %s model because it doesn't exist.",
                modelType)
            return
        self._models[modelType].setFilter({filterType: parameter})
        self.filterChanged.emit()

    @pyqtSlot()
    def removeFilters(self, modelType: str):
        if not self._models[modelType]:
            Logger.log(
                "w",
                "Toolbox: Couldn't remove filters on %s model because it doesn't exist.",
                modelType)
            return
        self._models[modelType].setFilter({})
        self.filterChanged.emit()
예제 #19
0
 def createRequest(self, op, request, outgoingData=None):
     """
     Public method to create a request.
     
     @param op the operation to be performed
         (QNetworkAccessManager.Operation)
     @param request reference to the request object (QNetworkRequest)
     @param outgoingData reference to an IODevice containing data to be sent
         (QIODevice)
     @return reference to the created reply object (QNetworkReply)
     """
     scheme = request.url().scheme()
     if scheme == "https" and \
             (not SSL_AVAILABLE or not QSslSocket.supportsSsl()):
         from .NetworkProtocolUnknownErrorReply import \
             NetworkProtocolUnknownErrorReply
         return NetworkProtocolUnknownErrorReply(scheme, self)
     
     import Helpviewer.HelpWindow
     
     if op == QNetworkAccessManager.PostOperation and \
             outgoingData is not None:
         outgoingDataByteArray = outgoingData.peek(1024 * 1024)
         Helpviewer.HelpWindow.HelpWindow.passwordManager().post(
             request, outgoingDataByteArray)
     
     reply = None
     if scheme in self.__schemeHandlers:
         reply = self.__schemeHandlers[scheme]\
                     .createRequest(op, request, outgoingData)
     if reply is not None:
         return reply
     
     # give GreaseMonkey the chance to create a request
     reply = Helpviewer.HelpWindow.HelpWindow.greaseMonkeyManager()\
         .createRequest(op, request, outgoingData)
     if reply is not None:
         return reply
     
     req = QNetworkRequest(request)
     if req.rawHeader(b"X-Eric6-UserLoadAction") == QByteArray(b"1"):
         req.setRawHeader(b"X-Eric6-UserLoadAction", QByteArray())
         req.setAttribute(QNetworkRequest.User + 200, "")
     else:
         req.setAttribute(
             QNetworkRequest.User + 200, req.rawHeader(b"Referer"))
     
     if hasattr(QNetworkRequest, 'HttpPipeliningAllowedAttribute'):
         req.setAttribute(
             QNetworkRequest.HttpPipeliningAllowedAttribute, True)
     if not self.__acceptLanguage.isEmpty():
         req.setRawHeader(b"Accept-Language", self.__acceptLanguage)
     
     # AdBlock code
     if op == QNetworkAccessManager.GetOperation:
         if self.__adblockNetwork is None:
             self.__adblockNetwork = \
                 Helpviewer.HelpWindow.HelpWindow.adBlockManager().network()
         reply = self.__adblockNetwork.block(req)
         if reply is not None:
             reply.setParent(self)
             return reply
     
     # set cache policy
     if op == QNetworkAccessManager.GetOperation:
         urlHost = req.url().host()
         for host in Preferences.getHelp("NoCacheHosts"):
             if host in urlHost:
                 req.setAttribute(
                     QNetworkRequest.CacheLoadControlAttribute,
                     QNetworkRequest.AlwaysNetwork)
                 break
         else:
             req.setAttribute(
                 QNetworkRequest.CacheLoadControlAttribute,
                 Preferences.getHelp("CachePolicy"))
     else:
         req.setAttribute(
             QNetworkRequest.CacheLoadControlAttribute,
             QNetworkRequest.AlwaysNetwork)
     
     # Do Not Track feature
     if self.__doNotTrack:
         req.setRawHeader(b"DNT", b"1")
         req.setRawHeader(b"X-Do-Not-Track", b"1")
     
     # Send referer header?
     if not self.__sendReferer and \
        req.url().host() not in Preferences.getHelp("SendRefererWhitelist"):
         req.setRawHeader(b"Referer", b"")
     
     reply = QNetworkAccessManager.createRequest(
         self, op, req, outgoingData)
     self.requestCreated.emit(op, req, reply)
     
     return reply
예제 #20
0
 def createRequest(self, url, callback):
     """创建网络请求"""
     req = QNetworkRequest(QUrl(url))
     # 回调函数用于加载图片显示
     req.setAttribute(QNetworkRequest.User + 3, callback)
     return req
예제 #21
0
    def _createRequest(
            self,
            http_method: str,
            url: str,
            headers_dict: Optional[Dict[str, str]] = None,
            data: Optional[Union[bytes, bytearray]] = None,
            callback: Optional[Callable[["QNetworkReply"], None]] = None,
            error_callback: Optional[Callable[
                ["QNetworkReply", "QNetworkReply.NetworkError"], None]] = None,
            download_progress_callback: Optional[Callable[[int, int],
                                                          None]] = None,
            upload_progress_callback: Optional[Callable[[int, int],
                                                        None]] = None,
            timeout: Optional[float] = None,
            scope: Optional[HttpRequestScope] = None) -> "HttpRequestData":
        # Sanity checks
        if timeout is not None and timeout <= 0:
            raise ValueError(
                "Timeout must be a positive number if provided, but [%s] was given"
                % timeout)

        request = QNetworkRequest(QUrl(url))

        # Make sure that Qt handles redirects
        if hasattr(QNetworkRequest, "FollowRedirectsAttribute"):
            # Patch for Qt 5.6-5.8
            request.setAttribute(QNetworkRequest.FollowRedirectsAttribute,
                                 True)
        if hasattr(QNetworkRequest, "RedirectPolicyAttribute"):
            # Patch for Qt 5.9+
            request.setAttribute(QNetworkRequest.RedirectPolicyAttribute, True)

        # Set headers
        if headers_dict is not None:
            for key, value in headers_dict.items():
                request.setRawHeader(key.encode("utf-8"),
                                     value.encode("utf-8"))

        if scope is not None:
            scope.requestHook(request)

        # Generate a unique request ID
        request_id = uuid.uuid4().hex

        # Create the request data
        request_data = HttpRequestData(
            request_id,
            http_method=http_method,
            request=request,
            data=data,
            manager_timeout_callback=self._onRequestTimeout,
            callback=callback,
            error_callback=error_callback,
            download_progress_callback=download_progress_callback,
            upload_progress_callback=upload_progress_callback,
            timeout=timeout)

        with self._request_lock:
            self._request_queue.append(request_data)

            # Schedule a call to process pending requests in the queue
            if not self._process_requests_scheduled:
                self.callLater(0, self._processNextRequestsInQueue)
                self._process_requests_scheduled = True

        return request_data
예제 #22
0
class Toolbox(QObject, Extension):
    def __init__(self, parent=None) -> None:
        super().__init__(parent)

        self._application = Application.getInstance()
        self._package_manager = None
        self._plugin_registry = Application.getInstance().getPluginRegistry()
        self._packages_version = self._plugin_registry.APIVersion
        self._api_version = 1
        self._api_url = "https://api-staging.ultimaker.com/cura-packages/v{api_version}/cura/v{package_version}".format( api_version = self._api_version, package_version = self._packages_version)

        # Network:
        self._get_packages_request = None
        self._get_showcase_request = None
        self._download_request = None
        self._download_reply = None
        self._download_progress = 0
        self._is_downloading = False
        self._network_manager = None
        self._request_header = [
            b"User-Agent",
            str.encode(
                "%s/%s (%s %s)" % (
                    Application.getInstance().getApplicationName(),
                    Application.getInstance().getVersion(),
                    platform.system(),
                    platform.machine(),
                )
            )
        ]
        self._request_urls = {
            "authors":            QUrl("{base_url}/authors".format(base_url = self._api_url)),
            "packages":           QUrl("{base_url}/packages".format(base_url = self._api_url)),
            "plugins_showcase":   QUrl("{base_url}/showcase".format(base_url = self._api_url)),
            "materials_showcase": QUrl("{base_url}/showcase".format(base_url = self._api_url))
        }

        # Data:
        self._metadata = {
            "authors": [],
            "packages": [],
            "plugins_showcase": [],
            "plugins_installed": [],
            "materials_showcase": [],
            "materials_installed": []
        }

        # Models:
        self._models = {
            "authors": AuthorsModel(self),
            "packages": PackagesModel(self),
            "plugins_showcase": PackagesModel(self),
            "plugins_available": PackagesModel(self),
            "plugins_installed": PackagesModel(self),
            "materials_showcase": AuthorsModel(self),
            "materials_available": PackagesModel(self),
            "materials_installed": PackagesModel(self)
        }

        # These properties are for keeping track of the UI state:
        # ----------------------------------------------------------------------
        # View category defines which filter to use, and therefore effectively
        # which category is currently being displayed. For example, possible
        # values include "plugin" or "material", but also "installed".
        self._view_category = "plugin"

        # View page defines which type of page layout to use. For example,
        # possible values include "overview", "detail" or "author".
        self._view_page = "loading"

        # Active package refers to which package is currently being downloaded,
        # installed, or otherwise modified.
        self._active_package = None

        self._dialog = None
        self._restart_required = False

        # variables for the license agreement dialog
        self._license_dialog_plugin_name = ""
        self._license_dialog_license_content = ""
        self._license_dialog_plugin_file_location = ""
        self._restart_dialog_message = ""

        Application.getInstance().initializationFinished.connect(self._onAppInitialized)



    # Signals:
    # --------------------------------------------------------------------------
    # Downloading changes
    activePackageChanged = pyqtSignal()
    onDownloadProgressChanged = pyqtSignal()
    onIsDownloadingChanged = pyqtSignal()
    restartRequiredChanged = pyqtSignal()
    installChanged = pyqtSignal()
    enabledChanged = pyqtSignal()

    # UI changes
    viewChanged = pyqtSignal()
    detailViewChanged = pyqtSignal()
    filterChanged = pyqtSignal()
    metadataChanged = pyqtSignal()
    showLicenseDialog = pyqtSignal()

    @pyqtSlot(result = str)
    def getLicenseDialogPluginName(self) -> str:
        return self._license_dialog_plugin_name

    @pyqtSlot(result = str)
    def getLicenseDialogPluginFileLocation(self) -> str:
        return self._license_dialog_plugin_file_location

    @pyqtSlot(result = str)
    def getLicenseDialogLicenseContent(self) -> str:
        return self._license_dialog_license_content

    def openLicenseDialog(self, plugin_name: str, license_content: str, plugin_file_location: str) -> None:
        self._license_dialog_plugin_name = plugin_name
        self._license_dialog_license_content = license_content
        self._license_dialog_plugin_file_location = plugin_file_location
        self.showLicenseDialog.emit()

    # This is a plugin, so most of the components required are not ready when
    # this is initialized. Therefore, we wait until the application is ready.
    def _onAppInitialized(self) -> None:
        self._package_manager = Application.getInstance().getCuraPackageManager()

    @pyqtSlot()
    def browsePackages(self) -> None:
        # Create the network manager:
        # This was formerly its own function but really had no reason to be as
        # it was never called more than once ever.
        if self._network_manager:
            self._network_manager.finished.disconnect(self._onRequestFinished)
            self._network_manager.networkAccessibleChanged.disconnect(self._onNetworkAccessibleChanged)
        self._network_manager = QNetworkAccessManager()
        self._network_manager.finished.connect(self._onRequestFinished)
        self._network_manager.networkAccessibleChanged.connect(self._onNetworkAccessibleChanged)

        # Make remote requests:
        self._makeRequestByType("packages")
        self._makeRequestByType("authors")
        self._makeRequestByType("plugins_showcase")
        self._makeRequestByType("materials_showcase")

        # Gather installed packages:
        self._updateInstalledModels()

        if not self._dialog:
            self._dialog = self._createDialog("Toolbox.qml")
        self._dialog.show()

        # Apply enabled/disabled state to installed plugins
        self.enabledChanged.emit()

    def _createDialog(self, qml_name: str) -> Optional[QObject]:
        Logger.log("d", "Toolbox: Creating dialog [%s].", qml_name)
        path = os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "resources", "qml", qml_name)
        dialog = Application.getInstance().createQmlComponent(path, {"toolbox": self})
        return dialog

    @pyqtSlot()
    def _updateInstalledModels(self) -> None:
        all_packages = self._package_manager.getAllInstalledPackagesInfo()
        if "plugin" in all_packages:
            self._metadata["plugins_installed"] = all_packages["plugin"]
            self._models["plugins_installed"].setMetadata(self._metadata["plugins_installed"])
            self.metadataChanged.emit()
        if "material" in all_packages:
            self._metadata["materials_installed"] = all_packages["material"]
            self._models["materials_installed"].setMetadata(self._metadata["materials_installed"])
            self.metadataChanged.emit()

    @pyqtSlot(str)
    def install(self, file_path: str) -> None:
        self._package_manager.installPackage(file_path)
        self.installChanged.emit()
        self._updateInstalledModels()
        self.metadataChanged.emit()
        self._restart_required = True
        self.restartRequiredChanged.emit()

    @pyqtSlot(str)
    def uninstall(self, plugin_id: str) -> None:
        self._package_manager.removePackage(plugin_id)
        self.installChanged.emit()
        self._updateInstalledModels()
        self.metadataChanged.emit()
        self._restart_required = True
        self.restartRequiredChanged.emit()

    @pyqtSlot(str)
    def enable(self, plugin_id: str) -> None:
        self._plugin_registry.enablePlugin(plugin_id)
        self.enabledChanged.emit()
        Logger.log("i", "%s was set as 'active'.", plugin_id)
        self._restart_required = True
        self.restartRequiredChanged.emit()

    @pyqtSlot(str)
    def disable(self, plugin_id: str) -> None:
        self._plugin_registry.disablePlugin(plugin_id)
        self.enabledChanged.emit()
        Logger.log("i", "%s was set as 'deactive'.", plugin_id)
        self._restart_required = True
        self.restartRequiredChanged.emit()

    @pyqtProperty(bool, notify = metadataChanged)
    def dataReady(self) -> bool:
        return self._packages_model is not None

    @pyqtProperty(bool, notify = restartRequiredChanged)
    def restartRequired(self) -> bool:
        return self._restart_required

    @pyqtSlot()
    def restart(self):
        self._package_manager._removeAllScheduledPackages()
        CuraApplication.getInstance().windowClosed()

    # Checks
    # --------------------------------------------------------------------------
    @pyqtSlot(str, result = bool)
    def canUpdate(self, package_id: str) -> bool:
        local_package = self._package_manager.getInstalledPackageInfo(package_id)
        if local_package is None:
            return False

        remote_package = None
        for package in self._metadata["packages"]:
            if package["package_id"] == package_id:
                remote_package = package
        if remote_package is None:
            return False

        local_version = local_package["package_version"]
        remote_version = remote_package["package_version"]
        return Version(remote_version) > Version(local_version)

    @pyqtSlot(str, result = bool)
    def isInstalled(self, package_id: str) -> bool:
        return self._package_manager.isPackageInstalled(package_id)

    @pyqtSlot(str, result = bool)
    def isEnabled(self, package_id: str) -> bool:
        if package_id in self._plugin_registry.getActivePlugins():
            return True
        return False

    def loadingComplete(self) -> bool:
        populated = 0
        for list in self._metadata.items():
            if len(list) > 0:
                populated += 1
        if populated == len(self._metadata.items()):
            return True
        return False

    # Make API Calls
    # --------------------------------------------------------------------------
    def _makeRequestByType(self, type: str) -> None:
        Logger.log("i", "Toolbox: Requesting %s metadata from server.", type)
        request = QNetworkRequest(self._request_urls[type])
        request.setRawHeader(*self._request_header)
        self._network_manager.get(request)

    @pyqtSlot(str)
    def startDownload(self, url: str) -> None:
        Logger.log("i", "Toolbox: Attempting to download & install package from %s.", url)
        url = QUrl(url)
        self._download_request = QNetworkRequest(url)
        if hasattr(QNetworkRequest, "FollowRedirectsAttribute"):
            # Patch for Qt 5.6-5.8
            self._download_request.setAttribute(QNetworkRequest.FollowRedirectsAttribute, True)
        if hasattr(QNetworkRequest, "RedirectPolicyAttribute"):
            # Patch for Qt 5.9+
            self._download_request.setAttribute(QNetworkRequest.RedirectPolicyAttribute, True)
        self._download_request.setRawHeader(*self._request_header)
        self._download_reply = self._network_manager.get(self._download_request)
        self.setDownloadProgress(0)
        self.setIsDownloading(True)
        self._download_reply.downloadProgress.connect(self._onDownloadProgress)

    @pyqtSlot()
    def cancelDownload(self) -> None:
        Logger.log("i", "Toolbox: User cancelled the download of a plugin.")
        self.resetDownload()

    def resetDownload(self) -> None:
        if self._download_reply:
            self._download_reply.abort()
            self._download_reply.downloadProgress.disconnect(self._onDownloadProgress)
        self._download_reply = None
        self._download_request = None
        self.setDownloadProgress(0)
        self.setIsDownloading(False)

    # Handlers for Network Events
    # --------------------------------------------------------------------------
    def _onNetworkAccessibleChanged(self, accessible: int) -> None:
        if accessible == 0:
            self.resetDownload()

    def _onRequestFinished(self, reply: QNetworkReply) -> None:

        if reply.error() == QNetworkReply.TimeoutError:
            Logger.log("w", "Got a timeout.")
            self.setViewPage("errored")
            self.resetDownload()
            return

        if reply.error() == QNetworkReply.HostNotFoundError:
            Logger.log("w", "Unable to reach server.")
            self.setViewPage("errored")
            self.resetDownload()
            return

        if reply.operation() == QNetworkAccessManager.GetOperation:
            for type, url in self._request_urls.items():
                if reply.url() == url:
                    try:
                        json_data = json.loads(bytes(reply.readAll()).decode("utf-8"))

                        # Check for errors:
                        if "errors" in json_data:
                            for error in json_data["errors"]:
                                Logger.log("e", "%s", error["title"])
                            return

                        # Create model and apply metadata:
                        if not self._models[type]:
                            Logger.log("e", "Could not find the %s model.", type)
                            break

                        # HACK: Eventually get rid of the code from here...
                        if type is "plugins_showcase" or type is "materials_showcase":
                            self._metadata["plugins_showcase"] = json_data["data"]["plugin"]["packages"]
                            self._models["plugins_showcase"].setMetadata(self._metadata["plugins_showcase"])
                            self._metadata["materials_showcase"] = json_data["data"]["material"]["authors"]
                            self._models["materials_showcase"].setMetadata(self._metadata["materials_showcase"])
                        else:
                            # ...until here.
                            # This hack arises for multiple reasons but the main
                            # one is because there are not separate API calls
                            # for different kinds of showcases.
                            self._metadata[type] = json_data["data"]
                            self._models[type].setMetadata(self._metadata[type])

                        # Do some auto filtering
                        # TODO: Make multiple API calls in the future to handle this
                        if type is "packages":
                            self._models[type].setFilter({"type": "plugin"})
                        if type is "authors":
                            self._models[type].setFilter({"package_types": "material"})

                        self.metadataChanged.emit()

                        if self.loadingComplete() is True:
                            self.setViewPage("overview")

                        return
                    except json.decoder.JSONDecodeError:
                        Logger.log("w", "Toolbox: Received invalid JSON for %s.", type)
                        break

        else:
            # Ignore any operation that is not a get operation
            pass

    def _onDownloadProgress(self, bytes_sent: int, bytes_total: int) -> None:
        if bytes_total > 0:
            new_progress = bytes_sent / bytes_total * 100
            self.setDownloadProgress(new_progress)
            if bytes_sent == bytes_total:
                self.setIsDownloading(False)
                self._download_reply.downloadProgress.disconnect(self._onDownloadProgress)
                # Must not delete the temporary file on Windows
                self._temp_plugin_file = tempfile.NamedTemporaryFile(mode = "w+b", suffix = ".curapackage", delete = False)
                file_path = self._temp_plugin_file.name
                # Write first and close, otherwise on Windows, it cannot read the file
                self._temp_plugin_file.write(self._download_reply.readAll())
                self._temp_plugin_file.close()
                self._onDownloadComplete(file_path)

    def _onDownloadComplete(self, file_path: str):
        Logger.log("i", "Toolbox: Download complete.")
        package_info = self._package_manager.getPackageInfo(file_path)
        if not package_info:
            Logger.log("w", "Toolbox: Package file [%s] was not a valid CuraPackage.", file_path)
            return

        license_content = self._package_manager.getPackageLicense(file_path)
        if license_content is not None:
            self.openLicenseDialog(package_info["package_id"], license_content, file_path)
            return

        self.install(file_path)
        return

    # Getter & Setters for Properties:
    # --------------------------------------------------------------------------
    def setDownloadProgress(self, progress: Union[int, float]) -> None:
        if progress != self._download_progress:
            self._download_progress = progress
            self.onDownloadProgressChanged.emit()

    @pyqtProperty(int, fset = setDownloadProgress, notify = onDownloadProgressChanged)
    def downloadProgress(self) -> int:
        return self._download_progress

    def setIsDownloading(self, is_downloading: bool) -> None:
        if self._is_downloading != is_downloading:
            self._is_downloading = is_downloading
            self.onIsDownloadingChanged.emit()

    @pyqtProperty(bool, fset = setIsDownloading, notify = onIsDownloadingChanged)
    def isDownloading(self) -> bool:
        return self._is_downloading

    def setActivePackage(self, package: Dict[str, Any]) -> None:
        self._active_package = package
        self.activePackageChanged.emit()

    @pyqtProperty(QObject, fset = setActivePackage, notify = activePackageChanged)
    def activePackage(self) -> Optional[Dict[str, Any]]:
        return self._active_package

    def setViewCategory(self, category: str = "plugin") -> None:
        self._view_category = category
        self.viewChanged.emit()

    @pyqtProperty(str, fset = setViewCategory, notify = viewChanged)
    def viewCategory(self) -> str:
        return self._view_category

    def setViewPage(self, page: str = "overview") -> None:
        self._view_page = page
        self.viewChanged.emit()

    @pyqtProperty(str, fset = setViewPage, notify = viewChanged)
    def viewPage(self) -> str:
        return self._view_page



    # Expose Models:
    # --------------------------------------------------------------------------
    @pyqtProperty(QObject, notify = metadataChanged)
    def authorsModel(self) -> AuthorsModel:
        return self._models["authors"]

    @pyqtProperty(QObject, notify = metadataChanged)
    def packagesModel(self) -> PackagesModel:
        return self._models["packages"]

    @pyqtProperty(QObject, notify = metadataChanged)
    def pluginsShowcaseModel(self) -> PackagesModel:
        return self._models["plugins_showcase"]

    @pyqtProperty(QObject, notify = metadataChanged)
    def pluginsInstalledModel(self) -> PackagesModel:
        return self._models["plugins_installed"]

    @pyqtProperty(QObject, notify = metadataChanged)
    def materialsShowcaseModel(self) -> PackagesModel:
        return self._models["materials_showcase"]

    @pyqtProperty(QObject, notify = metadataChanged)
    def materialsInstalledModel(self) -> PackagesModel:
        return self._models["materials_installed"]



    # Filter Models:
    # --------------------------------------------------------------------------
    @pyqtSlot(str, str, str)
    def filterModelByProp(self, modelType: str, filterType: str, parameter: str):
        if not self._models[modelType]:
            Logger.log("w", "Toolbox: Couldn't filter %s model because it doesn't exist.", modelType)
            return
        self._models[modelType].setFilter({ filterType: parameter })
        self.filterChanged.emit()

    @pyqtSlot()
    def removeFilters(self, modelType: str):
        if not self._models[modelType]:
            Logger.log("w", "Toolbox: Couldn't remove filters on %s model because it doesn't exist.", modelType)
            return
        self._models[modelType].setFilter({})
        self.filterChanged.emit()
예제 #23
0
class ProcessDialog(QDialog):
    def __init__(self, port, **kwargs):
        super().__init__()

        self.setWindowTitle('Cooking your TinyGS station...')
        self.setFixedWidth(400)

        self.exception = None

        esptool.sw.progress.connect(self.update_progress)

        self.nam = QNetworkAccessManager()
        self.nrBinFile = QNetworkRequest()
        self.bin_data = b''

        self.setLayout(VLayout(5, 5))
        self.actions_layout = QFormLayout()
        self.actions_layout.setSpacing(5)

        self.layout().addLayout(self.actions_layout)

        self._actions = []
        self._action_widgets = {}

        self.port = port

        self.auto_reset = kwargs.get('auto_reset', False)

        self.file_path = kwargs.get('file_path')
        if self.file_path and self.file_path.startswith('http'):
            self._actions.append('download')

        self.backup = kwargs.get('backup')
        if self.backup:
            self._actions.append('backup')
            self.backup_size = kwargs.get('backup_size')

        self.erase = kwargs.get('erase')
        if self.erase:
            self._actions.append('erase')

        if self.file_path:
            self._actions.append('write')

        self.create_ui()
        self.start_process()

    def create_ui(self):
        for action in self._actions:
            pb = QProgressBar()
            pb.setFixedHeight(35)
            self._action_widgets[action] = pb
            self.actions_layout.addRow(action.capitalize(), pb)

        self.btns = QDialogButtonBox(QDialogButtonBox.Abort)
        self.btns.rejected.connect(self.abort)
        self.layout().addWidget(self.btns)

        self.sb = QStatusBar()
        self.layout().addWidget(self.sb)

    def appendBinFile(self):
        self.bin_data += self.bin_reply.readAll()

    def saveBinFile(self):
        if self.bin_reply.error() == QNetworkReply.NoError:
            self.file_path = self.file_path.split('/')[-1]
            with open(self.file_path, 'wb') as f:
                f.write(self.bin_data)
            self.run_esp()
        else:
            raise NetworkError

    def updateBinProgress(self, recv, total):
        self._action_widgets['download'].setValue(recv // total * 100)

    def download_bin(self):
        self.nrBinFile.setAttribute(QNetworkRequest.FollowRedirectsAttribute,
                                    True)
        self.nrBinFile.setUrl(QUrl(self.file_path))
        self.bin_reply = self.nam.get(self.nrBinFile)
        self.bin_reply.readyRead.connect(self.appendBinFile)
        self.bin_reply.downloadProgress.connect(self.updateBinProgress)
        self.bin_reply.finished.connect(self.saveBinFile)

    def show_connection_state(self, state):
        self.sb.showMessage(state, 0)

    def run_esp(self):
        params = {
            'file_path': self.file_path,
            'auto_reset': self.auto_reset,
            'erase': self.erase
        }

        if self.backup:
            backup_size = f'0x{2 ** self.backup_size}00000'
            params['backup_size'] = backup_size

        self.esp_thread = QThread()
        self.esp = ESPWorker(self.port, self._actions, **params)
        esptool.sw.connection_state.connect(self.show_connection_state)
        self.esp.waiting.connect(self.wait_for_user)
        self.esp.done.connect(self.accept)
        self.esp.error.connect(self.error)
        self.esp.moveToThread(self.esp_thread)
        self.esp_thread.started.connect(self.esp.run)
        self.esp_thread.start()

    def start_process(self):
        if 'download' in self._actions:
            self.download_bin()
            self._actions = self._actions[1:]
        else:
            self.run_esp()

    def update_progress(self, action, value):
        self._action_widgets[action].setValue(value)

    @pyqtSlot()
    def wait_for_user(self):
        dlg = QMessageBox.information(
            self, 'User action required',
            'Please power cycle the device, wait a moment and press OK',
            QMessageBox.Ok | QMessageBox.Cancel)
        if dlg == QMessageBox.Ok:
            self.esp.continue_ok()
        elif dlg == QMessageBox.Cancel:
            self.esp.abort()
            self.esp.continue_ok()
            self.abort()

    def stop_thread(self):
        self.esp_thread.wait(2000)
        self.esp_thread.exit()

    def accept(self):
        self.stop_thread()
        self.done(QDialog.Accepted)

    def abort(self):
        self.sb.showMessage('Aborting...', 0)
        QApplication.processEvents()
        self.esp.abort()
        self.stop_thread()
        self.reject()

    def error(self, e):
        self.exception = e
        self.abort()

    def closeEvent(self, e):
        self.stop_thread()
예제 #24
0
class ProcessDialog(QDialog):
    def __init__(self, port, **kwargs):
        super().__init__()

        self.setWindowTitle('Preparando seu Satélite Educacional...')
        self.setFixedWidth(600)

        self.exception = None

        esptool.sw.progress.connect(self.update_progress)

        self.nam = QNetworkAccessManager()

        self.nrDownloads = 0

        self.nrBinFile1 = QNetworkRequest()
        self.bin_data1 = b''

        self.nrBinFile2 = QNetworkRequest()
        self.bin_data2 = b''

        self.nrBinFile3 = QNetworkRequest()
        self.bin_data3 = b''

        self.nrBinFile4 = QNetworkRequest()
        self.bin_data4 = b''

        self.setLayout(VLayout(5, 5))
        self.actions_layout = QFormLayout()
        self.actions_layout.setSpacing(5)

        self.layout().addLayout(self.actions_layout)

        self._actions = []
        self._action_widgets = {}

        self.port = port

        self.file_path = kwargs.get('file_url')
        self.file_pathBoot = kwargs.get('file_urlBoot')
        self.file_pathBootloader = kwargs.get('file_urlBootloader')
        self.file_pathPart = kwargs.get('file_urlPart')

        self._actions.append('download')

        self.erase = kwargs.get('erase')
        if self.erase:
            self._actions.append('erase')

        if self.file_path:
            self._actions.append('write')

        self.create_ui()
        self.start_process()

    def create_ui(self):
        for action in self._actions:
            pb = QProgressBar()
            pb.setFixedHeight(35)
            self._action_widgets[action] = pb
            self.actions_layout.addRow(action.capitalize(), pb)

        self.btns = QDialogButtonBox(QDialogButtonBox.Abort)
        self.btns.rejected.connect(self.abort)
        self.layout().addWidget(self.btns)

        self.sb = QStatusBar()
        self.layout().addWidget(self.sb)

    def appendBinFile1(self):
        self.bin_data1 += self.bin_reply1.readAll()

    def appendBinFile2(self):
        self.bin_data2 += self.bin_reply2.readAll()

    def appendBinFile3(self):
        self.bin_data3 += self.bin_reply3.readAll()

    def appendBinFile4(self):
        self.bin_data4 += self.bin_reply4.readAll()

    def downloadsFinished(self):
        if self.nrDownloads == 4:
            self.run_esp()

    def saveBinFile1(self):
        if self.bin_reply1.error() == QNetworkReply.NoError:
            self.file_path = self.file_path.split('/')[-1]
            with open(self.file_path, 'wb') as f:
                f.write(self.bin_data1)
            self.nrDownloads += 1
            self.downloadsFinished()
        else:
            raise NetworkError

    def saveBinFile2(self):
        if self.bin_reply2.error() == QNetworkReply.NoError:
            self.file_pathBoot = self.file_pathBoot.split('/')[-1]
            with open(self.file_pathBoot, 'wb') as f:
                f.write(self.bin_data2)
            self.nrDownloads += 1
            self.downloadsFinished()
        else:
            raise NetworkError

    def saveBinFile3(self):
        if self.bin_reply3.error() == QNetworkReply.NoError:
            self.file_pathBootloader = self.file_pathBootloader.split('/')[-1]
            with open(self.file_pathBootloader, 'wb') as f:
                f.write(self.bin_data3)
            self.nrDownloads += 1
            self.downloadsFinished()
        else:
            raise NetworkError

    def saveBinFile4(self):
        if self.bin_reply4.error() == QNetworkReply.NoError:
            self.file_pathPart = self.file_pathPart.split('/')[-1]
            with open(self.file_pathPart, 'wb') as f:
                f.write(self.bin_data4)
            self.nrDownloads += 1
            self.downloadsFinished()
        else:
            raise NetworkError

    def updateBinProgress(self, recv, total):
        self._action_widgets['download'].setValue(recv // total * 100)

    def download_bin(self):
        self.nrBinFile1.setAttribute(QNetworkRequest.FollowRedirectsAttribute,
                                     True)
        self.nrBinFile1.setUrl(QUrl(self.file_path))
        self.bin_reply1 = self.nam.get(self.nrBinFile1)
        self.bin_reply1.readyRead.connect(self.appendBinFile1)
        self.bin_reply1.downloadProgress.connect(self.updateBinProgress)
        self.bin_reply1.finished.connect(self.saveBinFile1)

        self.nrBinFile2.setAttribute(QNetworkRequest.FollowRedirectsAttribute,
                                     True)
        self.nrBinFile2.setUrl(QUrl(self.file_pathBoot))
        self.bin_reply2 = self.nam.get(self.nrBinFile2)
        self.bin_reply2.readyRead.connect(self.appendBinFile2)
        self.bin_reply2.finished.connect(self.saveBinFile2)

        self.nrBinFile3.setAttribute(QNetworkRequest.FollowRedirectsAttribute,
                                     True)
        self.nrBinFile3.setUrl(QUrl(self.file_pathBootloader))
        self.bin_reply3 = self.nam.get(self.nrBinFile3)
        self.bin_reply3.readyRead.connect(self.appendBinFile3)
        self.bin_reply3.finished.connect(self.saveBinFile3)

        self.nrBinFile4.setAttribute(QNetworkRequest.FollowRedirectsAttribute,
                                     True)
        self.nrBinFile4.setUrl(QUrl(self.file_pathPart))
        self.bin_reply4 = self.nam.get(self.nrBinFile4)
        self.bin_reply4.readyRead.connect(self.appendBinFile4)
        self.bin_reply4.finished.connect(self.saveBinFile4)

    def show_connection_state(self, state):
        self.sb.showMessage(state, 0)

    def run_esp(self):
        params = {
            'file_path': self.file_path,
            'file_pathBoot': self.file_pathBoot,
            'file_pathBootloader': self.file_pathBootloader,
            'file_pathPart': self.file_pathPart,
            'erase': self.erase
        }

        self.esp_thread = QThread()
        self.esp = ESPWorker(self.port, self._actions, **params)
        esptool.sw.connection_state.connect(self.show_connection_state)
        self.esp.done.connect(self.accept)
        self.esp.error.connect(self.error)
        self.esp.moveToThread(self.esp_thread)
        self.esp_thread.started.connect(self.esp.run)
        self.esp_thread.start()

    def start_process(self):
        if 'download' in self._actions:
            self.download_bin()
            self._actions = self._actions[1:]
        else:
            self.run_esp()

    def update_progress(self, action, value):
        self._action_widgets[action].setValue(value)

    @pyqtSlot()
    def stop_thread(self):
        self.esp_thread.wait(2000)
        self.esp_thread.exit()

    def accept(self):
        self.stop_thread()
        self.done(QDialog.Accepted)

    def abort(self):
        self.sb.showMessage('Aborting...', 0)
        QApplication.processEvents()
        self.esp.abort()
        self.stop_thread()
        self.reject()

    def error(self, e):
        self.exception = e
        self.abort()

    def closeEvent(self, e):
        self.stop_thread()
예제 #25
0
    def createRequest(self, op, request, outgoingData=None):
        """
        Public method to create a request.
        
        @param op the operation to be performed
            (QNetworkAccessManager.Operation)
        @param request reference to the request object (QNetworkRequest)
        @param outgoingData reference to an IODevice containing data to be sent
            (QIODevice)
        @return reference to the created reply object (QNetworkReply)
        """
        scheme = request.url().scheme()
        if scheme == "https" and \
                (not SSL_AVAILABLE or not QSslSocket.supportsSsl()):
            from .NetworkProtocolUnknownErrorReply import \
                NetworkProtocolUnknownErrorReply
            return NetworkProtocolUnknownErrorReply(scheme, self)

        import Helpviewer.HelpWindow

        if op == QNetworkAccessManager.PostOperation and \
                outgoingData is not None:
            outgoingDataByteArray = outgoingData.peek(1024 * 1024)
            Helpviewer.HelpWindow.HelpWindow.passwordManager().post(
                request, outgoingDataByteArray)

        reply = None
        if scheme in self.__schemeHandlers:
            reply = self.__schemeHandlers[scheme]\
                        .createRequest(op, request, outgoingData)
        if reply is not None:
            return reply

        # give GreaseMonkey the chance to create a request
        reply = Helpviewer.HelpWindow.HelpWindow.greaseMonkeyManager()\
            .createRequest(op, request, outgoingData)
        if reply is not None:
            return reply

        req = QNetworkRequest(request)
        if req.rawHeader(b"X-Eric6-UserLoadAction") == QByteArray(b"1"):
            req.setRawHeader(b"X-Eric6-UserLoadAction", QByteArray())
            req.setAttribute(QNetworkRequest.User + 200, "")
        else:
            req.setAttribute(QNetworkRequest.User + 200,
                             req.rawHeader(b"Referer"))

        if hasattr(QNetworkRequest, 'HttpPipeliningAllowedAttribute'):
            req.setAttribute(QNetworkRequest.HttpPipeliningAllowedAttribute,
                             True)
        if not self.__acceptLanguage.isEmpty():
            req.setRawHeader(b"Accept-Language", self.__acceptLanguage)

        # AdBlock code
        if op == QNetworkAccessManager.GetOperation:
            if self.__adblockNetwork is None:
                self.__adblockNetwork = \
                    Helpviewer.HelpWindow.HelpWindow.adBlockManager().network()
            reply = self.__adblockNetwork.block(req)
            if reply is not None:
                reply.setParent(self)
                return reply

        # set cache policy
        if op == QNetworkAccessManager.GetOperation:
            urlHost = req.url().host()
            for host in Preferences.getHelp("NoCacheHosts"):
                if host in urlHost:
                    req.setAttribute(QNetworkRequest.CacheLoadControlAttribute,
                                     QNetworkRequest.AlwaysNetwork)
                    break
            else:
                req.setAttribute(QNetworkRequest.CacheLoadControlAttribute,
                                 Preferences.getHelp("CachePolicy"))
        else:
            req.setAttribute(QNetworkRequest.CacheLoadControlAttribute,
                             QNetworkRequest.AlwaysNetwork)

        # Do Not Track feature
        if self.__doNotTrack:
            req.setRawHeader(b"DNT", b"1")
            req.setRawHeader(b"X-Do-Not-Track", b"1")

        # Send referer header?
        if not self.__sendReferer and \
           req.url().host() not in Preferences.getHelp("SendRefererWhitelist"):
            req.setRawHeader(b"Referer", b"")

        reply = QNetworkAccessManager.createRequest(self, op, req,
                                                    outgoingData)
        self.requestCreated.emit(op, req, reply)

        return reply
예제 #26
0
    def GM_xmlhttpRequest(self, details):
        # we could actually mock a XMLHttpRequest to support progress
        # signals but who really uses them?
        # https://wiki.greasespot.net/GM_xmlhttpRequest
        # qtwebchannel.js calls JSON.stringify in QWebChannel.send() so any
        # method attributes of arguments (eg {'onload':function(...){...;}) are
        # stripped.
        # * handle method, url, headers, data
        # * figure out what headers we need to automatically set (referer, ...)
        # * can we use some qt thing (page.get()?) to do ^
        # * should probably check how cookies are handled
        #   chrome/tampermonkey sends cookies (for the requested domain,
        #   duh) with GM_xhr requests
        # https://openuserjs.org/
        # https://greasyfork.org/en/scripts

        # tampermoney on chrome prompts when a script tries to do a
        # cross-origin request.
        print("==============================================")
        print("GM_xmlhttpRequest")
        print(details)

        if not 'url' in details:
            return

        request_index = details['_qute_gm_request_index']
        if not request_index:
            log.greasemonkey.error(("GM_xmlhttpRequest received request "
                                    "without nonce, skipping."))
            return

        if objreg.get('host-blocker').is_blocked(QUrl(details['url'])):
            return

        # TODO: url might be relative, need to fix on the JS side.
        request = QNetworkRequest(QUrl(details['url']))
        request.setOriginatingObject(self)
        # The C++ docs say the default is to not follow any redirects.
        request.setAttribute(QNetworkRequest.RedirectionTargetAttribute,
                             QNetworkRequest.NoLessSafeRedirectPolicy)
        # TODO: Ensure these headers are encoded to spec if containing eg
        # unicodes
        if 'headers' in details:
            for k, v in details['headers'].items():
                # With this script: https://raw.githubusercontent.com/evazion/translate-pixiv-tags/master/translate-pixiv-tags.user.js
                # One of the headers it 'X-Twitter-Polling': True, which was
                # causing the below to error out because v is a bool. Not sure
                # where that is coming from or what value twitter expects.
                # That script is patching jquery so try with unpatched jquery
                # and see what it does.
                request.setRawHeader(k.encode('ascii'), str(v).encode('ascii'))

        # TODO: Should we allow xhr to set user-agent?
        if not request.header(QNetworkRequest.UserAgentHeader):
            request.setHeader(QNetworkRequest.UserAgentHeader,
                              self.profile.httpUserAgent())

        payload = details.get('data', None)
        if payload:
            # Should check encoding from content-type header?
            payload = payload.encode('utf-8')

        reply = self.nam.sendCustomRequest(
            request,
            details.get('method', 'GET').encode('ascii'), payload)

        if reply.isFinished():
            self.handle_xhr_reply(reply, request_index)
        else:
            reply.finished.connect(
                functools.partial(self.handle_xhr_reply, reply, request_index))
예제 #27
0
    def execute_request(self, url, **kwargs):
        """
        Uses QgsNetworkAccessManager and QgsAuthManager.
        """
        method = kwargs.get('http_method', 'get')

        headers = kwargs.get('headers', {})
        # This fixes a weird error with compressed content not being correctly
        # inflated.
        # If you set the header on the QNetworkRequest you are basically telling
        # QNetworkAccessManager "I know what I'm doing, please don't do any content
        # encoding processing".
        # See: https://bugs.webkit.org/show_bug.cgi?id=63696#c1
        try:
            del headers[b'Accept-Encoding']
        except KeyError as ke:
            # only debugging here as after 1st remove it isn't there anymore
            self.util.msg_log_debug(
                u'unexpected error deleting request header: {}'.format(ke))
            pass

        # Avoid double quoting form QUrl
        url = unquote(url)

        self.util.msg_log_debug(u'http_call request: {} {}'.format(
            method, url))

        class Response:
            status_code = 200
            status_message = 'OK'
            text = ''
            ok = True
            headers = {}
            reason = ''
            exception = None

            def iter_content(self, _):
                return [self.text]

        self.http_call_result = Response()
        url = self.util.remove_newline(url)

        req = QNetworkRequest()
        req.setUrl(QUrl(url))
        req.setAttribute(QNetworkRequest.FollowRedirectsAttribute, True)

        for k, v in headers.items():
            self.util.msg_log_debug("%s: %s" % (k, v))
            try:
                req.setRawHeader(k, v)
            except:
                self.util.msg_log_error(
                    u'FAILED to set header: {} => {}'.format(k, v))
                self.util.msg_log_last_exception()
        if self.settings.authcfg:
            self.util.msg_log_debug(u'before updateNetworkRequest')
            QgsApplication.authManager().updateNetworkRequest(
                req, self.settings.authcfg)
            self.util.msg_log_debug(u'before updateNetworkRequest')

        if self.reply is not None and self.reply.isRunning():
            self.reply.close()

        self.util.msg_log_debug(u'getting QgsNetworkAccessManager.instance()')
        #func = getattr(QgsNetworkAccessManager.instance(), method)
        #func = QgsNetworkAccessManager().get(req)

        #manager = QNetworkAccessManager()
        #event = QEventLoop()
        #response = manager.get(QNetworkRequest(QUrl(url)))
        #response.downloadProgress.connect(self.download_progress)
        #response.finished.connect(event.quit)
        #event.exec()
        #response_msg = response.readAll()
        ##response_msg = str(response_msg)
        #response_msg = str(response_msg, encoding='utf-8')
        ##response_msg = response_msg.decode('utf-8')
        #response.deleteLater()
        #self.util.msg_log_debug(u'response message:\n{} ...'.format(response_msg[:255]))
        #self.http_call_result.text = response_msg  # in Python3 all strings are unicode, so QString is not defined
        #return self.http_call_result

        # Calling the server ...
        self.util.msg_log_debug('before self.reply = func(req)')
        #self.reply = func(req)
        #self.reply = QgsNetworkAccessManager.instance().get(req)
        method_call = getattr(QgsNetworkAccessManager.instance(), method)
        self.reply = method_call(req)
        #self.reply.setReadBufferSize(1024*1024*1024)
        #self.reply.setReadBufferSize(1024 * 1024 * 1024 * 1024)
        self.reply.setReadBufferSize(0)
        self.util.msg_log_debug('after self.reply = func(req)')

        # Let's log the whole call for debugging purposes:
        if self.settings.debug:
            self.util.msg_log_debug("\nSending %s request to %s" %
                                    (method.upper(), req.url().toString()))
            headers = {
                str(h): str(req.rawHeader(h))
                for h in req.rawHeaderList()
            }
            for k, v in headers.items():
                try:
                    self.util.msg_log_debug("%s: %s" % (k, v))
                except:
                    self.util.msg_log_debug('error logging headers')

        if self.settings.authcfg:
            self.util.msg_log_debug("update reply w/ authcfg: {0}".format(
                self.settings.authcfg))
            QgsApplication.authManager().updateNetworkReply(
                self.reply, self.settings.authcfg)

        self.util.msg_log_debug('before connecting to events')

        # connect downloadProgress event
        try:
            self.reply.downloadProgress.connect(self.download_progress)
            #pass
        except:
            self.util.msg_log_error(
                'error connecting "downloadProgress" event')
            self.util.msg_log_last_exception()

        # connect reply finished event
        try:
            self.reply.finished.connect(self.reply_finished)
            #pass
        except:
            self.util.msg_log_error(
                'error connecting reply "finished" progress event')
            self.util.msg_log_last_exception()
        self.util.msg_log_debug('after connecting to events')

        # Call and block
        self.event_loop = QEventLoop()
        try:
            self.reply.finished.connect(self.event_loop.quit)
        except:
            self.util.msg_log_error(
                'error connecting reply "finished" progress event to event loop quit'
            )
            self.util.msg_log_last_exception()

        self.mb_downloaded = 0
        # Catch all exceptions (and clean up requests)
        self.event_loop.exec()

        # Let's log the whole response for debugging purposes:
        if self.settings.debug:
            self.util.msg_log_debug(
                u'\nGot response [{}/{}] ({} bytes) from:\n{}\nexception:{}'.
                format(self.http_call_result.status_code,
                       self.http_call_result.status_message,
                       len(self.http_call_result.text),
                       self.reply.url().toString(),
                       self.http_call_result.exception))
            headers = {
                str(h): str(self.reply.rawHeader(h))
                for h in self.reply.rawHeaderList()
            }
            for k, v in headers.items():
                self.util.msg_log_debug("%s: %s" % (k, v))
            self.util.msg_log_debug("Payload :\n%s ......" %
                                    self.http_call_result.text[:255])

        self.reply.close()
        self.util.msg_log_debug("Deleting reply ...")
        try:
            self.reply.deleteLater()
        except:
            self.util.msg_log_error('unexpected error deleting QNetworkReply')
            self.util.msg_log_last_exception()

        self.reply = None

        if self.http_call_result.exception is not None:
            self.util.msg_log_error('http_call_result.exception is not None')
            self.http_call_result.ok = False
            # raise self.http_call_result.exception
        return self.http_call_result