Merge branch 'master' of https://github.com/Ultimaker/Cura into master-CURA-1615

This commit is contained in:
Thomas Karl Pietrowski 2016-06-17 11:59:08 +02:00
commit 0275139a50
16 changed files with 24068 additions and 72 deletions

View File

@ -4,7 +4,7 @@
from UM.Qt.QtApplication import QtApplication
from UM.Scene.SceneNode import SceneNode
from UM.Scene.Camera import Camera
from UM.Scene.Platform import Platform
from UM.Scene.Platform import Platform as Scene_Platform
from UM.Math.Vector import Vector
from UM.Math.Quaternion import Quaternion
from UM.Math.AxisAlignedBox import AxisAlignedBox
@ -14,6 +14,7 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Mesh.ReadMeshJob import ReadMeshJob
from UM.Logger import Logger
from UM.Preferences import Preferences
from UM.Platform import Platform
from UM.JobQueue import JobQueue
from UM.SaveFile import SaveFile
from UM.Scene.Selection import Selection
@ -58,7 +59,7 @@ import urllib
numpy.seterr(all="ignore")
#WORKAROUND: GITHUB-88 GITHUB-385 GITHUB-612
if platform.system() == "Linux": # Needed for platform.linux_distribution, which is not available on Windows and OSX
if Platform.isLinux(): # Needed for platform.linux_distribution, which is not available on Windows and OSX
# For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826
if platform.linux_distribution()[0] in ("Ubuntu", ): # TODO: Needs a "if X11_GFX == 'nvidia'" here. The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix.
import ctypes
@ -340,7 +341,7 @@ class CuraApplication(QtApplication):
Selection.selectionChanged.connect(self.onSelectionChanged)
root = controller.getScene().getRoot()
self._platform = Platform(root)
self._platform = Scene_Platform(root)
self._volume = BuildVolume.BuildVolume(root)

View File

@ -1,10 +1,14 @@
# Copyright (c) 2016 Ultimaker B.V.
# Uranium is released under the terms of the AGPLv3 or higher.
# Cura is released under the terms of the AGPLv3 or higher.
import os
import os.path
import re
from PyQt5.QtWidgets import QMessageBox
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.InstanceContainer import InstanceContainer
from UM.Application import Application
from UM.Logger import Logger
from UM.Message import Message
@ -13,12 +17,45 @@ from UM.PluginRegistry import PluginRegistry #For getting the possible profile w
from UM.Util import parseBool
from UM.i18n import i18nCatalog
catalog = i18nCatalog("uranium")
catalog = i18nCatalog("cura")
class CuraContainerRegistry(ContainerRegistry):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
## Create a name that is not empty and unique
# \param container_type \type{string} Type of the container (machine, quality, ...)
# \param current_name \type{} Current name of the container, which may be an acceptable option
# \param new_name \type{string} Base name, which may not be unique
# \param fallback_name \type{string} Name to use when (stripped) new_name is empty
# \return \type{string} Name that is unique for the specified type and name/id
def createUniqueName(self, container_type, current_name, new_name, fallback_name):
new_name = new_name.strip()
num_check = re.compile("(.*?)\s*#\d+$").match(new_name)
if num_check:
new_name = num_check.group(1)
if new_name == "":
new_name = fallback_name
unique_name = new_name
i = 1
# In case we are renaming, the current name of the container is also a valid end-result
while self._containerExists(container_type, unique_name) and unique_name != current_name:
i += 1
unique_name = "%s #%d" % (new_name, i)
return unique_name
## Check if a container with of a certain type and a certain name or id exists
# Both the id and the name are checked, because they may not be the same and it is better if they are both unique
# \param container_type \type{string} Type of the container (machine, quality, ...)
# \param container_name \type{string} Name to check
def _containerExists(self, container_type, container_name):
container_class = ContainerStack if container_type == "machine" else InstanceContainer
return self.findContainers(container_class, id = container_name, type = container_type) or \
self.findContainers(container_class, name = container_name, type = container_type)
## Exports an profile to a file
#
# \param instance_id \type{str} the ID of the profile to export.
@ -73,14 +110,14 @@ class CuraContainerRegistry(ContainerRegistry):
# \param description
# \return The plugin object matching the given extension and description.
def _findProfileWriter(self, extension, description):
pr = PluginRegistry.getInstance()
plugin_registry = PluginRegistry.getInstance()
for plugin_id, meta_data in self._getIOPlugins("profile_writer"):
for supported_type in meta_data["profile_writer"]: # All file types this plugin can supposedly write.
supported_extension = supported_type.get("extension", None)
if supported_extension == extension: # This plugin supports a file type with the same extension.
supported_description = supported_type.get("description", None)
if supported_description == description: # The description is also identical. Assume it's the same file type.
return pr.getPluginObject(plugin_id)
return plugin_registry.getPluginObject(plugin_id)
return None
## Imports a profile from a file
@ -92,9 +129,9 @@ class CuraContainerRegistry(ContainerRegistry):
if not file_name:
return { "status": "error", "message": catalog.i18nc("@info:status", "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>", file_name, "Invalid path")}
pr = PluginRegistry.getInstance()
plugin_registry = PluginRegistry.getInstance()
for plugin_id, meta_data in self._getIOPlugins("profile_reader"):
profile_reader = pr.getPluginObject(plugin_id)
profile_reader = plugin_registry.getPluginObject(plugin_id)
try:
profile = profile_reader.read(file_name) #Try to open the file with the profile reader.
except Exception as e:
@ -104,6 +141,11 @@ class CuraContainerRegistry(ContainerRegistry):
if profile: #Success!
profile.setReadOnly(False)
new_name = self.createUniqueName("quality", "", os.path.splitext(os.path.basename(file_name))[0],
catalog.i18nc("@label", "Custom profile"))
profile.setName(new_name)
profile._id = new_name
if self._machineHasOwnQualities():
profile.setDefinition(self._activeDefinition())
if self._machineHasOwnMaterials():
@ -120,12 +162,12 @@ class CuraContainerRegistry(ContainerRegistry):
## Gets a list of profile writer plugins
# \return List of tuples of (plugin_id, meta_data).
def _getIOPlugins(self, io_type):
pr = PluginRegistry.getInstance()
active_plugin_ids = pr.getActivePlugins()
plugin_registry = PluginRegistry.getInstance()
active_plugin_ids = plugin_registry.getActivePlugins()
result = []
for plugin_id in active_plugin_ids:
meta_data = pr.getMetaData(plugin_id)
meta_data = plugin_registry.getMetaData(plugin_id)
if io_type in meta_data:
result.append( (plugin_id, meta_data) )
return result

View File

@ -67,12 +67,14 @@ class ExtruderManager(QObject):
self.activeExtruderChanged.emit()
def getActiveExtruderStack(self):
try:
return self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getBottom().getId()][str(self._active_extruder_index)]
except AttributeError:
return None
except KeyError:
return None
global_container_stack = UM.Application.getInstance().getGlobalContainerStack()
if global_container_stack:
global_definition_container = UM.Application.getInstance().getGlobalContainerStack().getBottom()
if global_definition_container:
if global_definition_container.getId() in self._extruder_trains:
if str(self._active_extruder_index) in self._extruder_trains[global_definition_container.getId()]:
return self._extruder_trains[global_definition_container.getId()][str(self._active_extruder_index)]
## Adds all extruders of a specific machine definition to the extruder
# manager.
@ -180,7 +182,7 @@ class ExtruderManager(QObject):
quality = qualities[0]
preferred_quality_id = machine_definition.getMetaDataEntry("preferred_quality")
if preferred_quality_id:
preferred_quality = container_registry.findInstanceContainers(id = preferred_quality_id.lower(), type = "quality")
preferred_quality = container_registry.findInstanceContainers(id = preferred_quality_id, type = "quality")
if len(preferred_quality) >= 1:
quality = preferred_quality[0]
else:

View File

@ -1,5 +1,5 @@
import re
# Copyright (c) 2016 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal
from UM.Application import Application
@ -8,6 +8,8 @@ from UM.Preferences import Preferences
import UM.Settings
from UM.Settings.Validator import ValidatorState
from UM.Settings.InstanceContainer import InstanceContainer
from cura.PrinterOutputDevice import PrinterOutputDevice
from UM.Settings.ContainerStack import ContainerStack
from . import ExtruderManager
from UM.i18n import i18nCatalog
@ -49,6 +51,8 @@ class MachineManagerModel(QObject):
active_machine_id = Preferences.getInstance().getValue("cura/active_machine")
Application.getInstance().getOutputDeviceManager().outputDevicesChanged.connect(self._onOutputDevicesChanged)
if active_machine_id != "":
# An active machine was saved, so restore it.
self.setActiveMachine(active_machine_id)
@ -65,14 +69,10 @@ class MachineManagerModel(QObject):
blurSettings = pyqtSignal() # Emitted to force fields in the advanced sidebar to un-focus, so they update properly
@pyqtProperty("QVariantMap", notify = globalContainerChanged)
def extrudersIds(self):
## Find all extruders that reference the new stack
extruders = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(**{"machine": self._global_container_stack.getId()})
result = {}
for extruder in extruders:
result[extruder.getMetaDataEntry("position")] = extruder.getId()
return result
outputDevicesChanged = pyqtSignal()
def _onOutputDevicesChanged(self):
self.outputDevicesChanged.emit()
def _onGlobalPropertyChanged(self, key, property_name):
if property_name == "value":
@ -164,6 +164,10 @@ class MachineManagerModel(QObject):
Application.getInstance().setGlobalContainerStack(new_global_stack)
@pyqtProperty("QVariantList", notify = outputDevicesChanged)
def printerOutputDevices(self):
return [printer_output_device for printer_output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices() if isinstance(printer_output_device, PrinterOutputDevice)]
## Create a name that is not empty and unique
# \param container_type \type{string} Type of the container (machine, quality, ...)
# \param current_name \type{} Current name of the container, which may be an acceptable option
@ -171,31 +175,7 @@ class MachineManagerModel(QObject):
# \param fallback_name \type{string} Name to use when (stripped) new_name is empty
# \return \type{string} Name that is unique for the specified type and name/id
def _createUniqueName(self, container_type, current_name, new_name, fallback_name):
new_name = new_name.strip()
num_check = re.compile("(.*?)\s*#\d+$").match(new_name)
if num_check:
new_name = num_check.group(1)
if new_name == "":
new_name = fallback_name
unique_name = new_name
i = 1
# In case we are renaming, the current name of the container is also a valid end-result
while self._containerExists(container_type, unique_name) and unique_name != current_name:
i += 1
unique_name = "%s #%d" % (new_name, i)
return unique_name
## Check if a container with of a certain type and a certain name or id exists
# Both the id and the name are checked, because they may not be the same and it is better if they are both unique
# \param container_type \type{string} Type of the container (machine, quality, ...)
# \param container_name \type{string} Name to check
def _containerExists(self, container_type, container_name):
container_class = ContainerStack if container_type == "machine" else InstanceContainer
return UM.Settings.ContainerRegistry.getInstance().findContainers(container_class, id = container_name, type = container_type) or \
UM.Settings.ContainerRegistry.getInstance().findContainers(container_class, name = container_name, type = container_type)
return UM.Settings.ContainerRegistry.getInstance().createUniqueName(container_type, current_name, new_name, fallback_name)
## Convenience function to check if a stack has errors.
def _checkStackForErrors(self, stack):
@ -446,7 +426,7 @@ class MachineManagerModel(QObject):
@pyqtProperty(str, notify = globalContainerChanged)
def activeDefinitionId(self):
if self._active_container_stack:
if self._global_container_stack:
definition = self._global_container_stack.getBottom()
if definition:
return definition.id
@ -480,14 +460,14 @@ class MachineManagerModel(QObject):
@pyqtProperty(bool, notify = globalContainerChanged)
def hasMaterials(self):
if self._active_container_stack:
if self._global_container_stack:
return bool(self._global_container_stack.getMetaDataEntry("has_materials", False))
return False
@pyqtProperty(bool, notify = globalContainerChanged)
def hasVariants(self):
if self._active_container_stack:
if self._global_container_stack:
return bool(self._global_container_stack.getMetaDataEntry("has_variants", False))
return False

View File

@ -33,10 +33,11 @@ sys.excepthook = exceptHook
# first seems to prevent Sip from going into a state where it
# tries to create PyQt objects on a non-main thread.
import Arcus #@UnusedImport
from UM.Platform import Platform
import cura.CuraApplication
import cura.CuraContainerRegistry
if sys.platform == "win32" and hasattr(sys, "frozen"):
if Platform.isWindows() and hasattr(sys, "frozen"):
dirpath = os.path.expanduser("~/AppData/Local/cura/")
os.makedirs(dirpath, exist_ok = True)
sys.stdout = open(os.path.join(dirpath, "stdout.log"), "w")

View File

@ -11,6 +11,7 @@ from UM.Message import Message
from UM.PluginRegistry import PluginRegistry
from UM.Resources import Resources
from UM.Settings.Validator import ValidatorState #To find if a setting is in an error state. We can't slice then.
from UM.Platform import Platform
from cura.ExtruderManager import ExtruderManager
@ -42,7 +43,7 @@ class CuraEngineBackend(Backend):
default_engine_location = os.path.join(Application.getInstallPrefix(), "bin", "CuraEngine")
if hasattr(sys, "frozen"):
default_engine_location = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "CuraEngine")
if sys.platform == "win32":
if Platform.isWindows():
default_engine_location += ".exe"
default_engine_location = os.path.abspath(default_engine_location)
Preferences.getInstance().addPreference("backend/location", default_engine_location)

View File

@ -232,7 +232,7 @@ class USBPrinterOutputDeviceManager(QObject, SignalEmitter, OutputDevicePlugin,
def getSerialPortList(self, only_list_usb = False):
base_list = []
if platform.system() == "Windows":
import winreg
import winreg #@UnresolvedImport
try:
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,"HARDWARE\\DEVICEMAP\\SERIALCOMM")
i = 0

View File

@ -23,8 +23,7 @@
"label": "Extruder",
"description": "The extruder train used for printing. This is used in multi-extrusion.",
"type": "extruder",
"default_value": 0,
"minimum_value": "0",
"default_value": "0",
"settable_per_mesh": true,
"settable_per_extruder": false,
"settable_per_meshgroup": false,

View File

@ -0,0 +1,94 @@
{
"id": "mendel90",
"name": "Mendel90",
"version": 2,
"inherits": "fdmprinter",
"metadata":
{
"visible": true,
"author": "Bo Herrmannsen",
"category": "Other",
"manufacturer": "Nophead",
"file_formats": "text/x-gcode",
"platform": "mendel90_platform.stl",
"platform_offset": [0, -23.6, 0]
},
"pages": [
"BedLeveling"
],
"overrides": {
"machine_start_gcode": {
"default_value": "G21 ;metric values\nG90 ;absolute positioning\nG92 E0 ;zero the extruded length\nM107 ;start with the fan off\nG1 X90 Y200 F6000 ;go to the middle of the front\nG1 Z0.05 ;close to the bed\nG1 Z0.3 ;lift Z\n"
},
"machine_end_gcode": {
"default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nM107 ;carriage fan off\nG91 ;relative positioning\nG1 Z10 ;Move up Z 10mm\nG90 ;back to absolute mode\nG1 E-1 F1200 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG92 E0 ;zero the extruded length\nG1 Y200 F5000 ;Move Y to middle of bed cooling fan\nM42 P42 S255 ;Turn on Bed cooling fan on\nG4 S420 ;Wait 7 mins\nM42 P42 S0 ;Turn off bed cooling fan\nG1 Y10 F5000 ;Move Y to front\nM84 ;steppers off\n"
},
"material_bed_temp_wait": {
"default_value": true
},
"material_print_temp_prepend": {
"default_value": true
},
"machine_width": {
"default_value": 200
},
"machine_height": {
"default_value": 200
},
"machine_depth": {
"default_value": 200
},
"machine_heated_bed": {
"default_value": true
},
"machine_center_is_zero": {
"default_value": false
},
"machine_extruder_count": {
"default_value": 1
},
"machine_nozzle_tip_outer_diameter": {
"default_value": 1
},
"machine_nozzle_head_distance": {
"default_value": 5
},
"machine_nozzle_expansion_angle": {
"default_value": 45
},
"machine_heat_zone_length": {
"default_value": 16
},
"machine_nozzle_heat_up_speed": {
"default_value": 2.0
},
"machine_nozzle_cool_down_speed": {
"default_value": 2.0
},
"machine_gcode_flavor": {
"default_value": "RepRap (Marlin/Sprinter)"
},
"gantry_height": {
"default_value": 55
},
"machine_nozzle_size": {
"default_value": 0.4
},
"material_diameter": {
"default_value": 1.75
},
"machine_head_with_fans_polygon":
{
"default_value": [
[ -12, 9 ],
[ -12, -9 ],
[ 14, 9 ],
[ 14, -9 ]
]
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -24,9 +24,10 @@ Button {
checkable: true
checked: definition.expanded
onClicked: definition.expanded ? settingDefinitionsModel.collapse(definition.key) : settingDefinitionsModel.expandAll(definition.key)
onClicked: { forceActiveFocus(); definition.expanded ? settingDefinitionsModel.collapse(definition.key) : settingDefinitionsModel.expandAll(definition.key) }
UM.SimpleButton {
UM.SimpleButton
{
id: settingsButton
visible: base.hovered || settingsButton.hovered
@ -60,7 +61,8 @@ Button {
height: parent.height / 2
width: height
onClicked: {
onClicked:
{
base.showAllHiddenInheritedSettings()
}
@ -68,11 +70,13 @@ Button {
hoverColor: UM.Theme.getColor("setting_control_button_hover")
iconSource: UM.Theme.getIcon("notice")
onEntered: {
onEntered:
{
base.showTooltip(catalog.i18nc("@label","Some hidden settings use values different from their normal calculated value.\n\nClick to make these settings visible."))
}
onExited: {
onExited:
{
base.hideTooltip();
}

View File

@ -31,7 +31,7 @@ SettingItem
}
}
onClicked: propertyProvider.setPropertyValue("value", !checked)
onClicked: { forceActiveFocus(); propertyProvider.setPropertyValue("value", !checked) }
Rectangle
{

View File

@ -86,7 +86,7 @@ SettingItem
}
}
onActivated: provider.setPropertyValue("value", definition.options[index].key)
onActivated: { forceActiveFocus(); provider.setPropertyValue("value", definition.options[index].key) }
onModelChanged: updateCurrentIndex();
Connections

View File

@ -102,7 +102,11 @@ SettingItem
}
}
onActivated: provider.setPropertyValue("value", extruders_model.getItem(index).index);
onActivated:
{
forceActiveFocus();
provider.setPropertyValue("value", extruders_model.getItem(index).index)
}
onModelChanged: updateCurrentIndex();
Connections

View File

@ -7,6 +7,7 @@ import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1
import UM 1.1 as UM
import Cura 1.0 as Cura
Rectangle
{
@ -14,6 +15,9 @@ Rectangle
property int currentModeIndex;
// Is there an output device for this printer?
property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
color: UM.Theme.getColor("sidebar");
UM.I18nCatalog { id: catalog; name:"cura"}

View File

@ -157,13 +157,19 @@ Column
Label
{
anchors.centerIn: parent
anchors.verticalCenter: parent.verticalCenter
anchors.left: swatch.right
anchors.leftMargin: UM.Theme.getSize("default_margin").width / 2
anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").width / 2
color: control.checked ? UM.Theme.getColor("toggle_checked_text") :
control.pressed ? UM.Theme.getColor("toggle_active_text") :
control.hovered ? UM.Theme.getColor("toggle_hovered_text") : UM.Theme.getColor("toggle_unchecked_text")
font: UM.Theme.getFont("default")
text: control.text;
text: control.text
elide: Text.ElideRight
}
}
label: Item { }