Merge branch 'master' of https://github.com/Ultimaker/Cura into clean_print_job_info_block

This commit is contained in:
Ian Paschal 2018-10-03 09:08:26 +02:00
commit 9caeddf824
128 changed files with 3074 additions and 2239 deletions

View file

@ -934,7 +934,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
root_material_id)
if material_node is not None and material_node.getContainer() is not None:
extruder_stack.material = material_node.getContainer()
extruder_stack.material = material_node.getContainer() # type: InstanceContainer
def _applyChangesToMachine(self, global_stack, extruder_stack_dict):
# Clear all first

View file

@ -29,6 +29,7 @@ message Object
bytes normals = 3; //An array of 3 floats.
bytes indices = 4; //An array of ints.
repeated Setting settings = 5; // Setting override per object, overruling the global settings.
string name = 6;
}
message Progress

View file

@ -179,8 +179,7 @@ class CuraEngineBackend(QObject, Backend):
# This is useful for debugging and used to actually start the engine.
# \return list of commands and args / parameters.
def getEngineCommand(self) -> List[str]:
json_path = Resources.getPath(Resources.DefinitionContainers, "fdmprinter.def.json")
command = [self._application.getPreferences().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", json_path, ""]
command = [self._application.getPreferences().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), ""]
parser = argparse.ArgumentParser(prog = "cura", add_help = False)
parser.add_argument("--debug", action = "store_true", default = False, help = "Turn on the debug mode by setting this option.")

View file

@ -270,7 +270,7 @@ class StartSliceJob(Job):
obj = group_message.addRepeatedMessage("objects")
obj.id = id(object)
obj.name = object.getName()
indices = mesh_data.getIndices()
if indices is not None:
flat_verts = numpy.take(verts, indices.flatten(), axis=0)
@ -440,8 +440,7 @@ class StartSliceJob(Job):
Job.yieldThread()
# Ensure that the engine is aware what the build extruder is.
if stack.getProperty("machine_extruder_count", "value") > 1:
changed_setting_keys.add("extruder_nr")
changed_setting_keys.add("extruder_nr")
# Get values for all changed settings
for key in changed_setting_keys:

View file

@ -4,15 +4,22 @@
import gzip
from UM.Mesh.MeshReader import MeshReader #The class we're extending/implementing.
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType #To add the .gcode.gz files to the MIME type database.
from UM.PluginRegistry import PluginRegistry
## A file reader that reads gzipped g-code.
#
# If you're zipping g-code, you might as well use gzip!
class GCodeGzReader(MeshReader):
def __init__(self) -> None:
super().__init__()
MimeTypeDatabase.addMimeType(
MimeType(
name = "application/x-cura-compressed-gcode-file",
comment = "Cura Compressed GCode File",
suffixes = ["gcode.gz"]
)
)
self._supported_extensions = [".gcode.gz"]
def _read(self, file_name):

View file

@ -1,4 +1,5 @@
# Copyright (c) 2017 Aleph Objects, Inc.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from UM.FileHandler.FileReader import FileReader
@ -11,13 +12,7 @@ catalog = i18nCatalog("cura")
from . import MarlinFlavorParser, RepRapFlavorParser
MimeTypeDatabase.addMimeType(
MimeType(
name = "application/x-cura-gcode-file",
comment = "Cura GCode File",
suffixes = ["gcode", "gcode.gz"]
)
)
# Class for loading and parsing G-code files
@ -29,7 +24,15 @@ class GCodeReader(MeshReader):
def __init__(self) -> None:
super().__init__()
MimeTypeDatabase.addMimeType(
MimeType(
name = "application/x-cura-gcode-file",
comment = "Cura GCode File",
suffixes = ["gcode"]
)
)
self._supported_extensions = [".gcode", ".g"]
self._flavor_reader = None
Application.getInstance().getPreferences().addPreference("gcodereader/show_caution", True)

View file

@ -70,7 +70,7 @@ class GCodeWriter(MeshWriter):
active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
scene = Application.getInstance().getController().getScene()
if not hasattr(scene, "gcode_dict"):
self.setInformation(catalog.i18nc("@warning:status", "Please generate G-code before saving."))
self.setInformation(catalog.i18nc("@warning:status", "Please prepare G-code before exporting."))
return False
gcode_dict = getattr(scene, "gcode_dict")
gcode_list = gcode_dict.get(active_build_plate, None)
@ -86,7 +86,7 @@ class GCodeWriter(MeshWriter):
stream.write(settings)
return True
self.setInformation(catalog.i18nc("@warning:status", "Please generate G-code before saving."))
self.setInformation(catalog.i18nc("@warning:status", "Please prepare G-code before exporting."))
return False
## Create a new container with container 2 as base and container 1 written over it.

View file

@ -185,6 +185,12 @@ Item {
{
selectedObjectId: UM.ActiveTool.properties.getValue("SelectedObjectId")
}
// For some reason the model object is updated after removing him from the memory and
// it happens only on Windows. For this reason, set the destroyed value manually.
Component.onDestruction: {
setDestroyed(true);
}
}
delegate: Row

View file

@ -0,0 +1,53 @@
# Cura PostProcessingPlugin
# Author: Amanda de Castilho
# Date: August 28, 2018
# Description: This plugin inserts a line at the start of each layer,
# M117 - displays the filename and layer height to the LCD
# Alternatively, user can override the filename to display alt text + layer height
from ..Script import Script
from UM.Application import Application
class DisplayFilenameAndLayerOnLCD(Script):
def __init__(self):
super().__init__()
def getSettingDataString(self):
return """{
"name": "Display filename and layer on LCD",
"key": "DisplayFilenameAndLayerOnLCD",
"metadata": {},
"version": 2,
"settings":
{
"name":
{
"label": "text to display:",
"description": "By default the current filename will be displayed on the LCD. Enter text here to override the filename and display something else.",
"type": "str",
"default_value": ""
}
}
}"""
def execute(self, data):
if self.getSettingValueByKey("name") != "":
name = self.getSettingValueByKey("name")
else:
name = Application.getInstance().getPrintInformation().jobName
lcd_text = "M117 " + name + " layer: "
i = 0
for layer in data:
display_text = lcd_text + str(i)
layer_index = data.index(layer)
lines = layer.split("\n")
for line in lines:
if line.startswith(";LAYER:"):
line_index = lines.index(line)
lines.insert(line_index + 1, display_text)
i += 1
final_lines = "\n".join(lines)
data[layer_index] = final_lines
return data

View file

@ -0,0 +1,51 @@
from ..Script import Script
class PauseAtHeightRepRapFirmwareDuet(Script):
def getSettingDataString(self):
return """{
"name": "Pause at height for RepRapFirmware DuetWifi / Duet Ethernet / Duet Maestro",
"key": "PauseAtHeightRepRapFirmwareDuet",
"metadata": {},
"version": 2,
"settings":
{
"pause_height":
{
"label": "Pause height",
"description": "At what height should the pause occur",
"unit": "mm",
"type": "float",
"default_value": 5.0
}
}
}"""
def execute(self, data):
current_z = 0.
pause_z = self.getSettingValueByKey("pause_height")
layers_started = False
for layer_number, layer in enumerate(data):
lines = layer.split("\n")
for line in lines:
if ";LAYER:0" in line:
layers_started = True
continue
if not layers_started:
continue
if self.getValue(line, 'G') == 1 or self.getValue(line, 'G') == 0:
current_z = self.getValue(line, 'Z')
if current_z != None:
if current_z >= pause_z:
prepend_gcode = ";TYPE:CUSTOM\n"
prepend_gcode += "; -- Pause at height (%.2f mm) --\n" % pause_z
prepend_gcode += self.putValue(M = 226) + "\n"
layer = prepend_gcode + layer
data[layer_number] = layer # Override the data of this layer with the modified data
return data
break
return data

View file

@ -18,10 +18,13 @@ from UM.Platform import Platform
from UM.PluginRegistry import PluginRegistry
from UM.Resources import Resources
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Scene.Selection import Selection
from UM.Signal import Signal
from UM.View.GL.OpenGL import OpenGL
from UM.View.GL.OpenGLContext import OpenGLContext
from UM.View.View import View
from UM.i18n import i18nCatalog
from cura.Scene.ConvexHullNode import ConvexHullNode
@ -30,11 +33,20 @@ from cura.CuraApplication import CuraApplication
from .NozzleNode import NozzleNode
from .SimulationPass import SimulationPass
from .SimulationViewProxy import SimulationViewProxy
import numpy
import os.path
from typing import Optional, TYPE_CHECKING, List
if TYPE_CHECKING:
from UM.Scene.SceneNode import SceneNode
from UM.Scene.Scene import Scene
from UM.View.GL.ShaderProgram import ShaderProgram
from UM.View.RenderPass import RenderPass
from UM.Settings.ContainerStack import ContainerStack
catalog = i18nCatalog("cura")
import numpy
import os.path
## View used to display g-code paths.
class SimulationView(View):
@ -44,7 +56,7 @@ class SimulationView(View):
LAYER_VIEW_TYPE_FEEDRATE = 2
LAYER_VIEW_TYPE_THICKNESS = 3
def __init__(self):
def __init__(self) -> None:
super().__init__()
self._max_layers = 0
@ -64,21 +76,21 @@ class SimulationView(View):
self._busy = False
self._simulation_running = False
self._ghost_shader = None
self._layer_pass = None
self._composite_pass = None
self._ghost_shader = None # type: Optional["ShaderProgram"]
self._layer_pass = None # type: Optional[SimulationPass]
self._composite_pass = None # type: Optional[RenderPass]
self._old_layer_bindings = None
self._simulationview_composite_shader = None
self._simulationview_composite_shader = None # type: Optional["ShaderProgram"]
self._old_composite_shader = None
self._global_container_stack = None
self._global_container_stack = None # type: Optional[ContainerStack]
self._proxy = SimulationViewProxy()
self._controller.getScene().getRoot().childrenChanged.connect(self._onSceneChanged)
self._resetSettings()
self._legend_items = None
self._show_travel_moves = False
self._nozzle_node = None
self._nozzle_node = None # type: Optional[NozzleNode]
Application.getInstance().getPreferences().addPreference("view/top_layer_count", 5)
Application.getInstance().getPreferences().addPreference("view/only_show_top_layers", False)
@ -102,29 +114,29 @@ class SimulationView(View):
self._wireprint_warning_message = Message(catalog.i18nc("@info:status", "Cura does not accurately display layers when Wire Printing is enabled"),
title = catalog.i18nc("@info:title", "Simulation View"))
def _evaluateCompatibilityMode(self):
def _evaluateCompatibilityMode(self) -> bool:
return OpenGLContext.isLegacyOpenGL() or bool(Application.getInstance().getPreferences().getValue("view/force_layer_view_compatibility_mode"))
def _resetSettings(self):
self._layer_view_type = 0 # 0 is material color, 1 is color by linetype, 2 is speed, 3 is layer thickness
def _resetSettings(self) -> None:
self._layer_view_type = 0 # type: int # 0 is material color, 1 is color by linetype, 2 is speed, 3 is layer thickness
self._extruder_count = 0
self._extruder_opacity = [1.0, 1.0, 1.0, 1.0]
self._show_travel_moves = 0
self._show_helpers = 1
self._show_skin = 1
self._show_infill = 1
self._show_travel_moves = False
self._show_helpers = True
self._show_skin = True
self._show_infill = True
self.resetLayerData()
def getActivity(self):
def getActivity(self) -> bool:
return self._activity
def setActivity(self, activity):
def setActivity(self, activity: bool) -> None:
if self._activity == activity:
return
self._activity = activity
self.activityChanged.emit()
def getSimulationPass(self):
def getSimulationPass(self) -> SimulationPass:
if not self._layer_pass:
# Currently the RenderPass constructor requires a size > 0
# This should be fixed in RenderPass's constructor.
@ -133,30 +145,30 @@ class SimulationView(View):
self._layer_pass.setSimulationView(self)
return self._layer_pass
def getCurrentLayer(self):
def getCurrentLayer(self) -> int:
return self._current_layer_num
def getMinimumLayer(self):
def getMinimumLayer(self) -> int:
return self._minimum_layer_num
def getMaxLayers(self):
def getMaxLayers(self) -> int:
return self._max_layers
def getCurrentPath(self):
def getCurrentPath(self) -> int:
return self._current_path_num
def getMinimumPath(self):
def getMinimumPath(self) -> int:
return self._minimum_path_num
def getMaxPaths(self):
def getMaxPaths(self) -> int:
return self._max_paths
def getNozzleNode(self):
def getNozzleNode(self) -> NozzleNode:
if not self._nozzle_node:
self._nozzle_node = NozzleNode()
return self._nozzle_node
def _onSceneChanged(self, node):
def _onSceneChanged(self, node: "SceneNode") -> None:
if node.getMeshData() is None:
self.resetLayerData()
@ -164,21 +176,21 @@ class SimulationView(View):
self.calculateMaxLayers()
self.calculateMaxPathsOnLayer(self._current_layer_num)
def isBusy(self):
def isBusy(self) -> bool:
return self._busy
def setBusy(self, busy):
def setBusy(self, busy: bool) -> None:
if busy != self._busy:
self._busy = busy
self.busyChanged.emit()
def isSimulationRunning(self):
def isSimulationRunning(self) -> bool:
return self._simulation_running
def setSimulationRunning(self, running):
def setSimulationRunning(self, running: bool) -> None:
self._simulation_running = running
def resetLayerData(self):
def resetLayerData(self) -> None:
self._current_layer_mesh = None
self._current_layer_jumps = None
self._max_feedrate = sys.float_info.min
@ -186,7 +198,7 @@ class SimulationView(View):
self._max_thickness = sys.float_info.min
self._min_thickness = sys.float_info.max
def beginRendering(self):
def beginRendering(self) -> None:
scene = self.getController().getScene()
renderer = self.getRenderer()
@ -204,7 +216,7 @@ class SimulationView(View):
if (node.getMeshData()) and node.isVisible():
renderer.queueNode(node, transparent = True, shader = self._ghost_shader)
def setLayer(self, value):
def setLayer(self, value: int) -> None:
if self._current_layer_num != value:
self._current_layer_num = value
if self._current_layer_num < 0:
@ -218,7 +230,7 @@ class SimulationView(View):
self.currentLayerNumChanged.emit()
def setMinimumLayer(self, value):
def setMinimumLayer(self, value: int) -> None:
if self._minimum_layer_num != value:
self._minimum_layer_num = value
if self._minimum_layer_num < 0:
@ -232,7 +244,7 @@ class SimulationView(View):
self.currentLayerNumChanged.emit()
def setPath(self, value):
def setPath(self, value: int) -> None:
if self._current_path_num != value:
self._current_path_num = value
if self._current_path_num < 0:
@ -246,7 +258,7 @@ class SimulationView(View):
self.currentPathNumChanged.emit()
def setMinimumPath(self, value):
def setMinimumPath(self, value: int) -> None:
if self._minimum_path_num != value:
self._minimum_path_num = value
if self._minimum_path_num < 0:
@ -263,24 +275,24 @@ class SimulationView(View):
## Set the layer view type
#
# \param layer_view_type integer as in SimulationView.qml and this class
def setSimulationViewType(self, layer_view_type):
def setSimulationViewType(self, layer_view_type: int) -> None:
self._layer_view_type = layer_view_type
self.currentLayerNumChanged.emit()
## Return the layer view type, integer as in SimulationView.qml and this class
def getSimulationViewType(self):
def getSimulationViewType(self) -> int:
return self._layer_view_type
## Set the extruder opacity
#
# \param extruder_nr 0..3
# \param opacity 0.0 .. 1.0
def setExtruderOpacity(self, extruder_nr, opacity):
def setExtruderOpacity(self, extruder_nr: int, opacity: float) -> None:
if 0 <= extruder_nr <= 3:
self._extruder_opacity[extruder_nr] = opacity
self.currentLayerNumChanged.emit()
def getExtruderOpacities(self):
def getExtruderOpacities(self)-> List[float]:
return self._extruder_opacity
def setShowTravelMoves(self, show):
@ -290,46 +302,46 @@ class SimulationView(View):
def getShowTravelMoves(self):
return self._show_travel_moves
def setShowHelpers(self, show):
def setShowHelpers(self, show: bool) -> None:
self._show_helpers = show
self.currentLayerNumChanged.emit()
def getShowHelpers(self):
def getShowHelpers(self) -> bool:
return self._show_helpers
def setShowSkin(self, show):
def setShowSkin(self, show: bool) -> None:
self._show_skin = show
self.currentLayerNumChanged.emit()
def getShowSkin(self):
def getShowSkin(self) -> bool:
return self._show_skin
def setShowInfill(self, show):
def setShowInfill(self, show: bool) -> None:
self._show_infill = show
self.currentLayerNumChanged.emit()
def getShowInfill(self):
def getShowInfill(self) -> bool:
return self._show_infill
def getCompatibilityMode(self):
def getCompatibilityMode(self) -> bool:
return self._compatibility_mode
def getExtruderCount(self):
def getExtruderCount(self) -> int:
return self._extruder_count
def getMinFeedrate(self):
def getMinFeedrate(self) -> float:
return self._min_feedrate
def getMaxFeedrate(self):
def getMaxFeedrate(self) -> float:
return self._max_feedrate
def getMinThickness(self):
def getMinThickness(self) -> float:
return self._min_thickness
def getMaxThickness(self):
def getMaxThickness(self) -> float:
return self._max_thickness
def calculateMaxLayers(self):
def calculateMaxLayers(self) -> None:
scene = self.getController().getScene()
self._old_max_layers = self._max_layers
@ -383,7 +395,7 @@ class SimulationView(View):
self.maxLayersChanged.emit()
self._startUpdateTopLayers()
def calculateMaxPathsOnLayer(self, layer_num):
def calculateMaxPathsOnLayer(self, layer_num: int) -> None:
# Update the currentPath
scene = self.getController().getScene()
for node in DepthFirstIterator(scene.getRoot()):
@ -415,10 +427,10 @@ class SimulationView(View):
def getProxy(self, engine, script_engine):
return self._proxy
def endRendering(self):
def endRendering(self) -> None:
pass
def event(self, event):
def event(self, event) -> bool:
modifiers = QApplication.keyboardModifiers()
ctrl_is_active = modifiers & Qt.ControlModifier
shift_is_active = modifiers & Qt.ShiftModifier
@ -447,7 +459,7 @@ class SimulationView(View):
if QOpenGLContext.currentContext() is None:
Logger.log("d", "current context of OpenGL is empty on Mac OS X, will try to create shaders later")
CuraApplication.getInstance().callLater(lambda e=event: self.event(e))
return
return False
# Make sure the SimulationPass is created
layer_pass = self.getSimulationPass()
@ -480,11 +492,14 @@ class SimulationView(View):
Application.getInstance().globalContainerStackChanged.disconnect(self._onGlobalStackChanged)
if self._global_container_stack:
self._global_container_stack.propertyChanged.disconnect(self._onPropertyChanged)
self._nozzle_node.setParent(None)
if self._nozzle_node:
self._nozzle_node.setParent(None)
self.getRenderer().removeRenderPass(self._layer_pass)
self._composite_pass.setLayerBindings(self._old_layer_bindings)
self._composite_pass.setCompositeShader(self._old_composite_shader)
if self._composite_pass:
self._composite_pass.setLayerBindings(self._old_layer_bindings)
self._composite_pass.setCompositeShader(self._old_composite_shader)
return False
def getCurrentLayerMesh(self):
return self._current_layer_mesh
@ -492,7 +507,7 @@ class SimulationView(View):
def getCurrentLayerJumps(self):
return self._current_layer_jumps
def _onGlobalStackChanged(self):
def _onGlobalStackChanged(self) -> None:
if self._global_container_stack:
self._global_container_stack.propertyChanged.disconnect(self._onPropertyChanged)
self._global_container_stack = Application.getInstance().getGlobalContainerStack()
@ -504,17 +519,17 @@ class SimulationView(View):
else:
self._wireprint_warning_message.hide()
def _onPropertyChanged(self, key, property_name):
def _onPropertyChanged(self, key: str, property_name: str) -> None:
if key == "wireframe_enabled" and property_name == "value":
if self._global_container_stack.getProperty("wireframe_enabled", "value"):
if self._global_container_stack and self._global_container_stack.getProperty("wireframe_enabled", "value"):
self._wireprint_warning_message.show()
else:
self._wireprint_warning_message.hide()
def _onCurrentLayerNumChanged(self):
def _onCurrentLayerNumChanged(self) -> None:
self.calculateMaxPathsOnLayer(self._current_layer_num)
def _startUpdateTopLayers(self):
def _startUpdateTopLayers(self) -> None:
if not self._compatibility_mode:
return
@ -525,10 +540,10 @@ class SimulationView(View):
self.setBusy(True)
self._top_layers_job = _CreateTopLayersJob(self._controller.getScene(), self._current_layer_num, self._solid_layers)
self._top_layers_job.finished.connect(self._updateCurrentLayerMesh)
self._top_layers_job.start()
self._top_layers_job.finished.connect(self._updateCurrentLayerMesh) # type: ignore # mypy doesn't understand the whole private class thing that's going on here.
self._top_layers_job.start() # type: ignore
def _updateCurrentLayerMesh(self, job):
def _updateCurrentLayerMesh(self, job: "_CreateTopLayersJob") -> None:
self.setBusy(False)
if not job.getResult():
@ -539,9 +554,9 @@ class SimulationView(View):
self._current_layer_jumps = job.getResult().get("jumps")
self._controller.getScene().sceneChanged.emit(self._controller.getScene().getRoot())
self._top_layers_job = None
self._top_layers_job = None # type: Optional["_CreateTopLayersJob"]
def _updateWithPreferences(self):
def _updateWithPreferences(self) -> None:
self._solid_layers = int(Application.getInstance().getPreferences().getValue("view/top_layer_count"))
self._only_show_top_layers = bool(Application.getInstance().getPreferences().getValue("view/only_show_top_layers"))
self._compatibility_mode = self._evaluateCompatibilityMode()
@ -563,7 +578,7 @@ class SimulationView(View):
self._startUpdateTopLayers()
self.preferencesChanged.emit()
def _onPreferencesChanged(self, preference):
def _onPreferencesChanged(self, preference: str) -> None:
if preference not in {
"view/top_layer_count",
"view/only_show_top_layers",
@ -581,7 +596,7 @@ class SimulationView(View):
class _CreateTopLayersJob(Job):
def __init__(self, scene, layer_number, solid_layers):
def __init__(self, scene: "Scene", layer_number: int, solid_layers: int) -> None:
super().__init__()
self._scene = scene
@ -589,7 +604,7 @@ class _CreateTopLayersJob(Job):
self._solid_layers = solid_layers
self._cancel = False
def run(self):
def run(self) -> None:
layer_data = None
for node in DepthFirstIterator(self._scene.getRoot()):
layer_data = node.callDecoration("getLayerData")
@ -638,6 +653,6 @@ class _CreateTopLayersJob(Job):
self.setResult({"layers": layer_mesh.build(), "jumps": jump_mesh})
def cancel(self):
def cancel(self) -> None:
self._cancel = True
super().cancel()

View file

@ -256,6 +256,7 @@ fragment41core =
out vec4 frag_color;
uniform mediump vec4 u_ambientColor;
uniform mediump vec4 u_minimumAlbedo;
uniform highp vec3 u_lightPosition;
void main()
@ -263,7 +264,7 @@ fragment41core =
mediump vec4 finalColor = vec4(0.0);
float alpha = f_color.a;
finalColor.rgb += f_color.rgb * 0.3;
finalColor.rgb += f_color.rgb * 0.2 + u_minimumAlbedo.rgb;
highp vec3 normal = normalize(f_normal);
highp vec3 light_dir = normalize(u_lightPosition - f_vertex);
@ -285,6 +286,7 @@ u_extruder_opacity = [1.0, 1.0, 1.0, 1.0]
u_specularColor = [0.4, 0.4, 0.4, 1.0]
u_ambientColor = [0.3, 0.3, 0.3, 0.0]
u_diffuseColor = [1.0, 0.79, 0.14, 1.0]
u_minimumAlbedo = [0.1, 0.1, 0.1, 1.0]
u_shininess = 20.0
u_show_travel_moves = 0

View file

@ -33,30 +33,35 @@ class SliceInfo(QObject, Extension):
def __init__(self, parent = None):
QObject.__init__(self, parent)
Extension.__init__(self)
Application.getInstance().getOutputDeviceManager().writeStarted.connect(self._onWriteStarted)
Application.getInstance().getPreferences().addPreference("info/send_slice_info", True)
Application.getInstance().getPreferences().addPreference("info/asked_send_slice_info", False)
self._application = Application.getInstance()
self._application.getOutputDeviceManager().writeStarted.connect(self._onWriteStarted)
self._application.getPreferences().addPreference("info/send_slice_info", True)
self._application.getPreferences().addPreference("info/asked_send_slice_info", False)
self._more_info_dialog = None
self._example_data_content = None
if not Application.getInstance().getPreferences().getValue("info/asked_send_slice_info"):
self._application.initializationFinished.connect(self._onAppInitialized)
def _onAppInitialized(self):
# DO NOT read any preferences values in the constructor because at the time plugins are created, no version
# upgrade has been performed yet because version upgrades are plugins too!
if not self._application.getPreferences().getValue("info/asked_send_slice_info"):
self.send_slice_info_message = Message(catalog.i18nc("@info", "Cura collects anonymized usage statistics."),
lifetime = 0,
dismissable = False,
title = catalog.i18nc("@info:title", "Collecting Data"))
self.send_slice_info_message.addAction("MoreInfo", name = catalog.i18nc("@action:button", "More info"), icon = None,
description = catalog.i18nc("@action:tooltip", "See more information on what data Cura sends."), button_style = Message.ActionButtonStyle.LINK)
description = catalog.i18nc("@action:tooltip", "See more information on what data Cura sends."), button_style = Message.ActionButtonStyle.LINK)
self.send_slice_info_message.addAction("Dismiss", name = catalog.i18nc("@action:button", "Allow"), icon = None,
description = catalog.i18nc("@action:tooltip", "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing."))
description = catalog.i18nc("@action:tooltip", "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing."))
self.send_slice_info_message.actionTriggered.connect(self.messageActionTriggered)
self.send_slice_info_message.show()
Application.getInstance().initializationFinished.connect(self._onAppInitialized)
def _onAppInitialized(self):
if self._more_info_dialog is None:
self._more_info_dialog = self._createDialog("MoreInfoWindow.qml")

View file

@ -33,6 +33,7 @@ Window
{
id: header
}
Item
{
id: mainView
@ -75,6 +76,7 @@ Window
visible: toolbox.viewCategory == "installed"
}
}
ToolboxFooter
{
id: footer

View file

@ -15,7 +15,7 @@ Item
{
id: sidebar
}
Rectangle
Item
{
id: header
anchors

View file

@ -23,6 +23,7 @@ Item
{
id: button
text: catalog.i18nc("@action:button", "Back")
enabled: !toolbox.isDownloading
UM.RecolorImage
{
id: backArrow
@ -39,7 +40,7 @@ Item
width: width
height: height
}
color: button.hovered ? UM.Theme.getColor("primary") : UM.Theme.getColor("text")
color: button.enabled ? (button.hovered ? UM.Theme.getColor("primary") : UM.Theme.getColor("text")) : UM.Theme.getColor("text_inactive")
source: UM.Theme.getIcon("arrow_left")
}
width: UM.Theme.getSize("toolbox_back_button").width
@ -59,7 +60,7 @@ Item
{
id: labelStyle
text: control.text
color: control.hovered ? UM.Theme.getColor("primary") : UM.Theme.getColor("text")
color: control.enabled ? (control.hovered ? UM.Theme.getColor("primary") : UM.Theme.getColor("text")) : UM.Theme.getColor("text_inactive")
font: UM.Theme.getFont("default_bold")
horizontalAlignment: Text.AlignRight
width: control.width

View file

@ -9,9 +9,8 @@ import UM 1.1 as UM
Item
{
id: page
property var details: base.selection
property var details: base.selection || {}
anchors.fill: parent
width: parent.width
ToolboxBackColumn
{
id: sidebar

View file

@ -9,6 +9,7 @@ import UM 1.1 as UM
Item
{
id: toolboxDownloadsGridTile
property int packageCount: (toolbox.viewCategory == "material" && model.type === undefined) ? toolbox.getTotalNumberOfMaterialPackagesByAuthor(model.id) : 1
property int installedPackages: (toolbox.viewCategory == "material" && model.type === undefined) ? toolbox.getNumberOfInstalledPackagesByAuthor(model.id) : (toolbox.isInstalled(model.id) ? 1 : 0)
height: childrenRect.height

View file

@ -21,11 +21,13 @@ Item
left: parent.left
leftMargin: UM.Theme.getSize("default_margin").width
}
ToolboxTabButton
{
id: pluginsTabButton
text: catalog.i18nc("@title:tab", "Plugins")
active: toolbox.viewCategory == "plugin" && enabled
enabled: toolbox.viewPage != "loading" && toolbox.viewPage != "errored"
enabled: !toolbox.isDownloading && toolbox.viewPage != "loading" && toolbox.viewPage != "errored"
onClicked:
{
toolbox.filterModelByProp("packages", "type", "plugin")
@ -34,12 +36,12 @@ Item
}
}
/* // NOTE: Remember to re-enable for v3.6!
ToolboxTabButton
{
id: materialsTabButton
text: catalog.i18nc("@title:tab", "Materials")
active: toolbox.viewCategory == "material" && enabled
enabled: toolbox.viewPage != "loading" && toolbox.viewPage != "errored"
enabled: !toolbox.isDownloading && toolbox.viewPage != "loading" && toolbox.viewPage != "errored"
onClicked:
{
toolbox.filterModelByProp("authors", "package_types", "material")
@ -47,12 +49,13 @@ Item
toolbox.viewPage = "overview"
}
}
*/
}
ToolboxTabButton
{
id: installedTabButton
text: catalog.i18nc("@title:tab", "Installed")
active: toolbox.viewCategory == "installed"
enabled: !toolbox.isDownloading
anchors
{
right: parent.right

View file

@ -603,7 +603,7 @@ class Toolbox(QObject, Extension):
@pyqtSlot()
def cancelDownload(self) -> None:
Logger.log("i", "Toolbox: User cancelled the download of a plugin.")
Logger.log("i", "Toolbox: User cancelled the download of a package.")
self.resetDownload()
def resetDownload(self) -> None:
@ -755,6 +755,7 @@ class Toolbox(QObject, Extension):
self._active_package = package
self.activePackageChanged.emit()
## The active package is the package that is currently being downloaded
@pyqtProperty(QObject, fset = setActivePackage, notify = activePackageChanged)
def activePackage(self) -> Optional[Dict[str, Any]]:
return self._active_package

View file

@ -1,5 +1,6 @@
#Copyright (c) 2018 Ultimaker B.V.
#Cura is released under the terms of the LGPLv3 or higher.
from typing import cast
from Charon.VirtualFile import VirtualFile #To open UFP files.
@ -9,6 +10,7 @@ from io import StringIO #For converting g-code to bytes.
from UM.Application import Application
from UM.Logger import Logger
from UM.Mesh.MeshWriter import MeshWriter #The writer we need to implement.
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
from UM.PluginRegistry import PluginRegistry #To get the g-code writer.
from PyQt5.QtCore import QBuffer
@ -22,6 +24,15 @@ catalog = i18nCatalog("cura")
class UFPWriter(MeshWriter):
def __init__(self):
super().__init__(add_to_recent_files = False)
MimeTypeDatabase.addMimeType(
MimeType(
name = "application/x-cura-stl-file",
comment = "Cura UFP File",
suffixes = ["ufp"]
)
)
self._snapshot = None
Application.getInstance().getOutputDeviceManager().writeStarted.connect(self._createSnapshot)

View file

@ -11,16 +11,6 @@ except ImportError:
from UM.i18n import i18nCatalog #To translate the file format description.
from UM.Mesh.MeshWriter import MeshWriter #For the binary mode flag.
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
MimeTypeDatabase.addMimeType(
MimeType(
name = "application/x-cura-stl-file",
comment = "Cura UFP File",
suffixes = ["ufp"]
)
)
i18n_catalog = i18nCatalog("cura")

View file

@ -260,6 +260,19 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
# or "Legacy" UM3 device.
cluster_size = int(properties.get(b"cluster_size", -1))
printer_type = properties.get(b"machine", b"").decode("utf-8")
printer_type_identifiers = {
"9066": "ultimaker3",
"9511": "ultimaker3_extended",
"9051": "ultimaker_s5"
}
for key, value in printer_type_identifiers.items():
if printer_type.startswith(key):
properties[b"printer_type"] = bytes(value, encoding="utf8")
break
else:
properties[b"printer_type"] = b"Unknown"
if cluster_size >= 0:
device = ClusterUM3OutputDevice.ClusterUM3OutputDevice(name, address, properties)
else:

View file

@ -86,6 +86,12 @@ class VersionUpgrade34to35(VersionUpgrade):
parser = configparser.ConfigParser(interpolation = None)
parser.read_string(serialized)
# Need to show the data collection agreement again because the data Cura collects has been changed.
if parser.has_option("info", "asked_send_slice_info"):
parser.set("info", "asked_send_slice_info", "False")
if parser.has_option("info", "send_slice_info"):
parser.set("info", "send_slice_info", "True")
# Update version number.
parser["general"]["version"] = "6"
if "metadata" not in parser:

View file

@ -17,6 +17,10 @@ test_upgrade_version_nr_data = [
version = 5
[metadata]
setting_version = 4
[info]
asked_send_slice_info = True
send_slice_info = True
"""
)
]
@ -32,4 +36,8 @@ def test_upgradeVersionNr(test_name, file_data, upgrader):
#Check the new version.
assert parser["general"]["version"] == "6"
assert parser["metadata"]["setting_version"] == "5"
assert parser["metadata"]["setting_version"] == "5"
# Check if the data collection values have been reset to their defaults
assert parser.get("info", "asked_send_slice_info") == "False"
assert parser.get("info", "send_slice_info") == "True"