Browse Source

Merge branch 'master' into python_type_hinting

Simon Edwards 8 years ago
parent
commit
fb70eb6813

+ 1 - 0
CMakeLists.txt

@@ -10,6 +10,7 @@ set(URANIUM_SCRIPTS_DIR "${CMAKE_SOURCE_DIR}/../uranium/scripts" CACHE DIRECTORY
 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)
 
 set(CURA_VERSION "master" CACHE STRING "Version name of Cura")
 set(CURA_BUILDTYPE "" CACHE STRING "Build type of Cura, eg. 'PPA'")

+ 9 - 0
cura/BlockSlicingDecorator.py

@@ -0,0 +1,9 @@
+from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
+
+
+class BlockSlicingDecorator(SceneNodeDecorator):
+    def __init__(self):
+        super().__init__()
+
+    def isBlockSlicing(self):
+        return True

+ 40 - 15
cura/BuildVolume.py

@@ -223,7 +223,7 @@ class BuildVolume(SceneNode):
             scale_matrix = Matrix()
             if self._width != 0:
                 # Scale circular meshes by aspect ratio if width != height
-                aspect = self._height / self._width
+                aspect = self._depth / self._width
                 scale_matrix.compose(scale = Vector(1, 1, aspect))
             mb = MeshBuilder()
             mb.addArc(max_w, Vector.Unit_Y, center = (0, min_h - z_fight_distance, 0), color = self.VolumeOutlineColor)
@@ -283,6 +283,9 @@ class BuildVolume(SceneNode):
             color = Color(0.0, 0.0, 0.0, 0.15)
             for polygon in self._disallowed_areas:
                 points = polygon.getPoints()
+                if len(points) == 0:
+                    continue
+
                 first = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d))
                 previous_point = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d))
                 for point in points:
@@ -533,8 +536,11 @@ class BuildVolume(SceneNode):
             prime_tower_size = self._global_container_stack.getProperty("prime_tower_size", "value")
             machine_width = self._global_container_stack.getProperty("machine_width", "value")
             machine_depth = self._global_container_stack.getProperty("machine_depth", "value")
-            prime_tower_x = self._global_container_stack.getProperty("prime_tower_position_x", "value") - machine_width / 2 #Offset by half machine_width and _depth to put the origin in the front-left.
-            prime_tower_y = - self._global_container_stack.getProperty("prime_tower_position_y", "value") + machine_depth / 2
+            prime_tower_x = self._global_container_stack.getProperty("prime_tower_position_x", "value")
+            prime_tower_y = - self._global_container_stack.getProperty("prime_tower_position_y", "value")
+            if not self._global_container_stack.getProperty("machine_center_is_zero", "value"):
+                prime_tower_x = prime_tower_x - machine_width / 2 #Offset by half machine_width and _depth to put the origin in the front-left.
+                prime_tower_y = prime_tower_y + machine_depth / 2
 
             prime_tower_area = Polygon([
                 [prime_tower_x - prime_tower_size, prime_tower_y - prime_tower_size],
@@ -565,8 +571,17 @@ class BuildVolume(SceneNode):
         machine_width = self._global_container_stack.getProperty("machine_width", "value")
         machine_depth = self._global_container_stack.getProperty("machine_depth", "value")
         for extruder in used_extruders:
-            prime_x = extruder.getProperty("extruder_prime_pos_x", "value") - machine_width / 2 #Offset by half machine_width and _depth to put the origin in the front-left.
-            prime_y = machine_depth / 2 - extruder.getProperty("extruder_prime_pos_y", "value")
+            prime_x = extruder.getProperty("extruder_prime_pos_x", "value")
+            prime_y = - extruder.getProperty("extruder_prime_pos_y", "value")
+
+            #Ignore extruder prime position if it is not set
+            if prime_x == 0 and prime_y == 0:
+                result[extruder.getId()] = []
+                continue
+
+            if not self._global_container_stack.getProperty("machine_center_is_zero", "value"):
+                prime_x = prime_x - machine_width / 2 #Offset by half machine_width and _depth to put the origin in the front-left.
+                prime_y = prime_x + machine_depth / 2
 
             prime_polygon = Polygon.approximatedCircle(PRIME_CLEARANCE)
             prime_polygon = prime_polygon.translate(prime_x, prime_y)
@@ -716,7 +731,12 @@ class BuildVolume(SceneNode):
     #
     #   \return A sequence of setting values, one for each extruder.
     def _getSettingFromAllExtruders(self, setting_key, property = "value"):
-        return ExtruderManager.getInstance().getAllExtruderSettings(setting_key, property)
+        all_values = ExtruderManager.getInstance().getAllExtruderSettings(setting_key, property)
+        all_types = ExtruderManager.getInstance().getAllExtruderSettings(setting_key, "type")
+        for i in range(len(all_values)):
+            if not all_values[i] and (all_types[i] == "int" or all_types[i] == "float"):
+                all_values[i] = 0
+        return all_values
 
     ##  Private convenience function to get a setting from the support infill
     #   extruder.
@@ -740,16 +760,21 @@ class BuildVolume(SceneNode):
         multi_extrusion = self._global_container_stack.getProperty("machine_extruder_count", "value") > 1
 
         if not multi_extrusion:
-            return self._global_container_stack.getProperty(setting_key, property)
-
-        extruder_index = self._global_container_stack.getProperty(extruder_setting_key, "value")
+            stack = self._global_container_stack
+        else:
+            extruder_index = self._global_container_stack.getProperty(extruder_setting_key, "value")
 
-        if extruder_index == "-1":  # If extruder index is -1 use global instead
-            return self._global_container_stack.getProperty(setting_key, property)
+            if extruder_index == "-1":  # If extruder index is -1 use global instead
+                stack = self._global_container_stack
+            else:
+                extruder_stack_id = ExtruderManager.getInstance().extruderIds[str(extruder_index)]
+                stack = ContainerRegistry.getInstance().findContainerStacks(id = extruder_stack_id)[0]
 
-        extruder_stack_id = ExtruderManager.getInstance().extruderIds[str(extruder_index)]
-        stack = ContainerRegistry.getInstance().findContainerStacks(id = extruder_stack_id)[0]
-        return stack.getProperty(setting_key, property)
+        value = stack.getProperty(setting_key, property)
+        setting_type = stack.getProperty(setting_key, "type")
+        if not value and (setting_type == "int" or setting_type == "float"):
+            return 0
+        return value
 
     ##  Convenience function to calculate the disallowed radius around the edge.
     #
@@ -825,4 +850,4 @@ class BuildVolume(SceneNode):
     _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"]
     _distance_settings = ["infill_wipe_dist", "travel_avoid_distance", "support_offset", "support_enable", "travel_avoid_other_parts"]
-    _extruder_settings = ["support_enable", "support_interface_enable", "support_infill_extruder_nr", "support_extruder_nr_layer_0", "support_interface_extruder_nr", "brim_line_count", "adhesion_extruder_nr", "adhesion_type"] #Settings that can affect which extruders are used.
+    _extruder_settings = ["support_enable", "support_interface_enable", "support_infill_extruder_nr", "support_extruder_nr_layer_0", "support_interface_extruder_nr", "brim_line_count", "adhesion_extruder_nr", "adhesion_type"] #Settings that can affect which extruders are used.

+ 6 - 4
cura/CrashHandler.py

@@ -12,6 +12,11 @@ from UM.Logger import Logger
 from UM.i18n import i18nCatalog
 catalog = i18nCatalog("cura")
 
+try:
+    from cura.CuraVersion import CuraDebugMode
+except ImportError:
+    CuraDebugMode = False  # [CodeStyle: Reflecting imported value]
+
 # List of exceptions that should be considered "fatal" and abort the program.
 # These are primarily some exception types that we simply cannot really recover from
 # (MemoryError and SystemError) and exceptions that indicate grave errors in the
@@ -24,21 +29,18 @@ fatal_exception_types = [
 ]
 
 def show(exception_type, value, tb):
-    debug_mode = True
-
     Logger.log("c", "An uncaught exception has occurred!")
     for line in traceback.format_exception(exception_type, value, tb):
         for part in line.rstrip("\n").split("\n"):
             Logger.log("c", part)
 
-    if not debug_mode and exception_type not in fatal_exception_types:
+    if not CuraDebugMode and exception_type not in fatal_exception_types:
         return
 
     application = QCoreApplication.instance()
     if not application:
         sys.exit(1)
 
-
     dialog = QDialog()
     dialog.setMinimumWidth(640)
     dialog.setMinimumHeight(640)

+ 2 - 1
cura/CuraActions.py

@@ -1,5 +1,6 @@
-from PyQt5.QtCore import QObject, pyqtSlot, QUrl
+from PyQt5.QtCore import QObject, QUrl
 from PyQt5.QtGui import QDesktopServices
+from UM.FlameProfiler import pyqtSlot
 
 from UM.Event import CallFunctionEvent
 from UM.Application import Application

+ 94 - 7
cura/CuraApplication.py

@@ -19,13 +19,16 @@ from UM.SaveFile import SaveFile
 from UM.Scene.Selection import Selection
 from UM.Scene.GroupDecorator import GroupDecorator
 from UM.Settings.Validator import Validator
+from UM.Message import Message
+from UM.i18n import i18nCatalog
 
 from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
 from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
 from UM.Operations.GroupedOperation import GroupedOperation
 from UM.Operations.SetTransformOperation import SetTransformOperation
-from UM.Operations.TranslateOperation import TranslateOperation
 from cura.SetParentOperation import SetParentOperation
+from cura.SliceableObjectDecorator import SliceableObjectDecorator
+from cura.BlockSlicingDecorator import BlockSlicingDecorator
 
 from UM.Settings.SettingDefinition import SettingDefinition, DefinitionPropertyType
 from UM.Settings.ContainerRegistry import ContainerRegistry
@@ -34,8 +37,6 @@ from cura.Settings.MachineNameValidator import MachineNameValidator
 from cura.Settings.ProfilesModel import ProfilesModel
 from cura.Settings.QualityAndUserProfilesModel import QualityAndUserProfilesModel
 from cura.Settings.SettingInheritanceManager import SettingInheritanceManager
-
-from UM.i18n import i18nCatalog
 from cura.Settings.UserProfilesModel import UserProfilesModel
 
 from . import PlatformPhysics
@@ -57,7 +58,8 @@ from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisi
 from cura.Settings.QualitySettingsModel import QualitySettingsModel
 from cura.Settings.ContainerManager import ContainerManager
 
-from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
+from PyQt5.QtCore import QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
+from UM.FlameProfiler import pyqtSlot
 from PyQt5.QtGui import QColor, QIcon
 from PyQt5.QtWidgets import QMessageBox
 from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
@@ -151,6 +153,9 @@ class CuraApplication(QtApplication):
             }
         )
 
+        self._currently_loading_files = []
+        self._non_sliceable_extensions = []
+
         self._machine_action_manager = MachineActionManager.MachineActionManager()
         self._machine_manager = None    # This is initialized on demand.
         self._setting_inheritance_manager = None
@@ -228,7 +233,7 @@ class CuraApplication(QtApplication):
         Preferences.getInstance().addPreference("cura/recent_files", "")
         Preferences.getInstance().addPreference("cura/categories_expanded", "")
         Preferences.getInstance().addPreference("cura/jobname_prefix", True)
-        Preferences.getInstance().addPreference("view/center_on_select", True)
+        Preferences.getInstance().addPreference("view/center_on_select", False)
         Preferences.getInstance().addPreference("mesh/scale_to_fit", True)
         Preferences.getInstance().addPreference("mesh/scale_tiny_meshes", True)
         Preferences.getInstance().addPreference("cura/dialog_on_project_save", True)
@@ -598,9 +603,12 @@ class CuraApplication(QtApplication):
     def updatePlatformActivity(self, node = None):
         count = 0
         scene_bounding_box = None
+        is_block_slicing_node = False
         for node in DepthFirstIterator(self.getController().getScene().getRoot()):
-            if type(node) is not SceneNode or not node.getMeshData():
+            if type(node) is not SceneNode or (not node.getMeshData() and not node.callDecoration("getLayerData")):
                 continue
+            if node.callDecoration("isBlockSlicing"):
+                is_block_slicing_node = True
 
             count += 1
             if not scene_bounding_box:
@@ -610,6 +618,10 @@ class CuraApplication(QtApplication):
                 if other_bb is not None:
                     scene_bounding_box = scene_bounding_box + node.getBoundingBox()
 
+        print_information = self.getPrintInformation()
+        if print_information:
+            print_information.setPreSliced(is_block_slicing_node)
+
         if not scene_bounding_box:
             scene_bounding_box = AxisAlignedBox.Null
 
@@ -730,7 +742,7 @@ class CuraApplication(QtApplication):
         for node in DepthFirstIterator(self.getController().getScene().getRoot()):
             if type(node) is not SceneNode:
                 continue
-            if not node.getMeshData() and not node.callDecoration("isGroup"):
+            if (not node.getMeshData() and not node.callDecoration("getLayerData")) and not node.callDecoration("isGroup"):
                 continue  # Node that doesnt have a mesh and is not a group.
             if node.getParent() and node.getParent().callDecoration("isGroup"):
                 continue  # Grouped nodes don't need resetting as their parent (the group) is resetted)
@@ -1025,3 +1037,78 @@ class CuraApplication(QtApplication):
     @pyqtSlot(str)
     def log(self, msg):
         Logger.log("d", msg)
+
+    @pyqtSlot(QUrl)
+    def readLocalFile(self, file):
+        if not file.isValid():
+            return
+
+        scene = self.getController().getScene()
+
+        for node in DepthFirstIterator(scene.getRoot()):
+            if node.callDecoration("isBlockSlicing"):
+                self.deleteAll()
+                break
+
+        f = file.toLocalFile()
+        extension = os.path.splitext(f)[1]
+        filename = os.path.basename(f)
+        if len(self._currently_loading_files) > 0:
+            # If a non-slicable file is already being loaded, we prevent loading of any further non-slicable files
+            if extension.lower() in self._non_sliceable_extensions:
+                message = Message(
+                    self._i18n_catalog.i18nc("@info:status",
+                                       "Only one G-code file can be loaded at a time. Skipped importing {0}",
+                                       filename))
+                message.show()
+                return
+            # If file being loaded is non-slicable file, then prevent loading of any other files
+            extension = os.path.splitext(self._currently_loading_files[0])[1]
+            if extension.lower() in self._non_sliceable_extensions:
+                message = Message(
+                    self._i18n_catalog.i18nc("@info:status",
+                                       "Can't open any other file if G-code is loading. Skipped importing {0}",
+                                       filename))
+                message.show()
+                return
+
+        self._currently_loading_files.append(f)
+        if extension in self._non_sliceable_extensions:
+            self.deleteAll()
+
+        job = ReadMeshJob(f)
+        job.finished.connect(self._readMeshFinished)
+        job.start()
+
+    def _readMeshFinished(self, job):
+        nodes = job.getResult()
+        filename = job.getFileName()
+        self._currently_loading_files.remove(filename)
+
+        for node in nodes:
+            node.setSelectable(True)
+            node.setName(os.path.basename(filename))
+
+            extension = os.path.splitext(filename)[1]
+            if extension.lower() in self._non_sliceable_extensions:
+                self.getController().setActiveView("LayerView")
+                view = self.getController().getActiveView()
+                view.resetLayerData()
+                view.setLayer(9999999)
+                view.calculateMaxLayers()
+
+                block_slicing_decorator = BlockSlicingDecorator()
+                node.addDecorator(block_slicing_decorator)
+            else:
+                sliceable_decorator = SliceableObjectDecorator()
+                node.addDecorator(sliceable_decorator)
+
+            scene = self.getController().getScene()
+
+            op = AddSceneNodeOperation(node, scene.getRoot())
+            op.push()
+
+            scene.sceneChanged.emit(node)
+
+    def addNonSliceableExtension(self, extension):
+        self._non_sliceable_extensions.append(extension)

+ 2 - 1
cura/CuraVersion.py.in

@@ -2,4 +2,5 @@
 # Cura is released under the terms of the AGPLv3 or higher.
 
 CuraVersion = "@CURA_VERSION@"
-CuraBuildType = "@CURA_BUILDTYPE@"
+CuraBuildType = "@CURA_BUILDTYPE@"
+CuraDebugMode = True if "@CURA_DEBUGMODE@" == "ON" else False

+ 13 - 0
cura/GCodeListDecorator.py

@@ -0,0 +1,13 @@
+from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
+
+
+class GCodeListDecorator(SceneNodeDecorator):
+    def __init__(self):
+        super().__init__()
+        self._gcode_list = []
+
+    def getGCodeList(self):
+        return self._gcode_list
+
+    def setGCodeList(self, list):
+        self._gcode_list = list

+ 2 - 1
cura/MachineActionManager.py

@@ -6,7 +6,8 @@ from UM.PluginRegistry import PluginRegistry  # So MachineAction can be added as
 from UM.Settings.ContainerRegistry import ContainerRegistry
 from UM.Settings.DefinitionContainer import DefinitionContainer
 
-from PyQt5.QtCore import QObject, pyqtSlot
+from PyQt5.QtCore import QObject
+from UM.FlameProfiler import pyqtSlot
 
 ##  Raised when trying to add an unknown machine action as a required action
 class UnknownMachineActionError(Exception):

+ 21 - 3
cura/PrintInformation.py

@@ -1,7 +1,8 @@
 # Copyright (c) 2015 Ultimaker B.V.
 # Cura is released under the terms of the AGPLv3 or higher.
 
-from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot
+from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty
+from UM.FlameProfiler import pyqtSlot
 
 from UM.Application import Application
 from UM.Qt.Duration import Duration
@@ -13,6 +14,9 @@ import math
 import os.path
 import unicodedata
 
+from UM.i18n import i18nCatalog
+catalog = i18nCatalog("cura")
+
 ##  A class for processing and calculating minimum, current and maximum print time as well as managing the job name
 #
 #   This class contains all the logic relating to calculation and slicing for the
@@ -49,6 +53,8 @@ class PrintInformation(QObject):
         self._material_lengths = []
         self._material_weights = []
 
+        self._pre_sliced = False
+
         self._backend = Application.getInstance().getBackend()
         if self._backend:
             self._backend.printDurationMessage.connect(self._onPrintDurationMessage)
@@ -61,6 +67,16 @@ class PrintInformation(QObject):
 
     currentPrintTimeChanged = pyqtSignal()
 
+    preSlicedChanged = pyqtSignal()
+
+    @pyqtProperty(bool, notify=preSlicedChanged)
+    def preSliced(self):
+        return self._pre_sliced
+
+    def setPreSliced(self, pre_sliced):
+        self._pre_sliced = pre_sliced
+        self.preSlicedChanged.emit()
+
     @pyqtProperty(Duration, notify = currentPrintTimeChanged)
     def currentPrintTime(self):
         return self._current_print_time
@@ -122,7 +138,9 @@ class PrintInformation(QObject):
     def createJobName(self, base_name):
         base_name = self._stripAccents(base_name)
         self._setAbbreviatedMachineName()
-        if Preferences.getInstance().getValue("cura/jobname_prefix"):
+        if self._pre_sliced:
+            return catalog.i18nc("@label", "Pre-sliced file {0}", base_name)
+        elif Preferences.getInstance().getValue("cura/jobname_prefix"):
             return self._abbr_machine + "_" + base_name
         else:
             return base_name
@@ -150,4 +168,4 @@ class PrintInformation(QObject):
 
     ##  Utility method that strips accents from characters (eg: â -> a)
     def _stripAccents(self, str):
-       return ''.join(char for char in unicodedata.normalize('NFD', str) if unicodedata.category(char) != 'Mn')
+       return ''.join(char for char in unicodedata.normalize('NFD', str) if unicodedata.category(char) != 'Mn')

Some files were not shown because too many files changed in this diff