mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-23 22:54:01 -06:00
Merge branch 'master' into CURA-5570_beyond_comprehension
This commit is contained in:
commit
de1b8edb72
51 changed files with 527 additions and 92 deletions
|
@ -25,6 +25,9 @@ except ImportError:
|
|||
import zipfile
|
||||
import UM.Application
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
class ThreeMFWriter(MeshWriter):
|
||||
def __init__(self):
|
||||
|
@ -173,6 +176,7 @@ class ThreeMFWriter(MeshWriter):
|
|||
archive.writestr(relations_file, b'<?xml version="1.0" encoding="UTF-8"?> \n' + ET.tostring(relations_element))
|
||||
except Exception as e:
|
||||
Logger.logException("e", "Error writing zip file")
|
||||
self.setInformation(catalog.i18nc("@error:zip", "Error writing 3mf file."))
|
||||
return False
|
||||
finally:
|
||||
if not self._store_archive:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import argparse #To run the engine in debug mode if the front-end is in debug mode.
|
||||
from collections import defaultdict
|
||||
import os
|
||||
from PyQt5.QtCore import QObject, QTimer, pyqtSlot
|
||||
|
@ -179,7 +180,15 @@ class CuraEngineBackend(QObject, Backend):
|
|||
# \return list of commands and args / parameters.
|
||||
def getEngineCommand(self) -> List[str]:
|
||||
json_path = Resources.getPath(Resources.DefinitionContainers, "fdmprinter.def.json")
|
||||
return [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), "-j", json_path, ""]
|
||||
|
||||
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.")
|
||||
known_args = vars(parser.parse_known_args()[0])
|
||||
if known_args["debug"]:
|
||||
command.append("-vvv")
|
||||
|
||||
return command
|
||||
|
||||
## Emitted when we get a message containing print duration and material amount.
|
||||
# This also implies the slicing has finished.
|
||||
|
@ -541,6 +550,9 @@ class CuraEngineBackend(QObject, Backend):
|
|||
|
||||
## Remove old layer data (if any)
|
||||
def _clearLayerData(self, build_plate_numbers: Set = None) -> None:
|
||||
# Clear out any old gcode
|
||||
self._scene.gcode_dict = {} # type: ignore
|
||||
|
||||
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
|
||||
if node.callDecoration("getLayerData"):
|
||||
if not build_plate_numbers or node.callDecoration("getBuildPlateNumber") in build_plate_numbers:
|
||||
|
|
|
@ -41,7 +41,7 @@ class StartJobResult(IntEnum):
|
|||
|
||||
## Formatter class that handles token expansion in start/end gcode
|
||||
class GcodeStartEndFormatter(Formatter):
|
||||
def get_value(self, key: str, *args: str, default_extruder_nr: str = "-1", **kwargs) -> str: #type: ignore # [CodeStyle: get_value is an overridden function from the Formatter class]
|
||||
def get_value(self, key: str, args: str, kwargs: dict, default_extruder_nr: str = "-1") -> str: #type: ignore # [CodeStyle: get_value is an overridden function from the Formatter class]
|
||||
# The kwargs dictionary contains a dictionary for each stack (with a string of the extruder_nr as their key),
|
||||
# and a default_extruder_nr to use when no extruder_nr is specified
|
||||
|
||||
|
|
|
@ -10,6 +10,9 @@ from UM.Mesh.MeshWriter import MeshWriter #The class we're extending/implementin
|
|||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Scene.SceneNode import SceneNode #For typing.
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
## A file writer that writes gzipped g-code.
|
||||
#
|
||||
# If you're zipping g-code, you might as well use gzip!
|
||||
|
@ -28,12 +31,15 @@ class GCodeGzWriter(MeshWriter):
|
|||
def write(self, stream: BufferedIOBase, nodes: List[SceneNode], mode = MeshWriter.OutputMode.BinaryMode) -> bool:
|
||||
if mode != MeshWriter.OutputMode.BinaryMode:
|
||||
Logger.log("e", "GCodeGzWriter does not support text mode.")
|
||||
self.setInformation(catalog.i18nc("@error:not supported", "GCodeGzWriter does not support text mode."))
|
||||
return False
|
||||
|
||||
#Get the g-code from the g-code writer.
|
||||
gcode_textio = StringIO() #We have to convert the g-code into bytes.
|
||||
success = cast(MeshWriter, PluginRegistry.getInstance().getPluginObject("GCodeWriter")).write(gcode_textio, None)
|
||||
gcode_writer = cast(MeshWriter, PluginRegistry.getInstance().getPluginObject("GCodeWriter"))
|
||||
success = gcode_writer.write(gcode_textio, None)
|
||||
if not success: #Writing the g-code failed. Then I can also not write the gzipped g-code.
|
||||
self.setInformation(gcode_writer.getInformation())
|
||||
return False
|
||||
|
||||
result = gzip.compress(gcode_textio.getvalue().encode("utf-8"))
|
||||
|
|
|
@ -12,6 +12,8 @@ from UM.Settings.InstanceContainer import InstanceContainer
|
|||
|
||||
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
## Writes g-code to a file.
|
||||
#
|
||||
|
@ -62,11 +64,13 @@ class GCodeWriter(MeshWriter):
|
|||
def write(self, stream, nodes, mode = MeshWriter.OutputMode.TextMode):
|
||||
if mode != MeshWriter.OutputMode.TextMode:
|
||||
Logger.log("e", "GCodeWriter does not support non-text mode.")
|
||||
self.setInformation(catalog.i18nc("@error:not supported", "GCodeWriter does not support non-text mode."))
|
||||
return False
|
||||
|
||||
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."))
|
||||
return False
|
||||
gcode_dict = getattr(scene, "gcode_dict")
|
||||
gcode_list = gcode_dict.get(active_build_plate, None)
|
||||
|
@ -82,6 +86,7 @@ class GCodeWriter(MeshWriter):
|
|||
stream.write(settings)
|
||||
return True
|
||||
|
||||
self.setInformation(catalog.i18nc("@warning:status", "Please generate G-code before saving."))
|
||||
return False
|
||||
|
||||
## Create a new container with container 2 as base and container 1 written over it.
|
||||
|
|
|
@ -40,33 +40,37 @@ Item {
|
|||
|
||||
property bool layersVisible: true
|
||||
|
||||
function getUpperValueFromSliderHandle () {
|
||||
function getUpperValueFromSliderHandle() {
|
||||
return upperHandle.getValue()
|
||||
}
|
||||
|
||||
function setUpperValue (value) {
|
||||
function setUpperValue(value) {
|
||||
upperHandle.setValue(value)
|
||||
updateRangeHandle()
|
||||
}
|
||||
|
||||
function getLowerValueFromSliderHandle () {
|
||||
function getLowerValueFromSliderHandle() {
|
||||
return lowerHandle.getValue()
|
||||
}
|
||||
|
||||
function setLowerValue (value) {
|
||||
function setLowerValue(value) {
|
||||
lowerHandle.setValue(value)
|
||||
updateRangeHandle()
|
||||
}
|
||||
|
||||
function updateRangeHandle () {
|
||||
function updateRangeHandle() {
|
||||
rangeHandle.height = lowerHandle.y - (upperHandle.y + upperHandle.height)
|
||||
}
|
||||
|
||||
// set the active handle to show only one label at a time
|
||||
function setActiveHandle (handle) {
|
||||
function setActiveHandle(handle) {
|
||||
activeHandle = handle
|
||||
}
|
||||
|
||||
function normalizeValue(value) {
|
||||
return Math.min(Math.max(value, sliderRoot.minimumValue), sliderRoot.maximumValue)
|
||||
}
|
||||
|
||||
// slider track
|
||||
Rectangle {
|
||||
id: track
|
||||
|
@ -188,6 +192,8 @@ Item {
|
|||
|
||||
// set the slider position based on the upper value
|
||||
function setValue (value) {
|
||||
// Normalize values between range, since using arrow keys will create out-of-the-range values
|
||||
value = sliderRoot.normalizeValue(value)
|
||||
|
||||
UM.SimulationView.setCurrentLayer(value)
|
||||
|
||||
|
@ -274,6 +280,8 @@ Item {
|
|||
|
||||
// set the slider position based on the lower value
|
||||
function setValue (value) {
|
||||
// Normalize values between range, since using arrow keys will create out-of-the-range values
|
||||
value = sliderRoot.normalizeValue(value)
|
||||
|
||||
UM.SimulationView.setMinimumLayer(value)
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ Item {
|
|||
|
||||
// value properties
|
||||
property real maximumValue: 100
|
||||
property real minimumValue: 0
|
||||
property bool roundValues: true
|
||||
property real handleValue: maximumValue
|
||||
|
||||
|
@ -47,6 +48,10 @@ Item {
|
|||
rangeHandle.width = handle.x - sliderRoot.handleSize
|
||||
}
|
||||
|
||||
function normalizeValue(value) {
|
||||
return Math.min(Math.max(value, sliderRoot.minimumValue), sliderRoot.maximumValue)
|
||||
}
|
||||
|
||||
// slider track
|
||||
Rectangle {
|
||||
id: track
|
||||
|
@ -110,6 +115,8 @@ Item {
|
|||
|
||||
// set the slider position based on the value
|
||||
function setValue (value) {
|
||||
// Normalize values between range, since using arrow keys will create out-of-the-range values
|
||||
value = sliderRoot.normalizeValue(value)
|
||||
|
||||
UM.SimulationView.setCurrentPath(value)
|
||||
|
||||
|
|
|
@ -25,10 +25,12 @@ from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
|
|||
|
||||
from UM.Settings.SettingInstance import SettingInstance
|
||||
|
||||
import numpy
|
||||
|
||||
class SupportEraser(Tool):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._shortcut_key = Qt.Key_G
|
||||
self._shortcut_key = Qt.Key_E
|
||||
self._controller = self.getController()
|
||||
|
||||
self._selection_pass = None
|
||||
|
@ -96,8 +98,7 @@ class SupportEraser(Tool):
|
|||
|
||||
node.setName("Eraser")
|
||||
node.setSelectable(True)
|
||||
mesh = MeshBuilder()
|
||||
mesh.addCube(10,10,10)
|
||||
mesh = self._createCube(10)
|
||||
node.setMeshData(mesh.build())
|
||||
|
||||
active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
|
||||
|
@ -160,3 +161,28 @@ class SupportEraser(Tool):
|
|||
self._skip_press = False
|
||||
|
||||
self._had_selection = has_selection
|
||||
|
||||
def _createCube(self, size):
|
||||
mesh = MeshBuilder()
|
||||
|
||||
# Can't use MeshBuilder.addCube() because that does not get per-vertex normals
|
||||
# Per-vertex normals require duplication of vertices
|
||||
s = size / 2
|
||||
verts = [ # 6 faces with 4 corners each
|
||||
[-s, -s, s], [-s, s, s], [ s, s, s], [ s, -s, s],
|
||||
[-s, s, -s], [-s, -s, -s], [ s, -s, -s], [ s, s, -s],
|
||||
[ s, -s, -s], [-s, -s, -s], [-s, -s, s], [ s, -s, s],
|
||||
[-s, s, -s], [ s, s, -s], [ s, s, s], [-s, s, s],
|
||||
[-s, -s, s], [-s, -s, -s], [-s, s, -s], [-s, s, s],
|
||||
[ s, -s, -s], [ s, -s, s], [ s, s, s], [ s, s, -s]
|
||||
]
|
||||
mesh.setVertices(numpy.asarray(verts, dtype=numpy.float32))
|
||||
|
||||
indices = []
|
||||
for i in range(0, 24, 4): # All 6 quads (12 triangles)
|
||||
indices.append([i, i+2, i+1])
|
||||
indices.append([i, i+3, i+2])
|
||||
mesh.setIndices(numpy.asarray(indices, dtype=numpy.int32))
|
||||
|
||||
mesh.calculateNormals()
|
||||
return mesh
|
||||
|
|
|
@ -29,6 +29,16 @@ Item
|
|||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width
|
||||
frameVisible: false
|
||||
|
||||
// Workaround for scroll issues (QTBUG-49652)
|
||||
flickableItem.interactive: false
|
||||
Component.onCompleted:
|
||||
{
|
||||
for (var i = 0; i < flickableItem.children.length; ++i)
|
||||
{
|
||||
flickableItem.children[i].enabled = false
|
||||
}
|
||||
}
|
||||
selectionMode: 0
|
||||
model: packageData.supported_configs
|
||||
headerDelegate: Rectangle
|
||||
|
|
|
@ -114,7 +114,10 @@ Item
|
|||
else
|
||||
{
|
||||
toolbox.viewPage = "author"
|
||||
toolbox.filterModelByProp("packages", "author_id", model.id)
|
||||
toolbox.setFilters("packages", {
|
||||
"author_id": model.id,
|
||||
"type": "material"
|
||||
})
|
||||
}
|
||||
break
|
||||
default:
|
||||
|
|
|
@ -105,8 +105,21 @@ Rectangle
|
|||
switch(toolbox.viewCategory)
|
||||
{
|
||||
case "material":
|
||||
toolbox.viewPage = "author"
|
||||
toolbox.filterModelByProp("packages", "author_name", model.name)
|
||||
|
||||
// If model has a type, it must be a package
|
||||
if (model.type !== undefined)
|
||||
{
|
||||
toolbox.viewPage = "detail"
|
||||
toolbox.filterModelByProp("packages", "id", model.id)
|
||||
}
|
||||
else
|
||||
{
|
||||
toolbox.viewPage = "author"
|
||||
toolbox.setFilters("packages", {
|
||||
"author_id": model.id,
|
||||
"type": "material"
|
||||
})
|
||||
}
|
||||
break
|
||||
default:
|
||||
toolbox.viewPage = "detail"
|
||||
|
|
|
@ -776,17 +776,25 @@ class Toolbox(QObject, Extension):
|
|||
# Filter Models:
|
||||
# --------------------------------------------------------------------------
|
||||
@pyqtSlot(str, str, str)
|
||||
def filterModelByProp(self, modelType: str, filterType: str, parameter: str):
|
||||
if not self._models[modelType]:
|
||||
Logger.log("w", "Toolbox: Couldn't filter %s model because it doesn't exist.", modelType)
|
||||
def filterModelByProp(self, model_type: str, filter_type: str, parameter: str) -> None:
|
||||
if not self._models[model_type]:
|
||||
Logger.log("w", "Toolbox: Couldn't filter %s model because it doesn't exist.", model_type)
|
||||
return
|
||||
self._models[modelType].setFilter({ filterType: parameter })
|
||||
self._models[model_type].setFilter({filter_type: parameter})
|
||||
self.filterChanged.emit()
|
||||
|
||||
@pyqtSlot()
|
||||
def removeFilters(self, modelType: str):
|
||||
if not self._models[modelType]:
|
||||
Logger.log("w", "Toolbox: Couldn't remove filters on %s model because it doesn't exist.", modelType)
|
||||
@pyqtSlot(str, "QVariantMap")
|
||||
def setFilters(self, model_type: str, filter_dict: dict) -> None:
|
||||
if not self._models[model_type]:
|
||||
Logger.log("w", "Toolbox: Couldn't filter %s model because it doesn't exist.", model_type)
|
||||
return
|
||||
self._models[modelType].setFilter({})
|
||||
self._models[model_type].setFilter(filter_dict)
|
||||
self.filterChanged.emit()
|
||||
|
||||
@pyqtSlot(str)
|
||||
def removeFilters(self, model_type: str) -> None:
|
||||
if not self._models[model_type]:
|
||||
Logger.log("w", "Toolbox: Couldn't remove filters on %s model because it doesn't exist.", model_type)
|
||||
return
|
||||
self._models[model_type].setFilter({})
|
||||
self.filterChanged.emit()
|
||||
|
|
|
@ -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.
|
||||
from Charon.OpenMode import OpenMode #To indicate that we want to write to UFP files.
|
||||
|
@ -13,6 +14,9 @@ from PyQt5.QtCore import QBuffer
|
|||
|
||||
from cura.Snapshot import Snapshot
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
class UFPWriter(MeshWriter):
|
||||
def __init__(self):
|
||||
|
@ -32,7 +36,11 @@ class UFPWriter(MeshWriter):
|
|||
#Store the g-code from the scene.
|
||||
archive.addContentType(extension = "gcode", mime_type = "text/x-gcode")
|
||||
gcode_textio = StringIO() #We have to convert the g-code into bytes.
|
||||
PluginRegistry.getInstance().getPluginObject("GCodeWriter").write(gcode_textio, None)
|
||||
gcode_writer = cast(MeshWriter, PluginRegistry.getInstance().getPluginObject("GCodeWriter"))
|
||||
success = gcode_writer.write(gcode_textio, None)
|
||||
if not success: #Writing the g-code failed. Then I can also not write the gzipped g-code.
|
||||
self.setInformation(gcode_writer.getInformation())
|
||||
return False
|
||||
gcode = archive.getStream("/3D/model.gcode")
|
||||
gcode.write(gcode_textio.getvalue().encode("UTF-8"))
|
||||
archive.addRelation(virtual_path = "/3D/model.gcode", relation_type = "http://schemas.ultimaker.org/package/2018/relationships/gcode")
|
||||
|
|
|
@ -30,7 +30,12 @@ Component
|
|||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.right:parent.right
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
text: Cura.MachineManager.printerOutputDevices[0].name
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Rectangle
|
||||
|
|
|
@ -9,6 +9,7 @@ Component
|
|||
{
|
||||
Rectangle
|
||||
{
|
||||
id: monitorFrame
|
||||
width: maximumWidth
|
||||
height: maximumHeight
|
||||
color: UM.Theme.getColor("viewport_background")
|
||||
|
@ -103,5 +104,15 @@ Component
|
|||
visible: OutputDevice.activePrinter != null
|
||||
anchors.fill:parent
|
||||
}
|
||||
|
||||
onVisibleChanged:
|
||||
{
|
||||
if (!monitorFrame.visible)
|
||||
{
|
||||
// After switching the Tab ensure that active printer is Null, the video stream image
|
||||
// might be active
|
||||
OutputDevice.setActivePrinter(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -299,11 +299,11 @@ Cura.MachineAction
|
|||
}
|
||||
else if (base.selectedDevice.clusterSize === 0)
|
||||
{
|
||||
return catalog.i18nc("@label", "This printer is not set up to host a group of Ultimaker 3 printers.");
|
||||
return catalog.i18nc("@label", "This printer is not set up to host a group of printers.");
|
||||
}
|
||||
else
|
||||
{
|
||||
return catalog.i18nc("@label", "This printer is the host for a group of %1 Ultimaker 3 printers.".arg(base.selectedDevice.clusterSize));
|
||||
return catalog.i18nc("@label", "This printer is the host for a group of %1 printers.".arg(base.selectedDevice.clusterSize));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -349,11 +349,6 @@ Cura.MachineAction
|
|||
addressField.focus = true;
|
||||
}
|
||||
|
||||
onAccepted:
|
||||
{
|
||||
manager.setManualDevice(printerKey, addressText)
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
spacing: UM.Theme.getSize("default_margin").height
|
||||
|
@ -393,7 +388,7 @@ Cura.MachineAction
|
|||
text: catalog.i18nc("@action:button", "OK")
|
||||
onClicked:
|
||||
{
|
||||
manualPrinterDialog.accept()
|
||||
manager.setManualDevice(manualPrinterDialog.printerKey, manualPrinterDialog.addressText)
|
||||
manualPrinterDialog.hide()
|
||||
}
|
||||
enabled: manualPrinterDialog.addressText.trim() != ""
|
||||
|
|
|
@ -89,9 +89,11 @@ Item
|
|||
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill: cameraImage
|
||||
onClicked: { /* no-op */ }
|
||||
z: 1
|
||||
anchors.fill: cameraImage
|
||||
onClicked:
|
||||
{
|
||||
OutputDevice.setActivePrinter(null)
|
||||
}
|
||||
z: 1
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -39,12 +39,12 @@ class SendMaterialJob(Job):
|
|||
try:
|
||||
remote_materials_list = json.loads(remote_materials_list)
|
||||
except json.JSONDecodeError:
|
||||
Logger.log("e", "Current material storage on printer was a corrupted reply.")
|
||||
Logger.log("e", "Request material storage on printer: I didn't understand the printer's answer.")
|
||||
return
|
||||
try:
|
||||
remote_materials_by_guid = {material["guid"]: material for material in remote_materials_list} #Index by GUID.
|
||||
except KeyError:
|
||||
Logger.log("e", "Current material storage on printer was an invalid reply (missing GUIDs).")
|
||||
Logger.log("e", "Request material storage on printer: Printer's answer was missing GUIDs.")
|
||||
return
|
||||
|
||||
container_registry = ContainerRegistry.getInstance()
|
||||
|
|
|
@ -198,7 +198,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
|
|||
has_cluster_capable_firmware = Version(system_info["firmware"]) > self._min_cluster_version
|
||||
instance_name = "manual:%s" % address
|
||||
properties = {
|
||||
b"name": system_info["name"].encode("utf-8"),
|
||||
b"name": (system_info["name"] + " (manual)").encode("utf-8"),
|
||||
b"address": address.encode("utf-8"),
|
||||
b"firmware_version": system_info["firmware"].encode("utf-8"),
|
||||
b"manual": b"true",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.Job import Job
|
||||
|
@ -21,7 +21,6 @@ class AutoDetectBaudJob(Job):
|
|||
|
||||
def run(self):
|
||||
Logger.log("d", "Auto detect baud rate started.")
|
||||
timeout = 3
|
||||
wait_response_timeouts = [3, 15, 30]
|
||||
wait_bootloader_times = [1.5, 5, 15]
|
||||
write_timeout = 3
|
||||
|
@ -52,7 +51,7 @@ class AutoDetectBaudJob(Job):
|
|||
if serial is None:
|
||||
try:
|
||||
serial = Serial(str(self._serial_port), baud_rate, timeout = read_timeout, writeTimeout = write_timeout)
|
||||
except SerialException as e:
|
||||
except SerialException:
|
||||
Logger.logException("w", "Unable to create serial")
|
||||
continue
|
||||
else:
|
||||
|
|
|
@ -15,7 +15,7 @@ from cura.PrinterOutput.GenericOutputController import GenericOutputController
|
|||
from .AutoDetectBaudJob import AutoDetectBaudJob
|
||||
from .avr_isp import stk500v2, intelHex
|
||||
|
||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty
|
||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty, QUrl
|
||||
|
||||
from serial import Serial, SerialException, SerialTimeoutException
|
||||
from threading import Thread, Event
|
||||
|
@ -146,8 +146,11 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
|
||||
@pyqtSlot(str)
|
||||
def updateFirmware(self, file):
|
||||
# the file path is qurl encoded.
|
||||
self._firmware_location = file.replace("file://", "")
|
||||
# the file path could be url-encoded.
|
||||
if file.startswith("file://"):
|
||||
self._firmware_location = QUrl(file).toLocalFile()
|
||||
else:
|
||||
self._firmware_location = file
|
||||
self.showFirmwareInterface()
|
||||
self.setFirmwareUpdateState(FirmwareUpdateState.updating)
|
||||
self._update_firmware_thread.start()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue