Solved merge conflict. CURA-3321

This commit is contained in:
Jack Ha 2017-03-07 10:34:53 +01:00
commit 42d939b34e
65 changed files with 1358 additions and 418 deletions

22
cura/BuildVolume.py Normal file → Executable file
View file

@ -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"]

View file

@ -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):

58
cura/CuraApplication.py Normal file → Executable file
View file

@ -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.

View file

@ -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

2
cura/LayerDataBuilder.py Normal file → Executable file
View file

@ -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

View file

@ -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.

View file

@ -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)

View file

@ -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):

View file

@ -6,7 +6,7 @@ from PyQt5.QtGui import QValidator
import os #For statvfs.
import urllib #To escape machine names for how they're saved to file.
import UM.Resources
from UM.Resources import Resources
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.InstanceContainer import InstanceContainer
@ -19,7 +19,7 @@ class MachineNameValidator(QObject):
#Compute the validation regex for printer names. This is limited by the maximum file name length.
try:
filename_max_length = os.statvfs(UM.Resources.getDataStoragePath()).f_namemax
filename_max_length = os.statvfs(Resources.getDataStoragePath()).f_namemax
except AttributeError: #Doesn't support statvfs. Probably because it's not a Unix system.
filename_max_length = 255 #Assume it's Windows on NTFS.
machine_name_max_length = filename_max_length - len("_current_settings.") - len(ContainerRegistry.getMimeTypeForContainer(InstanceContainer).preferredSuffix)
@ -41,7 +41,7 @@ class MachineNameValidator(QObject):
def validate(self, name, position):
#Check for file name length of the current settings container (which is the longest file we're saving with the name).
try:
filename_max_length = os.statvfs(UM.Resources.getDataStoragePath()).f_namemax
filename_max_length = os.statvfs(Resources.getDataStoragePath()).f_namemax
except AttributeError: #Doesn't support statvfs. Probably because it's not a Unix system.
filename_max_length = 255 #Assume it's Windows on NTFS.
escaped_name = urllib.parse.quote_plus(name)

View file

@ -8,7 +8,7 @@ from UM.Signal import Signal, signalemitter
from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.InstanceContainer import InstanceContainer
from UM.Settings.ContainerRegistry import ContainerRegistry
import UM.Logger
from UM.Logger import Logger
from UM.Application import Application
@ -99,7 +99,7 @@ class SettingOverrideDecorator(SceneNodeDecorator):
Application.getInstance().getBackend().needsSlicing()
Application.getInstance().getBackend().tickle()
else:
UM.Logger.log("e", "Extruder stack %s below per-object settings does not exist.", self._extruder_stack)
Logger.log("e", "Extruder stack %s below per-object settings does not exist.", self._extruder_stack)
else:
self._stack.setNextStack(Application.getInstance().getGlobalContainerStack())

View file

@ -0,0 +1,115 @@
from UM.Qt.ListModel import ListModel
from PyQt5.QtCore import pyqtSlot, Qt
from UM.Application import Application
from cura.Settings.ExtruderManager import ExtruderManager
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.i18n import i18nCatalog
from UM.Settings.SettingFunction import SettingFunction
import os
class UserChangesModel(ListModel):
KeyRole = Qt.UserRole + 1
LabelRole = Qt.UserRole + 2
ExtruderRole = Qt.UserRole +3
OriginalValueRole = Qt.UserRole + 4
UserValueRole = Qt.UserRole + 6
CategoryRole = Qt.UserRole + 7
def __init__(self, parent = None):
super().__init__(parent = parent)
self.addRoleName(self.KeyRole, "key")
self.addRoleName(self.LabelRole, "label")
self.addRoleName(self.ExtruderRole, "extruder")
self.addRoleName(self.OriginalValueRole, "original_value")
self.addRoleName(self.UserValueRole, "user_value")
self.addRoleName(self.CategoryRole, "category")
self._i18n_catalog = None
self._update()
@pyqtSlot()
def forceUpdate(self):
self._update()
def _update(self):
items = []
global_stack = Application.getInstance().getGlobalContainerStack()
if not global_stack:
return
stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks()
# Check if the definition container has a translation file and ensure it's loaded.
definition = global_stack.getBottom()
definition_suffix = ContainerRegistry.getMimeTypeForContainer(type(definition)).preferredSuffix
catalog = i18nCatalog(os.path.basename(definition.getId() + "." + definition_suffix))
if catalog.hasTranslationLoaded():
self._i18n_catalog = catalog
for file_name in definition.getInheritedFiles():
catalog = i18nCatalog(os.path.basename(file_name))
if catalog.hasTranslationLoaded():
self._i18n_catalog = catalog
for stack in stacks:
# Make a list of all containers in the stack.
containers = []
latest_stack = stack
while latest_stack:
containers.extend(latest_stack.getContainers())
latest_stack = latest_stack.getNextStack()
# Drop the user container.
user_changes = containers.pop(0)
for setting_key in user_changes.getAllKeys():
original_value = None
# Find the category of the instance by moving up until we find a category.
category = user_changes.getInstance(setting_key).definition
while category.type != "category":
category = category.parent
# Handle translation (and fallback if we weren't able to find any translation files.
if self._i18n_catalog:
category_label = self._i18n_catalog.i18nc(category.key + " label", category.label)
else:
category_label = category.label
if self._i18n_catalog:
label = self._i18n_catalog.i18nc(setting_key + " label", stack.getProperty(setting_key, "label"))
else:
label = stack.getProperty(setting_key, "label")
for container in containers:
if stack == global_stack:
resolve = global_stack.getProperty(setting_key, "resolve")
if resolve is not None:
original_value = resolve
break
original_value = container.getProperty(setting_key, "value")
# If a value is a function, ensure it's called with the stack it's in.
if isinstance(original_value, SettingFunction):
original_value = original_value(stack)
if original_value is not None:
break
item_to_add = {"key": setting_key,
"label": label,
"user_value": str(user_changes.getProperty(setting_key, "value")),
"original_value": str(original_value),
"extruder": "",
"category": category_label}
if stack != global_stack:
item_to_add["extruder"] = stack.getName()
items.append(item_to_add)
self.setItems(items)