mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-08-14 11:05:57 +08:00
Merge branch 'master' into feature_enable_disable_extruder
This commit is contained in:
commit
859ac88357
@ -102,6 +102,13 @@ class MaterialManager(QObject):
|
|||||||
# GUID -> material group list
|
# GUID -> material group list
|
||||||
self._guid_material_groups_map = defaultdict(list)
|
self._guid_material_groups_map = defaultdict(list)
|
||||||
for root_material_id, material_group in self._material_group_map.items():
|
for root_material_id, material_group in self._material_group_map.items():
|
||||||
|
# This can happen when we are updating with incomplete data.
|
||||||
|
if material_group.root_material_node is None:
|
||||||
|
Logger.log("e", "Missing root material node for [%s]. Probably caused by update using incomplete data."
|
||||||
|
" Check all related signals for further debugging.",
|
||||||
|
material_group.name)
|
||||||
|
# Do nothing here, we wait for a next signal to trigger an update.
|
||||||
|
return
|
||||||
guid = material_group.root_material_node.metadata["GUID"]
|
guid = material_group.root_material_node.metadata["GUID"]
|
||||||
self._guid_material_groups_map[guid].append(material_group)
|
self._guid_material_groups_map[guid].append(material_group)
|
||||||
|
|
||||||
|
@ -40,8 +40,6 @@ class BaseMaterialsModel(ListModel):
|
|||||||
self._extruder_position = 0
|
self._extruder_position = 0
|
||||||
self._extruder_stack = None
|
self._extruder_stack = None
|
||||||
|
|
||||||
self._machine_manager.globalContainerChanged.connect(self._updateExtruderStack)
|
|
||||||
|
|
||||||
def _updateExtruderStack(self):
|
def _updateExtruderStack(self):
|
||||||
global_stack = self._machine_manager.activeMachine
|
global_stack = self._machine_manager.activeMachine
|
||||||
if global_stack is None:
|
if global_stack is None:
|
||||||
|
@ -6,6 +6,7 @@ from UM.Application import Application
|
|||||||
|
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from UM.i18n import i18nCatalog
|
from UM.i18n import i18nCatalog
|
||||||
|
from UM.Signal import postponeSignals, CompressTechnique
|
||||||
from UM.Settings.ContainerStack import ContainerStack
|
from UM.Settings.ContainerStack import ContainerStack
|
||||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||||
from UM.Settings.InstanceContainer import InstanceContainer
|
from UM.Settings.InstanceContainer import InstanceContainer
|
||||||
@ -434,6 +435,24 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||||||
# \param file_name
|
# \param file_name
|
||||||
@call_on_qt_thread
|
@call_on_qt_thread
|
||||||
def read(self, file_name):
|
def read(self, file_name):
|
||||||
|
container_registry = ContainerRegistry.getInstance()
|
||||||
|
signals = [container_registry.containerAdded,
|
||||||
|
container_registry.containerRemoved,
|
||||||
|
container_registry.containerMetaDataChanged]
|
||||||
|
#
|
||||||
|
# We now have different managers updating their lookup tables upon container changes. It is critical to make
|
||||||
|
# sure that the managers have a complete set of data when they update.
|
||||||
|
#
|
||||||
|
# In project loading, lots of the container-related signals are loosely emitted, which can create timing gaps
|
||||||
|
# for incomplete data update or other kinds of issues to happen.
|
||||||
|
#
|
||||||
|
# To avoid this, we postpone all signals so they don't get emitted immediately. But, please also be aware that,
|
||||||
|
# because of this, do not expect to have the latest data in the lookup tables in project loading.
|
||||||
|
#
|
||||||
|
with postponeSignals(*signals, compress = CompressTechnique.CompressSingle):
|
||||||
|
return self._read(file_name)
|
||||||
|
|
||||||
|
def _read(self, file_name):
|
||||||
archive = zipfile.ZipFile(file_name, "r")
|
archive = zipfile.ZipFile(file_name, "r")
|
||||||
|
|
||||||
cura_file_names = [name for name in archive.namelist() if name.startswith("Cura/")]
|
cura_file_names = [name for name in archive.namelist() if name.startswith("Cura/")]
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
# Copyright (c) 2017 Ultimaker B.V.
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
from UM.Workspace.WorkspaceWriter import WorkspaceWriter
|
import configparser
|
||||||
|
from io import StringIO
|
||||||
|
import zipfile
|
||||||
|
|
||||||
from UM.Application import Application
|
from UM.Application import Application
|
||||||
|
from UM.Logger import Logger
|
||||||
from UM.Preferences import Preferences
|
from UM.Preferences import Preferences
|
||||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
from UM.Workspace.WorkspaceWriter import WorkspaceWriter
|
||||||
import zipfile
|
|
||||||
from io import StringIO
|
|
||||||
import configparser
|
|
||||||
|
|
||||||
|
|
||||||
class ThreeMFWorkspaceWriter(WorkspaceWriter):
|
class ThreeMFWorkspaceWriter(WorkspaceWriter):
|
||||||
@ -16,7 +17,10 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def write(self, stream, nodes, mode=WorkspaceWriter.OutputMode.BinaryMode):
|
def write(self, stream, nodes, mode=WorkspaceWriter.OutputMode.BinaryMode):
|
||||||
mesh_writer = Application.getInstance().getMeshFileHandler().getWriter("3MFWriter")
|
application = Application.getInstance()
|
||||||
|
machine_manager = application.getMachineManager()
|
||||||
|
|
||||||
|
mesh_writer = application.getMeshFileHandler().getWriter("3MFWriter")
|
||||||
|
|
||||||
if not mesh_writer: # We need to have the 3mf mesh writer, otherwise we can't save the entire workspace
|
if not mesh_writer: # We need to have the 3mf mesh writer, otherwise we can't save the entire workspace
|
||||||
return False
|
return False
|
||||||
@ -29,17 +33,17 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
|
|||||||
if archive is None: # This happens if there was no mesh data to write.
|
if archive is None: # This happens if there was no mesh data to write.
|
||||||
archive = zipfile.ZipFile(stream, "w", compression = zipfile.ZIP_DEFLATED)
|
archive = zipfile.ZipFile(stream, "w", compression = zipfile.ZIP_DEFLATED)
|
||||||
|
|
||||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
global_stack = machine_manager.activeMachine
|
||||||
|
|
||||||
# Add global container stack data to the archive.
|
# Add global container stack data to the archive.
|
||||||
self._writeContainerToArchive(global_container_stack, archive)
|
self._writeContainerToArchive(global_stack, archive)
|
||||||
|
|
||||||
# Also write all containers in the stack to the file
|
# Also write all containers in the stack to the file
|
||||||
for container in global_container_stack.getContainers():
|
for container in global_stack.getContainers():
|
||||||
self._writeContainerToArchive(container, archive)
|
self._writeContainerToArchive(container, archive)
|
||||||
|
|
||||||
# Check if the machine has extruders and save all that data as well.
|
# Check if the machine has extruders and save all that data as well.
|
||||||
for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(global_container_stack.getId()):
|
for extruder_stack in global_stack.extruders.values():
|
||||||
self._writeContainerToArchive(extruder_stack, archive)
|
self._writeContainerToArchive(extruder_stack, archive)
|
||||||
for container in extruder_stack.getContainers():
|
for container in extruder_stack.getContainers():
|
||||||
self._writeContainerToArchive(container, archive)
|
self._writeContainerToArchive(container, archive)
|
||||||
@ -59,9 +63,9 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
|
|||||||
version_file = zipfile.ZipInfo("Cura/version.ini")
|
version_file = zipfile.ZipInfo("Cura/version.ini")
|
||||||
version_config_parser = configparser.ConfigParser(interpolation = None)
|
version_config_parser = configparser.ConfigParser(interpolation = None)
|
||||||
version_config_parser.add_section("versions")
|
version_config_parser.add_section("versions")
|
||||||
version_config_parser.set("versions", "cura_version", Application.getInstance().getVersion())
|
version_config_parser.set("versions", "cura_version", application.getVersion())
|
||||||
version_config_parser.set("versions", "build_type", Application.getInstance().getBuildType())
|
version_config_parser.set("versions", "build_type", application.getBuildType())
|
||||||
version_config_parser.set("versions", "is_debug_mode", str(Application.getInstance().getIsDebugMode()))
|
version_config_parser.set("versions", "is_debug_mode", str(application.getIsDebugMode()))
|
||||||
|
|
||||||
version_file_string = StringIO()
|
version_file_string = StringIO()
|
||||||
version_config_parser.write(version_file_string)
|
version_config_parser.write(version_file_string)
|
||||||
@ -85,7 +89,8 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
|
|||||||
# Some containers have a base file, which should then be the file to use.
|
# Some containers have a base file, which should then be the file to use.
|
||||||
if "base_file" in container.getMetaData():
|
if "base_file" in container.getMetaData():
|
||||||
base_file = container.getMetaDataEntry("base_file")
|
base_file = container.getMetaDataEntry("base_file")
|
||||||
container = ContainerRegistry.getInstance().findContainers(id = base_file)[0]
|
if base_file != container.getId():
|
||||||
|
container = ContainerRegistry.getInstance().findContainers(id = base_file)[0]
|
||||||
|
|
||||||
file_name = "Cura/%s.%s" % (container.getId(), file_suffix)
|
file_name = "Cura/%s.%s" % (container.getId(), file_suffix)
|
||||||
|
|
||||||
|
@ -2,17 +2,15 @@
|
|||||||
# under the terms of the AGPLv3 or higher
|
# under the terms of the AGPLv3 or higher
|
||||||
|
|
||||||
from ..Script import Script
|
from ..Script import Script
|
||||||
#from UM.Logger import Logger
|
|
||||||
# from cura.Settings.ExtruderManager import ExtruderManager
|
|
||||||
|
|
||||||
class ColorChange(Script):
|
class FilamentChange(Script):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def getSettingDataString(self):
|
def getSettingDataString(self):
|
||||||
return """{
|
return """{
|
||||||
"name":"Color Change",
|
"name":"Filament Change",
|
||||||
"key": "ColorChange",
|
"key": "FilamentChange",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"settings":
|
"settings":
|
||||||
@ -60,17 +58,17 @@ class ColorChange(Script):
|
|||||||
if later_retract is not None and later_retract > 0.:
|
if later_retract is not None and later_retract > 0.:
|
||||||
color_change = color_change + (" L%.2f" % later_retract)
|
color_change = color_change + (" L%.2f" % later_retract)
|
||||||
|
|
||||||
color_change = color_change + " ; Generated by ColorChange plugin"
|
color_change = color_change + " ; Generated by FilamentChange plugin"
|
||||||
|
|
||||||
layer_targets = layer_nums.split(',')
|
layer_targets = layer_nums.split(",")
|
||||||
if len(layer_targets) > 0:
|
if len(layer_targets) > 0:
|
||||||
for layer_num in layer_targets:
|
for layer_num in layer_targets:
|
||||||
layer_num = int( layer_num.strip() )
|
layer_num = int(layer_num.strip())
|
||||||
if layer_num < len(data):
|
if layer_num < len(data):
|
||||||
layer = data[ layer_num - 1 ]
|
layer = data[layer_num - 1]
|
||||||
lines = layer.split("\n")
|
lines = layer.split("\n")
|
||||||
lines.insert(2, color_change )
|
lines.insert(2, color_change)
|
||||||
final_line = "\n".join( lines )
|
final_line = "\n".join(lines)
|
||||||
data[ layer_num - 1 ] = final_line
|
data[layer_num - 1] = final_line
|
||||||
|
|
||||||
return data
|
return data
|
@ -93,13 +93,15 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||||||
|
|
||||||
self._gcode = gcode_list
|
self._gcode = gcode_list
|
||||||
|
|
||||||
|
is_job_sent = True
|
||||||
if len(self._printers) > 1:
|
if len(self._printers) > 1:
|
||||||
self._spawnPrinterSelectionDialog()
|
self._spawnPrinterSelectionDialog()
|
||||||
else:
|
else:
|
||||||
self.sendPrintJob()
|
is_job_sent = self.sendPrintJob()
|
||||||
|
|
||||||
# Notify the UI that a switch to the print monitor should happen
|
# Notify the UI that a switch to the print monitor should happen
|
||||||
Application.getInstance().getController().setActiveStage("MonitorStage")
|
if is_job_sent:
|
||||||
|
Application.getInstance().getController().setActiveStage("MonitorStage")
|
||||||
|
|
||||||
def _spawnPrinterSelectionDialog(self):
|
def _spawnPrinterSelectionDialog(self):
|
||||||
if self._printer_selection_dialog is None:
|
if self._printer_selection_dialog is None:
|
||||||
@ -121,7 +123,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||||||
i18n_catalog.i18nc("@info:status",
|
i18n_catalog.i18nc("@info:status",
|
||||||
"Sending new jobs (temporarily) blocked, still sending the previous print job."))
|
"Sending new jobs (temporarily) blocked, still sending the previous print job."))
|
||||||
self._error_message.show()
|
self._error_message.show()
|
||||||
return
|
return False
|
||||||
|
|
||||||
self._sending_gcode = True
|
self._sending_gcode = True
|
||||||
|
|
||||||
@ -134,7 +136,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||||||
compressed_gcode = self._compressGCode()
|
compressed_gcode = self._compressGCode()
|
||||||
if compressed_gcode is None:
|
if compressed_gcode is None:
|
||||||
# Abort was called.
|
# Abort was called.
|
||||||
return
|
return False
|
||||||
|
|
||||||
parts = []
|
parts = []
|
||||||
|
|
||||||
@ -152,6 +154,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||||||
|
|
||||||
self._latest_reply_handler = self.postFormWithParts("print_jobs/", parts, onFinished=self._onPostPrintJobFinished, onProgress=self._onUploadPrintJobProgress)
|
self._latest_reply_handler = self.postFormWithParts("print_jobs/", parts, onFinished=self._onPostPrintJobFinished, onProgress=self._onUploadPrintJobProgress)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
@pyqtProperty(QObject, notify=activePrinterChanged)
|
@pyqtProperty(QObject, notify=activePrinterChanged)
|
||||||
def activePrinter(self) -> Optional["PrinterOutputModel"]:
|
def activePrinter(self) -> Optional["PrinterOutputModel"]:
|
||||||
return self._active_printer
|
return self._active_printer
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2016 Ultimaker B.V.
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
@ -17,7 +17,7 @@ from .avr_isp import stk500v2, intelHex
|
|||||||
|
|
||||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty
|
from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty
|
||||||
|
|
||||||
from serial import Serial, SerialException
|
from serial import Serial, SerialException, SerialTimeoutException
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from time import time, sleep
|
from time import time, sleep
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
@ -266,8 +266,11 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||||||
command = (command + "\n").encode()
|
command = (command + "\n").encode()
|
||||||
if not command.endswith(b"\n"):
|
if not command.endswith(b"\n"):
|
||||||
command += b"\n"
|
command += b"\n"
|
||||||
self._serial.write(b"\n")
|
try:
|
||||||
self._serial.write(command)
|
self._serial.write(b"\n")
|
||||||
|
self._serial.write(command)
|
||||||
|
except SerialTimeoutException:
|
||||||
|
Logger.log("w", "Timeout when sending command to printer via USB.")
|
||||||
|
|
||||||
def _update(self):
|
def _update(self):
|
||||||
while self._connection_state == ConnectionState.connected and self._serial is not None:
|
while self._connection_state == ConnectionState.connected and self._serial is not None:
|
||||||
|
@ -200,18 +200,25 @@ class XmlMaterialProfile(InstanceContainer):
|
|||||||
## Begin Settings Block
|
## Begin Settings Block
|
||||||
builder.start("settings")
|
builder.start("settings")
|
||||||
|
|
||||||
if self.getDefinition().getId() == "fdmprinter":
|
if self.getMetaDataEntry("definition") == "fdmprinter":
|
||||||
for instance in self.findInstances():
|
for instance in self.findInstances():
|
||||||
self._addSettingElement(builder, instance)
|
self._addSettingElement(builder, instance)
|
||||||
|
|
||||||
machine_container_map = {}
|
machine_container_map = {}
|
||||||
machine_nozzle_map = {}
|
machine_nozzle_map = {}
|
||||||
|
|
||||||
variant_manager = CuraApplication.getInstance()._variant_manager
|
variant_manager = CuraApplication.getInstance().getVariantManager()
|
||||||
|
material_manager = CuraApplication.getInstance().getMaterialManager()
|
||||||
|
|
||||||
|
root_material_id = self.getMetaDataEntry("base_file") # if basefile is self.getId, this is a basefile.
|
||||||
|
material_group = material_manager.getMaterialGroup(root_material_id)
|
||||||
|
|
||||||
|
all_containers = []
|
||||||
|
for node in [material_group.root_material_node] + material_group.derived_material_node_list:
|
||||||
|
all_containers.append(node.getContainer())
|
||||||
|
|
||||||
all_containers = registry.findInstanceContainers(GUID = self.getMetaDataEntry("GUID"), base_file = self.getId())
|
|
||||||
for container in all_containers:
|
for container in all_containers:
|
||||||
definition_id = container.getDefinition().getId()
|
definition_id = container.getMetaDataEntry("definition")
|
||||||
if definition_id == "fdmprinter":
|
if definition_id == "fdmprinter":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -233,7 +240,8 @@ class XmlMaterialProfile(InstanceContainer):
|
|||||||
product_id_map = self.getProductIdMap()
|
product_id_map = self.getProductIdMap()
|
||||||
|
|
||||||
for definition_id, container in machine_container_map.items():
|
for definition_id, container in machine_container_map.items():
|
||||||
definition = container.getDefinition()
|
definition_id = container.getMetaDataEntry("definition")
|
||||||
|
definition_metadata = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = definition_id)[0]
|
||||||
|
|
||||||
product = definition_id
|
product = definition_id
|
||||||
for product_name, product_id_list in product_id_map.items():
|
for product_name, product_id_list in product_id_map.items():
|
||||||
@ -243,13 +251,14 @@ class XmlMaterialProfile(InstanceContainer):
|
|||||||
|
|
||||||
builder.start("machine")
|
builder.start("machine")
|
||||||
builder.start("machine_identifier", {
|
builder.start("machine_identifier", {
|
||||||
"manufacturer": container.getMetaDataEntry("machine_manufacturer", definition.getMetaDataEntry("manufacturer", "Unknown")),
|
"manufacturer": container.getMetaDataEntry("machine_manufacturer",
|
||||||
|
definition_metadata.get("manufacturer", "Unknown")),
|
||||||
"product": product
|
"product": product
|
||||||
})
|
})
|
||||||
builder.end("machine_identifier")
|
builder.end("machine_identifier")
|
||||||
|
|
||||||
for instance in container.findInstances():
|
for instance in container.findInstances():
|
||||||
if self.getDefinition().getId() == "fdmprinter" and self.getInstance(instance.definition.key) and self.getProperty(instance.definition.key, "value") == instance.value:
|
if self.getMetaDataEntry("definition") == "fdmprinter" and self.getInstance(instance.definition.key) and self.getProperty(instance.definition.key, "value") == instance.value:
|
||||||
# If the settings match that of the base profile, just skip since we inherit the base profile.
|
# If the settings match that of the base profile, just skip since we inherit the base profile.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user