Просмотр исходного кода

Solved merge conflict. CURA-3321

Jack Ha 8 лет назад
Родитель
Сommit
42d939b34e

+ 3 - 0
CMakeLists.txt

@@ -11,6 +11,9 @@ add_custom_target(tests)
 add_custom_command(TARGET tests POST_BUILD COMMAND "PYTHONPATH=${CMAKE_SOURCE_DIR}/../Uranium/:${CMAKE_SOURCE_DIR}" ${PYTHON_EXECUTABLE} -m pytest -r a --junitxml=${CMAKE_BINARY_DIR}/junit.xml ${CMAKE_SOURCE_DIR} || exit 0)
 
 option(CURA_DEBUGMODE "Enable debug dialog and other debug features" OFF)
+if(CURA_DEBUGMODE)
+    set(_cura_debugmode "ON")
+endif()
 
 set(CURA_VERSION "master" CACHE STRING "Version name of Cura")
 set(CURA_BUILDTYPE "" CACHE STRING "Build type of Cura, eg. 'PPA'")

+ 1 - 0
README.md

@@ -50,6 +50,7 @@ Third party plugins
 * [X3G Writer](https://github.com/Ghostkeeper/X3GWriter): Adds support for exporting X3G files.
 * [Auto orientation](https://github.com/nallath/CuraOrientationPlugin): Calculate the optimal orientation for a model.
 * [OctoPrint Plugin](https://github.com/fieldofview/OctoPrintPlugin): Send printjobs directly to OctoPrint and monitor their progress in Cura.
+* [WirelessPrinting Plugin](https://github.com/probonopd/WirelessPrinting): Print wirelessly from Cura to your 3D printer connected to an ESP8266 module.
 
 Making profiles for other printers
 ----------------------------------

+ 12 - 10
cura/BuildVolume.py

@@ -385,20 +385,22 @@ class BuildVolume(SceneNode):
             self.setPosition(Vector(0, -self._raft_thickness, 0), SceneNode.TransformSpace.World)
             self.raftThicknessChanged.emit()
 
-    def _updateExtraZClearance(self):
-        extra_z = None
+    def _updateExtraZClearance(self) -> None:
+        extra_z = 0.0
         extruders = ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())
+        use_extruders = False
         for extruder in extruders:
-            retraction_hop = extruder.getProperty("retraction_hop", "value")
-            if extra_z is None or retraction_hop > extra_z:
-                extra_z = retraction_hop
-        if extra_z is None:
+            if extruder.getProperty("retraction_hop_enabled", "value"):
+                retraction_hop = extruder.getProperty("retraction_hop", "value")
+                if extra_z is None or retraction_hop > extra_z:
+                    extra_z = retraction_hop
+            use_extruders = True
+        if not use_extruders:
             # If no extruders, take global value.
-            extra_z = self._global_container_stack.getProperty("retraction_hop", "value")
+            if self._global_container_stack.getProperty("retraction_hop_enabled", "value"):
+                extra_z = self._global_container_stack.getProperty("retraction_hop", "value")
         if extra_z != self._extra_z_clearance:
             self._extra_z_clearance = extra_z
-            from UM.Logger import Logger
-            Logger.log("d", "   ###  Extra z clearance changed: %s" % extra_z)
 
     ##  Update the build volume visualization
     def _onStackChanged(self):
@@ -892,7 +894,7 @@ class BuildVolume(SceneNode):
 
     _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"]
     _raft_settings = ["adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", "raft_surface_thickness", "raft_airgap"]
-    _extra_z_settings = ["retraction_hop"]
+    _extra_z_settings = ["retraction_hop_enabled", "retraction_hop"]
     _prime_settings = ["extruder_prime_pos_x", "extruder_prime_pos_y", "extruder_prime_pos_z"]
     _tower_settings = ["prime_tower_enable", "prime_tower_size", "prime_tower_position_x", "prime_tower_position_y"]
     _ooze_shield_settings = ["ooze_shield_enabled", "ooze_shield_dist"]

+ 0 - 1
cura/CameraAnimation.py

@@ -6,7 +6,6 @@ from PyQt5.QtCore import QVariantAnimation, QEasingCurve
 from PyQt5.QtGui import QVector3D
 
 from UM.Math.Vector import Vector
-from UM.Logger import Logger
 
 
 class CameraAnimation(QVariantAnimation):

+ 37 - 21
cura/CuraApplication.py

@@ -20,6 +20,8 @@ from UM.JobQueue import JobQueue
 from UM.SaveFile import SaveFile
 from UM.Scene.Selection import Selection
 from UM.Scene.GroupDecorator import GroupDecorator
+from UM.Settings.ContainerStack import ContainerStack
+from UM.Settings.InstanceContainer import InstanceContainer
 from UM.Settings.Validator import Validator
 from UM.Message import Message
 from UM.i18n import i18nCatalog
@@ -53,7 +55,7 @@ from . import MachineActionManager
 
 from cura.Settings.MachineManager import MachineManager
 from cura.Settings.ExtruderManager import ExtruderManager
-from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
+from cura.Settings.UserChangesModel import UserChangesModel
 from cura.Settings.ExtrudersModel import ExtrudersModel
 from cura.Settings.ContainerSettingsModel import ContainerSettingsModel
 from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
@@ -124,6 +126,8 @@ class CuraApplication(QtApplication):
 
         SettingDefinition.addSettingType("extruder", None, str, Validator)
 
+        SettingDefinition.addSettingType("[int]", None, str, None)
+
         SettingFunction.registerOperator("extruderValues", ExtruderManager.getExtruderValues)
         SettingFunction.registerOperator("extruderValue", ExtruderManager.getExtruderValue)
         SettingFunction.registerOperator("resolveOrValue", ExtruderManager.getResolveOrValue)
@@ -148,11 +152,11 @@ class CuraApplication(QtApplication):
 
         UM.VersionUpgradeManager.VersionUpgradeManager.getInstance().setCurrentVersions(
             {
-                ("quality", UM.Settings.InstanceContainer.InstanceContainer.Version):    (self.ResourceTypes.QualityInstanceContainer, "application/x-uranium-instancecontainer"),
-                ("machine_stack", UM.Settings.ContainerStack.ContainerStack.Version): (self.ResourceTypes.MachineStack, "application/x-uranium-containerstack"),
-                ("extruder_train", UM.Settings.ContainerStack.ContainerStack.Version): (self.ResourceTypes.ExtruderStack, "application/x-uranium-extruderstack"),
+                ("quality", InstanceContainer.Version):    (self.ResourceTypes.QualityInstanceContainer, "application/x-uranium-instancecontainer"),
+                ("machine_stack", ContainerStack.Version): (self.ResourceTypes.MachineStack, "application/x-uranium-containerstack"),
+                ("extruder_train", ContainerStack.Version): (self.ResourceTypes.ExtruderStack, "application/x-uranium-extruderstack"),
                 ("preferences", Preferences.Version):               (Resources.Preferences, "application/x-uranium-preferences"),
-                ("user", UM.Settings.InstanceContainer.InstanceContainer.Version):       (self.ResourceTypes.UserInstanceContainer, "application/x-uranium-instancecontainer")
+                ("user", InstanceContainer.Version):       (self.ResourceTypes.UserInstanceContainer, "application/x-uranium-instancecontainer")
             }
         )
 
@@ -241,6 +245,7 @@ class CuraApplication(QtApplication):
         Preferences.getInstance().addPreference("mesh/scale_tiny_meshes", True)
         Preferences.getInstance().addPreference("cura/dialog_on_project_save", True)
         Preferences.getInstance().addPreference("cura/asked_dialog_on_project_save", False)
+        Preferences.getInstance().addPreference("cura/choice_on_profile_override", 0)
 
         Preferences.getInstance().addPreference("cura/currency", "€")
         Preferences.getInstance().addPreference("cura/material_settings", "{}")
@@ -323,11 +328,35 @@ class CuraApplication(QtApplication):
     ## A reusable dialogbox
     #
     showMessageBox = pyqtSignal(str, str, str, str, int, int, arguments = ["title", "text", "informativeText", "detailedText", "buttons", "icon"])
+
     def messageBox(self, title, text, informativeText = "", detailedText = "", buttons = QMessageBox.Ok, icon = QMessageBox.NoIcon, callback = None, callback_arguments = []):
         self._message_box_callback = callback
         self._message_box_callback_arguments = callback_arguments
         self.showMessageBox.emit(title, text, informativeText, detailedText, buttons, icon)
 
+    showDiscardOrKeepProfileChanges = pyqtSignal()
+
+    def discardOrKeepProfileChanges(self):
+        choice = Preferences.getInstance().getValue("cura/choice_on_profile_override")
+        if choice == 1:
+            # don't show dialog and DISCARD the profile
+            self.discardOrKeepProfileChangesClosed("discard")
+        elif choice == 2:
+            # don't show dialog and KEEP the profile
+            self.discardOrKeepProfileChangesClosed("keep")
+        else:
+            # ALWAYS ask whether to keep or discard the profile
+            self.showDiscardOrKeepProfileChanges.emit()
+
+    @pyqtSlot(str)
+    def discardOrKeepProfileChangesClosed(self, option):
+        if option == "discard":
+            global_stack = self.getGlobalContainerStack()
+            for extruder in ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()):
+                extruder.getTop().clear()
+
+            global_stack.getTop().clear()
+
     @pyqtSlot(int)
     def messageBoxClosed(self, button):
         if self._message_box_callback:
@@ -648,6 +677,7 @@ class CuraApplication(QtApplication):
         qmlRegisterType(MaterialSettingsVisibilityHandler, "Cura", 1, 0, "MaterialSettingsVisibilityHandler")
         qmlRegisterType(QualitySettingsModel, "Cura", 1, 0, "QualitySettingsModel")
         qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator")
+        qmlRegisterType(UserChangesModel, "Cura", 1, 1, "UserChangesModel")
 
         qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.createContainerManager)
 
@@ -1071,18 +1101,6 @@ class CuraApplication(QtApplication):
 
     fileLoaded = pyqtSignal(str)
 
-    def _onFileLoaded(self, job):
-        nodes = job.getResult()
-        for node in nodes:
-            if node is not None:
-                self.fileLoaded.emit(job.getFileName())
-                node.setSelectable(True)
-                node.setName(os.path.basename(job.getFileName()))
-                op = AddSceneNodeOperation(node, self.getController().getScene().getRoot())
-                op.push()
-
-                self.getController().getScene().sceneChanged.emit(node) #Force scene change.
-
     def _onJobFinished(self, job):
         if type(job) is not ReadMeshJob or not job.getResult():
             return
@@ -1110,10 +1128,8 @@ class CuraApplication(QtApplication):
         else:
             Logger.log("w", "Could not find a mesh in reloaded node.")
 
-    def _openFile(self, file):
-        job = ReadMeshJob(os.path.abspath(file))
-        job.finished.connect(self._onFileLoaded)
-        job.start()
+    def _openFile(self, filename):
+        self.readLocalFile(QUrl.fromLocalFile(filename))
 
     def _addProfileReader(self, profile_reader):
         # TODO: Add the profile reader to the list of plug-ins that can be used when importing profiles.

+ 1 - 1
cura/CuraVersion.py.in

@@ -3,4 +3,4 @@
 
 CuraVersion = "@CURA_VERSION@"
 CuraBuildType = "@CURA_BUILDTYPE@"
-CuraDebugMode = True if "@CURA_DEBUGMODE@" == "ON" else False
+CuraDebugMode = True if "@_cura_debugmode@" == "ON" else False

+ 1 - 1
cura/LayerDataBuilder.py

@@ -63,7 +63,7 @@ class LayerDataBuilder(MeshBuilder):
         line_dimensions = numpy.empty((vertex_count, 2), numpy.float32)
         colors = numpy.empty((vertex_count, 4), numpy.float32)
         indices = numpy.empty((index_count, 2), numpy.int32)
-        extruders = numpy.empty((vertex_count), numpy.int32)  # Only usable for newer OpenGL versions
+        extruders = numpy.empty((vertex_count), numpy.float32)
         line_types = numpy.empty((vertex_count), numpy.float32)
 
         vertex_offset = 0

+ 6 - 2
cura/PrinterOutputDevice.py

@@ -47,8 +47,8 @@ class PrinterOutputDevice(QObject, OutputDevice):
         self._job_name = ""
         self._error_text = ""
         self._accepts_commands = True
-        self._preheat_bed_timeout = 900 #Default time-out for pre-heating the bed, in seconds.
-        self._preheat_bed_timer = QTimer() #Timer that tracks how long to preheat still.
+        self._preheat_bed_timeout = 900  # Default time-out for pre-heating the bed, in seconds.
+        self._preheat_bed_timer = QTimer()  # Timer that tracks how long to preheat still.
         self._preheat_bed_timer.setSingleShot(True)
         self._preheat_bed_timer.timeout.connect(self.cancelPreheatBed)
 
@@ -232,11 +232,15 @@ class PrinterOutputDevice(QObject, OutputDevice):
     #   \return The duration of the time-out to pre-heat the bed, formatted.
     @pyqtProperty(str, notify = preheatBedRemainingTimeChanged)
     def preheatBedRemainingTime(self):
+        if not self._preheat_bed_timer.isActive():
+            return ""
         period = self._preheat_bed_timer.remainingTime()
         if period <= 0:
             return ""
         minutes, period = divmod(period, 60000) #60000 milliseconds in a minute.
         seconds, _ = divmod(period, 1000) #1000 milliseconds in a second.
+        if minutes <= 0 and seconds <= 0:
+            return ""
         return "%d:%02d" % (minutes, seconds)
 
     ## Time the print has been printing.

+ 22 - 18
cura/Settings/ContainerManager.py

@@ -3,15 +3,15 @@
 
 import os.path
 import urllib
+from typing import Dict, Union
 
-from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, QUrl, QVariant
+from PyQt5.QtCore import QObject, QUrl, QVariant
 from UM.FlameProfiler import pyqtSlot
 from PyQt5.QtWidgets import QMessageBox
 
 from UM.PluginRegistry import PluginRegistry
-
-from UM.Platform import Platform
 from UM.SaveFile import SaveFile
+from UM.Platform import Platform
 from UM.MimeTypeDatabase import MimeTypeDatabase
 
 from UM.Logger import Logger
@@ -307,18 +307,20 @@ class ContainerManager(QObject):
     #
     #   \param container_id The ID of the container to export
     #   \param file_type The type of file to save as. Should be in the form of "description (*.extension, *.ext)"
-    #   \param file_url The URL where to save the file.
+    #   \param file_url_or_string The URL where to save the file.
     #
     #   \return A dictionary containing a key "status" with a status code and a key "message" with a message
     #           explaining the status.
     #           The status code can be one of "error", "cancelled", "success"
     @pyqtSlot(str, str, QUrl, result = "QVariantMap")
-    def exportContainer(self, container_id, file_type, file_url):
-        if not container_id or not file_type or not file_url:
+    def exportContainer(self, container_id: str, file_type: str, file_url_or_string: Union[QUrl, str]) -> Dict[str, str]:
+        if not container_id or not file_type or not file_url_or_string:
             return { "status": "error", "message": "Invalid arguments"}
 
-        if isinstance(file_url, QUrl):
-            file_url = file_url.toLocalFile()
+        if isinstance(file_url_or_string, QUrl):
+            file_url = file_url_or_string.toLocalFile()
+        else:
+            file_url = file_url_or_string
 
         if not file_url:
             return { "status": "error", "message": "Invalid path"}
@@ -373,12 +375,14 @@ class ContainerManager(QObject):
     #   \return \type{Dict} dict with a 'status' key containing the string 'success' or 'error', and a 'message' key
     #       containing a message for the user
     @pyqtSlot(QUrl, result = "QVariantMap")
-    def importContainer(self, file_url):
-        if not file_url:
+    def importContainer(self, file_url_or_string: Union[QUrl, str]) -> Dict[str, str]:
+        if not file_url_or_string:
             return { "status": "error", "message": "Invalid path"}
 
-        if isinstance(file_url, QUrl):
-            file_url = file_url.toLocalFile()
+        if isinstance(file_url_or_string, QUrl):
+            file_url = file_url_or_string.toLocalFile()
+        else:
+            file_url = file_url_or_string
 
         if not file_url or not os.path.exists(file_url):
             return { "status": "error", "message": "Invalid path" }
@@ -438,7 +442,7 @@ class ContainerManager(QObject):
 
     ##  Clear the top-most (user) containers of the active stacks.
     @pyqtSlot()
-    def clearUserContainers(self):
+    def clearUserContainers(self) -> None:
         self._machine_manager.blurSettings.emit()
 
         send_emits_containers = []
@@ -668,7 +672,7 @@ class ContainerManager(QObject):
         return new_change_instances
 
     @pyqtSlot(str, result = str)
-    def duplicateMaterial(self, material_id):
+    def duplicateMaterial(self, material_id: str) -> str:
         containers = self._container_registry.findInstanceContainers(id=material_id)
         if not containers:
             Logger.log("d", "Unable to duplicate the material with id %s, because it doesn't exist.", material_id)
@@ -692,7 +696,7 @@ class ContainerManager(QObject):
 
     ##  Get the singleton instance for this class.
     @classmethod
-    def getInstance(cls):
+    def getInstance(cls) -> "ContainerManager":
         # Note: Explicit use of class name to prevent issues with inheritance.
         if ContainerManager.__instance is None:
             ContainerManager.__instance = cls()
@@ -717,7 +721,7 @@ class ContainerManager(QObject):
         if clear_settings:
             merge.clear()
 
-    def _updateContainerNameFilters(self):
+    def _updateContainerNameFilters(self) -> None:
         self._container_name_filters = {}
         for plugin_id, container_type in self._container_registry.getContainerTypes():
             # Ignore default container types since those are not plugins
@@ -852,10 +856,10 @@ class ContainerManager(QObject):
         return self._container_registry.importProfile(path)
 
     @pyqtSlot("QVariantList", QUrl, str)
-    def exportProfile(self, instance_id, file_url, file_type):
+    def exportProfile(self, instance_id: str, file_url: QUrl, file_type: str) -> None:
         if not file_url.isValid():
             return
         path = file_url.toLocalFile()
         if not path:
             return
-            self._container_registry.exportProfile(instance_id, path, file_type)
+        self._container_registry.exportProfile(instance_id, path, file_type)

+ 1 - 42
cura/Settings/MachineManager.py

@@ -939,48 +939,7 @@ class MachineManager(QObject):
         container.nameChanged.connect(self._onQualityNameChanged)
 
     def _askUserToKeepOrClearCurrentSettings(self):
-        # Ask the user if the user profile should be cleared or not (discarding the current settings)
-        # In Simple Mode we assume the user always wants to keep the (limited) current settings
-        details_text = catalog.i18nc("@label", "You made changes to the following setting(s)/override(s):")
-
-        # user changes in global stack
-        details_list = [setting.definition.label for setting in self._global_container_stack.getTop().findInstances(**{})]
-
-        # user changes in extruder stacks
-        stacks = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()))
-        for stack in stacks:
-            details_list.extend([
-                "%s (%s)" % (setting.definition.label, stack.getName())
-                for setting in stack.getTop().findInstances(**{})])
-
-        # Format to output string
-        details = "\n    ".join([details_text, ] + details_list)
-
-        num_changed_settings = len(details_list)
-        Application.getInstance().messageBox(
-            catalog.i18nc("@window:title", "Switched profiles"),
-            catalog.i18nc(
-                "@label",
-                "Do you want to transfer your %d changed setting(s)/override(s) to this profile?") % num_changed_settings,
-            catalog.i18nc(
-                "@label",
-                "If you transfer your settings they will override settings in the profile. If you don't transfer these settings, they will be lost."),
-            details,
-            buttons=QMessageBox.Yes + QMessageBox.No,
-            icon=QMessageBox.Question,
-            callback=self._keepUserSettingsDialogCallback)
-
-    def _keepUserSettingsDialogCallback(self, button):
-        if button == QMessageBox.Yes:
-            # Yes, keep the settings in the user profile with this profile
-            pass
-        elif button == QMessageBox.No:
-            # No, discard the settings in the user profile
-            global_stack = Application.getInstance().getGlobalContainerStack()
-            for extruder in ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()):
-                extruder.getTop().clear()
-
-            global_stack.getTop().clear()
+        Application.getInstance().discardOrKeepProfileChanges()
 
     @pyqtProperty(str, notify = activeVariantChanged)
     def activeVariantName(self):

Некоторые файлы не были показаны из-за большого количества измененных файлов