Skip to content

Commit

Permalink
Merge pull request #3 from Ghostkeeper/kaleidoscopeit-master
Browse files Browse the repository at this point in the history
Implement version upgrade for Deltacomb printers
  • Loading branch information
kaleidoscopeit authored Apr 10, 2020
2 parents f61051e + 2e12cc1 commit 0fa8286
Show file tree
Hide file tree
Showing 1,780 changed files with 82,331 additions and 81,696 deletions.
38 changes: 24 additions & 14 deletions cura/Backups/BackupsManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,37 @@
from cura.CuraApplication import CuraApplication


## The BackupsManager is responsible for managing the creating and restoring of
# back-ups.
#
# Back-ups themselves are represented in a different class.
class BackupsManager:
"""
The BackupsManager is responsible for managing the creating and restoring of
back-ups.
Back-ups themselves are represented in a different class.
"""

def __init__(self, application: "CuraApplication") -> None:
self._application = application

## Get a back-up of the current configuration.
# \return A tuple containing a ZipFile (the actual back-up) and a dict
# containing some metadata (like version).
def createBackup(self) -> Tuple[Optional[bytes], Optional[Dict[str, str]]]:
"""
Get a back-up of the current configuration.
:return: A tuple containing a ZipFile (the actual back-up) and a dict containing some metadata (like version).
"""

self._disableAutoSave()
backup = Backup(self._application)
backup.makeFromCurrent()
self._enableAutoSave()
# We don't return a Backup here because we want plugins only to interact with our API and not full objects.
return backup.zip_file, backup.meta_data

## Restore a back-up from a given ZipFile.
# \param zip_file A bytes object containing the actual back-up.
# \param meta_data A dict containing some metadata that is needed to
# restore the back-up correctly.
def restoreBackup(self, zip_file: bytes, meta_data: Dict[str, str]) -> None:
"""
Restore a back-up from a given ZipFile.
:param zip_file: A bytes object containing the actual back-up.
:param meta_data: A dict containing some metadata that is needed to restore the back-up correctly.
"""

if not meta_data.get("cura_release", None):
# If there is no "cura_release" specified in the meta data, we don't execute a backup restore.
Logger.log("w", "Tried to restore a backup without specifying a Cura version number.")
Expand All @@ -48,18 +55,21 @@ def restoreBackup(self, zip_file: bytes, meta_data: Dict[str, str]) -> None:
# We don't want to store the data at this point as that would override the just-restored backup.
self._application.windowClosed(save_data = False)

## Here we try to disable the auto-save plug-in as it might interfere with
# restoring a back-up.
def _disableAutoSave(self) -> None:
"""Here we (try to) disable the saving as it might interfere with restoring a back-up."""

self._application.enableSave(False)
auto_save = self._application.getAutoSave()
# The auto save is only not created if the application has not yet started.
if auto_save:
auto_save.setEnabled(False)
else:
Logger.log("e", "Unable to disable the autosave as application init has not been completed")

## Re-enable auto-save after we're done.
def _enableAutoSave(self) -> None:
"""Re-enable auto-save and other saving after we're done."""

self._application.enableSave(True)
auto_save = self._application.getAutoSave()
# The auto save is only not created if the application has not yet started.
if auto_save:
Expand Down
2 changes: 1 addition & 1 deletion cura/BuildVolume.py
Original file line number Diff line number Diff line change
Expand Up @@ -1128,7 +1128,7 @@ def _clamp(self, value, min_value, max_value):
_skirt_settings = ["adhesion_type", "skirt_gap", "skirt_line_count", "skirt_brim_line_width", "brim_width", "brim_line_count", "raft_margin", "draft_shield_enabled", "draft_shield_dist", "initial_layer_line_width_factor"]
_raft_settings = ["adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", "raft_surface_thickness", "raft_airgap", "layer_0_z_overlap"]
_extra_z_settings = ["retraction_hop_enabled", "retraction_hop"]
_prime_settings = ["extruder_prime_pos_x", "extruder_prime_pos_y", "extruder_prime_pos_z", "prime_blob_enable"]
_prime_settings = ["extruder_prime_pos_x", "extruder_prime_pos_y", "prime_blob_enable"]
_tower_settings = ["prime_tower_enable", "prime_tower_size", "prime_tower_position_x", "prime_tower_position_y", "prime_tower_brim_enable"]
_ooze_shield_settings = ["ooze_shield_enabled", "ooze_shield_dist"]
_distance_settings = ["infill_wipe_dist", "travel_avoid_distance", "support_offset", "support_enable", "travel_avoid_other_parts", "travel_avoid_supports"]
Expand Down
10 changes: 8 additions & 2 deletions cura/CuraApplication.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ class CuraApplication(QtApplication):
# SettingVersion represents the set of settings available in the machine/extruder definitions.
# You need to make sure that this version number needs to be increased if there is any non-backwards-compatible
# changes of the settings.
SettingVersion = 11
SettingVersion = 13

Created = False

Expand Down Expand Up @@ -242,6 +242,7 @@ def __init__(self, *args, **kwargs):

# Backups
self._auto_save = None # type: Optional[AutoSave]
self._enable_save = True

self._container_registry_class = CuraContainerRegistry
# Redefined here in order to please the typing.
Expand Down Expand Up @@ -685,15 +686,20 @@ def messageBoxClosed(self, button):
self._message_box_callback = None
self._message_box_callback_arguments = []

def enableSave(self, enable: bool):
self._enable_save = enable

# Cura has multiple locations where instance containers need to be saved, so we need to handle this differently.
def saveSettings(self) -> None:
if not self.started:
if not self.started or not self._enable_save:
# Do not do saving during application start or when data should not be saved on quit.
return
ContainerRegistry.getInstance().saveDirtyContainers()
self.savePreferences()

def saveStack(self, stack):
if not self._enable_save:
return
ContainerRegistry.getInstance().saveContainer(stack)

@pyqtSlot(str, result = QUrl)
Expand Down
10 changes: 8 additions & 2 deletions cura/PlatformPhysics.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Copyright (c) 2015 Ultimaker B.V.
# Copyright (c) 2020 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.

from PyQt5.QtCore import QTimer
from shapely.errors import TopologicalError # To capture errors if Shapely messes up.

from UM.Application import Application
from UM.Logger import Logger
from UM.Scene.SceneNode import SceneNode
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
from UM.Math.Vector import Vector
Expand Down Expand Up @@ -136,7 +138,11 @@ def _onChangeTimerFinished(self):
own_convex_hull = node.callDecoration("getConvexHull")
other_convex_hull = other_node.callDecoration("getConvexHull")
if own_convex_hull and other_convex_hull:
overlap = own_convex_hull.translate(move_vector.x, move_vector.z).intersectsPolygon(other_convex_hull)
try:
overlap = own_convex_hull.translate(move_vector.x, move_vector.z).intersectsPolygon(other_convex_hull)
except TopologicalError as e: # Can happen if the convex hull is degenerate?
Logger.warning("Got a topological error when calculating convex hull intersection: {err}".format(err = str(e)))
overlap = False
if overlap: # Moving ensured that overlap was still there. Try anew!
temp_move_vector = move_vector.set(x = move_vector.x + overlap[0] * self._move_factor,
z = move_vector.z + overlap[1] * self._move_factor)
Expand Down
4 changes: 4 additions & 0 deletions cura/PrinterOutput/FirmwareUpdater.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ def updateFirmware(self, firmware_file: Union[str, QUrl]) -> None:
else:
self._firmware_file = firmware_file

if self._firmware_file == "":
self._setFirmwareUpdateState(FirmwareUpdateState.firmware_not_found_error)
return

self._setFirmwareUpdateState(FirmwareUpdateState.updating)

self._update_firmware_thread.start()
Expand Down
5 changes: 4 additions & 1 deletion cura/Settings/MachineManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,10 @@ def removeMachine(self, machine_id: str) -> None:
if other_machine_stacks:
self.setActiveMachine(other_machine_stacks[0]["id"])

metadata = CuraContainerRegistry.getInstance().findContainerStacksMetadata(id = machine_id)[0]
metadatas = CuraContainerRegistry.getInstance().findContainerStacksMetadata(id = machine_id)
if not metadatas:
return # machine_id doesn't exist. Nothing to remove.
metadata = metadatas[0]
ExtruderManager.getInstance().removeMachineExtruders(machine_id)
containers = CuraContainerRegistry.getInstance().findInstanceContainersMetadata(type = "user", machine = machine_id)
for container in containers:
Expand Down
5 changes: 4 additions & 1 deletion plugins/3MFReader/ThreeMFReader.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ def _createMatrixFromTransformationString(self, transformation: str) -> Matrix:
# \returns Scene node.
def _convertSavitarNodeToUMNode(self, savitar_node: Savitar.SceneNode, file_name: str = "") -> Optional[SceneNode]:
self._object_count += 1
node_name = "Object %s" % self._object_count

node_name = savitar_node.getName()
if node_name == "":
node_name = "Object %s" % self._object_count

active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate

Expand Down
29 changes: 19 additions & 10 deletions plugins/3MFWriter/ThreeMFWorkspaceWriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from UM.Preferences import Preferences
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Workspace.WorkspaceWriter import WorkspaceWriter
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")

from cura.Utils.Threading import call_on_qt_thread

Expand All @@ -26,6 +28,7 @@ def write(self, stream, nodes, mode=WorkspaceWriter.OutputMode.BinaryMode):
mesh_writer = application.getMeshFileHandler().getWriter("3MFWriter")

if not mesh_writer: # We need to have the 3mf mesh writer, otherwise we can't save the entire workspace
self.setInformation(catalog.i18nc("@error:zip", "3MF Writer plug-in is corrupt."))
Logger.error("3MF Writer class is unavailable. Can't write workspace.")
return False

Expand All @@ -39,19 +42,24 @@ def write(self, stream, nodes, mode=WorkspaceWriter.OutputMode.BinaryMode):

global_stack = machine_manager.activeMachine

# Add global container stack data to the archive.
self._writeContainerToArchive(global_stack, archive)

# Also write all containers in the stack to the file
for container in global_stack.getContainers():
self._writeContainerToArchive(container, archive)
try:
# Add global container stack data to the archive.
self._writeContainerToArchive(global_stack, archive)

# Check if the machine has extruders and save all that data as well.
for extruder_stack in global_stack.extruders.values():
self._writeContainerToArchive(extruder_stack, archive)
for container in extruder_stack.getContainers():
# Also write all containers in the stack to the file
for container in global_stack.getContainers():
self._writeContainerToArchive(container, archive)

# Check if the machine has extruders and save all that data as well.
for extruder_stack in global_stack.extruders.values():
self._writeContainerToArchive(extruder_stack, archive)
for container in extruder_stack.getContainers():
self._writeContainerToArchive(container, archive)
except PermissionError:
self.setInformation(catalog.i18nc("@error:zip", "No permission to write the workspace here."))
Logger.error("No permission to write workspace to this stream.")
return False

# Write preferences to archive
original_preferences = Application.getInstance().getPreferences() #Copy only the preferences that we use to the workspace.
temp_preferences = Preferences()
Expand Down Expand Up @@ -81,6 +89,7 @@ def write(self, stream, nodes, mode=WorkspaceWriter.OutputMode.BinaryMode):
# Close the archive & reset states.
archive.close()
except PermissionError:
self.setInformation(catalog.i18nc("@error:zip", "No permission to write the workspace here."))
Logger.error("No permission to write workspace to this stream.")
return False
mesh_writer.setStoreArchive(False)
Expand Down
1 change: 1 addition & 0 deletions plugins/3MFWriter/ThreeMFWriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ def _convertUMNodeToSavitarNode(self, um_node, transformation = Matrix()):
return

savitar_node = Savitar.SceneNode()
savitar_node.setName(um_node.getName())

node_matrix = um_node.getLocalTransformation()

Expand Down
25 changes: 20 additions & 5 deletions plugins/CuraDrive/src/CreateBackupJob.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
from datetime import datetime
from typing import Any, Dict, Optional

from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
import sentry_sdk
from PyQt5.QtNetwork import QNetworkReply

from UM.Job import Job
from UM.Logger import Logger
Expand Down Expand Up @@ -90,13 +91,27 @@ def _requestUploadSlot(self, backup_metadata: Dict[str, Any], backup_size: int)
scope = self._json_cloud_scope)

def _onUploadSlotCompleted(self, reply: QNetworkReply, error: Optional["QNetworkReply.NetworkError"] = None) -> None:
if error is not None:
Logger.warning(str(error))
if HttpRequestManager.safeHttpStatus(reply) >= 300:
replyText = HttpRequestManager.readText(reply)
Logger.warning("Could not request backup upload: %s", replyText)
self.backup_upload_error_message = self.DEFAULT_UPLOAD_ERROR_MESSAGE

if HttpRequestManager.safeHttpStatus(reply) == 400:
errors = json.loads(replyText)["errors"]
if "moreThanMaximum" in [error["code"] for error in errors if error["meta"] and error["meta"]["field_name"] == "backup_size"]:
if self._backup_zip is None: # will never happen; keep mypy happy
zip_error = "backup is None."
else:
zip_error = "{} exceeds max size.".format(str(len(self._backup_zip)))
sentry_sdk.capture_message("backup failed: {}".format(zip_error), level ="warning")
self.backup_upload_error_message = catalog.i18nc("@error:file_size", "The backup exceeds the maximum file size.")
from sentry_sdk import capture_message

self._job_done.set()
return
if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) >= 300:
Logger.warning("Could not request backup upload: %s", HttpRequestManager.readText(reply))

if error is not None:
Logger.warning("Could not request backup upload: %s", HttpRequestManager.qt_network_error_name(error))
self.backup_upload_error_message = self.DEFAULT_UPLOAD_ERROR_MESSAGE
self._job_done.set()
return
Expand Down
36 changes: 28 additions & 8 deletions plugins/ImageReader/ImageReaderUI.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,19 +92,30 @@ def _createConfigUI(self):
def onOkButtonClicked(self):
self._cancelled = False
self._ui_view.close()
self._ui_lock.release()
try:
self._ui_lock.release()
except RuntimeError:
# We don't really care if it was held or not. Just make sure it's not held now
pass

@pyqtSlot()
def onCancelButtonClicked(self):
self._cancelled = True
self._ui_view.close()
self._ui_lock.release()
try:
self._ui_lock.release()
except RuntimeError:
# We don't really care if it was held or not. Just make sure it's not held now
pass

@pyqtSlot(str)
def onWidthChanged(self, value):
if self._ui_view and not self._disable_size_callbacks:
if len(value) > 0:
self._width = float(value.replace(",", "."))
try:
self._width = float(value.replace(",", "."))
except ValueError: # Can happen with incomplete numbers, such as "-".
self._width = 0
else:
self._width = 0

Expand All @@ -117,7 +128,10 @@ def onWidthChanged(self, value):
def onDepthChanged(self, value):
if self._ui_view and not self._disable_size_callbacks:
if len(value) > 0:
self._depth = float(value.replace(",", "."))
try:
self._depth = float(value.replace(",", "."))
except ValueError: # Can happen with incomplete numbers, such as "-".
self._depth = 0
else:
self._depth = 0

Expand All @@ -128,15 +142,21 @@ def onDepthChanged(self, value):

@pyqtSlot(str)
def onBaseHeightChanged(self, value):
if (len(value) > 0):
self.base_height = float(value.replace(",", "."))
if len(value) > 0:
try:
self.base_height = float(value.replace(",", "."))
except ValueError: # Can happen with incomplete numbers, such as "-".
self.base_height = 0
else:
self.base_height = 0

@pyqtSlot(str)
def onPeakHeightChanged(self, value):
if (len(value) > 0):
self.peak_height = float(value.replace(",", "."))
if len(value) > 0:
try:
self.peak_height = float(value.replace(",", "."))
except ValueError: # Can happen with incomplete numbers, such as "-".
self._width = 0
else:
self.peak_height = 0

Expand Down
Loading

0 comments on commit 0fa8286

Please sign in to comment.