Skip to content

Commit

Permalink
Merge pull request #3 from Ultimaker/master
Browse files Browse the repository at this point in the history
update
  • Loading branch information
maukcc authored May 8, 2017
2 parents b00c8e8 + 17ad6a3 commit 09d30c5
Show file tree
Hide file tree
Showing 108 changed files with 5,647 additions and 1,301 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ plugins/GodMode
plugins/PostProcessingPlugin
plugins/X3GWriter
plugins/FlatProfileExporter
plugins/ProfileFlattener
plugins/cura-god-mode-plugin

#Build stuff
Expand Down
1 change: 0 additions & 1 deletion cura.desktop.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
[Desktop Entry]
Version=1
Name=Cura
Name[de]=Cura
GenericName=3D Printing Software
Expand Down
29 changes: 15 additions & 14 deletions cura/BuildVolume.py
Original file line number Diff line number Diff line change
Expand Up @@ -600,20 +600,21 @@ def _updateDisallowedAreas(self):
result_areas[extruder_id].append(polygon) #Don't perform the offset on these.

# Add prime tower location as disallowed area.
prime_tower_collision = False
prime_tower_areas = self._computeDisallowedAreasPrinted(used_extruders)
for extruder_id in prime_tower_areas:
for prime_tower_area in prime_tower_areas[extruder_id]:
for area in result_areas[extruder_id]:
if prime_tower_area.intersectsPolygon(area) is not None:
prime_tower_collision = True
if len(used_extruders) > 1: #No prime tower in single-extrusion.
prime_tower_collision = False
prime_tower_areas = self._computeDisallowedAreasPrinted(used_extruders)
for extruder_id in prime_tower_areas:
for prime_tower_area in prime_tower_areas[extruder_id]:
for area in result_areas[extruder_id]:
if prime_tower_area.intersectsPolygon(area) is not None:
prime_tower_collision = True
break
if prime_tower_collision: #Already found a collision.
break
if prime_tower_collision: #Already found a collision.
break
if not prime_tower_collision:
result_areas[extruder_id].extend(prime_tower_areas[extruder_id])
else:
self._error_areas.extend(prime_tower_areas[extruder_id])
if not prime_tower_collision:
result_areas[extruder_id].extend(prime_tower_areas[extruder_id])
else:
self._error_areas.extend(prime_tower_areas[extruder_id])

self._has_errors = len(self._error_areas) > 0

Expand Down Expand Up @@ -955,4 +956,4 @@ def _clamp(self, value, min_value, max_value):
_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_bottom_enable", "support_roof_enable", "support_infill_extruder_nr", "support_extruder_nr_layer_0", "support_bottom_extruder_nr", "support_roof_extruder_nr", "brim_line_count", "adhesion_extruder_nr", "adhesion_type"] #Settings that can affect which extruders are used.
37 changes: 17 additions & 20 deletions cura/CrashHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,35 +48,32 @@ def show(exception_type, value, tb):
dialog = QDialog()
dialog.setMinimumWidth(640)
dialog.setMinimumHeight(640)
dialog.setWindowTitle(catalog.i18nc("@title:window", "Oops!"))
dialog.setWindowTitle(catalog.i18nc("@title:window", "Crash Report"))

layout = QVBoxLayout(dialog)

label = QLabel(dialog)
pixmap = QPixmap()

try:
data = urllib.request.urlopen("http://www.randomkittengenerator.com/cats/rotator.php").read()
pixmap.loadFromData(data)
except:
try:
from UM.Resources import Resources
path = Resources.getPath(Resources.Images, "kitten.jpg")
pixmap.load(path)
except:
pass

pixmap = pixmap.scaled(150, 150)
label.setPixmap(pixmap)
label.setAlignment(Qt.AlignCenter)
layout.addWidget(label)
#label = QLabel(dialog)
#pixmap = QPixmap()
#try:
# data = urllib.request.urlopen("http://www.randomkittengenerator.com/cats/rotator.php").read()
# pixmap.loadFromData(data)
#except:
# try:
# from UM.Resources import Resources
# path = Resources.getPath(Resources.Images, "kitten.jpg")
# pixmap.load(path)
# except:
# pass
#pixmap = pixmap.scaled(150, 150)
#label.setPixmap(pixmap)
#label.setAlignment(Qt.AlignCenter)
#layout.addWidget(label)

label = QLabel(dialog)
layout.addWidget(label)

#label.setScaledContents(True)
label.setText(catalog.i18nc("@label", """<p>A fatal exception has occurred that we could not recover from!</p>
<p>We hope this picture of a kitten helps you recover from the shock.</p>
<p>Please use the information below to post a bug report at <a href=\"http://github.com/Ultimaker/Cura/issues\">http://github.com/Ultimaker/Cura/issues</a></p>
"""))

Expand Down
94 changes: 93 additions & 1 deletion cura/CuraActions.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.

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
from UM.Math.Vector import Vector
from UM.Scene.Selection import Selection
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
from UM.Operations.GroupedOperation import GroupedOperation
from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
from UM.Operations.SetTransformOperation import SetTransformOperation

from cura.SetParentOperation import SetParentOperation
from cura.MultiplyObjectsJob import MultiplyObjectsJob
from cura.Settings.SetObjectExtruderOperation import SetObjectExtruderOperation
from cura.Settings.ExtruderManager import ExtruderManager

class CuraActions(QObject):
def __init__(self, parent = None):
Expand All @@ -23,5 +36,84 @@ def openBugReportPage(self):
event = CallFunctionEvent(self._openUrl, [QUrl("http://github.com/Ultimaker/Cura/issues")], {})
Application.getInstance().functionEvent(event)

## Center all objects in the selection
@pyqtSlot()
def centerSelection(self) -> None:
operation = GroupedOperation()
for node in Selection.getAllSelectedObjects():
current_node = node
while current_node.getParent() and current_node.getParent().callDecoration("isGroup"):
current_node = current_node.getParent()

center_operation = SetTransformOperation(current_node, Vector())
operation.addOperation(center_operation)
operation.push()

## Multiply all objects in the selection
#
# \param count The number of times to multiply the selection.
@pyqtSlot(int)
def multiplySelection(self, count: int) -> None:
job = MultiplyObjectsJob(Selection.getAllSelectedObjects(), count, 8)
job.start()

## Delete all selected objects.
@pyqtSlot()
def deleteSelection(self) -> None:
if not Application.getInstance().getController().getToolsEnabled():
return

removed_group_nodes = []
op = GroupedOperation()
nodes = Selection.getAllSelectedObjects()
for node in nodes:
op.addOperation(RemoveSceneNodeOperation(node))
group_node = node.getParent()
if group_node and group_node.callDecoration("isGroup") and group_node not in removed_group_nodes:
remaining_nodes_in_group = list(set(group_node.getChildren()) - set(nodes))
if len(remaining_nodes_in_group) == 1:
removed_group_nodes.append(group_node)
op.addOperation(SetParentOperation(remaining_nodes_in_group[0], group_node.getParent()))
op.addOperation(RemoveSceneNodeOperation(group_node))
op.push()

## Set the extruder that should be used to print the selection.
#
# \param extruder_id The ID of the extruder stack to use for the selected objects.
@pyqtSlot(str)
def setExtruderForSelection(self, extruder_id: str) -> None:
operation = GroupedOperation()

nodes_to_change = []
for node in Selection.getAllSelectedObjects():
# Do not change any nodes that already have the right extruder set.
if node.callDecoration("getActiveExtruder") == extruder_id:
continue

# If the node is a group, apply the active extruder to all children of the group.
if node.callDecoration("isGroup"):
for grouped_node in BreadthFirstIterator(node):
if grouped_node.callDecoration("getActiveExtruder") == extruder_id:
continue

if grouped_node.callDecoration("isGroup"):
continue

nodes_to_change.append(grouped_node)
continue

nodes_to_change.append(node)

if not nodes_to_change:
# If there are no changes to make, we still need to reset the selected extruders.
# This is a workaround for checked menu items being deselected while still being
# selected.
ExtruderManager.getInstance().resetSelectedObjectExtruders()
return

for node in nodes_to_change:
operation.addOperation(SetObjectExtruderOperation(node, extruder_id))
operation.push()

def _openUrl(self, url):
QDesktopServices.openUrl(url)
QDesktopServices.openUrl(url)
54 changes: 45 additions & 9 deletions cura/CuraApplication.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from UM.i18n import i18nCatalog
from UM.Workspace.WorkspaceReader import WorkspaceReader
from UM.Platform import Platform
from UM.Decorators import deprecated

from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
Expand Down Expand Up @@ -68,6 +69,8 @@
from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
from cura.Settings.QualitySettingsModel import QualitySettingsModel
from cura.Settings.ContainerManager import ContainerManager
from cura.Settings.GlobalStack import GlobalStack
from cura.Settings.ExtruderStack import ExtruderStack

from PyQt5.QtCore import QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
from UM.FlameProfiler import pyqtSlot
Expand Down Expand Up @@ -105,10 +108,15 @@ class ResourceTypes:
UserInstanceContainer = Resources.UserType + 6
MachineStack = Resources.UserType + 7
ExtruderStack = Resources.UserType + 8
DefinitionChangesContainer = Resources.UserType + 9

Q_ENUMS(ResourceTypes)

def __init__(self):
# this list of dir names will be used by UM to detect an old cura directory
for dir_name in ["extruders", "machine_instances", "materials", "plugins", "quality", "user", "variants"]:
Resources.addExpectedDirNameInData(dir_name)

Resources.addSearchPath(os.path.join(QtApplication.getInstallPrefix(), "share", "cura", "resources"))
if not hasattr(sys, "frozen"):
Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources"))
Expand Down Expand Up @@ -146,13 +154,15 @@ def __init__(self):
Resources.addStorageType(self.ResourceTypes.UserInstanceContainer, "user")
Resources.addStorageType(self.ResourceTypes.ExtruderStack, "extruders")
Resources.addStorageType(self.ResourceTypes.MachineStack, "machine_instances")
Resources.addStorageType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes")

ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer)
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer)
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer)
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer)
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.ExtruderStack)
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MachineStack)
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.DefinitionChangesContainer)

## Initialise the version upgrade manager with Cura's storage paths.
import UM.VersionUpgradeManager #Needs to be here to prevent circular dependencies.
Expand Down Expand Up @@ -214,6 +224,7 @@ def __init__(self):

self.getController().getScene().sceneChanged.connect(self.updatePlatformActivity)
self.getController().toolOperationStopped.connect(self._onToolOperationStopped)
self.getController().contextMenuRequested.connect(self._onContextMenuRequested)

Resources.addType(self.ResourceTypes.QmlFiles, "qml")
Resources.addType(self.ResourceTypes.Firmware, "firmware")
Expand Down Expand Up @@ -407,7 +418,7 @@ def saveSettings(self):
elif instance_type == "variant":
path = Resources.getStoragePath(self.ResourceTypes.VariantInstanceContainer, file_name)
elif instance_type == "definition_changes":
path = Resources.getStoragePath(self.ResourceTypes.MachineStack, file_name)
path = Resources.getStoragePath(self.ResourceTypes.DefinitionChangesContainer, file_name)

if path:
instance.setPath(path)
Expand All @@ -430,16 +441,18 @@ def saveStack(self, stack):

mime_type = ContainerRegistry.getMimeTypeForContainer(type(stack))
file_name = urllib.parse.quote_plus(stack.getId()) + "." + mime_type.preferredSuffix
stack_type = stack.getMetaDataEntry("type", None)

path = None
if not stack_type or stack_type == "machine":
if isinstance(stack, GlobalStack):
path = Resources.getStoragePath(self.ResourceTypes.MachineStack, file_name)
elif stack_type == "extruder_train":
elif isinstance(stack, ExtruderStack):
path = Resources.getStoragePath(self.ResourceTypes.ExtruderStack, file_name)
if path:
stack.setPath(path)
with SaveFile(path, "wt") as f:
f.write(data)
else:
path = Resources.getStoragePath(Resources.ContainerStacks, file_name)

stack.setPath(path)
with SaveFile(path, "wt") as f:
f.write(data)


@pyqtSlot(str, result = QUrl)
Expand Down Expand Up @@ -803,6 +816,7 @@ def updatePlatformActivity(self, node = None):

# Remove all selected objects from the scene.
@pyqtSlot()
@deprecated("Moved to CuraActions", "2.6")
def deleteSelection(self):
if not self.getController().getToolsEnabled():
return
Expand All @@ -823,6 +837,7 @@ def deleteSelection(self):
## Remove an object from the scene.
# Note that this only removes an object if it is selected.
@pyqtSlot("quint64")
@deprecated("Use deleteSelection instead", "2.6")
def deleteObject(self, object_id):
if not self.getController().getToolsEnabled():
return
Expand Down Expand Up @@ -850,13 +865,22 @@ def deleteObject(self, object_id):
# \param count number of copies
# \param min_offset minimum offset to other objects.
@pyqtSlot("quint64", int)
@deprecated("Use CuraActions::multiplySelection", "2.6")
def multiplyObject(self, object_id, count, min_offset = 8):
job = MultiplyObjectsJob(object_id, count, min_offset)
node = self.getController().getScene().findObject(object_id)
if not node:
node = Selection.getSelectedObject(0)

while node.getParent() and node.getParent().callDecoration("isGroup"):
node = node.getParent()

job = MultiplyObjectsJob([node], count, min_offset)
job.start()
return

## Center object on platform.
@pyqtSlot("quint64")
@deprecated("Use CuraActions::centerSelection", "2.6")
def centerObject(self, object_id):
node = self.getController().getScene().findObject(object_id)
if not node and object_id != 0: # Workaround for tool handles overlapping the selected object
Expand Down Expand Up @@ -1257,6 +1281,8 @@ def _readMeshFinished(self, job):
arranger = Arrange.create(scene_root = root)
min_offset = 8

self.fileLoaded.emit(filename)

for node in nodes:
node.setSelectable(True)
node.setName(os.path.basename(filename))
Expand Down Expand Up @@ -1316,3 +1342,13 @@ def checkIsValidProjectFile(self, file_url):
except Exception as e:
Logger.log("e", "Could not check file %s: %s", file_url, e)
return False

def _onContextMenuRequested(self, x: float, y: float) -> None:
# Ensure we select the object if we request a context menu over an object without having a selection.
if not Selection.hasSelection():
node = self.getController().getScene().findObject(self.getRenderer().getRenderPass("selection").getIdAtPosition(x, y))
if node:
while(node.getParent() and node.getParent().callDecoration("isGroup")):
node = node.getParent()

Selection.add(node)
Loading

0 comments on commit 09d30c5

Please sign in to comment.