mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-08-07 05:53:59 -06:00
Merge branch 'master' into cura4.0_header
This commit is contained in:
commit
b670cd82c4
106 changed files with 1289 additions and 947 deletions
|
@ -1012,7 +1012,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
|
||||
## Get the list of ID's of all containers in a container stack by partially parsing it's serialized data.
|
||||
def _getContainerIdListFromSerialized(self, serialized):
|
||||
parser = ConfigParser(interpolation=None, empty_lines_in_values=False)
|
||||
parser = ConfigParser(interpolation = None, empty_lines_in_values = False)
|
||||
parser.read_string(serialized)
|
||||
|
||||
container_ids = []
|
||||
|
@ -1033,7 +1033,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
return container_ids
|
||||
|
||||
def _getMachineNameFromSerializedStack(self, serialized):
|
||||
parser = ConfigParser(interpolation=None, empty_lines_in_values=False)
|
||||
parser = ConfigParser(interpolation = None, empty_lines_in_values = False)
|
||||
parser.read_string(serialized)
|
||||
return parser["general"].get("name", "")
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ from . import ThreeMFWorkspaceWriter
|
|||
from UM.i18n import i18nCatalog
|
||||
from UM.Platform import Platform
|
||||
|
||||
i18n_catalog = i18nCatalog("uranium")
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
def getMetaData():
|
||||
workspace_extension = "3mf"
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
from . import ChangeLog
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
def getMetaData():
|
||||
return {}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import gc
|
||||
import sys
|
||||
|
||||
from UM.Job import Job
|
||||
from UM.Application import Application
|
||||
|
@ -95,23 +96,35 @@ class ProcessSlicedLayersJob(Job):
|
|||
layer_count = len(self._layers)
|
||||
|
||||
# Find the minimum layer number
|
||||
# When disabling the remove empty first layers setting, the minimum layer number will be a positive
|
||||
# value. In that case the first empty layers will be discarded and start processing layers from the
|
||||
# first layer with data.
|
||||
# When using a raft, the raft layers are sent as layers < 0. Instead of allowing layers < 0, we
|
||||
# instead simply offset all other layers so the lowest layer is always 0. It could happens that
|
||||
# the first raft layer has value -8 but there are just 4 raft (negative) layers.
|
||||
min_layer_number = 0
|
||||
# simply offset all other layers so the lowest layer is always 0. It could happens that the first
|
||||
# raft layer has value -8 but there are just 4 raft (negative) layers.
|
||||
min_layer_number = sys.maxsize
|
||||
negative_layers = 0
|
||||
for layer in self._layers:
|
||||
if layer.id < min_layer_number:
|
||||
min_layer_number = layer.id
|
||||
if layer.id < 0:
|
||||
negative_layers += 1
|
||||
if layer.repeatedMessageCount("path_segment") > 0:
|
||||
if layer.id < min_layer_number:
|
||||
min_layer_number = layer.id
|
||||
if layer.id < 0:
|
||||
negative_layers += 1
|
||||
|
||||
current_layer = 0
|
||||
|
||||
for layer in self._layers:
|
||||
# Negative layers are offset by the minimum layer number, but the positive layers are just
|
||||
# offset by the number of negative layers so there is no layer gap between raft and model
|
||||
abs_layer_number = layer.id + abs(min_layer_number) if layer.id < 0 else layer.id + negative_layers
|
||||
# If the layer is below the minimum, it means that there is no data, so that we don't create a layer
|
||||
# data. However, if there are empty layers in between, we compute them.
|
||||
if layer.id < min_layer_number:
|
||||
continue
|
||||
|
||||
# Layers are offset by the minimum layer number. In case the raft (negative layers) is being used,
|
||||
# then the absolute layer number is adjusted by removing the empty layers that can be in between raft
|
||||
# and the model
|
||||
abs_layer_number = layer.id - min_layer_number
|
||||
if layer.id >= 0 and negative_layers != 0:
|
||||
abs_layer_number += (min_layer_number + negative_layers)
|
||||
|
||||
layer_data.addLayer(abs_layer_number)
|
||||
this_layer = layer_data.getLayer(abs_layer_number)
|
||||
|
|
|
@ -41,11 +41,15 @@ class StartJobResult(IntEnum):
|
|||
|
||||
## Formatter class that handles token expansion in start/end gcode
|
||||
class GcodeStartEndFormatter(Formatter):
|
||||
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]
|
||||
def __init__(self, default_extruder_nr: int = -1) -> None:
|
||||
super().__init__()
|
||||
self._default_extruder_nr = default_extruder_nr
|
||||
|
||||
def get_value(self, key: str, args: str, kwargs: dict) -> 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
|
||||
|
||||
extruder_nr = int(default_extruder_nr)
|
||||
extruder_nr = self._default_extruder_nr
|
||||
|
||||
key_fragments = [fragment.strip() for fragment in key.split(",")]
|
||||
if len(key_fragments) == 2:
|
||||
|
@ -247,7 +251,10 @@ class StartSliceJob(Job):
|
|||
self._buildGlobalInheritsStackMessage(stack)
|
||||
|
||||
# Build messages for extruder stacks
|
||||
for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(stack.getId()):
|
||||
# Send the extruder settings in the order of extruder positions. Somehow, if you send e.g. extruder 3 first,
|
||||
# then CuraEngine can slice with the wrong settings. This I think should be fixed in CuraEngine as well.
|
||||
extruder_stack_list = sorted(list(global_stack.extruders.items()), key = lambda item: int(item[0]))
|
||||
for _, extruder_stack in extruder_stack_list:
|
||||
self._buildExtruderMessage(extruder_stack)
|
||||
|
||||
for group in filtered_object_groups:
|
||||
|
@ -339,7 +346,7 @@ class StartSliceJob(Job):
|
|||
|
||||
try:
|
||||
# any setting can be used as a token
|
||||
fmt = GcodeStartEndFormatter()
|
||||
fmt = GcodeStartEndFormatter(default_extruder_nr = default_extruder_nr)
|
||||
settings = self._all_extruders_settings.copy()
|
||||
settings["default_extruder_nr"] = default_extruder_nr
|
||||
return str(fmt.format(value, **settings))
|
||||
|
|
|
@ -50,7 +50,7 @@ class CuraProfileReader(ProfileReader):
|
|||
# \param profile_id \type{str} The name of the profile.
|
||||
# \return \type{List[Tuple[str,str]]} List of serialized profile strings and matching profile names.
|
||||
def _upgradeProfile(self, serialized, profile_id):
|
||||
parser = configparser.ConfigParser(interpolation=None)
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialized)
|
||||
|
||||
if "general" not in parser:
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import os
|
||||
from PyQt5.QtCore import QUrl
|
||||
from PyQt5.QtGui import QDesktopServices
|
||||
|
||||
from typing import Set
|
||||
|
||||
from UM.Extension import Extension
|
||||
from UM.Application import Application
|
||||
from UM.Logger import Logger
|
||||
|
@ -13,6 +16,7 @@ from UM.Settings.ContainerRegistry import ContainerRegistry
|
|||
from cura.Settings.GlobalStack import GlobalStack
|
||||
|
||||
from .FirmwareUpdateCheckerJob import FirmwareUpdateCheckerJob
|
||||
from .FirmwareUpdateCheckerMessage import FirmwareUpdateCheckerMessage
|
||||
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
|
@ -21,32 +25,31 @@ i18n_catalog = i18nCatalog("cura")
|
|||
# The plugin is currently only usable for applications maintained by Ultimaker. But it should be relatively easy
|
||||
# to change it to work for other applications.
|
||||
class FirmwareUpdateChecker(Extension):
|
||||
JEDI_VERSION_URL = "http://software.ultimaker.com/jedi/releases/latest.version?utm_source=cura&utm_medium=software&utm_campaign=resources"
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
# Initialize the Preference called `latest_checked_firmware` that stores the last version
|
||||
# checked for the UM3. In the future if we need to check other printers' firmware
|
||||
Application.getInstance().getPreferences().addPreference("info/latest_checked_firmware", "")
|
||||
|
||||
# Listen to a Signal that indicates a change in the list of printers, just if the user has enabled the
|
||||
# 'check for updates' option
|
||||
# "check for updates" option
|
||||
Application.getInstance().getPreferences().addPreference("info/automatic_update_check", True)
|
||||
if Application.getInstance().getPreferences().getValue("info/automatic_update_check"):
|
||||
ContainerRegistry.getInstance().containerAdded.connect(self._onContainerAdded)
|
||||
|
||||
self._download_url = None
|
||||
self._check_job = None
|
||||
self._checked_printer_names = set() # type: Set[str]
|
||||
|
||||
## Callback for the message that is spawned when there is a new version.
|
||||
def _onActionTriggered(self, message, action):
|
||||
if action == "download":
|
||||
if self._download_url is not None:
|
||||
QDesktopServices.openUrl(QUrl(self._download_url))
|
||||
|
||||
def _onSetDownloadUrl(self, download_url):
|
||||
self._download_url = download_url
|
||||
if action == FirmwareUpdateCheckerMessage.STR_ACTION_DOWNLOAD:
|
||||
machine_id = message.getMachineId()
|
||||
download_url = message.getDownloadUrl()
|
||||
if download_url is not None:
|
||||
if QDesktopServices.openUrl(QUrl(download_url)):
|
||||
Logger.log("i", "Redirected browser to {0} to show newly available firmware.".format(download_url))
|
||||
else:
|
||||
Logger.log("e", "Can't reach URL: {0}".format(download_url))
|
||||
else:
|
||||
Logger.log("e", "Can't find URL for {0}".format(machine_id))
|
||||
|
||||
def _onContainerAdded(self, container):
|
||||
# Only take care when a new GlobalStack was added
|
||||
|
@ -63,13 +66,18 @@ class FirmwareUpdateChecker(Extension):
|
|||
# \param silent type(boolean) Suppresses messages other than "new version found" messages.
|
||||
# This is used when checking for a new firmware version at startup.
|
||||
def checkFirmwareVersion(self, container = None, silent = False):
|
||||
# Do not run multiple check jobs in parallel
|
||||
if self._check_job is not None:
|
||||
Logger.log("i", "A firmware update check is already running, do nothing.")
|
||||
container_name = container.definition.getName()
|
||||
if container_name in self._checked_printer_names:
|
||||
return
|
||||
self._checked_printer_names.add(container_name)
|
||||
|
||||
metadata = container.definition.getMetaData().get("firmware_update_info")
|
||||
if metadata is None:
|
||||
Logger.log("i", "No machine with name {0} in list of firmware to check.".format(container_name))
|
||||
return
|
||||
|
||||
self._check_job = FirmwareUpdateCheckerJob(container = container, silent = silent, url = self.JEDI_VERSION_URL,
|
||||
callback = self._onActionTriggered,
|
||||
set_download_url_callback = self._onSetDownloadUrl)
|
||||
self._check_job = FirmwareUpdateCheckerJob(container = container, silent = silent,
|
||||
machine_name = container_name, metadata = metadata,
|
||||
callback = self._onActionTriggered)
|
||||
self._check_job.start()
|
||||
self._check_job.finished.connect(self._onJobFinished)
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
# 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.Application import Application
|
||||
from UM.Message import Message
|
||||
from UM.Logger import Logger
|
||||
from UM.Job import Job
|
||||
from UM.Version import Version
|
||||
|
||||
import urllib.request
|
||||
import codecs
|
||||
from urllib.error import URLError
|
||||
from typing import Dict, Optional
|
||||
|
||||
from .FirmwareUpdateCheckerLookup import FirmwareUpdateCheckerLookup, getSettingsKeyForMachine
|
||||
from .FirmwareUpdateCheckerMessage import FirmwareUpdateCheckerMessage
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
@ -15,46 +20,86 @@ i18n_catalog = i18nCatalog("cura")
|
|||
|
||||
## This job checks if there is an update available on the provided URL.
|
||||
class FirmwareUpdateCheckerJob(Job):
|
||||
def __init__(self, container = None, silent = False, url = None, callback = None, set_download_url_callback = None):
|
||||
STRING_ZERO_VERSION = "0.0.0"
|
||||
STRING_EPSILON_VERSION = "0.0.1"
|
||||
ZERO_VERSION = Version(STRING_ZERO_VERSION)
|
||||
EPSILON_VERSION = Version(STRING_EPSILON_VERSION)
|
||||
|
||||
def __init__(self, container, silent, machine_name, metadata, callback) -> None:
|
||||
super().__init__()
|
||||
self._container = container
|
||||
self.silent = silent
|
||||
self._url = url
|
||||
self._callback = callback
|
||||
self._set_download_url_callback = set_download_url_callback
|
||||
|
||||
def run(self):
|
||||
if not self._url:
|
||||
Logger.log("e", "Can not check for a new release. URL not set!")
|
||||
return
|
||||
self._machine_name = machine_name
|
||||
self._metadata = metadata
|
||||
self._lookups = None # type:Optional[FirmwareUpdateCheckerLookup]
|
||||
self._headers = {} # type:Dict[str, str] # Don't set headers yet.
|
||||
|
||||
def getUrlResponse(self, url: str) -> str:
|
||||
result = self.STRING_ZERO_VERSION
|
||||
|
||||
try:
|
||||
request = urllib.request.Request(url, headers = self._headers)
|
||||
response = urllib.request.urlopen(request)
|
||||
result = response.read().decode("utf-8")
|
||||
except URLError:
|
||||
Logger.log("w", "Could not reach '{0}', if this URL is old, consider removal.".format(url))
|
||||
|
||||
return result
|
||||
|
||||
def parseVersionResponse(self, response: str) -> Version:
|
||||
raw_str = response.split("\n", 1)[0].rstrip()
|
||||
return Version(raw_str)
|
||||
|
||||
def getCurrentVersion(self) -> Version:
|
||||
max_version = self.ZERO_VERSION
|
||||
if self._lookups is None:
|
||||
return max_version
|
||||
|
||||
machine_urls = self._lookups.getCheckUrls()
|
||||
if machine_urls is not None:
|
||||
for url in machine_urls:
|
||||
version = self.parseVersionResponse(self.getUrlResponse(url))
|
||||
if version > max_version:
|
||||
max_version = version
|
||||
|
||||
if max_version < self.EPSILON_VERSION:
|
||||
Logger.log("w", "MachineID {0} not handled!".format(self._lookups.getMachineName()))
|
||||
|
||||
return max_version
|
||||
|
||||
def run(self):
|
||||
if self._lookups is None:
|
||||
self._lookups = FirmwareUpdateCheckerLookup(self._machine_name, self._metadata)
|
||||
|
||||
try:
|
||||
# Initialize a Preference that stores the last version checked for this printer.
|
||||
Application.getInstance().getPreferences().addPreference(
|
||||
getSettingsKeyForMachine(self._lookups.getMachineId()), "")
|
||||
|
||||
# Get headers
|
||||
application_name = Application.getInstance().getApplicationName()
|
||||
headers = {"User-Agent": "%s - %s" % (application_name, Application.getInstance().getVersion())}
|
||||
request = urllib.request.Request(self._url, headers = headers)
|
||||
current_version_file = urllib.request.urlopen(request)
|
||||
reader = codecs.getreader("utf-8")
|
||||
application_version = Application.getInstance().getVersion()
|
||||
self._headers = {"User-Agent": "%s - %s" % (application_name, application_version)}
|
||||
|
||||
# get machine name from the definition container
|
||||
machine_name = self._container.definition.getName()
|
||||
machine_name_parts = machine_name.lower().split(" ")
|
||||
|
||||
# If it is not None, then we compare between the checked_version and the current_version
|
||||
# Now we just do that if the active printer is Ultimaker 3 or Ultimaker 3 Extended or any
|
||||
# other Ultimaker 3 that will come in the future
|
||||
if len(machine_name_parts) >= 2 and machine_name_parts[:2] == ["ultimaker", "3"]:
|
||||
Logger.log("i", "You have a UM3 in printer list. Let's check the firmware!")
|
||||
machine_id = self._lookups.getMachineId()
|
||||
if machine_id is not None:
|
||||
Logger.log("i", "You have a(n) {0} in the printer list. Let's check the firmware!".format(machine_name))
|
||||
|
||||
# Nothing to parse, just get the string
|
||||
# TODO: In the future may be done by parsing a JSON file with diferent version for each printer model
|
||||
current_version = reader(current_version_file).readline().rstrip()
|
||||
current_version = self.getCurrentVersion()
|
||||
|
||||
# If it is the first time the version is checked, the checked_version is ''
|
||||
checked_version = Application.getInstance().getPreferences().getValue("info/latest_checked_firmware")
|
||||
# If it is the first time the version is checked, the checked_version is ""
|
||||
setting_key_str = getSettingsKeyForMachine(machine_id)
|
||||
checked_version = Version(Application.getInstance().getPreferences().getValue(setting_key_str))
|
||||
|
||||
# If the checked_version is '', it's because is the first time we check firmware and in this case
|
||||
# If the checked_version is "", it's because is the first time we check firmware and in this case
|
||||
# we will not show the notification, but we will store it for the next time
|
||||
Application.getInstance().getPreferences().setValue("info/latest_checked_firmware", current_version)
|
||||
Application.getInstance().getPreferences().setValue(setting_key_str, current_version)
|
||||
Logger.log("i", "Reading firmware version of %s: checked = %s - latest = %s", machine_name, checked_version, current_version)
|
||||
|
||||
# The first time we want to store the current version, the notification will not be shown,
|
||||
|
@ -62,28 +107,11 @@ class FirmwareUpdateCheckerJob(Job):
|
|||
# notify the user when no new firmware version is available.
|
||||
if (checked_version != "") and (checked_version != current_version):
|
||||
Logger.log("i", "SHOWING FIRMWARE UPDATE MESSAGE")
|
||||
|
||||
message = Message(i18n_catalog.i18nc(
|
||||
"@info Don't translate {machine_name}, since it gets replaced by a printer name!",
|
||||
"New features are available for your {machine_name}! It is recommended to update the firmware on your printer.").format(
|
||||
machine_name=machine_name),
|
||||
title=i18n_catalog.i18nc(
|
||||
"@info:title The %s gets replaced with the printer name.",
|
||||
"New %s firmware available") % machine_name)
|
||||
|
||||
message.addAction("download",
|
||||
i18n_catalog.i18nc("@action:button", "How to update"),
|
||||
"[no_icon]",
|
||||
"[no_description]",
|
||||
button_style=Message.ActionButtonStyle.LINK,
|
||||
button_align=Message.ActionButtonStyle.BUTTON_ALIGN_LEFT)
|
||||
|
||||
|
||||
# If we do this in a cool way, the download url should be available in the JSON file
|
||||
if self._set_download_url_callback:
|
||||
self._set_download_url_callback("https://ultimaker.com/en/resources/20500-upgrade-firmware")
|
||||
message = FirmwareUpdateCheckerMessage(machine_id, machine_name, self._lookups.getRedirectUserUrl())
|
||||
message.actionTriggered.connect(self._callback)
|
||||
message.show()
|
||||
else:
|
||||
Logger.log("i", "No machine with name {0} in list of firmware to check.".format(machine_name))
|
||||
|
||||
except Exception as e:
|
||||
Logger.log("w", "Failed to check for new version: %s", e)
|
||||
|
|
35
plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerLookup.py
Normal file
35
plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerLookup.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
def getSettingsKeyForMachine(machine_id: int) -> str:
|
||||
return "info/latest_checked_firmware_for_{0}".format(machine_id)
|
||||
|
||||
|
||||
class FirmwareUpdateCheckerLookup:
|
||||
|
||||
def __init__(self, machine_name, machine_json) -> None:
|
||||
# Parse all the needed lookup-tables from the ".json" file(s) in the resources folder.
|
||||
self._machine_id = machine_json.get("id")
|
||||
self._machine_name = machine_name.lower() # Lower in-case upper-case chars are added to the original json.
|
||||
self._check_urls = [] # type:List[str]
|
||||
for check_url in machine_json.get("check_urls"):
|
||||
self._check_urls.append(check_url)
|
||||
self._redirect_user = machine_json.get("update_url")
|
||||
|
||||
def getMachineId(self) -> Optional[int]:
|
||||
return self._machine_id
|
||||
|
||||
def getMachineName(self) -> Optional[int]:
|
||||
return self._machine_name
|
||||
|
||||
def getCheckUrls(self) -> Optional[List[str]]:
|
||||
return self._check_urls
|
||||
|
||||
def getRedirectUserUrl(self) -> Optional[str]:
|
||||
return self._redirect_user
|
|
@ -0,0 +1,37 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Message import Message
|
||||
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
# Make a separate class, since we need an extra field: The machine-id that this message is about.
|
||||
class FirmwareUpdateCheckerMessage(Message):
|
||||
STR_ACTION_DOWNLOAD = "download"
|
||||
|
||||
def __init__(self, machine_id: int, machine_name: str, download_url: str) -> None:
|
||||
super().__init__(i18n_catalog.i18nc(
|
||||
"@info Don't translate {machine_name}, since it gets replaced by a printer name!",
|
||||
"New features are available for your {machine_name}! It is recommended to update the firmware on your printer.").format(
|
||||
machine_name = machine_name),
|
||||
title = i18n_catalog.i18nc(
|
||||
"@info:title The %s gets replaced with the printer name.",
|
||||
"New %s firmware available") % machine_name)
|
||||
|
||||
self._machine_id = machine_id
|
||||
self._download_url = download_url
|
||||
|
||||
self.addAction(self.STR_ACTION_DOWNLOAD,
|
||||
i18n_catalog.i18nc("@action:button", "How to update"),
|
||||
"[no_icon]",
|
||||
"[no_description]",
|
||||
button_style = Message.ActionButtonStyle.LINK,
|
||||
button_align = Message.ActionButtonStyle.BUTTON_ALIGN_LEFT)
|
||||
|
||||
def getMachineId(self) -> int:
|
||||
return self._machine_id
|
||||
|
||||
def getDownloadUrl(self) -> str:
|
||||
return self._download_url
|
|
@ -1,12 +1,8 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
from . import FirmwareUpdateChecker
|
||||
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
def getMetaData():
|
||||
return {}
|
||||
|
|
69
plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.py
Normal file
69
plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.py
Normal file
|
@ -0,0 +1,69 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
from cura.MachineAction import MachineAction
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from cura.PrinterOutput.FirmwareUpdater import FirmwareUpdateState
|
||||
|
||||
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject
|
||||
from typing import Optional
|
||||
|
||||
MYPY = False
|
||||
if MYPY:
|
||||
from cura.PrinterOutput.FirmwareUpdater import FirmwareUpdater
|
||||
from cura.PrinterOutput.PrinterOutputDevice import PrinterOutputDevice
|
||||
from UM.Settings.ContainerInterface import ContainerInterface
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
## Upgrade the firmware of a machine by USB with this action.
|
||||
class FirmwareUpdaterMachineAction(MachineAction):
|
||||
def __init__(self) -> None:
|
||||
super().__init__("UpgradeFirmware", catalog.i18nc("@action", "Update Firmware"))
|
||||
self._qml_url = "FirmwareUpdaterMachineAction.qml"
|
||||
ContainerRegistry.getInstance().containerAdded.connect(self._onContainerAdded)
|
||||
|
||||
self._active_output_device = None # type: Optional[PrinterOutputDevice]
|
||||
self._active_firmware_updater = None # type: Optional[FirmwareUpdater]
|
||||
|
||||
CuraApplication.getInstance().engineCreatedSignal.connect(self._onEngineCreated)
|
||||
|
||||
def _onEngineCreated(self) -> None:
|
||||
CuraApplication.getInstance().getMachineManager().outputDevicesChanged.connect(self._onOutputDevicesChanged)
|
||||
|
||||
def _onContainerAdded(self, container: "ContainerInterface") -> None:
|
||||
# Add this action as a supported action to all machine definitions if they support USB connection
|
||||
if isinstance(container, DefinitionContainer) and container.getMetaDataEntry("type") == "machine" and container.getMetaDataEntry("supports_usb_connection"):
|
||||
CuraApplication.getInstance().getMachineActionManager().addSupportedAction(container.getId(), self.getKey())
|
||||
|
||||
def _onOutputDevicesChanged(self) -> None:
|
||||
if self._active_output_device and self._active_output_device.activePrinter:
|
||||
self._active_output_device.activePrinter.getController().canUpdateFirmwareChanged.disconnect(self._onControllerCanUpdateFirmwareChanged)
|
||||
|
||||
output_devices = CuraApplication.getInstance().getMachineManager().printerOutputDevices
|
||||
self._active_output_device = output_devices[0] if output_devices else None
|
||||
|
||||
if self._active_output_device and self._active_output_device.activePrinter:
|
||||
self._active_output_device.activePrinter.getController().canUpdateFirmwareChanged.connect(self._onControllerCanUpdateFirmwareChanged)
|
||||
|
||||
self.outputDeviceCanUpdateFirmwareChanged.emit()
|
||||
|
||||
def _onControllerCanUpdateFirmwareChanged(self) -> None:
|
||||
self.outputDeviceCanUpdateFirmwareChanged.emit()
|
||||
|
||||
outputDeviceCanUpdateFirmwareChanged = pyqtSignal()
|
||||
@pyqtProperty(QObject, notify = outputDeviceCanUpdateFirmwareChanged)
|
||||
def firmwareUpdater(self) -> Optional["FirmwareUpdater"]:
|
||||
if self._active_output_device and self._active_output_device.activePrinter.getController().can_update_firmware:
|
||||
self._active_firmware_updater = self._active_output_device.getFirmwareUpdater()
|
||||
return self._active_firmware_updater
|
||||
|
||||
elif self._active_firmware_updater and self._active_firmware_updater.firmwareUpdateState not in [FirmwareUpdateState.idle, FirmwareUpdateState.completed]:
|
||||
# During a firmware update, the PrinterOutputDevice is disconnected but the FirmwareUpdater is still there
|
||||
return self._active_firmware_updater
|
||||
|
||||
self._active_firmware_updater = None
|
||||
return None
|
191
plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.qml
Normal file
191
plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.qml
Normal file
|
@ -0,0 +1,191 @@
|
|||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Window 2.1
|
||||
import QtQuick.Dialogs 1.2 // For filedialog
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
|
||||
Cura.MachineAction
|
||||
{
|
||||
anchors.fill: parent;
|
||||
property bool printerConnected: Cura.MachineManager.printerConnected
|
||||
property var activeOutputDevice: printerConnected ? Cura.MachineManager.printerOutputDevices[0] : null
|
||||
property bool canUpdateFirmware: activeOutputDevice ? activeOutputDevice.activePrinter.canUpdateFirmware : false
|
||||
|
||||
Column
|
||||
{
|
||||
id: firmwareUpdaterMachineAction
|
||||
anchors.fill: parent;
|
||||
UM.I18nCatalog { id: catalog; name:"cura"}
|
||||
spacing: UM.Theme.getSize("default_margin").height
|
||||
|
||||
Label
|
||||
{
|
||||
width: parent.width
|
||||
text: catalog.i18nc("@title", "Update Firmware")
|
||||
wrapMode: Text.WordWrap
|
||||
font.pointSize: 18
|
||||
}
|
||||
Label
|
||||
{
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label", "Firmware is the piece of software running directly on your 3D printer. This firmware controls the step motors, regulates the temperature and ultimately makes your printer work.")
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label", "The firmware shipping with new printers works, but new versions tend to have more features and improvements.");
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: childrenRect.width
|
||||
spacing: UM.Theme.getSize("default_margin").width
|
||||
property string firmwareName: Cura.MachineManager.activeMachine.getDefaultFirmwareName()
|
||||
Button
|
||||
{
|
||||
id: autoUpgradeButton
|
||||
text: catalog.i18nc("@action:button", "Automatically upgrade Firmware");
|
||||
enabled: parent.firmwareName != "" && canUpdateFirmware
|
||||
onClicked:
|
||||
{
|
||||
updateProgressDialog.visible = true;
|
||||
activeOutputDevice.updateFirmware(parent.firmwareName);
|
||||
}
|
||||
}
|
||||
Button
|
||||
{
|
||||
id: manualUpgradeButton
|
||||
text: catalog.i18nc("@action:button", "Upload custom Firmware");
|
||||
enabled: canUpdateFirmware
|
||||
onClicked:
|
||||
{
|
||||
customFirmwareDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
visible: !printerConnected && !updateProgressDialog.visible
|
||||
text: catalog.i18nc("@label", "Firmware can not be updated because there is no connection with the printer.");
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
visible: printerConnected && !canUpdateFirmware
|
||||
text: catalog.i18nc("@label", "Firmware can not be updated because the connection with the printer does not support upgrading firmware.");
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog
|
||||
{
|
||||
id: customFirmwareDialog
|
||||
title: catalog.i18nc("@title:window", "Select custom firmware")
|
||||
nameFilters: "Firmware image files (*.hex)"
|
||||
selectExisting: true
|
||||
onAccepted:
|
||||
{
|
||||
updateProgressDialog.visible = true;
|
||||
activeOutputDevice.updateFirmware(fileUrl);
|
||||
}
|
||||
}
|
||||
|
||||
UM.Dialog
|
||||
{
|
||||
id: updateProgressDialog
|
||||
|
||||
width: minimumWidth
|
||||
minimumWidth: 500 * screenScaleFactor
|
||||
height: minimumHeight
|
||||
minimumHeight: 100 * screenScaleFactor
|
||||
|
||||
modality: Qt.ApplicationModal
|
||||
|
||||
title: catalog.i18nc("@title:window","Firmware Update")
|
||||
|
||||
Column
|
||||
{
|
||||
anchors.fill: parent
|
||||
|
||||
Label
|
||||
{
|
||||
anchors
|
||||
{
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
text: {
|
||||
if(manager.firmwareUpdater == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
switch (manager.firmwareUpdater.firmwareUpdateState)
|
||||
{
|
||||
case 0:
|
||||
return ""; //Not doing anything (eg; idling)
|
||||
case 1:
|
||||
return catalog.i18nc("@label","Updating firmware.");
|
||||
case 2:
|
||||
return catalog.i18nc("@label","Firmware update completed.");
|
||||
case 3:
|
||||
return catalog.i18nc("@label","Firmware update failed due to an unknown error.");
|
||||
case 4:
|
||||
return catalog.i18nc("@label","Firmware update failed due to an communication error.");
|
||||
case 5:
|
||||
return catalog.i18nc("@label","Firmware update failed due to an input/output error.");
|
||||
case 6:
|
||||
return catalog.i18nc("@label","Firmware update failed due to missing firmware.");
|
||||
}
|
||||
}
|
||||
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
ProgressBar
|
||||
{
|
||||
id: prog
|
||||
value: (manager.firmwareUpdater != null) ? manager.firmwareUpdater.firmwareProgress : 0
|
||||
minimumValue: 0
|
||||
maximumValue: 100
|
||||
indeterminate:
|
||||
{
|
||||
if(manager.firmwareUpdater == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return manager.firmwareUpdater.firmwareProgress < 1 && manager.firmwareUpdater.firmwareProgress > 0;
|
||||
}
|
||||
anchors
|
||||
{
|
||||
left: parent.left;
|
||||
right: parent.right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rightButtons: [
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button","Close");
|
||||
enabled: (manager.firmwareUpdater != null) ? manager.firmwareUpdater.firmwareUpdateState != 1 : true;
|
||||
onClicked: updateProgressDialog.visible = false;
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
12
plugins/FirmwareUpdater/__init__.py
Normal file
12
plugins/FirmwareUpdater/__init__.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from . import FirmwareUpdaterMachineAction
|
||||
|
||||
def getMetaData():
|
||||
return {}
|
||||
|
||||
def register(app):
|
||||
return { "machine_action": [
|
||||
FirmwareUpdaterMachineAction.FirmwareUpdaterMachineAction()
|
||||
]}
|
8
plugins/FirmwareUpdater/plugin.json
Normal file
8
plugins/FirmwareUpdater/plugin.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "Firmware Updater",
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.0",
|
||||
"description": "Provides a machine actions for updating firmware.",
|
||||
"api": 5,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
|
@ -44,6 +44,7 @@ class FlavorParser:
|
|||
self._extruder_offsets = {} # type: Dict[int, List[float]] # Offsets for multi extruders. key is index, value is [x-offset, y-offset]
|
||||
self._current_layer_thickness = 0.2 # default
|
||||
self._filament_diameter = 2.85 # default
|
||||
self._previous_extrusion_value = 0.0 # keep track of the filament retractions
|
||||
|
||||
CuraApplication.getInstance().getPreferences().addPreference("gcodereader/show_caution", True)
|
||||
|
||||
|
@ -182,6 +183,7 @@ class FlavorParser:
|
|||
new_extrusion_value = params.e if self._is_absolute_extrusion else e[self._extruder_number] + params.e
|
||||
if new_extrusion_value > e[self._extruder_number]:
|
||||
path.append([x, y, z, f, new_extrusion_value + self._extrusion_length_offset[self._extruder_number], self._layer_type]) # extrusion
|
||||
self._previous_extrusion_value = new_extrusion_value
|
||||
else:
|
||||
path.append([x, y, z, f, new_extrusion_value + self._extrusion_length_offset[self._extruder_number], LayerPolygon.MoveRetractionType]) # retraction
|
||||
e[self._extruder_number] = new_extrusion_value
|
||||
|
@ -191,6 +193,12 @@ class FlavorParser:
|
|||
if z > self._previous_z and (z - self._previous_z < 1.5):
|
||||
self._current_layer_thickness = z - self._previous_z # allow a tiny overlap
|
||||
self._previous_z = z
|
||||
elif self._previous_extrusion_value > e[self._extruder_number]:
|
||||
path.append([x, y, z, f, e[self._extruder_number] + self._extrusion_length_offset[self._extruder_number], LayerPolygon.MoveRetractionType])
|
||||
|
||||
# This case only for initial start, for the first coordinate in GCode
|
||||
elif e[self._extruder_number] == 0 and self._previous_extrusion_value == 0:
|
||||
path.append([x, y, z, f, e[self._extruder_number] + self._extrusion_length_offset[self._extruder_number], LayerPolygon.MoveRetractionType])
|
||||
else:
|
||||
path.append([x, y, z, f, e[self._extruder_number] + self._extrusion_length_offset[self._extruder_number], LayerPolygon.MoveCombingType])
|
||||
return self._position(x, y, z, f, e)
|
||||
|
@ -235,6 +243,7 @@ class FlavorParser:
|
|||
position.e)
|
||||
|
||||
def processGCode(self, G: int, line: str, position: Position, path: List[List[Union[float, int]]]) -> Position:
|
||||
self._previous_extrusion_value = 0.0
|
||||
func = getattr(self, "_gCode%s" % G, None)
|
||||
line = line.split(";", 1)[0] # Remove comments (if any)
|
||||
if func is not None:
|
||||
|
|
|
@ -35,7 +35,7 @@ UM.Dialog
|
|||
width: parent.width
|
||||
|
||||
Label {
|
||||
text: catalog.i18nc("@action:label","Height (mm)")
|
||||
text: catalog.i18nc("@action:label", "Height (mm)")
|
||||
width: 150 * screenScaleFactor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ UM.Dialog
|
|||
width: parent.width
|
||||
|
||||
Label {
|
||||
text: catalog.i18nc("@action:label","Base (mm)")
|
||||
text: catalog.i18nc("@action:label", "Base (mm)")
|
||||
width: 150 * screenScaleFactor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ UM.Dialog
|
|||
width: parent.width
|
||||
|
||||
Label {
|
||||
text: catalog.i18nc("@action:label","Width (mm)")
|
||||
text: catalog.i18nc("@action:label", "Width (mm)")
|
||||
width: 150 * screenScaleFactor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ UM.Dialog
|
|||
width: parent.width
|
||||
|
||||
Label {
|
||||
text: catalog.i18nc("@action:label","Depth (mm)")
|
||||
text: catalog.i18nc("@action:label", "Depth (mm)")
|
||||
width: 150 * screenScaleFactor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ UM.Dialog
|
|||
width: parent.width
|
||||
|
||||
Label {
|
||||
text: catalog.i18nc("@action:label","Smoothing")
|
||||
text: catalog.i18nc("@action:label", "Smoothing")
|
||||
width: 150 * screenScaleFactor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
|
|
@ -152,7 +152,7 @@ class LegacyProfileReader(ProfileReader):
|
|||
profile.setDirty(True)
|
||||
|
||||
#Serialise and deserialise in order to perform the version upgrade.
|
||||
parser = configparser.ConfigParser(interpolation=None)
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
data = profile.serialize()
|
||||
parser.read_string(data)
|
||||
parser["general"]["version"] = "1"
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
from . import MachineSettingsAction
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
def getMetaData():
|
||||
return {}
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# This example is released under the terms of the AGPLv3 or higher.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from . import ModelChecker
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
def getMetaData():
|
||||
return {}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
from . import MonitorStage
|
||||
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ UM.Dialog
|
|||
anchors.right: parent.right
|
||||
anchors.rightMargin: base.textMargin
|
||||
font: UM.Theme.getFont("large")
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
ListView
|
||||
{
|
||||
|
@ -115,6 +116,7 @@ UM.Dialog
|
|||
{
|
||||
wrapMode: Text.Wrap
|
||||
text: control.text
|
||||
elide: Text.ElideRight
|
||||
color: activeScriptButton.checked ? palette.highlightedText : palette.text
|
||||
}
|
||||
}
|
||||
|
@ -275,6 +277,7 @@ UM.Dialog
|
|||
anchors.leftMargin: base.textMargin
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: base.textMargin
|
||||
elide: Text.ElideRight
|
||||
height: 20 * screenScaleFactor
|
||||
font: UM.Theme.getFont("large")
|
||||
color: UM.Theme.getColor("text")
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
# The PostProcessingPlugin is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from . import PostProcessingPlugin
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
def getMetaData():
|
||||
return {}
|
||||
|
||||
|
||||
def register(app):
|
||||
return {"extension": PostProcessingPlugin.PostProcessingPlugin()}
|
|
@ -3,12 +3,10 @@
|
|||
|
||||
from UM.Platform import Platform
|
||||
from UM.Logger import Logger
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
def getMetaData():
|
||||
return {
|
||||
}
|
||||
return {}
|
||||
|
||||
def register(app):
|
||||
if Platform.isWindows():
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from . import SliceInfo
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
def getMetaData():
|
||||
return {
|
||||
}
|
||||
return {}
|
||||
|
||||
def register(app):
|
||||
return { "extension": SliceInfo.SliceInfo()}
|
|
@ -17,7 +17,7 @@ UM.Dialog
|
|||
// This dialog asks the user whether he/she wants to open a project file as a project or import models.
|
||||
id: base
|
||||
|
||||
title: catalog.i18nc("@title:window", "Confirm uninstall ") + toolbox.pluginToUninstall
|
||||
title: catalog.i18nc("@title:window", "Confirm uninstall") + toolbox.pluginToUninstall
|
||||
width: 450 * screenScaleFactor
|
||||
height: 50 * screenScaleFactor + dialogText.height + buttonBar.height
|
||||
|
||||
|
|
|
@ -27,14 +27,13 @@ class UFPWriter(MeshWriter):
|
|||
|
||||
MimeTypeDatabase.addMimeType(
|
||||
MimeType(
|
||||
name = "application/x-cura-stl-file",
|
||||
name = "application/x-ufp",
|
||||
comment = "Cura UFP File",
|
||||
suffixes = ["ufp"]
|
||||
)
|
||||
)
|
||||
|
||||
self._snapshot = None
|
||||
Application.getInstance().getOutputDeviceManager().writeStarted.connect(self._createSnapshot)
|
||||
|
||||
def _createSnapshot(self, *args):
|
||||
# must be called from the main thread because of OpenGL
|
||||
|
@ -62,6 +61,8 @@ class UFPWriter(MeshWriter):
|
|||
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")
|
||||
|
||||
self._createSnapshot()
|
||||
|
||||
#Store the thumbnail.
|
||||
if self._snapshot:
|
||||
archive.addContentType(extension = "png", mime_type = "image/png")
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from .src import DiscoverUM3Action
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
from .src import UM3OutputDevicePlugin
|
||||
|
||||
def getMetaData():
|
||||
|
|
|
@ -100,8 +100,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
title=i18n_catalog.i18nc("@info:title",
|
||||
"Authentication status"))
|
||||
|
||||
self._authentication_failed_message = Message(i18n_catalog.i18nc("@info:status", ""),
|
||||
title=i18n_catalog.i18nc("@info:title", "Authentication Status"))
|
||||
self._authentication_failed_message = Message("", title=i18n_catalog.i18nc("@info:title", "Authentication Status"))
|
||||
self._authentication_failed_message.addAction("Retry", i18n_catalog.i18nc("@action:button", "Retry"), None,
|
||||
i18n_catalog.i18nc("@info:tooltip", "Re-send the access request"))
|
||||
self._authentication_failed_message.actionTriggered.connect(self._messageCallback)
|
||||
|
|
68
plugins/USBPrinting/AvrFirmwareUpdater.py
Normal file
68
plugins/USBPrinting/AvrFirmwareUpdater.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.Logger import Logger
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.PrinterOutput.FirmwareUpdater import FirmwareUpdater, FirmwareUpdateState
|
||||
|
||||
from .avr_isp import stk500v2, intelHex
|
||||
from serial import SerialException
|
||||
|
||||
from time import sleep
|
||||
|
||||
MYPY = False
|
||||
if MYPY:
|
||||
from cura.PrinterOutputDevice import PrinterOutputDevice
|
||||
|
||||
|
||||
class AvrFirmwareUpdater(FirmwareUpdater):
|
||||
def __init__(self, output_device: "PrinterOutputDevice") -> None:
|
||||
super().__init__(output_device)
|
||||
|
||||
def _updateFirmware(self) -> None:
|
||||
try:
|
||||
hex_file = intelHex.readHex(self._firmware_file)
|
||||
assert len(hex_file) > 0
|
||||
except (FileNotFoundError, AssertionError):
|
||||
Logger.log("e", "Unable to read provided hex file. Could not update firmware.")
|
||||
self._setFirmwareUpdateState(FirmwareUpdateState.firmware_not_found_error)
|
||||
return
|
||||
|
||||
programmer = stk500v2.Stk500v2()
|
||||
programmer.progress_callback = self._onFirmwareProgress
|
||||
|
||||
# Ensure that other connections are closed.
|
||||
if self._output_device.isConnected():
|
||||
self._output_device.close()
|
||||
|
||||
try:
|
||||
programmer.connect(self._output_device._serial_port)
|
||||
except:
|
||||
programmer.close()
|
||||
Logger.logException("e", "Failed to update firmware")
|
||||
self._setFirmwareUpdateState(FirmwareUpdateState.communication_error)
|
||||
return
|
||||
|
||||
# Give programmer some time to connect. Might need more in some cases, but this worked in all tested cases.
|
||||
sleep(1)
|
||||
if not programmer.isConnected():
|
||||
Logger.log("e", "Unable to connect with serial. Could not update firmware")
|
||||
self._setFirmwareUpdateState(FirmwareUpdateState.communication_error)
|
||||
try:
|
||||
programmer.programChip(hex_file)
|
||||
except SerialException as e:
|
||||
Logger.log("e", "A serial port exception occured during firmware update: %s" % e)
|
||||
self._setFirmwareUpdateState(FirmwareUpdateState.io_error)
|
||||
return
|
||||
except Exception as e:
|
||||
Logger.log("e", "An unknown exception occured during firmware update: %s" % e)
|
||||
self._setFirmwareUpdateState(FirmwareUpdateState.unknown_error)
|
||||
return
|
||||
|
||||
programmer.close()
|
||||
|
||||
# Try to re-connect with the machine again, which must be done on the Qt thread, so we use call later.
|
||||
CuraApplication.getInstance().callLater(self._output_device.connect)
|
||||
|
||||
self._cleanupAfterUpdate()
|
|
@ -1,89 +0,0 @@
|
|||
// Copyright (c) 2017 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick.Controls 1.2
|
||||
|
||||
import UM 1.1 as UM
|
||||
|
||||
UM.Dialog
|
||||
{
|
||||
id: base;
|
||||
|
||||
width: minimumWidth;
|
||||
minimumWidth: 500 * screenScaleFactor;
|
||||
height: minimumHeight;
|
||||
minimumHeight: 100 * screenScaleFactor;
|
||||
|
||||
visible: true;
|
||||
modality: Qt.ApplicationModal;
|
||||
|
||||
title: catalog.i18nc("@title:window","Firmware Update");
|
||||
|
||||
Column
|
||||
{
|
||||
anchors.fill: parent;
|
||||
|
||||
Label
|
||||
{
|
||||
anchors
|
||||
{
|
||||
left: parent.left;
|
||||
right: parent.right;
|
||||
}
|
||||
|
||||
text: {
|
||||
switch (manager.firmwareUpdateState)
|
||||
{
|
||||
case 0:
|
||||
return "" //Not doing anything (eg; idling)
|
||||
case 1:
|
||||
return catalog.i18nc("@label","Updating firmware.")
|
||||
case 2:
|
||||
return catalog.i18nc("@label","Firmware update completed.")
|
||||
case 3:
|
||||
return catalog.i18nc("@label","Firmware update failed due to an unknown error.")
|
||||
case 4:
|
||||
return catalog.i18nc("@label","Firmware update failed due to an communication error.")
|
||||
case 5:
|
||||
return catalog.i18nc("@label","Firmware update failed due to an input/output error.")
|
||||
case 6:
|
||||
return catalog.i18nc("@label","Firmware update failed due to missing firmware.")
|
||||
}
|
||||
}
|
||||
|
||||
wrapMode: Text.Wrap;
|
||||
}
|
||||
|
||||
ProgressBar
|
||||
{
|
||||
id: prog
|
||||
value: manager.firmwareProgress
|
||||
minimumValue: 0
|
||||
maximumValue: 100
|
||||
indeterminate: manager.firmwareProgress < 1 && manager.firmwareProgress > 0
|
||||
anchors
|
||||
{
|
||||
left: parent.left;
|
||||
right: parent.right;
|
||||
}
|
||||
}
|
||||
|
||||
SystemPalette
|
||||
{
|
||||
id: palette;
|
||||
}
|
||||
|
||||
UM.I18nCatalog { id: catalog; name: "cura"; }
|
||||
}
|
||||
|
||||
rightButtons: [
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button","Close");
|
||||
enabled: manager.firmwareUpdateCompleteStatus;
|
||||
onClicked: base.visible = false;
|
||||
}
|
||||
]
|
||||
}
|
|
@ -4,7 +4,6 @@
|
|||
from UM.Logger import Logger
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Qt.Duration import DurationFormat
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState
|
||||
|
@ -13,28 +12,21 @@ from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
|
|||
from cura.PrinterOutput.GenericOutputController import GenericOutputController
|
||||
|
||||
from .AutoDetectBaudJob import AutoDetectBaudJob
|
||||
from .avr_isp import stk500v2, intelHex
|
||||
|
||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty, QUrl
|
||||
from .AvrFirmwareUpdater import AvrFirmwareUpdater
|
||||
|
||||
from serial import Serial, SerialException, SerialTimeoutException
|
||||
from threading import Thread, Event
|
||||
from time import time, sleep
|
||||
from time import time
|
||||
from queue import Queue
|
||||
from enum import IntEnum
|
||||
from typing import Union, Optional, List, cast
|
||||
|
||||
import re
|
||||
import functools # Used for reduce
|
||||
import os
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
class USBPrinterOutputDevice(PrinterOutputDevice):
|
||||
firmwareProgressChanged = pyqtSignal()
|
||||
firmwareUpdateStateChanged = pyqtSignal()
|
||||
|
||||
def __init__(self, serial_port: str, baud_rate: Optional[int] = None) -> None:
|
||||
super().__init__(serial_port)
|
||||
self.setName(catalog.i18nc("@item:inmenu", "USB printing"))
|
||||
|
@ -59,9 +51,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
self._all_baud_rates = [115200, 250000, 230400, 57600, 38400, 19200, 9600]
|
||||
|
||||
# Instead of using a timer, we really need the update to be as a thread, as reading from serial can block.
|
||||
self._update_thread = Thread(target=self._update, daemon = True)
|
||||
|
||||
self._update_firmware_thread = Thread(target=self._updateFirmware, daemon = True)
|
||||
self._update_thread = Thread(target = self._update, daemon = True)
|
||||
|
||||
self._last_temperature_request = None # type: Optional[int]
|
||||
|
||||
|
@ -75,11 +65,6 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
|
||||
self._paused = False
|
||||
|
||||
self._firmware_view = None
|
||||
self._firmware_location = None
|
||||
self._firmware_progress = 0
|
||||
self._firmware_update_state = FirmwareUpdateState.idle
|
||||
|
||||
self.setConnectionText(catalog.i18nc("@info:status", "Connected via USB"))
|
||||
|
||||
# Queue for commands that need to be sent.
|
||||
|
@ -88,6 +73,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
self._command_received = Event()
|
||||
self._command_received.set()
|
||||
|
||||
self._firmware_updater = AvrFirmwareUpdater(self)
|
||||
|
||||
CuraApplication.getInstance().getOnExitCallbackManager().addCallback(self._checkActivePrintingUponAppExit)
|
||||
|
||||
# This is a callback function that checks if there is any printing in progress via USB when the application tries
|
||||
|
@ -109,7 +96,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
|
||||
## Reset USB device settings
|
||||
#
|
||||
def resetDeviceSettings(self):
|
||||
def resetDeviceSettings(self) -> None:
|
||||
self._firmware_name = None
|
||||
|
||||
## Request the current scene to be sent to a USB-connected printer.
|
||||
|
@ -135,93 +122,6 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
|
||||
self._printGCode(gcode_list)
|
||||
|
||||
## Show firmware interface.
|
||||
# This will create the view if its not already created.
|
||||
def showFirmwareInterface(self):
|
||||
if self._firmware_view is None:
|
||||
path = os.path.join(PluginRegistry.getInstance().getPluginPath("USBPrinting"), "FirmwareUpdateWindow.qml")
|
||||
self._firmware_view = CuraApplication.getInstance().createQmlComponent(path, {"manager": self})
|
||||
|
||||
self._firmware_view.show()
|
||||
|
||||
@pyqtSlot(str)
|
||||
def updateFirmware(self, 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()
|
||||
|
||||
def _updateFirmware(self):
|
||||
# Ensure that other connections are closed.
|
||||
if self._connection_state != ConnectionState.closed:
|
||||
self.close()
|
||||
|
||||
try:
|
||||
hex_file = intelHex.readHex(self._firmware_location)
|
||||
assert len(hex_file) > 0
|
||||
except (FileNotFoundError, AssertionError):
|
||||
Logger.log("e", "Unable to read provided hex file. Could not update firmware.")
|
||||
self.setFirmwareUpdateState(FirmwareUpdateState.firmware_not_found_error)
|
||||
return
|
||||
|
||||
programmer = stk500v2.Stk500v2()
|
||||
programmer.progress_callback = self._onFirmwareProgress
|
||||
|
||||
try:
|
||||
programmer.connect(self._serial_port)
|
||||
except:
|
||||
programmer.close()
|
||||
Logger.logException("e", "Failed to update firmware")
|
||||
self.setFirmwareUpdateState(FirmwareUpdateState.communication_error)
|
||||
return
|
||||
|
||||
# Give programmer some time to connect. Might need more in some cases, but this worked in all tested cases.
|
||||
sleep(1)
|
||||
if not programmer.isConnected():
|
||||
Logger.log("e", "Unable to connect with serial. Could not update firmware")
|
||||
self.setFirmwareUpdateState(FirmwareUpdateState.communication_error)
|
||||
try:
|
||||
programmer.programChip(hex_file)
|
||||
except SerialException:
|
||||
self.setFirmwareUpdateState(FirmwareUpdateState.io_error)
|
||||
return
|
||||
except:
|
||||
self.setFirmwareUpdateState(FirmwareUpdateState.unknown_error)
|
||||
return
|
||||
|
||||
programmer.close()
|
||||
|
||||
# Clean up for next attempt.
|
||||
self._update_firmware_thread = Thread(target=self._updateFirmware, daemon=True)
|
||||
self._firmware_location = ""
|
||||
self._onFirmwareProgress(100)
|
||||
self.setFirmwareUpdateState(FirmwareUpdateState.completed)
|
||||
|
||||
# Try to re-connect with the machine again, which must be done on the Qt thread, so we use call later.
|
||||
CuraApplication.getInstance().callLater(self.connect)
|
||||
|
||||
@pyqtProperty(float, notify = firmwareProgressChanged)
|
||||
def firmwareProgress(self):
|
||||
return self._firmware_progress
|
||||
|
||||
@pyqtProperty(int, notify=firmwareUpdateStateChanged)
|
||||
def firmwareUpdateState(self):
|
||||
return self._firmware_update_state
|
||||
|
||||
def setFirmwareUpdateState(self, state):
|
||||
if self._firmware_update_state != state:
|
||||
self._firmware_update_state = state
|
||||
self.firmwareUpdateStateChanged.emit()
|
||||
|
||||
# Callback function for firmware update progress.
|
||||
def _onFirmwareProgress(self, progress, max_progress = 100):
|
||||
self._firmware_progress = (progress / max_progress) * 100 # Convert to scale of 0-100
|
||||
self.firmwareProgressChanged.emit()
|
||||
|
||||
## Start a print based on a g-code.
|
||||
# \param gcode_list List with gcode (strings).
|
||||
def _printGCode(self, gcode_list: List[str]):
|
||||
|
@ -258,7 +158,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
self._baud_rate = baud_rate
|
||||
|
||||
def connect(self):
|
||||
self._firmware_name = None # after each connection ensure that the firmware name is removed
|
||||
self._firmware_name = None # after each connection ensure that the firmware name is removed
|
||||
|
||||
if self._baud_rate is None:
|
||||
if self._use_auto_detect:
|
||||
|
@ -272,13 +172,19 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
except SerialException:
|
||||
Logger.log("w", "An exception occured while trying to create serial connection")
|
||||
return
|
||||
CuraApplication.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged)
|
||||
self._onGlobalContainerStackChanged()
|
||||
self.setConnectionState(ConnectionState.connected)
|
||||
self._update_thread.start()
|
||||
|
||||
def _onGlobalContainerStackChanged(self):
|
||||
container_stack = CuraApplication.getInstance().getGlobalContainerStack()
|
||||
num_extruders = container_stack.getProperty("machine_extruder_count", "value")
|
||||
# Ensure that a printer is created.
|
||||
self._printers = [PrinterOutputModel(output_controller=GenericOutputController(self), number_of_extruders=num_extruders)]
|
||||
controller = GenericOutputController(self)
|
||||
controller.setCanUpdateFirmware(True)
|
||||
self._printers = [PrinterOutputModel(output_controller = controller, number_of_extruders = num_extruders)]
|
||||
self._printers[0].updateName(container_stack.getName())
|
||||
self.setConnectionState(ConnectionState.connected)
|
||||
self._update_thread.start()
|
||||
|
||||
def close(self):
|
||||
super().close()
|
||||
|
@ -295,6 +201,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
self._command_queue.put(command)
|
||||
else:
|
||||
self._sendCommand(command)
|
||||
|
||||
def _sendCommand(self, command: Union[str, bytes]):
|
||||
if self._serial is None or self._connection_state != ConnectionState.connected:
|
||||
return
|
||||
|
@ -445,7 +352,9 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
elapsed_time = int(time() - self._print_start_time)
|
||||
print_job = self._printers[0].activePrintJob
|
||||
if print_job is None:
|
||||
print_job = PrintJobOutputModel(output_controller = GenericOutputController(self), name= CuraApplication.getInstance().getPrintInformation().jobName)
|
||||
controller = GenericOutputController(self)
|
||||
controller.setCanUpdateFirmware(True)
|
||||
print_job = PrintJobOutputModel(output_controller=controller, name=CuraApplication.getInstance().getPrintInformation().jobName)
|
||||
print_job.updateState("printing")
|
||||
self._printers[0].updateActivePrintJob(print_job)
|
||||
|
||||
|
@ -456,13 +365,3 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
print_job.updateTimeTotal(estimated_time)
|
||||
|
||||
self._gcode_position += 1
|
||||
|
||||
|
||||
class FirmwareUpdateState(IntEnum):
|
||||
idle = 0
|
||||
updating = 1
|
||||
completed = 2
|
||||
unknown_error = 3
|
||||
communication_error = 4
|
||||
io_error = 5
|
||||
firmware_not_found_error = 6
|
||||
|
|
|
@ -2,14 +2,12 @@
|
|||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import threading
|
||||
import platform
|
||||
import time
|
||||
import serial.tools.list_ports
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal
|
||||
|
||||
from UM.Logger import Logger
|
||||
from UM.Resources import Resources
|
||||
from UM.Signal import Signal, signalemitter
|
||||
from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin
|
||||
from UM.i18n import i18nCatalog
|
||||
|
@ -87,65 +85,6 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin):
|
|||
self._addRemovePorts(port_list)
|
||||
time.sleep(5)
|
||||
|
||||
@pyqtSlot(result = str)
|
||||
def getDefaultFirmwareName(self):
|
||||
# Check if there is a valid global container stack
|
||||
global_container_stack = self._application.getGlobalContainerStack()
|
||||
if not global_container_stack:
|
||||
Logger.log("e", "There is no global container stack. Can not update firmware.")
|
||||
self._firmware_view.close()
|
||||
return ""
|
||||
|
||||
# The bottom of the containerstack is the machine definition
|
||||
machine_id = global_container_stack.getBottom().id
|
||||
|
||||
machine_has_heated_bed = global_container_stack.getProperty("machine_heated_bed", "value")
|
||||
|
||||
if platform.system() == "Linux":
|
||||
baudrate = 115200
|
||||
else:
|
||||
baudrate = 250000
|
||||
|
||||
# NOTE: The keyword used here is the id of the machine. You can find the id of your machine in the *.json file, eg.
|
||||
# https://github.com/Ultimaker/Cura/blob/master/resources/machines/ultimaker_original.json#L2
|
||||
# The *.hex files are stored at a seperate repository:
|
||||
# https://github.com/Ultimaker/cura-binary-data/tree/master/cura/resources/firmware
|
||||
machine_without_extras = {"bq_witbox" : "MarlinWitbox.hex",
|
||||
"bq_hephestos_2" : "MarlinHephestos2.hex",
|
||||
"ultimaker_original" : "MarlinUltimaker-{baudrate}.hex",
|
||||
"ultimaker_original_plus" : "MarlinUltimaker-UMOP-{baudrate}.hex",
|
||||
"ultimaker_original_dual" : "MarlinUltimaker-{baudrate}-dual.hex",
|
||||
"ultimaker2" : "MarlinUltimaker2.hex",
|
||||
"ultimaker2_go" : "MarlinUltimaker2go.hex",
|
||||
"ultimaker2_plus" : "MarlinUltimaker2plus.hex",
|
||||
"ultimaker2_extended" : "MarlinUltimaker2extended.hex",
|
||||
"ultimaker2_extended_plus" : "MarlinUltimaker2extended-plus.hex",
|
||||
}
|
||||
machine_with_heated_bed = {"ultimaker_original" : "MarlinUltimaker-HBK-{baudrate}.hex",
|
||||
"ultimaker_original_dual" : "MarlinUltimaker-HBK-{baudrate}-dual.hex",
|
||||
}
|
||||
##TODO: Add check for multiple extruders
|
||||
hex_file = None
|
||||
if machine_id in machine_without_extras.keys(): # The machine needs to be defined here!
|
||||
if machine_id in machine_with_heated_bed.keys() and machine_has_heated_bed:
|
||||
Logger.log("d", "Choosing firmware with heated bed enabled for machine %s.", machine_id)
|
||||
hex_file = machine_with_heated_bed[machine_id] # Return firmware with heated bed enabled
|
||||
else:
|
||||
Logger.log("d", "Choosing basic firmware for machine %s.", machine_id)
|
||||
hex_file = machine_without_extras[machine_id] # Return "basic" firmware
|
||||
else:
|
||||
Logger.log("w", "There is no firmware for machine %s.", machine_id)
|
||||
|
||||
if hex_file:
|
||||
try:
|
||||
return Resources.getPath(CuraApplication.ResourceTypes.Firmware, hex_file.format(baudrate=baudrate))
|
||||
except FileNotFoundError:
|
||||
Logger.log("w", "Could not find any firmware for machine %s.", machine_id)
|
||||
return ""
|
||||
else:
|
||||
Logger.log("w", "Could not find any firmware for machine %s.", machine_id)
|
||||
return ""
|
||||
|
||||
## Helper to identify serial ports (and scan for them)
|
||||
def _addRemovePorts(self, serial_ports):
|
||||
# First, find and add all new or changed keys
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from . import USBPrinterOutputDeviceManager
|
||||
from PyQt5.QtQml import qmlRegisterSingletonType
|
||||
from UM.i18n import i18nCatalog
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
def getMetaData():
|
||||
|
@ -14,5 +11,4 @@ def getMetaData():
|
|||
def register(app):
|
||||
# We are violating the QT API here (as we use a factory, which is technically not allowed).
|
||||
# but we don't really have another means for doing this (and it seems to you know -work-)
|
||||
qmlRegisterSingletonType(USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager, "Cura", 1, 0, "USBPrinterManager", USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager.getInstance)
|
||||
return {"output_device": USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager(app)}
|
||||
|
|
|
@ -17,7 +17,7 @@ Cura.MachineAction
|
|||
property int rightRow: (checkupMachineAction.width * 0.60) | 0
|
||||
property bool heatupHotendStarted: false
|
||||
property bool heatupBedStarted: false
|
||||
property bool usbConnected: Cura.USBPrinterManager.connectedPrinterList.rowCount() > 0
|
||||
property bool printerConnected: Cura.MachineManager.printerConnected
|
||||
|
||||
UM.I18nCatalog { id: catalog; name:"cura"}
|
||||
Label
|
||||
|
@ -86,7 +86,7 @@ Cura.MachineAction
|
|||
anchors.left: connectionLabel.right
|
||||
anchors.top: parent.top
|
||||
wrapMode: Text.WordWrap
|
||||
text: checkupMachineAction.usbConnected ? catalog.i18nc("@info:status","Connected"): catalog.i18nc("@info:status","Not connected")
|
||||
text: checkupMachineAction.printerConnected ? catalog.i18nc("@info:status","Connected"): catalog.i18nc("@info:status","Not connected")
|
||||
}
|
||||
//////////////////////////////////////////////////////////
|
||||
Label
|
||||
|
@ -97,7 +97,7 @@ Cura.MachineAction
|
|||
anchors.top: connectionLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Min endstop X: ")
|
||||
visible: checkupMachineAction.usbConnected
|
||||
visible: checkupMachineAction.printerConnected
|
||||
}
|
||||
Label
|
||||
{
|
||||
|
@ -107,7 +107,7 @@ Cura.MachineAction
|
|||
anchors.top: connectionLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: manager.xMinEndstopTestCompleted ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked")
|
||||
visible: checkupMachineAction.usbConnected
|
||||
visible: checkupMachineAction.printerConnected
|
||||
}
|
||||
//////////////////////////////////////////////////////////////
|
||||
Label
|
||||
|
@ -118,7 +118,7 @@ Cura.MachineAction
|
|||
anchors.top: endstopXLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Min endstop Y: ")
|
||||
visible: checkupMachineAction.usbConnected
|
||||
visible: checkupMachineAction.printerConnected
|
||||
}
|
||||
Label
|
||||
{
|
||||
|
@ -128,7 +128,7 @@ Cura.MachineAction
|
|||
anchors.top: endstopXLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: manager.yMinEndstopTestCompleted ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked")
|
||||
visible: checkupMachineAction.usbConnected
|
||||
visible: checkupMachineAction.printerConnected
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
Label
|
||||
|
@ -139,7 +139,7 @@ Cura.MachineAction
|
|||
anchors.top: endstopYLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Min endstop Z: ")
|
||||
visible: checkupMachineAction.usbConnected
|
||||
visible: checkupMachineAction.printerConnected
|
||||
}
|
||||
Label
|
||||
{
|
||||
|
@ -149,7 +149,7 @@ Cura.MachineAction
|
|||
anchors.top: endstopYLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: manager.zMinEndstopTestCompleted ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked")
|
||||
visible: checkupMachineAction.usbConnected
|
||||
visible: checkupMachineAction.printerConnected
|
||||
}
|
||||
////////////////////////////////////////////////////////////
|
||||
Label
|
||||
|
@ -161,7 +161,7 @@ Cura.MachineAction
|
|||
anchors.top: endstopZLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Nozzle temperature check: ")
|
||||
visible: checkupMachineAction.usbConnected
|
||||
visible: checkupMachineAction.printerConnected
|
||||
}
|
||||
Label
|
||||
{
|
||||
|
@ -171,7 +171,7 @@ Cura.MachineAction
|
|||
anchors.left: nozzleTempLabel.right
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@info:status","Not checked")
|
||||
visible: checkupMachineAction.usbConnected
|
||||
visible: checkupMachineAction.printerConnected
|
||||
}
|
||||
Item
|
||||
{
|
||||
|
@ -181,7 +181,7 @@ Cura.MachineAction
|
|||
anchors.top: nozzleTempLabel.top
|
||||
anchors.left: bedTempStatus.right
|
||||
anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").width/2)
|
||||
visible: checkupMachineAction.usbConnected
|
||||
visible: checkupMachineAction.printerConnected
|
||||
Button
|
||||
{
|
||||
text: checkupMachineAction.heatupHotendStarted ? catalog.i18nc("@action:button","Stop Heating") : catalog.i18nc("@action:button","Start Heating")
|
||||
|
@ -209,7 +209,7 @@ Cura.MachineAction
|
|||
wrapMode: Text.WordWrap
|
||||
text: manager.hotendTemperature + "°C"
|
||||
font.bold: true
|
||||
visible: checkupMachineAction.usbConnected
|
||||
visible: checkupMachineAction.printerConnected
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
Label
|
||||
|
@ -221,7 +221,7 @@ Cura.MachineAction
|
|||
anchors.top: nozzleTempLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Build plate temperature check:")
|
||||
visible: checkupMachineAction.usbConnected && manager.hasHeatedBed
|
||||
visible: checkupMachineAction.printerConnected && manager.hasHeatedBed
|
||||
}
|
||||
|
||||
Label
|
||||
|
@ -232,7 +232,7 @@ Cura.MachineAction
|
|||
anchors.left: bedTempLabel.right
|
||||
wrapMode: Text.WordWrap
|
||||
text: manager.bedTestCompleted ? catalog.i18nc("@info:status","Not checked"): catalog.i18nc("@info:status","Checked")
|
||||
visible: checkupMachineAction.usbConnected && manager.hasHeatedBed
|
||||
visible: checkupMachineAction.printerConnected && manager.hasHeatedBed
|
||||
}
|
||||
Item
|
||||
{
|
||||
|
@ -242,7 +242,7 @@ Cura.MachineAction
|
|||
anchors.top: bedTempLabel.top
|
||||
anchors.left: bedTempStatus.right
|
||||
anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").width/2)
|
||||
visible: checkupMachineAction.usbConnected && manager.hasHeatedBed
|
||||
visible: checkupMachineAction.printerConnected && manager.hasHeatedBed
|
||||
Button
|
||||
{
|
||||
text: checkupMachineAction.heatupBedStarted ?catalog.i18nc("@action:button","Stop Heating") : catalog.i18nc("@action:button","Start Heating")
|
||||
|
@ -270,7 +270,7 @@ Cura.MachineAction
|
|||
wrapMode: Text.WordWrap
|
||||
text: manager.bedTemperature + "°C"
|
||||
font.bold: true
|
||||
visible: checkupMachineAction.usbConnected && manager.hasHeatedBed
|
||||
visible: checkupMachineAction.printerConnected && manager.hasHeatedBed
|
||||
}
|
||||
Label
|
||||
{
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
from UM.Application import Application
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
from cura.MachineAction import MachineAction
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
## Upgrade the firmware of a machine by USB with this action.
|
||||
class UpgradeFirmwareMachineAction(MachineAction):
|
||||
def __init__(self):
|
||||
super().__init__("UpgradeFirmware", catalog.i18nc("@action", "Upgrade Firmware"))
|
||||
self._qml_url = "UpgradeFirmwareMachineAction.qml"
|
||||
ContainerRegistry.getInstance().containerAdded.connect(self._onContainerAdded)
|
||||
|
||||
def _onContainerAdded(self, container):
|
||||
# Add this action as a supported action to all machine definitions if they support USB connection
|
||||
if isinstance(container, DefinitionContainer) and container.getMetaDataEntry("type") == "machine" and container.getMetaDataEntry("supports_usb_connection"):
|
||||
Application.getInstance().getMachineActionManager().addSupportedAction(container.getId(), self.getKey())
|
|
@ -1,93 +0,0 @@
|
|||
// Copyright (c) 2016 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Window 2.1
|
||||
import QtQuick.Dialogs 1.2 // For filedialog
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
|
||||
Cura.MachineAction
|
||||
{
|
||||
anchors.fill: parent;
|
||||
property bool printerConnected: Cura.MachineManager.printerConnected
|
||||
property var activeOutputDevice: printerConnected ? Cura.MachineManager.printerOutputDevices[0] : null
|
||||
|
||||
Item
|
||||
{
|
||||
id: upgradeFirmwareMachineAction
|
||||
anchors.fill: parent;
|
||||
UM.I18nCatalog { id: catalog; name:"cura"}
|
||||
|
||||
Label
|
||||
{
|
||||
id: pageTitle
|
||||
width: parent.width
|
||||
text: catalog.i18nc("@title", "Upgrade Firmware")
|
||||
wrapMode: Text.WordWrap
|
||||
font.pointSize: 18
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: pageDescription
|
||||
anchors.top: pageTitle.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label", "Firmware is the piece of software running directly on your 3D printer. This firmware controls the step motors, regulates the temperature and ultimately makes your printer work.")
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: upgradeText1
|
||||
anchors.top: pageDescription.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label", "The firmware shipping with new printers works, but new versions tend to have more features and improvements.");
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
anchors.top: upgradeText1.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: childrenRect.width
|
||||
spacing: UM.Theme.getSize("default_margin").width
|
||||
property var firmwareName: Cura.USBPrinterManager.getDefaultFirmwareName()
|
||||
Button
|
||||
{
|
||||
id: autoUpgradeButton
|
||||
text: catalog.i18nc("@action:button", "Automatically upgrade Firmware");
|
||||
enabled: parent.firmwareName != "" && activeOutputDevice
|
||||
onClicked:
|
||||
{
|
||||
activeOutputDevice.updateFirmware(parent.firmwareName)
|
||||
}
|
||||
}
|
||||
Button
|
||||
{
|
||||
id: manualUpgradeButton
|
||||
text: catalog.i18nc("@action:button", "Upload custom Firmware");
|
||||
enabled: activeOutputDevice != null
|
||||
onClicked:
|
||||
{
|
||||
customFirmwareDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog
|
||||
{
|
||||
id: customFirmwareDialog
|
||||
title: catalog.i18nc("@title:window", "Select custom firmware")
|
||||
nameFilters: "Firmware image files (*.hex)"
|
||||
selectExisting: true
|
||||
onAccepted: activeOutputDevice.updateFirmware(fileUrl)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +1,16 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from . import BedLevelMachineAction
|
||||
from . import UpgradeFirmwareMachineAction
|
||||
from . import UMOUpgradeSelection
|
||||
from . import UM2UpgradeSelection
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
def getMetaData():
|
||||
return {
|
||||
}
|
||||
return {}
|
||||
|
||||
def register(app):
|
||||
return { "machine_action": [
|
||||
BedLevelMachineAction.BedLevelMachineAction(),
|
||||
UpgradeFirmwareMachineAction.UpgradeFirmwareMachineAction(),
|
||||
UMOUpgradeSelection.UMOUpgradeSelection(),
|
||||
UM2UpgradeSelection.UM2UpgradeSelection()
|
||||
]}
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
|
||||
from . import VersionUpgrade21to22
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
upgrade = VersionUpgrade21to22.VersionUpgrade21to22()
|
||||
|
||||
def getMetaData():
|
||||
|
|
|
@ -73,7 +73,7 @@ class VersionUpgrade22to24(VersionUpgrade):
|
|||
|
||||
def __convertVariant(self, variant_path):
|
||||
# Copy the variant to the machine_instances/*_settings.inst.cfg
|
||||
variant_config = configparser.ConfigParser(interpolation=None)
|
||||
variant_config = configparser.ConfigParser(interpolation = None)
|
||||
with open(variant_path, "r", encoding = "utf-8") as fhandle:
|
||||
variant_config.read_file(fhandle)
|
||||
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
|
||||
from . import VersionUpgrade
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
upgrade = VersionUpgrade.VersionUpgrade22to24()
|
||||
|
||||
def getMetaData():
|
||||
|
|
|
@ -117,7 +117,7 @@ class VersionUpgrade25to26(VersionUpgrade):
|
|||
# \param serialised The serialised form of a quality profile.
|
||||
# \param filename The name of the file to upgrade.
|
||||
def upgradeMachineStack(self, serialised, filename):
|
||||
parser = configparser.ConfigParser(interpolation=None)
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialised)
|
||||
|
||||
# NOTE: This is for Custom FDM printers
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
|
||||
from . import VersionUpgrade25to26
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
upgrade = VersionUpgrade25to26.VersionUpgrade25to26()
|
||||
|
||||
def getMetaData():
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
|
||||
from . import VersionUpgrade26to27
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
upgrade = VersionUpgrade26to27.VersionUpgrade26to27()
|
||||
|
||||
def getMetaData():
|
||||
|
|
|
@ -84,7 +84,7 @@ class VersionUpgrade30to31(VersionUpgrade):
|
|||
# \param serialised The serialised form of a preferences file.
|
||||
# \param filename The name of the file to upgrade.
|
||||
def upgradePreferences(self, serialised, filename):
|
||||
parser = configparser.ConfigParser(interpolation=None)
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialised)
|
||||
|
||||
# Update version numbers
|
||||
|
@ -105,7 +105,7 @@ class VersionUpgrade30to31(VersionUpgrade):
|
|||
# \param serialised The serialised form of the container file.
|
||||
# \param filename The name of the file to upgrade.
|
||||
def upgradeInstanceContainer(self, serialised, filename):
|
||||
parser = configparser.ConfigParser(interpolation=None)
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialised)
|
||||
|
||||
for each_section in ("general", "metadata"):
|
||||
|
@ -130,7 +130,7 @@ class VersionUpgrade30to31(VersionUpgrade):
|
|||
# \param serialised The serialised form of a container stack.
|
||||
# \param filename The name of the file to upgrade.
|
||||
def upgradeStack(self, serialised, filename):
|
||||
parser = configparser.ConfigParser(interpolation=None)
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialised)
|
||||
|
||||
for each_section in ("general", "metadata"):
|
||||
|
|
|
@ -5,7 +5,6 @@ from . import VersionUpgrade33to34
|
|||
|
||||
upgrade = VersionUpgrade33to34.VersionUpgrade33to34()
|
||||
|
||||
|
||||
def getMetaData():
|
||||
return {
|
||||
"version_upgrade": {
|
||||
|
|
|
@ -5,7 +5,6 @@ from . import VersionUpgrade34to35
|
|||
|
||||
upgrade = VersionUpgrade34to35.VersionUpgrade34to35()
|
||||
|
||||
|
||||
def getMetaData():
|
||||
return {
|
||||
"version_upgrade": {
|
||||
|
|
|
@ -5,10 +5,7 @@ from . import XmlMaterialProfile
|
|||
from . import XmlMaterialUpgrader
|
||||
|
||||
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
upgrader = XmlMaterialUpgrader.XmlMaterialUpgrader()
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue