Beispiel #1
0
def test_disconnectAll():
    test = SignalReceiver()

    signal = Signal(type=Signal.Direct)
    signal.connect(test.slot)
    signal.disconnectAll()
    signal.emit()
    assert test.getEmitCount() == 0
def test_disconnectAll():
    test = SignalReceiver()

    signal = Signal(type=Signal.Direct)
    signal.connect(test.slot)
    signal.disconnectAll()
    signal.emit()
    assert test.getEmitCount() == 0
Beispiel #3
0
def test_disconnectWhilePostponed():
    test = SignalReceiver()

    signal = Signal(type=Signal.Direct)
    signal.connect(test.slot)
    with postponeSignals(signal):
        signal.disconnect(test.slot)  # This won't do anything, as we're postponing at the moment!
        signal.disconnectAll()  # Same holds true for the disconnect all
        signal.emit()
    assert test.getEmitCount() == 1  # Despite attempting to disconnect, this didn't happen because of the postpone
def test_disconnectWhilePostponed():
    test = SignalReceiver()

    signal = Signal(type=Signal.Direct)
    signal.connect(test.slot)
    with postponeSignals(signal):
        signal.disconnect(
            test.slot
        )  # This won't do anything, as we're postponing at the moment!
        signal.disconnectAll()  # Same holds true for the disconnect all
        signal.emit()
    assert test.getEmitCount(
    ) == 1  # Despite attempting to disconnect, this didn't happen because of the postpone
Beispiel #5
0
class DownloadPresenter:

    DISK_WRITE_BUFFER_SIZE = 256 * 1024  # 256 KB

    def __init__(self, app: CuraApplication) -> None:
        # Emits (Dict[str, str], List[str]) # (success_items, error_items)
        # Dict{success_package_id, temp_file_path}
        # List[errored_package_id]
        self.done = Signal()

        self._app = app
        self._scope = UltimakerCloudScope(app)

        self._started = False
        self._progress_message = self._createProgressMessage()
        self._progress = {
        }  # type: Dict[str, Dict[str, Any]] # package_id, Dict
        self._error = []  # type: List[str] # package_id

    def download(self, model: SubscribedPackagesModel) -> None:
        if self._started:
            Logger.error("Download already started. Create a new %s instead",
                         self.__class__.__name__)
            return

        manager = HttpRequestManager.getInstance()
        for item in model.items:
            package_id = item["package_id"]

            def finishedCallback(reply: QNetworkReply, pid=package_id) -> None:
                self._onFinished(pid, reply)

            def progressCallback(rx: int, rt: int, pid=package_id) -> None:
                self._onProgress(pid, rx, rt)

            def errorCallback(reply: QNetworkReply,
                              error: QNetworkReply.NetworkError,
                              pid=package_id) -> None:
                self._onError(pid)

            request_data = manager.get(
                item["download_url"],
                callback=finishedCallback,
                download_progress_callback=progressCallback,
                error_callback=errorCallback,
                scope=self._scope)

            self._progress[package_id] = {
                "received": 0,
                "total":
                1,  # make sure this is not considered done yet. Also divByZero-safe
                "file_written": None,
                "request_data": request_data,
                "package_model": item
            }

        self._started = True
        self._progress_message.show()

    def abort(self) -> None:
        manager = HttpRequestManager.getInstance()
        for item in self._progress.values():
            manager.abortRequest(item["request_data"])

    # Aborts all current operations and returns a copy with the same settings such as app and scope
    def resetCopy(self) -> "DownloadPresenter":
        self.abort()
        self.done.disconnectAll()
        return DownloadPresenter(self._app)

    def _createProgressMessage(self) -> Message:
        return Message(i18n_catalog.i18nc("@info:generic", "Syncing..."),
                       lifetime=0,
                       use_inactivity_timer=False,
                       progress=0.0,
                       title=i18n_catalog.i18nc(
                           "@info:title",
                           "Changes detected from your Ultimaker account",
                       ))

    def _onFinished(self, package_id: str, reply: QNetworkReply) -> None:
        self._progress[package_id]["received"] = self._progress[package_id][
            "total"]

        try:
            with tempfile.NamedTemporaryFile(mode="wb+",
                                             suffix=".curapackage",
                                             delete=False) as temp_file:
                bytes_read = reply.read(self.DISK_WRITE_BUFFER_SIZE)
                while bytes_read:
                    temp_file.write(bytes_read)
                    bytes_read = reply.read(self.DISK_WRITE_BUFFER_SIZE)
                    self._app.processEvents()
                self._progress[package_id]["file_written"] = temp_file.name
        except IOError as e:
            Logger.logException(
                "e", "Failed to write downloaded package to temp file", e)
            self._onError(package_id)
        temp_file.close()

        self._checkDone()

    def _onProgress(self, package_id: str, rx: int, rt: int) -> None:
        self._progress[package_id]["received"] = rx
        self._progress[package_id]["total"] = rt

        received = 0
        total = 0
        for item in self._progress.values():
            received += item["received"]
            total += item["total"]

        self._progress_message.setProgress(100.0 *
                                           (received / total))  # [0 .. 100] %

    def _onError(self, package_id: str) -> None:
        self._progress.pop(package_id)
        self._error.append(package_id)
        self._checkDone()

    def _checkDone(self) -> bool:
        for item in self._progress.values():
            if not item["file_written"]:
                return False

        success_items = {
            package_id: {
                "package_path": value["file_written"],
                "icon_url": value["package_model"]["icon_url"]
            }
            for package_id, value in self._progress.items()
        }
        error_items = [package_id for package_id in self._error]

        self._progress_message.hide()
        self.done.emit(success_items, error_items)
        return True
Beispiel #6
0
class LicensePresenter(QObject):
    """Presents licenses for a set of packages for the user to accept or reject.

    Call present() exactly once to show a licenseDialog for a set of packages
    Before presenting another set of licenses, create a new instance using resetCopy().

    licenseAnswers emits a list of Dicts containing answers when the user has made a choice for all provided packages.
    """
    def __init__(self, app: CuraApplication) -> None:
        super().__init__()
        self._presented = False
        """Whether present() has been called and state is expected to be initialized"""
        self._catalog = i18nCatalog("cura")
        self._dialog = None  # type: Optional[QObject]
        self._package_manager = app.getPackageManager()  # type: PackageManager
        # Emits List[Dict[str, [Any]] containing for example
        # [{ "package_id": "BarbarianPlugin", "package_path" : "/tmp/dg345as", "accepted" : True }]
        self.licenseAnswers = Signal()

        self._current_package_idx = 0
        self._package_models = []  # type: List[Dict]
        decline_button_text = self._catalog.i18nc(
            "@button", "Decline and remove from account")
        self._license_model = LicenseModel(
            decline_button_text=decline_button_text)  # type: LicenseModel
        self._page_count = 0

        self._app = app

        self._compatibility_dialog_path = "resources/qml/dialogs/ToolboxLicenseDialog.qml"

    def present(self, plugin_path: str,
                packages: Dict[str, Dict[str, str]]) -> None:
        """Show a license dialog for multiple packages where users can read a license and accept or decline them

        :param plugin_path: Root directory of the Toolbox plugin
        :param packages: Dict[package id, file path]
        """
        if self._presented:
            Logger.error("{clazz} is single-use. Create a new {clazz} instead",
                         clazz=self.__class__.__name__)
            return

        path = os.path.join(plugin_path, self._compatibility_dialog_path)

        self._initState(packages)

        if self._page_count == 0:
            self.licenseAnswers.emit(self._package_models)
            return

        if self._dialog is None:

            context_properties = {
                "catalog": self._catalog,
                "licenseModel": self._license_model,
                "handler": self
            }
            self._dialog = self._app.createQmlComponent(
                path, context_properties)
        self._presentCurrentPackage()
        self._presented = True

    def resetCopy(self) -> "LicensePresenter":
        """Clean up and return a new copy with the same settings such as app"""
        if self._dialog:
            self._dialog.close()
        self.licenseAnswers.disconnectAll()
        return LicensePresenter(self._app)

    @pyqtSlot()
    def onLicenseAccepted(self) -> None:
        self._package_models[self._current_package_idx]["accepted"] = True
        self._checkNextPage()

    @pyqtSlot()
    def onLicenseDeclined(self) -> None:
        self._package_models[self._current_package_idx]["accepted"] = False
        self._checkNextPage()

    def _initState(self, packages: Dict[str, Dict[str, Any]]) -> None:

        implicitly_accepted_count = 0

        for package_id, item in packages.items():
            item["package_id"] = package_id
            item["licence_content"] = self._package_manager.getPackageLicense(
                item["package_path"])
            if item["licence_content"] is None:
                # Implicitly accept when there is no license
                item["accepted"] = True
                implicitly_accepted_count = implicitly_accepted_count + 1
                self._package_models.append(item)
            else:
                item["accepted"] = None  #: None: no answer yet
                # When presenting the packages, we want to show packages which have a license first.
                # In fact, we don't want to show the others at all because they are implicitly accepted
                self._package_models.insert(0, item)
            CuraApplication.getInstance().processEvents()
        self._page_count = len(
            self._package_models) - implicitly_accepted_count
        self._license_model.setPageCount(self._page_count)

    def _presentCurrentPackage(self) -> None:
        package_model = self._package_models[self._current_package_idx]
        package_info = self._package_manager.getPackageInfo(
            package_model["package_path"])

        self._license_model.setCurrentPageIdx(self._current_package_idx)
        self._license_model.setPackageName(package_info["display_name"])
        self._license_model.setIconUrl(package_model["icon_url"])
        self._license_model.setLicenseText(package_model["licence_content"])
        if self._dialog:
            self._dialog.open()  # Does nothing if already open

    def _checkNextPage(self) -> None:
        if self._current_package_idx + 1 < self._page_count:
            self._current_package_idx += 1
            self._presentCurrentPackage()
        else:
            if self._dialog:
                self._dialog.close()
            self.licenseAnswers.emit(self._package_models)