Merge branch 'master' into refactor_singleton_settingsbase
|
@ -225,7 +225,7 @@ class ThreeMFReader(MeshReader):
|
|||
|
||||
except Exception:
|
||||
Logger.logException("e", "An exception occurred in 3mf reader.")
|
||||
return []
|
||||
return None
|
||||
|
||||
return result
|
||||
|
||||
|
|
|
@ -85,14 +85,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
MimeTypeDatabase.addMimeType(
|
||||
MimeType(
|
||||
name="application/x-curaproject+xml",
|
||||
comment="Cura Project File",
|
||||
suffixes=["curaproject.3mf"]
|
||||
)
|
||||
)
|
||||
|
||||
self._supported_extensions = [".3mf"]
|
||||
self._dialog = WorkspaceDialog()
|
||||
self._3mf_mesh_reader = None
|
||||
|
@ -726,8 +718,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
nodes = []
|
||||
|
||||
base_file_name = os.path.basename(file_name)
|
||||
if base_file_name.endswith(".curaproject.3mf"):
|
||||
base_file_name = base_file_name[:base_file_name.rfind(".curaproject.3mf")]
|
||||
self.setWorkspaceName(base_file_name)
|
||||
return nodes
|
||||
|
||||
|
@ -944,7 +934,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
root_material_id)
|
||||
|
||||
if material_node is not None and material_node.getContainer() is not None:
|
||||
extruder_stack.material = material_node.getContainer()
|
||||
extruder_stack.material = material_node.getContainer() # type: InstanceContainer
|
||||
|
||||
def _applyChangesToMachine(self, global_stack, extruder_stack_dict):
|
||||
# Clear all first
|
||||
|
|
|
@ -18,11 +18,7 @@ catalog = i18nCatalog("cura")
|
|||
|
||||
|
||||
def getMetaData() -> Dict:
|
||||
# Workaround for osx not supporting double file extensions correctly.
|
||||
if Platform.isOSX():
|
||||
workspace_extension = "3mf"
|
||||
else:
|
||||
workspace_extension = "curaproject.3mf"
|
||||
workspace_extension = "3mf"
|
||||
|
||||
metaData = {}
|
||||
if "3MFReader.ThreeMFReader" in sys.modules:
|
||||
|
|
|
@ -15,11 +15,7 @@ from UM.Platform import Platform
|
|||
i18n_catalog = i18nCatalog("uranium")
|
||||
|
||||
def getMetaData():
|
||||
# Workarround for osx not supporting double file extensions correctly.
|
||||
if Platform.isOSX():
|
||||
workspace_extension = "3mf"
|
||||
else:
|
||||
workspace_extension = "curaproject.3mf"
|
||||
workspace_extension = "3mf"
|
||||
|
||||
metaData = {}
|
||||
|
||||
|
@ -36,7 +32,7 @@ def getMetaData():
|
|||
"output": [{
|
||||
"extension": workspace_extension,
|
||||
"description": i18n_catalog.i18nc("@item:inlistbox", "Cura Project 3MF file"),
|
||||
"mime_type": "application/x-curaproject+xml",
|
||||
"mime_type": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml",
|
||||
"mode": ThreeMFWorkspaceWriter.ThreeMFWorkspaceWriter.OutputMode.BinaryMode
|
||||
}]
|
||||
}
|
||||
|
|
|
@ -342,7 +342,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
if not self._global_container_stack:
|
||||
Logger.log("w", "Global container stack not assigned to CuraEngineBackend!")
|
||||
return
|
||||
extruders = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()))
|
||||
extruders = ExtruderManager.getInstance().getActiveExtruderStacks()
|
||||
error_keys = [] #type: List[str]
|
||||
for extruder in extruders:
|
||||
error_keys.extend(extruder.getErrorKeys())
|
||||
|
|
|
@ -178,7 +178,7 @@ class ProcessSlicedLayersJob(Job):
|
|||
# Find out colors per extruder
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
manager = ExtruderManager.getInstance()
|
||||
extruders = list(manager.getMachineExtruders(global_container_stack.getId()))
|
||||
extruders = manager.getActiveExtruderStacks()
|
||||
if extruders:
|
||||
material_color_map = numpy.zeros((len(extruders), 4), dtype=numpy.float32)
|
||||
for extruder in extruders:
|
||||
|
|
|
@ -333,7 +333,7 @@ class StartSliceJob(Job):
|
|||
"-1": self._buildReplacementTokens(global_stack)
|
||||
}
|
||||
|
||||
for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()):
|
||||
for extruder_stack in ExtruderManager.getInstance().getActiveExtruderStacks():
|
||||
extruder_nr = extruder_stack.getProperty("extruder_nr", "value")
|
||||
self._all_extruders_settings[str(extruder_nr)] = self._buildReplacementTokens(extruder_stack)
|
||||
|
||||
|
|
|
@ -275,7 +275,7 @@ class FlavorParser:
|
|||
## For showing correct x, y offsets for each extruder
|
||||
def _extruderOffsets(self) -> Dict[int, List[float]]:
|
||||
result = {}
|
||||
for extruder in ExtruderManager.getInstance().getExtruderStacks():
|
||||
for extruder in ExtruderManager.getInstance().getActiveExtruderStacks():
|
||||
result[int(extruder.getMetaData().get("position", "0"))] = [
|
||||
extruder.getProperty("machine_nozzle_offset_x", "value"),
|
||||
extruder.getProperty("machine_nozzle_offset_y", "value")]
|
||||
|
|
|
@ -16,6 +16,8 @@ Cura.MachineAction
|
|||
property var extrudersModel: Cura.ExtrudersModel{}
|
||||
property int extruderTabsCount: 0
|
||||
|
||||
property var activeMachineId: Cura.MachineManager.activeMachine != null ? Cura.MachineManager.activeMachine.id : ""
|
||||
|
||||
Connections
|
||||
{
|
||||
target: base.extrudersModel
|
||||
|
@ -511,7 +513,7 @@ Cura.MachineAction
|
|||
}
|
||||
return "";
|
||||
}
|
||||
return Cura.MachineManager.activeMachineId;
|
||||
return base.activeMachineId
|
||||
}
|
||||
key: settingKey
|
||||
watchedProperties: [ "value", "description" ]
|
||||
|
@ -564,7 +566,7 @@ Cura.MachineAction
|
|||
}
|
||||
return "";
|
||||
}
|
||||
return Cura.MachineManager.activeMachineId;
|
||||
return base.activeMachineId
|
||||
}
|
||||
key: settingKey
|
||||
watchedProperties: [ "value", "description" ]
|
||||
|
@ -655,7 +657,7 @@ Cura.MachineAction
|
|||
}
|
||||
return "";
|
||||
}
|
||||
return Cura.MachineManager.activeMachineId;
|
||||
return base.activeMachineId
|
||||
}
|
||||
key: settingKey
|
||||
watchedProperties: [ "value", "options", "description" ]
|
||||
|
@ -754,7 +756,7 @@ Cura.MachineAction
|
|||
}
|
||||
return "";
|
||||
}
|
||||
return Cura.MachineManager.activeMachineId;
|
||||
return base.activeMachineId
|
||||
}
|
||||
key: settingKey
|
||||
watchedProperties: [ "value", "description" ]
|
||||
|
@ -879,7 +881,7 @@ Cura.MachineAction
|
|||
{
|
||||
id: machineExtruderCountProvider
|
||||
|
||||
containerStackId: Cura.MachineManager.activeMachineId
|
||||
containerStackId: base.activeMachineId
|
||||
key: "machine_extruder_count"
|
||||
watchedProperties: [ "value", "description" ]
|
||||
storeIndex: manager.containerIndex
|
||||
|
@ -889,7 +891,7 @@ Cura.MachineAction
|
|||
{
|
||||
id: machineHeadPolygonProvider
|
||||
|
||||
containerStackId: Cura.MachineManager.activeMachineId
|
||||
containerStackId: base.acthiveMachineId
|
||||
key: "machine_head_with_fans_polygon"
|
||||
watchedProperties: [ "value" ]
|
||||
storeIndex: manager.containerIndex
|
||||
|
|
|
@ -17,7 +17,6 @@ Item {
|
|||
|
||||
width: childrenRect.width;
|
||||
height: childrenRect.height;
|
||||
|
||||
property var all_categories_except_support: [ "machine_settings", "resolution", "shell", "infill", "material", "speed",
|
||||
"travel", "cooling", "platform_adhesion", "dual", "meshfix", "blackmagic", "experimental"]
|
||||
|
||||
|
@ -45,7 +44,7 @@ Item {
|
|||
UM.SettingPropertyProvider
|
||||
{
|
||||
id: meshTypePropertyProvider
|
||||
containerStackId: Cura.MachineManager.activeMachineId
|
||||
containerStack: Cura.MachineManager.activeMachine
|
||||
watchedProperties: [ "enabled" ]
|
||||
}
|
||||
|
||||
|
@ -518,7 +517,7 @@ Item {
|
|||
{
|
||||
id: machineExtruderCount
|
||||
|
||||
containerStackId: Cura.MachineManager.activeMachineId
|
||||
containerStack: Cura.MachineManager.activeMachine
|
||||
key: "machine_extruder_count"
|
||||
watchedProperties: [ "value" ]
|
||||
storeIndex: 0
|
||||
|
@ -528,7 +527,7 @@ Item {
|
|||
{
|
||||
id: printSequencePropertyProvider
|
||||
|
||||
containerStackId: Cura.MachineManager.activeMachineId
|
||||
containerStack: Cura.MachineManager.activeMachine
|
||||
key: "print_sequence"
|
||||
watchedProperties: [ "value" ]
|
||||
storeIndex: 0
|
||||
|
|
|
@ -384,7 +384,7 @@ UM.Dialog
|
|||
UM.SettingPropertyProvider
|
||||
{
|
||||
id: inheritStackProvider
|
||||
containerStackId: Cura.MachineManager.activeMachineId
|
||||
containerStack: Cura.MachineManager.activeMachine
|
||||
key: model.key ? model.key : "None"
|
||||
watchedProperties: [ "limit_to_extruder" ]
|
||||
}
|
||||
|
|
|
@ -44,12 +44,11 @@ UM.PointingRectangle {
|
|||
id: valueLabel
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: Math.round(UM.Theme.getSize("default_margin").width / 2)
|
||||
verticalCenter: parent.verticalCenter
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
width: maximumValue.toString().length * 12 * screenScaleFactor
|
||||
width: (maximumValue.toString().length + 1) * 10 * screenScaleFactor
|
||||
text: sliderLabelRoot.value + startFrom // the current handle value, add 1 because layers is an array
|
||||
horizontalAlignment: TextInput.AlignRight
|
||||
|
||||
|
|
|
@ -667,8 +667,11 @@ Item
|
|||
{
|
||||
target: UM.SimulationView
|
||||
onMaxLayersChanged: layerSlider.setUpperValue(UM.SimulationView.currentLayer)
|
||||
onMinimumLayerChanged: layerSlider.setLowerValue(UM.SimulationView.minimumLayer)
|
||||
onCurrentLayerChanged: layerSlider.setUpperValue(UM.SimulationView.currentLayer)
|
||||
onCurrentLayerChanged:
|
||||
{
|
||||
playButton.pauseSimulation()
|
||||
layerSlider.setUpperValue(UM.SimulationView.currentLayer)
|
||||
}
|
||||
}
|
||||
|
||||
// make sure the slider handlers show the correct value after switching views
|
||||
|
@ -723,6 +726,7 @@ Item
|
|||
UM.SimulationView.setSimulationRunning(true)
|
||||
iconSource = "./resources/simulation_pause.svg"
|
||||
simulationTimer.start()
|
||||
status = 1
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -766,6 +770,7 @@ Item
|
|||
{
|
||||
UM.SimulationView.setCurrentLayer(currentLayer+1)
|
||||
UM.SimulationView.setCurrentPath(0)
|
||||
playButton.resumeSimulation()
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -82,9 +82,16 @@ Item
|
|||
}
|
||||
spacing: Math.floor(UM.Theme.getSize("narrow_margin").height)
|
||||
width: childrenRect.width
|
||||
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@label", "Contact") + ":"
|
||||
text: catalog.i18nc("@label", "Website") + ":"
|
||||
font: UM.Theme.getFont("very_small")
|
||||
color: UM.Theme.getColor("text_medium")
|
||||
}
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@label", "Email") + ":"
|
||||
font: UM.Theme.getFont("very_small")
|
||||
color: UM.Theme.getColor("text_medium")
|
||||
}
|
||||
|
@ -100,18 +107,32 @@ Item
|
|||
topMargin: UM.Theme.getSize("default_margin").height
|
||||
}
|
||||
spacing: Math.floor(UM.Theme.getSize("narrow_margin").height)
|
||||
|
||||
Label
|
||||
{
|
||||
text:
|
||||
{
|
||||
if (details.website)
|
||||
{
|
||||
return "<a href=\"" + details.website + "\">" + details.website + "</a>"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
font: UM.Theme.getFont("very_small")
|
||||
color: UM.Theme.getColor("text")
|
||||
linkColor: UM.Theme.getColor("text_link")
|
||||
onLinkActivated: Qt.openUrlExternally(link)
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
text:
|
||||
{
|
||||
if (details.email)
|
||||
{
|
||||
return "<a href=\"mailto:"+details.email+"\">"+details.name+"</a>"
|
||||
}
|
||||
else
|
||||
{
|
||||
return "<a href=\""+details.website+"\">"+details.name+"</a>"
|
||||
return "<a href=\"mailto:" + details.email + "\">" + details.email + "</a>"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
font: UM.Theme.getFont("very_small")
|
||||
color: UM.Theme.getColor("text")
|
||||
|
|
|
@ -1,14 +1,25 @@
|
|||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// Toolbox is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import UM 1.1 as UM
|
||||
|
||||
Item
|
||||
{
|
||||
id: base
|
||||
|
||||
property var packageData
|
||||
property var technicalDataSheetUrl: {
|
||||
var link = undefined
|
||||
if ("Technical Data Sheet" in packageData.links)
|
||||
{
|
||||
link = packageData.links["Technical Data Sheet"]
|
||||
}
|
||||
return link
|
||||
}
|
||||
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
height: visible ? childrenRect.height : 0
|
||||
visible: packageData.type == "material" && packageData.has_configs
|
||||
|
@ -132,4 +143,25 @@ Item
|
|||
width: Math.floor(table.width * 0.1)
|
||||
}
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: technical_data_sheet
|
||||
anchors.top: table.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height / 2
|
||||
visible: base.technicalDataSheetUrl !== undefined
|
||||
text:
|
||||
{
|
||||
if (base.technicalDataSheetUrl !== undefined)
|
||||
{
|
||||
return "<a href='%1'>%2</a>".arg(base.technicalDataSheetUrl).arg("Technical Data Sheet")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
font: UM.Theme.getFont("very_small")
|
||||
color: UM.Theme.getColor("text")
|
||||
linkColor: UM.Theme.getColor("text_link")
|
||||
onLinkActivated: Qt.openUrlExternally(link)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// Toolbox is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import UM 1.1 as UM
|
||||
|
|
|
@ -32,7 +32,7 @@ Item
|
|||
width: UM.Theme.getSize("toolbox_thumbnail_medium").width
|
||||
height: UM.Theme.getSize("toolbox_thumbnail_medium").height
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: details.icon_url || "../images/logobot.svg"
|
||||
source: details === null ? "" : (details.icon_url || "../images/logobot.svg")
|
||||
mipmap: true
|
||||
anchors
|
||||
{
|
||||
|
@ -55,7 +55,7 @@ Item
|
|||
rightMargin: UM.Theme.getSize("wide_margin").width
|
||||
bottomMargin: UM.Theme.getSize("default_margin").height
|
||||
}
|
||||
text: details.name || ""
|
||||
text: details === null ? "" : (details.name || "")
|
||||
font: UM.Theme.getFont("large")
|
||||
color: UM.Theme.getColor("text")
|
||||
wrapMode: Text.WordWrap
|
||||
|
@ -114,7 +114,7 @@ Item
|
|||
height: childrenRect.height
|
||||
Label
|
||||
{
|
||||
text: details.version || catalog.i18nc("@label", "Unknown")
|
||||
text: details === null ? "" : (details.version || catalog.i18nc("@label", "Unknown"))
|
||||
font: UM.Theme.getFont("very_small")
|
||||
color: UM.Theme.getColor("text")
|
||||
}
|
||||
|
@ -122,6 +122,10 @@ Item
|
|||
{
|
||||
text:
|
||||
{
|
||||
if (details === null)
|
||||
{
|
||||
return ""
|
||||
}
|
||||
var date = new Date(details.last_updated)
|
||||
return date.toLocaleString(UM.Preferences.getValue("general/language"))
|
||||
}
|
||||
|
@ -132,6 +136,10 @@ Item
|
|||
{
|
||||
text:
|
||||
{
|
||||
if (details === null)
|
||||
{
|
||||
return ""
|
||||
}
|
||||
if (details.author_email)
|
||||
{
|
||||
return "<a href=\"mailto:" + details.author_email+"?Subject=Cura: " + details.name + "\">" + details.author_name + "</a>"
|
||||
|
@ -148,7 +156,7 @@ Item
|
|||
}
|
||||
Label
|
||||
{
|
||||
text: details.download_count || catalog.i18nc("@label", "Unknown")
|
||||
text: details === null ? "" : (details.download_count || catalog.i18nc("@label", "Unknown"))
|
||||
font: UM.Theme.getFont("very_small")
|
||||
color: UM.Theme.getColor("text")
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// Toolbox is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import UM 1.1 as UM
|
||||
|
|
|
@ -9,7 +9,7 @@ import UM 1.1 as UM
|
|||
|
||||
Item
|
||||
{
|
||||
property int packageCount: (toolbox.viewCategory == "material" && model.type === undefined) ? toolbox.getTotalNumberOfPackagesByAuthor(model.id) : 1
|
||||
property int packageCount: (toolbox.viewCategory == "material" && model.type === undefined) ? toolbox.getTotalNumberOfMaterialPackagesByAuthor(model.id) : 1
|
||||
property int installedPackages: (toolbox.viewCategory == "material" && model.type === undefined) ? toolbox.getNumberOfInstalledPackagesByAuthor(model.id) : (toolbox.isInstalled(model.id) ? 1 : 0)
|
||||
height: childrenRect.height
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||
|
|
|
@ -12,7 +12,9 @@ ScrollView
|
|||
width: parent.width
|
||||
height: parent.height
|
||||
style: UM.Theme.styles.scrollview
|
||||
|
||||
flickableItem.flickableDirection: Flickable.VerticalFlick
|
||||
|
||||
Column
|
||||
{
|
||||
width: base.width
|
||||
|
@ -30,7 +32,7 @@ ScrollView
|
|||
id: allPlugins
|
||||
width: parent.width
|
||||
heading: toolbox.viewCategory == "material" ? catalog.i18nc("@label", "Community Contributions") : catalog.i18nc("@label", "Community Plugins")
|
||||
model: toolbox.viewCategory == "material" ? toolbox.authorsModel : toolbox.packagesModel
|
||||
model: toolbox.viewCategory == "material" ? toolbox.materialsAvailableModel : toolbox.pluginsAvailableModel
|
||||
}
|
||||
|
||||
ToolboxDownloadsGrid
|
||||
|
|
|
@ -9,7 +9,7 @@ import UM 1.1 as UM
|
|||
|
||||
Rectangle
|
||||
{
|
||||
property int packageCount: toolbox.viewCategory == "material" ? toolbox.getTotalNumberOfPackagesByAuthor(model.id) : 1
|
||||
property int packageCount: toolbox.viewCategory == "material" ? toolbox.getTotalNumberOfMaterialPackagesByAuthor(model.id) : 1
|
||||
property int installedPackages: toolbox.viewCategory == "material" ? toolbox.getNumberOfInstalledPackagesByAuthor(model.id) : (toolbox.isInstalled(model.id) ? 1 : 0)
|
||||
id: tileBase
|
||||
width: UM.Theme.getSize("toolbox_thumbnail_large").width + (2 * UM.Theme.getSize("default_lining").width)
|
||||
|
|
|
@ -6,6 +6,7 @@ import QtQuick.Dialogs 1.1
|
|||
import QtQuick.Window 2.2
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import UM 1.1 as UM
|
||||
|
||||
ScrollView
|
||||
|
@ -16,6 +17,7 @@ ScrollView
|
|||
height: parent.height
|
||||
style: UM.Theme.styles.scrollview
|
||||
flickableItem.flickableDirection: Flickable.VerticalFlick
|
||||
|
||||
Column
|
||||
{
|
||||
spacing: UM.Theme.getSize("default_margin").height
|
||||
|
|
|
@ -33,6 +33,9 @@ class AuthorsModel(ListModel):
|
|||
|
||||
def _update(self):
|
||||
items = []
|
||||
if not self._metadata:
|
||||
self.setItems([])
|
||||
return
|
||||
|
||||
for author in self._metadata:
|
||||
items.append({
|
||||
|
|
|
@ -5,9 +5,13 @@ import re
|
|||
from typing import Dict
|
||||
|
||||
from PyQt5.QtCore import Qt, pyqtProperty
|
||||
|
||||
from UM.Logger import Logger
|
||||
from UM.Qt.ListModel import ListModel
|
||||
|
||||
from .ConfigsModel import ConfigsModel
|
||||
|
||||
|
||||
## Model that holds cura packages. By setting the filter property the instances held by this model can be changed.
|
||||
class PackagesModel(ListModel):
|
||||
def __init__(self, parent = None):
|
||||
|
@ -34,6 +38,8 @@ class PackagesModel(ListModel):
|
|||
self.addRoleName(Qt.UserRole + 17, "supported_configs")
|
||||
self.addRoleName(Qt.UserRole + 18, "download_count")
|
||||
self.addRoleName(Qt.UserRole + 19, "tags")
|
||||
self.addRoleName(Qt.UserRole + 20, "links")
|
||||
self.addRoleName(Qt.UserRole + 21, "website")
|
||||
|
||||
# List of filters for queries. The result is the union of the each list of results.
|
||||
self._filter = {} # type: Dict[str, str]
|
||||
|
@ -45,10 +51,16 @@ class PackagesModel(ListModel):
|
|||
def _update(self):
|
||||
items = []
|
||||
|
||||
for package in self._metadata:
|
||||
if self._metadata is None:
|
||||
Logger.logException("w", "Failed to load packages for Toolbox")
|
||||
self.setItems(items)
|
||||
return
|
||||
|
||||
for package in self._metadata:
|
||||
has_configs = False
|
||||
configs_model = None
|
||||
|
||||
links_dict = {}
|
||||
if "data" in package:
|
||||
if "supported_configs" in package["data"]:
|
||||
if len(package["data"]["supported_configs"]) > 0:
|
||||
|
@ -56,41 +68,47 @@ class PackagesModel(ListModel):
|
|||
configs_model = ConfigsModel()
|
||||
configs_model.setConfigs(package["data"]["supported_configs"])
|
||||
|
||||
# Links is a list of dictionaries with "title" and "url". Convert this list into a dict so it's easier
|
||||
# to process.
|
||||
link_list = package['data']['links'] if 'links' in package['data'] else []
|
||||
links_dict = {d["title"]: d["url"] for d in link_list}
|
||||
|
||||
if "author_id" not in package["author"] or "display_name" not in package["author"]:
|
||||
package["author"]["author_id"] = ""
|
||||
package["author"]["display_name"] = ""
|
||||
# raise Exception("Detected a package with malformed author data.")
|
||||
|
||||
items.append({
|
||||
"id": package["package_id"],
|
||||
"type": package["package_type"],
|
||||
"name": package["display_name"],
|
||||
"version": package["package_version"],
|
||||
"author_id": package["author"]["author_id"],
|
||||
"author_name": package["author"]["display_name"],
|
||||
"author_email": package["author"]["email"] if "email" in package["author"] else None,
|
||||
"description": package["description"] if "description" in package else None,
|
||||
"icon_url": package["icon_url"] if "icon_url" in package else None,
|
||||
"image_urls": package["image_urls"] if "image_urls" in package else None,
|
||||
"download_url": package["download_url"] if "download_url" in package else None,
|
||||
"last_updated": package["last_updated"] if "last_updated" in package else None,
|
||||
"is_bundled": package["is_bundled"] if "is_bundled" in package else False,
|
||||
"is_active": package["is_active"] if "is_active" in package else False,
|
||||
"is_installed": package["is_installed"] if "is_installed" in package else False,
|
||||
"has_configs": has_configs,
|
||||
"supported_configs": configs_model,
|
||||
"download_count": package["download_count"] if "download_count" in package else 0,
|
||||
"tags": package["tags"] if "tags" in package else []
|
||||
"id": package["package_id"],
|
||||
"type": package["package_type"],
|
||||
"name": package["display_name"],
|
||||
"version": package["package_version"],
|
||||
"author_id": package["author"]["author_id"],
|
||||
"author_name": package["author"]["display_name"],
|
||||
"author_email": package["author"]["email"] if "email" in package["author"] else None,
|
||||
"description": package["description"] if "description" in package else None,
|
||||
"icon_url": package["icon_url"] if "icon_url" in package else None,
|
||||
"image_urls": package["image_urls"] if "image_urls" in package else None,
|
||||
"download_url": package["download_url"] if "download_url" in package else None,
|
||||
"last_updated": package["last_updated"] if "last_updated" in package else None,
|
||||
"is_bundled": package["is_bundled"] if "is_bundled" in package else False,
|
||||
"is_active": package["is_active"] if "is_active" in package else False,
|
||||
"is_installed": package["is_installed"] if "is_installed" in package else False,
|
||||
"has_configs": has_configs,
|
||||
"supported_configs": configs_model,
|
||||
"download_count": package["download_count"] if "download_count" in package else 0,
|
||||
"tags": package["tags"] if "tags" in package else [],
|
||||
"links": links_dict,
|
||||
"website": package["website"] if "website" in package else None,
|
||||
})
|
||||
|
||||
# Filter on all the key-word arguments.
|
||||
for key, value in self._filter.items():
|
||||
if key is "tags":
|
||||
key_filter = lambda item, value = value: value in item["tags"]
|
||||
key_filter = lambda item, v = value: v in item["tags"]
|
||||
elif "*" in value:
|
||||
key_filter = lambda candidate, key = key, value = value: self._matchRegExp(candidate, key, value)
|
||||
key_filter = lambda candidate, k = key, v = value: self._matchRegExp(candidate, k, v)
|
||||
else:
|
||||
key_filter = lambda candidate, key = key, value = value: self._matchString(candidate, key, value)
|
||||
key_filter = lambda candidate, k = key, v = value: self._matchString(candidate, k, v)
|
||||
items = filter(key_filter, items)
|
||||
|
||||
# Execute all filters.
|
||||
|
|
|
@ -6,7 +6,7 @@ import json
|
|||
import os
|
||||
import tempfile
|
||||
import platform
|
||||
from typing import cast, List
|
||||
from typing import cast, List, TYPE_CHECKING, Tuple, Optional
|
||||
|
||||
from PyQt5.QtCore import QUrl, QObject, pyqtProperty, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
|
||||
|
@ -20,9 +20,13 @@ from UM.Version import Version
|
|||
|
||||
import cura
|
||||
from cura.CuraApplication import CuraApplication
|
||||
|
||||
from .AuthorsModel import AuthorsModel
|
||||
from .PackagesModel import PackagesModel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
|
@ -34,19 +38,19 @@ class Toolbox(QObject, Extension):
|
|||
def __init__(self, application: CuraApplication) -> None:
|
||||
super().__init__()
|
||||
|
||||
self._application = application #type: CuraApplication
|
||||
self._application = application # type: CuraApplication
|
||||
|
||||
self._sdk_version = None # type: Optional[int]
|
||||
self._cloud_api_version = None # type: Optional[int]
|
||||
self._cloud_api_root = None # type: Optional[str]
|
||||
self._api_url = None # type: Optional[str]
|
||||
self._sdk_version = None # type: Optional[int]
|
||||
self._cloud_api_version = None # type: Optional[int]
|
||||
self._cloud_api_root = None # type: Optional[str]
|
||||
self._api_url = None # type: Optional[str]
|
||||
|
||||
# Network:
|
||||
self._download_request = None #type: Optional[QNetworkRequest]
|
||||
self._download_reply = None #type: Optional[QNetworkReply]
|
||||
self._download_progress = 0 #type: float
|
||||
self._is_downloading = False #type: bool
|
||||
self._network_manager = None #type: Optional[QNetworkAccessManager]
|
||||
self._download_request = None # type: Optional[QNetworkRequest]
|
||||
self._download_reply = None # type: Optional[QNetworkReply]
|
||||
self._download_progress = 0 # type: float
|
||||
self._is_downloading = False # type: bool
|
||||
self._network_manager = None # type: Optional[QNetworkAccessManager]
|
||||
self._request_header = [
|
||||
b"User-Agent",
|
||||
str.encode(
|
||||
|
@ -58,9 +62,9 @@ class Toolbox(QObject, Extension):
|
|||
)
|
||||
)
|
||||
]
|
||||
self._request_urls = {} # type: Dict[str, QUrl]
|
||||
self._request_urls = {} # type: Dict[str, QUrl]
|
||||
self._to_update = [] # type: List[str] # Package_ids that are waiting to be updated
|
||||
self._old_plugin_ids = [] # type: List[str]
|
||||
self._old_plugin_ids = [] # type: List[str]
|
||||
|
||||
# Data:
|
||||
self._metadata = {
|
||||
|
@ -73,7 +77,7 @@ class Toolbox(QObject, Extension):
|
|||
"materials_available": [],
|
||||
"materials_installed": [],
|
||||
"materials_generic": []
|
||||
} # type: Dict[str, List[Any]]
|
||||
} # type: Dict[str, List[Any]]
|
||||
|
||||
# Models:
|
||||
self._models = {
|
||||
|
@ -83,42 +87,40 @@ class Toolbox(QObject, Extension):
|
|||
"plugins_available": PackagesModel(self),
|
||||
"plugins_installed": PackagesModel(self),
|
||||
"materials_showcase": AuthorsModel(self),
|
||||
"materials_available": PackagesModel(self),
|
||||
"materials_available": AuthorsModel(self),
|
||||
"materials_installed": PackagesModel(self),
|
||||
"materials_generic": PackagesModel(self)
|
||||
} # type: Dict[str, ListModel]
|
||||
} # type: Dict[str, ListModel]
|
||||
|
||||
# These properties are for keeping track of the UI state:
|
||||
# ----------------------------------------------------------------------
|
||||
# View category defines which filter to use, and therefore effectively
|
||||
# which category is currently being displayed. For example, possible
|
||||
# values include "plugin" or "material", but also "installed".
|
||||
self._view_category = "plugin" #type: str
|
||||
self._view_category = "plugin" # type: str
|
||||
|
||||
# View page defines which type of page layout to use. For example,
|
||||
# possible values include "overview", "detail" or "author".
|
||||
self._view_page = "loading" #type: str
|
||||
self._view_page = "loading" # type: str
|
||||
|
||||
# Active package refers to which package is currently being downloaded,
|
||||
# installed, or otherwise modified.
|
||||
self._active_package = None # type: Optional[Dict[str, Any]]
|
||||
self._active_package = None # type: Optional[Dict[str, Any]]
|
||||
|
||||
self._dialog = None #type: Optional[QObject]
|
||||
self._confirm_reset_dialog = None #type: Optional[QObject]
|
||||
self._dialog = None # type: Optional[QObject]
|
||||
self._confirm_reset_dialog = None # type: Optional[QObject]
|
||||
self._resetUninstallVariables()
|
||||
|
||||
self._restart_required = False #type: bool
|
||||
self._restart_required = False # type: bool
|
||||
|
||||
# variables for the license agreement dialog
|
||||
self._license_dialog_plugin_name = "" #type: str
|
||||
self._license_dialog_license_content = "" #type: str
|
||||
self._license_dialog_plugin_file_location = "" #type: str
|
||||
self._restart_dialog_message = "" #type: str
|
||||
self._license_dialog_plugin_name = "" # type: str
|
||||
self._license_dialog_license_content = "" # type: str
|
||||
self._license_dialog_plugin_file_location = "" # type: str
|
||||
self._restart_dialog_message = "" # type: str
|
||||
|
||||
self._application.initializationFinished.connect(self._onAppInitialized)
|
||||
|
||||
|
||||
|
||||
# Signals:
|
||||
# --------------------------------------------------------------------------
|
||||
# Downloading changes
|
||||
|
@ -137,11 +139,11 @@ class Toolbox(QObject, Extension):
|
|||
showLicenseDialog = pyqtSignal()
|
||||
uninstallVariablesChanged = pyqtSignal()
|
||||
|
||||
def _resetUninstallVariables(self):
|
||||
self._package_id_to_uninstall = None
|
||||
def _resetUninstallVariables(self) -> None:
|
||||
self._package_id_to_uninstall = None # type: Optional[str]
|
||||
self._package_name_to_uninstall = ""
|
||||
self._package_used_materials = []
|
||||
self._package_used_qualities = []
|
||||
self._package_used_materials = [] # type: List[Tuple[GlobalStack, str, str]]
|
||||
self._package_used_qualities = [] # type: List[Tuple[GlobalStack, str, str]]
|
||||
|
||||
@pyqtSlot(result = str)
|
||||
def getLicenseDialogPluginName(self) -> str:
|
||||
|
@ -229,10 +231,12 @@ class Toolbox(QObject, Extension):
|
|||
# Make remote requests:
|
||||
self._makeRequestByType("packages")
|
||||
self._makeRequestByType("authors")
|
||||
self._makeRequestByType("plugins_showcase")
|
||||
self._makeRequestByType("materials_showcase")
|
||||
self._makeRequestByType("materials_available")
|
||||
self._makeRequestByType("materials_generic")
|
||||
# TODO: Uncomment in the future when the tag-filtered api calls work in the cloud server
|
||||
# self._makeRequestByType("plugins_showcase")
|
||||
# self._makeRequestByType("plugins_available")
|
||||
# self._makeRequestByType("materials_showcase")
|
||||
# self._makeRequestByType("materials_available")
|
||||
# self._makeRequestByType("materials_generic")
|
||||
|
||||
# Gather installed packages:
|
||||
self._updateInstalledModels()
|
||||
|
@ -344,26 +348,26 @@ class Toolbox(QObject, Extension):
|
|||
self.uninstall(package_id)
|
||||
|
||||
@pyqtProperty(str, notify = uninstallVariablesChanged)
|
||||
def pluginToUninstall(self):
|
||||
def pluginToUninstall(self) -> str:
|
||||
return self._package_name_to_uninstall
|
||||
|
||||
@pyqtProperty(str, notify = uninstallVariablesChanged)
|
||||
def uninstallUsedMaterials(self):
|
||||
def uninstallUsedMaterials(self) -> str:
|
||||
return "\n".join(["%s (%s)" % (str(global_stack.getName()), material) for global_stack, extruder_nr, material in self._package_used_materials])
|
||||
|
||||
@pyqtProperty(str, notify = uninstallVariablesChanged)
|
||||
def uninstallUsedQualities(self):
|
||||
def uninstallUsedQualities(self) -> str:
|
||||
return "\n".join(["%s (%s)" % (str(global_stack.getName()), quality) for global_stack, extruder_nr, quality in self._package_used_qualities])
|
||||
|
||||
@pyqtSlot()
|
||||
def closeConfirmResetDialog(self):
|
||||
def closeConfirmResetDialog(self) -> None:
|
||||
if self._confirm_reset_dialog is not None:
|
||||
self._confirm_reset_dialog.close()
|
||||
|
||||
## Uses "uninstall variables" to reset qualities and materials, then uninstall
|
||||
# It's used as an action on Confirm reset on Uninstall
|
||||
@pyqtSlot()
|
||||
def resetMaterialsQualitiesAndUninstall(self):
|
||||
def resetMaterialsQualitiesAndUninstall(self) -> None:
|
||||
application = CuraApplication.getInstance()
|
||||
material_manager = application.getMaterialManager()
|
||||
quality_manager = application.getQualityManager()
|
||||
|
@ -376,9 +380,9 @@ class Toolbox(QObject, Extension):
|
|||
default_quality_group = quality_manager.getDefaultQualityType(global_stack)
|
||||
machine_manager.setQualityGroup(default_quality_group, global_stack = global_stack)
|
||||
|
||||
self._markPackageMaterialsAsToBeUninstalled(self._package_id_to_uninstall)
|
||||
|
||||
self.uninstall(self._package_id_to_uninstall)
|
||||
if self._package_id_to_uninstall is not None:
|
||||
self._markPackageMaterialsAsToBeUninstalled(self._package_id_to_uninstall)
|
||||
self.uninstall(self._package_id_to_uninstall)
|
||||
self._resetUninstallVariables()
|
||||
self.closeConfirmResetDialog()
|
||||
|
||||
|
@ -514,12 +518,14 @@ class Toolbox(QObject, Extension):
|
|||
count += 1
|
||||
return count
|
||||
|
||||
# This slot is only used to get the number of material packages by author, not any other type of packages.
|
||||
@pyqtSlot(str, result = int)
|
||||
def getTotalNumberOfPackagesByAuthor(self, author_id: str) -> int:
|
||||
def getTotalNumberOfMaterialPackagesByAuthor(self, author_id: str) -> int:
|
||||
count = 0
|
||||
for package in self._metadata["materials_available"]:
|
||||
if package["author"]["author_id"] == author_id:
|
||||
count += 1
|
||||
for package in self._metadata["packages"]:
|
||||
if package["package_type"] == "material":
|
||||
if package["author"]["author_id"] == author_id:
|
||||
count += 1
|
||||
return count
|
||||
|
||||
@pyqtSlot(str, result = bool)
|
||||
|
@ -606,8 +612,22 @@ class Toolbox(QObject, Extension):
|
|||
self.resetDownload()
|
||||
return
|
||||
|
||||
# HACK: These request are not handled independently at this moment, but together from the "packages" call
|
||||
do_not_handle = [
|
||||
"materials_available",
|
||||
"materials_showcase",
|
||||
"materials_generic",
|
||||
"plugins_available",
|
||||
"plugins_showcase",
|
||||
]
|
||||
|
||||
if reply.operation() == QNetworkAccessManager.GetOperation:
|
||||
for type, url in self._request_urls.items():
|
||||
|
||||
# HACK: Do nothing because we'll handle these from the "packages" call
|
||||
if type in do_not_handle:
|
||||
return
|
||||
|
||||
if reply.url() == url:
|
||||
if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) == 200:
|
||||
try:
|
||||
|
@ -623,25 +643,16 @@ class Toolbox(QObject, Extension):
|
|||
if not self._models[type]:
|
||||
Logger.log("e", "Could not find the %s model.", type)
|
||||
break
|
||||
|
||||
# HACK: Eventually get rid of the code from here...
|
||||
if type is "plugins_showcase" or type is "materials_showcase":
|
||||
self._metadata["plugins_showcase"] = json_data["data"]["plugin"]["packages"]
|
||||
self._models["plugins_showcase"].setMetadata(self._metadata["plugins_showcase"])
|
||||
self._metadata["materials_showcase"] = json_data["data"]["material"]["authors"]
|
||||
self._models["materials_showcase"].setMetadata(self._metadata["materials_showcase"])
|
||||
else:
|
||||
# ...until here.
|
||||
# This hack arises for multiple reasons but the main
|
||||
# one is because there are not separate API calls
|
||||
# for different kinds of showcases.
|
||||
self._metadata[type] = json_data["data"]
|
||||
self._models[type].setMetadata(self._metadata[type])
|
||||
|
||||
self._metadata[type] = json_data["data"]
|
||||
self._models[type].setMetadata(self._metadata[type])
|
||||
|
||||
# Do some auto filtering
|
||||
# TODO: Make multiple API calls in the future to handle this
|
||||
if type is "packages":
|
||||
self._models[type].setFilter({"type": "plugin"})
|
||||
self.buildMaterialsModels()
|
||||
self.buildPluginsModels()
|
||||
if type is "authors":
|
||||
self._models[type].setFilter({"package_types": "material"})
|
||||
if type is "materials_generic":
|
||||
|
@ -680,7 +691,7 @@ class Toolbox(QObject, Extension):
|
|||
self._temp_plugin_file.close()
|
||||
self._onDownloadComplete(file_path)
|
||||
|
||||
def _onDownloadComplete(self, file_path: str):
|
||||
def _onDownloadComplete(self, file_path: str) -> None:
|
||||
Logger.log("i", "Toolbox: Download complete.")
|
||||
package_info = self._package_manager.getPackageInfo(file_path)
|
||||
if not package_info:
|
||||
|
@ -739,9 +750,7 @@ class Toolbox(QObject, Extension):
|
|||
def viewPage(self) -> str:
|
||||
return self._view_page
|
||||
|
||||
|
||||
|
||||
# Expose Models:
|
||||
# Exposed Models:
|
||||
# --------------------------------------------------------------------------
|
||||
@pyqtProperty(QObject, notify = metadataChanged)
|
||||
def authorsModel(self) -> AuthorsModel:
|
||||
|
@ -755,6 +764,10 @@ class Toolbox(QObject, Extension):
|
|||
def pluginsShowcaseModel(self) -> PackagesModel:
|
||||
return cast(PackagesModel, self._models["plugins_showcase"])
|
||||
|
||||
@pyqtProperty(QObject, notify = metadataChanged)
|
||||
def pluginsAvailableModel(self) -> PackagesModel:
|
||||
return cast(PackagesModel, self._models["plugins_available"])
|
||||
|
||||
@pyqtProperty(QObject, notify = metadataChanged)
|
||||
def pluginsInstalledModel(self) -> PackagesModel:
|
||||
return cast(PackagesModel, self._models["plugins_installed"])
|
||||
|
@ -763,6 +776,10 @@ class Toolbox(QObject, Extension):
|
|||
def materialsShowcaseModel(self) -> AuthorsModel:
|
||||
return cast(AuthorsModel, self._models["materials_showcase"])
|
||||
|
||||
@pyqtProperty(QObject, notify = metadataChanged)
|
||||
def materialsAvailableModel(self) -> AuthorsModel:
|
||||
return cast(AuthorsModel, self._models["materials_available"])
|
||||
|
||||
@pyqtProperty(QObject, notify = metadataChanged)
|
||||
def materialsInstalledModel(self) -> PackagesModel:
|
||||
return cast(PackagesModel, self._models["materials_installed"])
|
||||
|
@ -771,8 +788,6 @@ class Toolbox(QObject, Extension):
|
|||
def materialsGenericModel(self) -> PackagesModel:
|
||||
return cast(PackagesModel, self._models["materials_generic"])
|
||||
|
||||
|
||||
|
||||
# Filter Models:
|
||||
# --------------------------------------------------------------------------
|
||||
@pyqtSlot(str, str, str)
|
||||
|
@ -798,3 +813,48 @@ class Toolbox(QObject, Extension):
|
|||
return
|
||||
self._models[model_type].setFilter({})
|
||||
self.filterChanged.emit()
|
||||
|
||||
# HACK(S):
|
||||
# --------------------------------------------------------------------------
|
||||
def buildMaterialsModels(self) -> None:
|
||||
self._metadata["materials_showcase"] = []
|
||||
self._metadata["materials_available"] = []
|
||||
|
||||
processed_authors = [] # type: List[str]
|
||||
|
||||
for item in self._metadata["packages"]:
|
||||
if item["package_type"] == "material":
|
||||
|
||||
author = item["author"]
|
||||
if author["author_id"] in processed_authors:
|
||||
continue
|
||||
|
||||
# Generic materials to be in the same section
|
||||
if "generic" in item["tags"]:
|
||||
self._metadata["materials_generic"].append(item)
|
||||
else:
|
||||
if "showcase" in item["tags"]:
|
||||
self._metadata["materials_showcase"].append(author)
|
||||
else:
|
||||
self._metadata["materials_available"].append(author)
|
||||
|
||||
processed_authors.append(author["author_id"])
|
||||
|
||||
self._models["materials_showcase"].setMetadata(self._metadata["materials_showcase"])
|
||||
self._models["materials_available"].setMetadata(self._metadata["materials_available"])
|
||||
self._models["materials_generic"].setMetadata(self._metadata["materials_generic"])
|
||||
|
||||
def buildPluginsModels(self) -> None:
|
||||
self._metadata["plugins_showcase"] = []
|
||||
self._metadata["plugins_available"] = []
|
||||
|
||||
for item in self._metadata["packages"]:
|
||||
if item["package_type"] == "plugin":
|
||||
|
||||
if "showcase" in item["tags"]:
|
||||
self._metadata["plugins_showcase"].append(item)
|
||||
else:
|
||||
self._metadata["plugins_available"].append(item)
|
||||
|
||||
self._models["plugins_showcase"].setMetadata(self._metadata["plugins_showcase"])
|
||||
self._models["plugins_available"].setMetadata(self._metadata["plugins_available"])
|
||||
|
|
|
@ -13,6 +13,7 @@ from UM.PluginRegistry import PluginRegistry #To get the g-code writer.
|
|||
from PyQt5.QtCore import QBuffer
|
||||
|
||||
from cura.Snapshot import Snapshot
|
||||
from cura.Utils.Threading import call_on_qt_thread
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
@ -29,6 +30,11 @@ class UFPWriter(MeshWriter):
|
|||
Logger.log("d", "Creating thumbnail image...")
|
||||
self._snapshot = Snapshot.snapshot(width = 300, height = 300)
|
||||
|
||||
# This needs to be called on the main thread (Qt thread) because the serialization of material containers can
|
||||
# trigger loading other containers. Because those loaded containers are QtObjects, they must be created on the
|
||||
# Qt thread. The File read/write operations right now are executed on separated threads because they are scheduled
|
||||
# by the Job class.
|
||||
@call_on_qt_thread
|
||||
def write(self, stream, nodes, mode = MeshWriter.OutputMode.BinaryMode):
|
||||
archive = VirtualFile()
|
||||
archive.openStream(stream, "application/x-ufp", OpenMode.WriteOnly)
|
||||
|
@ -60,5 +66,50 @@ class UFPWriter(MeshWriter):
|
|||
else:
|
||||
Logger.log("d", "Thumbnail not created, cannot save it")
|
||||
|
||||
# Store the material.
|
||||
application = Application.getInstance()
|
||||
machine_manager = application.getMachineManager()
|
||||
material_manager = application.getMaterialManager()
|
||||
global_stack = machine_manager.activeMachine
|
||||
|
||||
material_extension = "xml.fdm_material"
|
||||
material_mime_type = "application/x-ultimaker-material-profile"
|
||||
|
||||
try:
|
||||
archive.addContentType(extension = material_extension, mime_type = material_mime_type)
|
||||
except:
|
||||
Logger.log("w", "The material extension: %s was already added", material_extension)
|
||||
|
||||
added_materials = []
|
||||
for extruder_stack in global_stack.extruders.values():
|
||||
material = extruder_stack.material
|
||||
material_file_name = material.getMetaData()["base_file"] + ".xml.fdm_material"
|
||||
material_file_name = "/Materials/" + material_file_name
|
||||
|
||||
#Same material cannot be added
|
||||
if material_file_name in added_materials:
|
||||
continue
|
||||
|
||||
material_root_id = material.getMetaDataEntry("base_file")
|
||||
material_group = material_manager.getMaterialGroup(material_root_id)
|
||||
if material_group is None:
|
||||
Logger.log("e", "Cannot find material container with root id [%s]", material_root_id)
|
||||
return False
|
||||
|
||||
material_container = material_group.root_material_node.getContainer()
|
||||
try:
|
||||
serialized_material = material_container.serialize()
|
||||
except NotImplementedError:
|
||||
Logger.log("e", "Unable serialize material container with root id: %s", material_root_id)
|
||||
return False
|
||||
|
||||
material_file = archive.getStream(material_file_name)
|
||||
material_file.write(serialized_material.encode("UTF-8"))
|
||||
archive.addRelation(virtual_path = material_file_name,
|
||||
relation_type = "http://schemas.ultimaker.org/package/2018/relationships/material",
|
||||
origin = "/3D/model.gcode")
|
||||
|
||||
added_materials.append(material_file_name)
|
||||
|
||||
archive.close()
|
||||
return True
|
||||
|
|
|
@ -1,241 +0,0 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
import UM 1.3 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Component
|
||||
{
|
||||
Rectangle
|
||||
{
|
||||
id: base
|
||||
property var manager: Cura.MachineManager.printerOutputDevices[0]
|
||||
property var lineColor: "#DCDCDC" // TODO: Should be linked to theme.
|
||||
property var cornerRadius: 4 * screenScaleFactor // TODO: Should be linked to theme.
|
||||
|
||||
visible: manager != null
|
||||
anchors.fill: parent
|
||||
color: UM.Theme.getColor("viewport_background")
|
||||
|
||||
UM.I18nCatalog
|
||||
{
|
||||
id: catalog
|
||||
name: "cura"
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: activePrintersLabel
|
||||
font: UM.Theme.getFont("large")
|
||||
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
|
||||
{
|
||||
id: printJobArea
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: lineColor
|
||||
anchors.top: activePrintersLabel.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin:UM.Theme.getSize("default_margin").width
|
||||
radius: cornerRadius
|
||||
height: childrenRect.height
|
||||
|
||||
Item
|
||||
{
|
||||
id: printJobTitleBar
|
||||
width: parent.width
|
||||
height: printJobTitleLabel.height + 2 * UM.Theme.getSize("default_margin").height
|
||||
|
||||
Label
|
||||
{
|
||||
id: printJobTitleLabel
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
text: catalog.i18nc("@title", "Print jobs")
|
||||
font: UM.Theme.getFont("default")
|
||||
opacity: 0.75
|
||||
}
|
||||
Rectangle
|
||||
{
|
||||
anchors.bottom: parent.bottom
|
||||
height: UM.Theme.getSize("default_lining").width
|
||||
color: lineColor
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
|
||||
Column
|
||||
{
|
||||
id: printJobColumn
|
||||
anchors.top: printJobTitleBar.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
|
||||
Item
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
opacity: 0.65
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@label", "Printing")
|
||||
font: UM.Theme.getFont("very_small")
|
||||
|
||||
}
|
||||
Label
|
||||
{
|
||||
text: manager.activePrintJobs.length
|
||||
font: UM.Theme.getFont("small")
|
||||
anchors.right: parent.right
|
||||
}
|
||||
}
|
||||
Item
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
opacity: 0.65
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@label", "Queued")
|
||||
font: UM.Theme.getFont("very_small")
|
||||
}
|
||||
Label
|
||||
{
|
||||
text: manager.queuedPrintJobs.length
|
||||
font: UM.Theme.getFont("small")
|
||||
anchors.right: parent.right
|
||||
}
|
||||
}
|
||||
}
|
||||
OpenPanelButton
|
||||
{
|
||||
anchors.top: printJobColumn.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: UM.Theme.getSize("default_margin").height
|
||||
id: configButton
|
||||
onClicked: base.manager.openPrintJobControlPanel()
|
||||
text: catalog.i18nc("@action:button", "View print jobs")
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
// spacer
|
||||
anchors.top: configButton.bottom
|
||||
width: UM.Theme.getSize("default_margin").width
|
||||
height: UM.Theme.getSize("default_margin").height
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: printersArea
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: lineColor
|
||||
anchors.top: printJobArea.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin:UM.Theme.getSize("default_margin").width
|
||||
radius: cornerRadius
|
||||
height: childrenRect.height
|
||||
|
||||
Item
|
||||
{
|
||||
id: printersTitleBar
|
||||
width: parent.width
|
||||
height: printJobTitleLabel.height + 2 * UM.Theme.getSize("default_margin").height
|
||||
|
||||
Label
|
||||
{
|
||||
id: printersTitleLabel
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
text: catalog.i18nc("@label:title", "Printers")
|
||||
font: UM.Theme.getFont("default")
|
||||
opacity: 0.75
|
||||
}
|
||||
Rectangle
|
||||
{
|
||||
anchors.bottom: parent.bottom
|
||||
height: UM.Theme.getSize("default_lining").width
|
||||
color: lineColor
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
Column
|
||||
{
|
||||
id: printersColumn
|
||||
anchors.top: printersTitleBar.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
|
||||
Repeater
|
||||
{
|
||||
model: manager.connectedPrintersTypeCount
|
||||
Item
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
opacity: 0.65
|
||||
Label
|
||||
{
|
||||
text: modelData.machine_type
|
||||
font: UM.Theme.getFont("very_small")
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
text: modelData.count
|
||||
font: UM.Theme.getFont("small")
|
||||
anchors.right: parent.right
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
OpenPanelButton
|
||||
{
|
||||
anchors.top: printersColumn.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: UM.Theme.getSize("default_margin").height
|
||||
id: printerConfigButton
|
||||
onClicked: base.manager.openPrinterControlPanel()
|
||||
|
||||
text: catalog.i18nc("@action:button", "View printers")
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
// spacer
|
||||
anchors.top: printerConfigButton.bottom
|
||||
width: UM.Theme.getSize("default_margin").width
|
||||
height: UM.Theme.getSize("default_margin").height
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import UM 1.3 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Component
|
||||
{
|
||||
Rectangle
|
||||
{
|
||||
id: monitorFrame
|
||||
width: maximumWidth
|
||||
height: maximumHeight
|
||||
color: UM.Theme.getColor("viewport_background")
|
||||
property var emphasisColor: UM.Theme.getColor("setting_control_border_highlight")
|
||||
property var lineColor: "#DCDCDC" // TODO: Should be linked to theme.
|
||||
property var cornerRadius: 4 * screenScaleFactor // TODO: Should be linked to theme.
|
||||
|
||||
UM.I18nCatalog
|
||||
{
|
||||
id: catalog
|
||||
name: "cura"
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: activePrintersLabel
|
||||
font: UM.Theme.getFont("large")
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: UM.Theme.getSize("default_margin").height * 2 // a bit more spacing to give it some breathing room
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
text: OutputDevice.printers.length == 0 ? catalog.i18nc("@label: arg 1 is group name", "%1 is not set up to host a group of connected Ultimaker 3 printers").arg(Cura.MachineManager.printerOutputDevices[0].name) : ""
|
||||
|
||||
visible: OutputDevice.printers.length == 0
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.top: parent.top
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
width: Math.min(800 * screenScaleFactor, maximumWidth)
|
||||
height: children.height
|
||||
visible: OutputDevice.printers.length != 0
|
||||
|
||||
Label
|
||||
{
|
||||
id: addRemovePrintersLabel
|
||||
anchors.right: parent.right
|
||||
text: catalog.i18nc("@label link to connect manager", "Add/Remove printers")
|
||||
font: UM.Theme.getFont("default")
|
||||
color: UM.Theme.getColor("text")
|
||||
linkColor: UM.Theme.getColor("text_link")
|
||||
}
|
||||
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill: addRemovePrintersLabel
|
||||
hoverEnabled: true
|
||||
onClicked: Cura.MachineManager.printerOutputDevices[0].openPrinterControlPanel()
|
||||
onEntered: addRemovePrintersLabel.font.underline = true
|
||||
onExited: addRemovePrintersLabel.font.underline = false
|
||||
}
|
||||
}
|
||||
|
||||
ScrollView
|
||||
{
|
||||
id: printerScrollView
|
||||
anchors.margins: UM.Theme.getSize("default_margin").width
|
||||
anchors.top: activePrintersLabel.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_lining").width // To ensure border can be drawn.
|
||||
anchors.rightMargin: UM.Theme.getSize("default_lining").width
|
||||
anchors.right: parent.right
|
||||
|
||||
ListView
|
||||
{
|
||||
anchors.fill: parent
|
||||
spacing: -UM.Theme.getSize("default_lining").height
|
||||
|
||||
model: OutputDevice.printers
|
||||
|
||||
delegate: PrinterInfoBlock
|
||||
{
|
||||
printer: modelData
|
||||
width: Math.min(800 * screenScaleFactor, maximumWidth)
|
||||
height: 125 * screenScaleFactor
|
||||
|
||||
// Add a 1 pix margin, as the border is sometimes cut off otherwise.
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PrinterVideoStream
|
||||
{
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
|
||||
import UM 1.1 as UM
|
||||
|
||||
Button {
|
||||
objectName: "openPanelSaveAreaButton"
|
||||
id: openPanelSaveAreaButton
|
||||
|
||||
UM.I18nCatalog { id: catalog; name: "cura"; }
|
||||
|
||||
height: UM.Theme.getSize("save_button_save_to_button").height
|
||||
tooltip: catalog.i18nc("@info:tooltip", "Opens the print jobs page with your default web browser.")
|
||||
text: catalog.i18nc("@action:button", "View print jobs")
|
||||
|
||||
// FIXME: This button style is copied and duplicated from SaveButton.qml
|
||||
style: ButtonStyle {
|
||||
background: Rectangle
|
||||
{
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color:
|
||||
{
|
||||
if(!control.enabled)
|
||||
return UM.Theme.getColor("action_button_disabled_border");
|
||||
else if(control.pressed)
|
||||
return UM.Theme.getColor("print_button_ready_pressed_border");
|
||||
else if(control.hovered)
|
||||
return UM.Theme.getColor("print_button_ready_hovered_border");
|
||||
else
|
||||
return UM.Theme.getColor("print_button_ready_border");
|
||||
}
|
||||
color:
|
||||
{
|
||||
if(!control.enabled)
|
||||
return UM.Theme.getColor("action_button_disabled");
|
||||
else if(control.pressed)
|
||||
return UM.Theme.getColor("print_button_ready_pressed");
|
||||
else if(control.hovered)
|
||||
return UM.Theme.getColor("print_button_ready_hovered");
|
||||
else
|
||||
return UM.Theme.getColor("print_button_ready");
|
||||
}
|
||||
|
||||
Behavior on color { ColorAnimation { duration: 50; } }
|
||||
|
||||
implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("sidebar_margin").width * 2)
|
||||
|
||||
Label {
|
||||
id: actualLabel
|
||||
anchors.centerIn: parent
|
||||
color:
|
||||
{
|
||||
if(!control.enabled)
|
||||
return UM.Theme.getColor("action_button_disabled_text");
|
||||
else if(control.pressed)
|
||||
return UM.Theme.getColor("print_button_ready_text");
|
||||
else if(control.hovered)
|
||||
return UM.Theme.getColor("print_button_ready_text");
|
||||
else
|
||||
return UM.Theme.getColor("print_button_ready_text");
|
||||
}
|
||||
font: UM.Theme.getFont("action_button")
|
||||
text: control.text;
|
||||
}
|
||||
}
|
||||
label: Item { }
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import UM 1.2 as UM
|
||||
|
||||
|
||||
Item
|
||||
{
|
||||
id: extruderInfo
|
||||
property var printCoreConfiguration
|
||||
|
||||
width: Math.round(parent.width / 2)
|
||||
height: childrenRect.height
|
||||
Label
|
||||
{
|
||||
id: materialLabel
|
||||
text: printCoreConfiguration.activeMaterial != null ? printCoreConfiguration.activeMaterial.name : ""
|
||||
elide: Text.ElideRight
|
||||
width: parent.width
|
||||
font: UM.Theme.getFont("very_small")
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: printCoreLabel
|
||||
text: printCoreConfiguration.hotendID
|
||||
anchors.top: materialLabel.bottom
|
||||
elide: Text.ElideRight
|
||||
width: parent.width
|
||||
font: UM.Theme.getFont("very_small")
|
||||
opacity: 0.5
|
||||
}
|
||||
}
|
|
@ -1,431 +0,0 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import UM 1.3 as UM
|
||||
|
||||
Rectangle
|
||||
{
|
||||
function strPadLeft(string, pad, length)
|
||||
{
|
||||
return (new Array(length + 1).join(pad) + string).slice(-length);
|
||||
}
|
||||
|
||||
function getPrettyTime(time)
|
||||
{
|
||||
return OutputDevice.formatDuration(time)
|
||||
}
|
||||
|
||||
function formatPrintJobPercent(printJob)
|
||||
{
|
||||
if (printJob == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
if (printJob.timeTotal === 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return Math.min(100, Math.round(printJob.timeElapsed / printJob.timeTotal * 100)) + "%";
|
||||
}
|
||||
|
||||
function printerStatusText(printer)
|
||||
{
|
||||
switch (printer.state)
|
||||
{
|
||||
case "pre_print":
|
||||
return catalog.i18nc("@label:status", "Preparing to print")
|
||||
case "printing":
|
||||
return catalog.i18nc("@label:status", "Printing");
|
||||
case "idle":
|
||||
return catalog.i18nc("@label:status", "Available");
|
||||
case "unreachable":
|
||||
return catalog.i18nc("@label:status", "Lost connection with the printer");
|
||||
case "maintenance":
|
||||
return catalog.i18nc("@label:status", "Unavailable");
|
||||
default:
|
||||
return catalog.i18nc("@label:status", "Unknown");
|
||||
}
|
||||
}
|
||||
|
||||
id: printerDelegate
|
||||
|
||||
property var printer: null
|
||||
property var printJob: printer != null ? printer.activePrintJob: null
|
||||
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: mouse.containsMouse ? emphasisColor : lineColor
|
||||
z: mouse.containsMouse ? 1 : 0 // Push this item up a bit on mouse over to ensure that the highlighted bottom border is visible.
|
||||
|
||||
MouseArea
|
||||
{
|
||||
id: mouse
|
||||
anchors.fill:parent
|
||||
onClicked: OutputDevice.setActivePrinter(printer)
|
||||
hoverEnabled: true;
|
||||
|
||||
// Only clickable if no printer is selected
|
||||
enabled: OutputDevice.activePrinter == null && printer.state !== "unreachable"
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: UM.Theme.getSize("default_margin").width
|
||||
|
||||
Rectangle
|
||||
{
|
||||
width: Math.round(parent.width / 3)
|
||||
height: parent.height
|
||||
|
||||
Label // Print job name
|
||||
{
|
||||
id: jobNameLabel
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
|
||||
text: printJob != null ? printJob.name : ""
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
elide: Text.ElideRight
|
||||
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: jobOwnerLabel
|
||||
anchors.top: jobNameLabel.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
text: printJob != null ? printJob.owner : ""
|
||||
opacity: 0.50
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: totalTimeLabel
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
text: printJob != null ? getPrettyTime(printJob.timeTotal) : ""
|
||||
opacity: 0.65
|
||||
font: UM.Theme.getFont("default")
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
width: Math.round(parent.width / 3 * 2)
|
||||
height: parent.height
|
||||
|
||||
Label // Friendly machine name
|
||||
{
|
||||
id: printerNameLabel
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
width: Math.round(parent.width / 2 - UM.Theme.getSize("default_margin").width - showCameraIcon.width)
|
||||
text: printer.name
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Label // Machine variant
|
||||
{
|
||||
id: printerTypeLabel
|
||||
anchors.top: printerNameLabel.bottom
|
||||
width: Math.round(parent.width / 2 - UM.Theme.getSize("default_margin").width)
|
||||
text: printer.type
|
||||
anchors.left: parent.left
|
||||
elide: Text.ElideRight
|
||||
font: UM.Theme.getFont("very_small")
|
||||
opacity: 0.50
|
||||
}
|
||||
|
||||
Rectangle // Camera icon
|
||||
{
|
||||
id: showCameraIcon
|
||||
width: 40 * screenScaleFactor
|
||||
height: width
|
||||
radius: width
|
||||
anchors.right: printProgressArea.left
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
color: emphasisColor
|
||||
opacity: printer != null && printer.state === "unreachable" ? 0.3 : 1
|
||||
|
||||
Image
|
||||
{
|
||||
width: parent.width
|
||||
height: width
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: parent.rightMargin
|
||||
source: "camera-icon.svg"
|
||||
}
|
||||
}
|
||||
|
||||
Row // PrintCore config
|
||||
{
|
||||
id: extruderInfo
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
width: Math.round(parent.width / 2 - UM.Theme.getSize("default_margin").width)
|
||||
height: childrenRect.height
|
||||
|
||||
spacing: UM.Theme.getSize("default_margin").width
|
||||
|
||||
PrintCoreConfiguration
|
||||
{
|
||||
id: leftExtruderInfo
|
||||
width: Math.round((parent.width - extruderSeperator.width) / 2)
|
||||
printCoreConfiguration: printer.extruders[0]
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: extruderSeperator
|
||||
width: UM.Theme.getSize("default_lining").width
|
||||
height: parent.height
|
||||
color: lineColor
|
||||
}
|
||||
|
||||
PrintCoreConfiguration
|
||||
{
|
||||
id: rightExtruderInfo
|
||||
width: Math.round((parent.width - extruderSeperator.width) / 2)
|
||||
printCoreConfiguration: printer.extruders[1]
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle // Print progress
|
||||
{
|
||||
id: printProgressArea
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
height: showExtended ? parent.height: printProgressTitleBar.height
|
||||
width: Math.round(parent.width / 2 - UM.Theme.getSize("default_margin").width)
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: lineColor
|
||||
radius: cornerRadius
|
||||
property var showExtended: {
|
||||
if(printJob != null)
|
||||
{
|
||||
var extendStates = ["sent_to_printer", "wait_for_configuration", "printing", "pre_print", "post_print", "wait_cleanup", "queued"];
|
||||
return extendStates.indexOf(printJob.state) !== -1;
|
||||
}
|
||||
return printer.state == "disabled"
|
||||
}
|
||||
|
||||
Item // Status and Percent
|
||||
{
|
||||
id: printProgressTitleBar
|
||||
|
||||
property var showPercent: {
|
||||
return printJob != null && (["printing", "post_print", "pre_print", "sent_to_printer"].indexOf(printJob.state) !== -1);
|
||||
}
|
||||
|
||||
width: parent.width
|
||||
//TODO: hardcoded value
|
||||
height: 40 * screenScaleFactor
|
||||
anchors.left: parent.left
|
||||
|
||||
Label
|
||||
{
|
||||
id: statusText
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.right: progressText.left
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: {
|
||||
if (printer.state == "disabled")
|
||||
{
|
||||
return catalog.i18nc("@label:status", "Disabled");
|
||||
}
|
||||
|
||||
if (printer.state === "unreachable")
|
||||
{
|
||||
return printerStatusText(printer);
|
||||
}
|
||||
|
||||
if (printJob != null)
|
||||
{
|
||||
switch (printJob.state)
|
||||
{
|
||||
case "printing":
|
||||
case "post_print":
|
||||
return catalog.i18nc("@label:status", "Printing")
|
||||
case "wait_for_configuration":
|
||||
return catalog.i18nc("@label:status", "Reserved")
|
||||
case "wait_cleanup":
|
||||
case "wait_user_action":
|
||||
return catalog.i18nc("@label:status", "Finished")
|
||||
case "pre_print":
|
||||
case "sent_to_printer":
|
||||
return catalog.i18nc("@label", "Preparing to print")
|
||||
case "queued":
|
||||
return catalog.i18nc("@label:status", "Action required");
|
||||
case "pausing":
|
||||
case "paused":
|
||||
return catalog.i18nc("@label:status", "Paused");
|
||||
case "resuming":
|
||||
return catalog.i18nc("@label:status", "Resuming");
|
||||
case "aborted":
|
||||
return catalog.i18nc("@label:status", "Print aborted");
|
||||
default:
|
||||
// If print job has unknown status show printer.status
|
||||
return printerStatusText(printer);
|
||||
}
|
||||
}
|
||||
return printerStatusText(printer);
|
||||
}
|
||||
|
||||
elide: Text.ElideRight
|
||||
font: UM.Theme.getFont("small")
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: progressText
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.top: statusText.top
|
||||
|
||||
text: formatPrintJobPercent(printJob)
|
||||
visible: printProgressTitleBar.showPercent
|
||||
//TODO: Hardcoded value
|
||||
opacity: 0.65
|
||||
font: UM.Theme.getFont("very_small")
|
||||
}
|
||||
|
||||
Image
|
||||
{
|
||||
width: statusText.height
|
||||
height: width
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.top: statusText.top
|
||||
|
||||
visible: !printProgressTitleBar.showPercent
|
||||
|
||||
source: {
|
||||
if (printer.state == "disabled")
|
||||
{
|
||||
return "blocked-icon.svg";
|
||||
}
|
||||
|
||||
if (printer.state === "unreachable")
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if (printJob != null)
|
||||
{
|
||||
if(printJob.state === "queued")
|
||||
{
|
||||
return "action-required-icon.svg";
|
||||
}
|
||||
else if (printJob.state === "wait_cleanup")
|
||||
{
|
||||
return "checkmark-icon.svg";
|
||||
}
|
||||
}
|
||||
return ""; // We're not going to show it, so it will not be resolved as a url.
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
//TODO: This will become a progress bar in the future
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("default_lining").height
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
visible: printProgressArea.showExtended
|
||||
color: lineColor
|
||||
}
|
||||
}
|
||||
|
||||
Column
|
||||
{
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
|
||||
anchors.top: printProgressTitleBar.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
|
||||
width: parent.width - 2 * UM.Theme.getSize("default_margin").width
|
||||
|
||||
visible: printProgressArea.showExtended
|
||||
|
||||
Label // Status detail
|
||||
{
|
||||
text:
|
||||
{
|
||||
if (printer.state == "disabled")
|
||||
{
|
||||
return catalog.i18nc("@label", "Not accepting print jobs");
|
||||
}
|
||||
|
||||
if (printer.state === "unreachable")
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if(printJob != null)
|
||||
{
|
||||
switch (printJob.state)
|
||||
{
|
||||
case "printing":
|
||||
case "post_print":
|
||||
return catalog.i18nc("@label", "Finishes at: ") + OutputDevice.getTimeCompleted(printJob.timeTotal - printJob.timeElapsed)
|
||||
case "wait_cleanup":
|
||||
return catalog.i18nc("@label", "Clear build plate")
|
||||
case "sent_to_printer":
|
||||
case "pre_print":
|
||||
return catalog.i18nc("@label", "Preparing to print")
|
||||
case "wait_for_configuration":
|
||||
return catalog.i18nc("@label", "Not accepting print jobs")
|
||||
case "queued":
|
||||
return catalog.i18nc("@label", "Waiting for configuration change");
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
font: UM.Theme.getFont("default")
|
||||
}
|
||||
|
||||
Label // Status 2nd row
|
||||
{
|
||||
text: {
|
||||
if(printJob != null)
|
||||
{
|
||||
if(printJob.state == "printing" || printJob.state == "post_print")
|
||||
{
|
||||
return OutputDevice.getDateCompleted(printJob.timeTotal - printJob.timeElapsed)
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
elide: Text.ElideRight
|
||||
font: UM.Theme.getFont("default")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import UM 1.3 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: base
|
||||
width: 250 * screenScaleFactor
|
||||
height: 250 * screenScaleFactor
|
||||
signal clicked()
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill:parent
|
||||
onClicked: base.clicked()
|
||||
}
|
||||
Rectangle
|
||||
{
|
||||
// TODO: Actually add UM icon / picture
|
||||
width: 100 * screenScaleFactor
|
||||
height: 100 * screenScaleFactor
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: nameLabel
|
||||
anchors.bottom: ipLabel.top
|
||||
anchors.bottomMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
text: modelData.friendly_name.toString()
|
||||
font: UM.Theme.getFont("large")
|
||||
elide: Text.ElideMiddle;
|
||||
height: UM.Theme.getSize("section").height;
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: ipLabel
|
||||
text: modelData.ip_address.toString()
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: UM.Theme.getSize("default_margin").height
|
||||
font: UM.Theme.getFont("default")
|
||||
height:10 * screenScaleFactor
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from . import DiscoverUM3Action
|
||||
from .src import DiscoverUM3Action
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
from . import UM3OutputDevicePlugin
|
||||
from .src import UM3OutputDevicePlugin
|
||||
|
||||
def getMetaData():
|
||||
return {}
|
||||
|
|
717
plugins/UM3NetworkPrinting/resources/qml/ClusterControlItem.qml
Normal file
|
@ -0,0 +1,717 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
import QtQuick.Controls 2.0 as Controls2
|
||||
|
||||
import UM 1.3 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
|
||||
Component
|
||||
{
|
||||
Rectangle
|
||||
{
|
||||
id: base
|
||||
property var lineColor: "#DCDCDC" // TODO: Should be linked to theme.
|
||||
|
||||
property var cornerRadius: 4 * screenScaleFactor // TODO: Should be linked to theme.
|
||||
visible: OutputDevice != null
|
||||
anchors.fill: parent
|
||||
color: "white"
|
||||
|
||||
UM.I18nCatalog
|
||||
{
|
||||
id: catalog
|
||||
name: "cura"
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: printingLabel
|
||||
font: UM.Theme.getFont("large")
|
||||
anchors
|
||||
{
|
||||
margins: 2 * UM.Theme.getSize("default_margin").width
|
||||
leftMargin: 4 * UM.Theme.getSize("default_margin").width
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
text: catalog.i18nc("@label", "Printing")
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: managePrintersLabel
|
||||
anchors.rightMargin: 4 * UM.Theme.getSize("default_margin").width
|
||||
anchors.right: printerScrollView.right
|
||||
anchors.bottom: printingLabel.bottom
|
||||
text: catalog.i18nc("@label link to connect manager", "Manage printers")
|
||||
font: UM.Theme.getFont("default")
|
||||
color: UM.Theme.getColor("primary")
|
||||
linkColor: UM.Theme.getColor("primary")
|
||||
}
|
||||
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill: managePrintersLabel
|
||||
hoverEnabled: true
|
||||
onClicked: Cura.MachineManager.printerOutputDevices[0].openPrinterControlPanel()
|
||||
onEntered: managePrintersLabel.font.underline = true
|
||||
onExited: managePrintersLabel.font.underline = false
|
||||
}
|
||||
|
||||
ScrollView
|
||||
{
|
||||
id: printerScrollView
|
||||
anchors
|
||||
{
|
||||
top: printingLabel.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
topMargin: UM.Theme.getSize("default_margin").height
|
||||
bottom: parent.bottom
|
||||
bottomMargin: UM.Theme.getSize("default_margin").height
|
||||
}
|
||||
|
||||
style: UM.Theme.styles.scrollview
|
||||
|
||||
ListView
|
||||
{
|
||||
anchors
|
||||
{
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
leftMargin: 2 * UM.Theme.getSize("default_margin").width
|
||||
rightMargin: 2 * UM.Theme.getSize("default_margin").width
|
||||
}
|
||||
spacing: UM.Theme.getSize("default_margin").height -10
|
||||
model: OutputDevice.printers
|
||||
|
||||
delegate: Item
|
||||
{
|
||||
width: parent.width
|
||||
height: base.height + 2 * base.shadowRadius // To ensure that the shadow doesn't get cut off.
|
||||
Rectangle
|
||||
{
|
||||
width: parent.width - 2 * shadowRadius
|
||||
height: childrenRect.height + UM.Theme.getSize("default_margin").height
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
id: base
|
||||
property var shadowRadius: 5
|
||||
property var collapsed: true
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: DropShadow
|
||||
{
|
||||
radius: base.shadowRadius
|
||||
verticalOffset: 2
|
||||
color: "#3F000000" // 25% shadow
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: printerInfo
|
||||
height: machineIcon.height
|
||||
anchors
|
||||
{
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
margins: UM.Theme.getSize("default_margin").width
|
||||
}
|
||||
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill: parent
|
||||
onClicked: base.collapsed = !base.collapsed
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: machineIcon
|
||||
// Yeah, this is hardcoded now, but I can't think of a good way to fix this.
|
||||
// The UI is going to get another update soon, so it's probably not worth the effort...
|
||||
width: 58
|
||||
height: 58
|
||||
anchors.top: parent.top
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.left: parent.left
|
||||
|
||||
UM.RecolorImage
|
||||
{
|
||||
anchors.centerIn: parent
|
||||
source:
|
||||
{
|
||||
switch(modelData.type)
|
||||
{
|
||||
case "Ultimaker 3":
|
||||
return "../svg/UM3-icon.svg"
|
||||
case "Ultimaker 3 Extended":
|
||||
return "../svg/UM3x-icon.svg"
|
||||
case "Ultimaker S5":
|
||||
return "../svg/UMs5-icon.svg"
|
||||
}
|
||||
}
|
||||
width: sourceSize.width
|
||||
height: sourceSize.height
|
||||
|
||||
color:
|
||||
{
|
||||
if(modelData.state == "disabled")
|
||||
{
|
||||
return UM.Theme.getColor("setting_control_disabled")
|
||||
}
|
||||
|
||||
if(modelData.activePrintJob != undefined)
|
||||
{
|
||||
return UM.Theme.getColor("primary")
|
||||
}
|
||||
|
||||
return UM.Theme.getColor("setting_control_disabled")
|
||||
}
|
||||
}
|
||||
}
|
||||
Item
|
||||
{
|
||||
height: childrenRect.height
|
||||
anchors
|
||||
{
|
||||
right: collapseIcon.left
|
||||
rightMargin: UM.Theme.getSize("default_margin").width
|
||||
left: machineIcon.right
|
||||
leftMargin: UM.Theme.getSize("default_margin").width
|
||||
|
||||
verticalCenter: machineIcon.verticalCenter
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: machineNameLabel
|
||||
text: modelData.name
|
||||
width: parent.width
|
||||
elide: Text.ElideRight
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: activeJobLabel
|
||||
text:
|
||||
{
|
||||
if (modelData.state == "disabled")
|
||||
{
|
||||
return catalog.i18nc("@label", "Not available")
|
||||
} else if (modelData.state == "unreachable")
|
||||
{
|
||||
return catalog.i18nc("@label", "Unreachable")
|
||||
}
|
||||
if (modelData.activePrintJob != null)
|
||||
{
|
||||
return modelData.activePrintJob.name
|
||||
}
|
||||
return catalog.i18nc("@label", "Available")
|
||||
}
|
||||
anchors.top: machineNameLabel.bottom
|
||||
width: parent.width
|
||||
elide: Text.ElideRight
|
||||
font: UM.Theme.getFont("default")
|
||||
opacity: 0.6
|
||||
}
|
||||
}
|
||||
|
||||
UM.RecolorImage
|
||||
{
|
||||
id: collapseIcon
|
||||
width: 15
|
||||
height: 15
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
source: base.collapsed ? UM.Theme.getIcon("arrow_left") : UM.Theme.getIcon("arrow_bottom")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
color: "black"
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: detailedInfo
|
||||
property var printJob: modelData.activePrintJob
|
||||
visible: height == childrenRect.height
|
||||
anchors.top: printerInfo.bottom
|
||||
width: parent.width
|
||||
height: !base.collapsed ? childrenRect.height : 0
|
||||
opacity: visible ? 1 : 0
|
||||
Behavior on height { NumberAnimation { duration: 100 } }
|
||||
Behavior on opacity { NumberAnimation { duration: 100 } }
|
||||
Rectangle
|
||||
{
|
||||
id: topSpacer
|
||||
color: UM.Theme.getColor("viewport_background")
|
||||
height: 2
|
||||
anchors
|
||||
{
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
margins: UM.Theme.getSize("default_margin").width
|
||||
top: parent.top
|
||||
topMargin: UM.Theme.getSize("default_margin").width
|
||||
}
|
||||
}
|
||||
PrinterFamilyPill
|
||||
{
|
||||
id: printerFamilyPill
|
||||
color: UM.Theme.getColor("viewport_background")
|
||||
anchors.top: topSpacer.bottom
|
||||
anchors.topMargin: 2 * UM.Theme.getSize("default_margin").height
|
||||
text: modelData.type
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
padding: 3
|
||||
}
|
||||
Row
|
||||
{
|
||||
id: extrudersInfo
|
||||
anchors.top: printerFamilyPill.bottom
|
||||
anchors.topMargin: 2 * UM.Theme.getSize("default_margin").height
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 2 * UM.Theme.getSize("default_margin").width
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 2 * UM.Theme.getSize("default_margin").width
|
||||
height: childrenRect.height
|
||||
spacing: UM.Theme.getSize("default_margin").width
|
||||
|
||||
PrintCoreConfiguration
|
||||
{
|
||||
id: leftExtruderInfo
|
||||
width: Math.round(parent.width / 2)
|
||||
printCoreConfiguration: modelData.printerConfiguration.extruderConfigurations[0]
|
||||
}
|
||||
|
||||
PrintCoreConfiguration
|
||||
{
|
||||
id: rightExtruderInfo
|
||||
width: Math.round(parent.width / 2)
|
||||
printCoreConfiguration: modelData.printerConfiguration.extruderConfigurations[1]
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: jobSpacer
|
||||
color: UM.Theme.getColor("viewport_background")
|
||||
height: 2
|
||||
anchors
|
||||
{
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
margins: UM.Theme.getSize("default_margin").width
|
||||
top: extrudersInfo.bottom
|
||||
topMargin: 2 * UM.Theme.getSize("default_margin").height
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: jobInfo
|
||||
property var showJobInfo: modelData.activePrintJob != null && modelData.activePrintJob.state != "queued"
|
||||
|
||||
anchors.top: jobSpacer.bottom
|
||||
anchors.topMargin: 2 * UM.Theme.getSize("default_margin").height
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: UM.Theme.getSize("default_margin").width
|
||||
anchors.leftMargin: 2 * UM.Theme.getSize("default_margin").width
|
||||
height: showJobInfo ? childrenRect.height + 2 * UM.Theme.getSize("default_margin").height: 0
|
||||
visible: showJobInfo
|
||||
Label
|
||||
{
|
||||
id: printJobName
|
||||
text: modelData.activePrintJob != null ? modelData.activePrintJob.name : ""
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
anchors.left: parent.left
|
||||
anchors.right: contextButton.left
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: ownerName
|
||||
anchors.top: printJobName.bottom
|
||||
text: modelData.activePrintJob != null ? modelData.activePrintJob.owner : ""
|
||||
font: UM.Theme.getFont("default")
|
||||
opacity: 0.6
|
||||
width: parent.width
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
function switchPopupState()
|
||||
{
|
||||
if (popup.visible)
|
||||
{
|
||||
popup.close()
|
||||
}
|
||||
else
|
||||
{
|
||||
popup.open()
|
||||
}
|
||||
}
|
||||
|
||||
Controls2.Button
|
||||
{
|
||||
id: contextButton
|
||||
text: "\u22EE" //Unicode; Three stacked points.
|
||||
font.pixelSize: 25
|
||||
width: 35
|
||||
height: width
|
||||
anchors
|
||||
{
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
}
|
||||
hoverEnabled: true
|
||||
|
||||
background: Rectangle
|
||||
{
|
||||
opacity: contextButton.down || contextButton.hovered ? 1 : 0
|
||||
width: contextButton.width
|
||||
height: contextButton.height
|
||||
radius: 0.5 * width
|
||||
color: UM.Theme.getColor("viewport_background")
|
||||
}
|
||||
|
||||
onClicked: parent.switchPopupState()
|
||||
}
|
||||
|
||||
Controls2.Popup
|
||||
{
|
||||
// TODO Change once updating to Qt5.10 - The 'opened' property is in 5.10 but the behavior is now implemented with the visible property
|
||||
id: popup
|
||||
clip: true
|
||||
closePolicy: Controls2.Popup.CloseOnPressOutsideParent
|
||||
x: parent.width - width
|
||||
y: contextButton.height
|
||||
width: 160
|
||||
height: contentItem.height + 2 * padding
|
||||
visible: false
|
||||
|
||||
transformOrigin: Controls2.Popup.Top
|
||||
contentItem: Item
|
||||
{
|
||||
width: popup.width - 2 * popup.padding
|
||||
height: childrenRect.height + 15
|
||||
Controls2.Button
|
||||
{
|
||||
id: pauseButton
|
||||
text: modelData.activePrintJob != null && modelData.activePrintJob.state == "paused" ? catalog.i18nc("@label", "Resume") : catalog.i18nc("@label", "Pause")
|
||||
onClicked:
|
||||
{
|
||||
if(modelData.activePrintJob.state == "paused")
|
||||
{
|
||||
modelData.activePrintJob.setState("print")
|
||||
}
|
||||
else if(modelData.activePrintJob.state == "printing")
|
||||
{
|
||||
modelData.activePrintJob.setState("pause")
|
||||
}
|
||||
popup.close()
|
||||
}
|
||||
width: parent.width
|
||||
enabled: modelData.activePrintJob != null && ["paused", "printing"].indexOf(modelData.activePrintJob.state) >= 0
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
hoverEnabled: true
|
||||
background: Rectangle
|
||||
{
|
||||
opacity: pauseButton.down || pauseButton.hovered ? 1 : 0
|
||||
color: UM.Theme.getColor("viewport_background")
|
||||
}
|
||||
}
|
||||
|
||||
Controls2.Button
|
||||
{
|
||||
id: abortButton
|
||||
text: catalog.i18nc("@label", "Abort")
|
||||
onClicked:
|
||||
{
|
||||
modelData.activePrintJob.setState("abort")
|
||||
popup.close()
|
||||
}
|
||||
width: parent.width
|
||||
anchors.top: pauseButton.bottom
|
||||
hoverEnabled: true
|
||||
enabled: modelData.activePrintJob != null && ["paused", "printing", "pre_print"].indexOf(modelData.activePrintJob.state) >= 0
|
||||
background: Rectangle
|
||||
{
|
||||
opacity: abortButton.down || abortButton.hovered ? 1 : 0
|
||||
color: UM.Theme.getColor("viewport_background")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
background: Item
|
||||
{
|
||||
width: popup.width
|
||||
height: popup.height
|
||||
|
||||
DropShadow
|
||||
{
|
||||
anchors.fill: pointedRectangle
|
||||
radius: 5
|
||||
color: "#3F000000" // 25% shadow
|
||||
source: pointedRectangle
|
||||
transparentBorder: true
|
||||
verticalOffset: 2
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: pointedRectangle
|
||||
width: parent.width -10
|
||||
height: parent.height -10
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: point
|
||||
height: 13
|
||||
width: 13
|
||||
color: UM.Theme.getColor("setting_control")
|
||||
transform: Rotation { angle: 45}
|
||||
anchors.right: bloop.right
|
||||
y: 1
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: bloop
|
||||
color: UM.Theme.getColor("setting_control")
|
||||
width: parent.width
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit: Transition
|
||||
{
|
||||
// This applies a default NumberAnimation to any changes a state change makes to x or y properties
|
||||
NumberAnimation { property: "visible"; duration: 75; }
|
||||
}
|
||||
enter: Transition
|
||||
{
|
||||
// This applies a default NumberAnimation to any changes a state change makes to x or y properties
|
||||
NumberAnimation { property: "visible"; duration: 75; }
|
||||
}
|
||||
|
||||
onClosed: visible = false
|
||||
onOpened: visible = true
|
||||
}
|
||||
|
||||
Image
|
||||
{
|
||||
id: printJobPreview
|
||||
source: modelData.activePrintJob != null ? modelData.activePrintJob.previewImageUrl : ""
|
||||
anchors.top: ownerName.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width / 2
|
||||
height: width
|
||||
opacity:
|
||||
{
|
||||
if(modelData.activePrintJob == null)
|
||||
{
|
||||
return 1.0
|
||||
}
|
||||
|
||||
switch(modelData.activePrintJob.state)
|
||||
{
|
||||
case "wait_cleanup":
|
||||
case "wait_user_action":
|
||||
case "paused":
|
||||
return 0.5
|
||||
default:
|
||||
return 1.0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
UM.RecolorImage
|
||||
{
|
||||
id: statusImage
|
||||
anchors.centerIn: printJobPreview
|
||||
source:
|
||||
{
|
||||
if(modelData.activePrintJob == null)
|
||||
{
|
||||
return ""
|
||||
}
|
||||
switch(modelData.activePrintJob.state)
|
||||
{
|
||||
case "paused":
|
||||
return "../svg/paused-icon.svg"
|
||||
case "wait_cleanup":
|
||||
if(modelData.activePrintJob.timeElapsed < modelData.activePrintJob.timeTotal)
|
||||
{
|
||||
return "../svg/aborted-icon.svg"
|
||||
}
|
||||
return "../svg/approved-icon.svg"
|
||||
case "wait_user_action":
|
||||
return "../svg/aborted-icon.svg"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
visible: source != ""
|
||||
width: 0.5 * printJobPreview.width
|
||||
height: 0.5 * printJobPreview.height
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
color: "black"
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: showCameraIcon
|
||||
width: 35 * screenScaleFactor
|
||||
height: width
|
||||
radius: 0.5 * width
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: printJobPreview.bottom
|
||||
color: UM.Theme.getColor("setting_control_border_highlight")
|
||||
Image
|
||||
{
|
||||
width: parent.width
|
||||
height: width
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: parent.rightMargin
|
||||
source: "../svg/camera-icon.svg"
|
||||
}
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill:parent
|
||||
onClicked:
|
||||
{
|
||||
OutputDevice.setActiveCamera(modelData.camera)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProgressBar
|
||||
{
|
||||
property var progress:
|
||||
{
|
||||
if(modelData.activePrintJob == null)
|
||||
{
|
||||
return 0
|
||||
}
|
||||
var result = modelData.activePrintJob.timeElapsed / modelData.activePrintJob.timeTotal
|
||||
if(result > 1.0)
|
||||
{
|
||||
result = 1.0
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
id: jobProgressBar
|
||||
width: parent.width
|
||||
value: progress
|
||||
anchors.top: detailedInfo.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
|
||||
visible: modelData.activePrintJob != null && modelData.activePrintJob != undefined
|
||||
|
||||
style: ProgressBarStyle
|
||||
{
|
||||
property var progressText:
|
||||
{
|
||||
if(modelData.activePrintJob == null)
|
||||
{
|
||||
return ""
|
||||
}
|
||||
|
||||
switch(modelData.activePrintJob.state)
|
||||
{
|
||||
case "wait_cleanup":
|
||||
if(modelData.activePrintJob.timeTotal > modelData.activePrintJob.timeElapsed)
|
||||
{
|
||||
return catalog.i18nc("@label:status", "Aborted")
|
||||
}
|
||||
return catalog.i18nc("@label:status", "Finished")
|
||||
case "pre_print":
|
||||
case "sent_to_printer":
|
||||
return catalog.i18nc("@label:status", "Preparing")
|
||||
case "aborted":
|
||||
case "wait_user_action":
|
||||
return catalog.i18nc("@label:status", "Aborted")
|
||||
case "pausing":
|
||||
return catalog.i18nc("@label:status", "Pausing")
|
||||
case "paused":
|
||||
return catalog.i18nc("@label:status", "Paused")
|
||||
case "resuming":
|
||||
return catalog.i18nc("@label:status", "Resuming")
|
||||
case "queued":
|
||||
return catalog.i18nc("@label:status", "Action required")
|
||||
default:
|
||||
OutputDevice.formatDuration(modelData.activePrintJob.timeTotal - modelData.activePrintJob.timeElapsed)
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle
|
||||
{
|
||||
implicitWidth: 100
|
||||
implicitHeight: visible ? 24 : 0
|
||||
color: UM.Theme.getColor("viewport_background")
|
||||
}
|
||||
|
||||
progress: Rectangle
|
||||
{
|
||||
color: UM.Theme.getColor("primary")
|
||||
id: progressItem
|
||||
function getTextOffset()
|
||||
{
|
||||
if(progressItem.width + progressLabel.width < control.width)
|
||||
{
|
||||
return progressItem.width + UM.Theme.getSize("default_margin").width
|
||||
}
|
||||
else
|
||||
{
|
||||
return progressItem.width - progressLabel.width - UM.Theme.getSize("default_margin").width
|
||||
}
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: progressLabel
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: getTextOffset()
|
||||
text: progressText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: progressItem.width + progressLabel.width < control.width ? "black" : "white"
|
||||
width: contentWidth
|
||||
font: UM.Theme.getFont("default")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
108
plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml
Normal file
|
@ -0,0 +1,108 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import UM 1.3 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Component
|
||||
{
|
||||
Rectangle
|
||||
{
|
||||
id: monitorFrame
|
||||
width: maximumWidth
|
||||
height: maximumHeight
|
||||
color: UM.Theme.getColor("viewport_background")
|
||||
property var emphasisColor: UM.Theme.getColor("setting_control_border_highlight")
|
||||
property var lineColor: "#DCDCDC" // TODO: Should be linked to theme.
|
||||
property var cornerRadius: 4 * screenScaleFactor // TODO: Should be linked to theme.
|
||||
|
||||
UM.I18nCatalog
|
||||
{
|
||||
id: catalog
|
||||
name: "cura"
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: manageQueueLabel
|
||||
anchors.rightMargin: 4 * UM.Theme.getSize("default_margin").width
|
||||
anchors.right: queuedPrintJobs.right
|
||||
anchors.bottom: queuedLabel.bottom
|
||||
text: catalog.i18nc("@label link to connect manager", "Manage queue")
|
||||
font: UM.Theme.getFont("default")
|
||||
color: UM.Theme.getColor("primary")
|
||||
linkColor: UM.Theme.getColor("primary")
|
||||
}
|
||||
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill: manageQueueLabel
|
||||
hoverEnabled: true
|
||||
onClicked: Cura.MachineManager.printerOutputDevices[0].openPrintJobControlPanel()
|
||||
onEntered: manageQueueLabel.font.underline = true
|
||||
onExited: manageQueueLabel.font.underline = false
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: queuedLabel
|
||||
anchors.left: queuedPrintJobs.left
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 2 * UM.Theme.getSize("default_margin").height
|
||||
anchors.leftMargin: 3 * UM.Theme.getSize("default_margin").width
|
||||
text: catalog.i18nc("@label", "Queued")
|
||||
font: UM.Theme.getFont("large")
|
||||
color: UM.Theme.getColor("text")
|
||||
}
|
||||
|
||||
ScrollView
|
||||
{
|
||||
id: queuedPrintJobs
|
||||
|
||||
anchors
|
||||
{
|
||||
top: queuedLabel.bottom
|
||||
topMargin: UM.Theme.getSize("default_margin").height
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
bottomMargin: 0
|
||||
bottom: parent.bottom
|
||||
}
|
||||
style: UM.Theme.styles.scrollview
|
||||
width: Math.min(800 * screenScaleFactor, maximumWidth)
|
||||
ListView
|
||||
{
|
||||
anchors.fill: parent
|
||||
//anchors.margins: UM.Theme.getSize("default_margin").height
|
||||
spacing: UM.Theme.getSize("default_margin").height - 10 // 2x the shadow radius
|
||||
|
||||
model: OutputDevice.queuedPrintJobs
|
||||
|
||||
delegate: PrintJobInfoBlock
|
||||
{
|
||||
printJob: modelData
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").height
|
||||
height: 175 * screenScaleFactor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PrinterVideoStream
|
||||
{
|
||||
visible: OutputDevice.activeCamera != null
|
||||
anchors.fill: parent
|
||||
camera: OutputDevice.activeCamera
|
||||
}
|
||||
|
||||
onVisibleChanged:
|
||||
{
|
||||
if (monitorFrame != null && !monitorFrame.visible)
|
||||
{
|
||||
OutputDevice.setActiveCamera(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -364,7 +364,6 @@ Cura.MachineAction
|
|||
{
|
||||
id: addressField
|
||||
width: parent.width
|
||||
maximumLength: 40
|
||||
validator: RegExpValidator
|
||||
{
|
||||
regExp: /[a-zA-Z0-9\.\-\_]*/
|
|
@ -0,0 +1,93 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import UM 1.2 as UM
|
||||
|
||||
|
||||
Item
|
||||
{
|
||||
id: extruderInfo
|
||||
property var printCoreConfiguration
|
||||
|
||||
width: Math.round(parent.width / 2)
|
||||
height: childrenRect.height
|
||||
|
||||
Item
|
||||
{
|
||||
id: extruderCircle
|
||||
width: 30
|
||||
height: 30
|
||||
|
||||
anchors.verticalCenter: printAndMaterialLabel.verticalCenter
|
||||
opacity:
|
||||
{
|
||||
if(printCoreConfiguration == null || printCoreConfiguration.activeMaterial == null || printCoreConfiguration.hotendID == null)
|
||||
{
|
||||
return 0.5
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
anchors.fill: parent
|
||||
radius: Math.round(width / 2)
|
||||
border.width: 2
|
||||
border.color: "black"
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
anchors.centerIn: parent
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
text: printCoreConfiguration.position + 1
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: printAndMaterialLabel
|
||||
anchors
|
||||
{
|
||||
right: parent.right
|
||||
left: extruderCircle.right
|
||||
margins: UM.Theme.getSize("default_margin").width
|
||||
}
|
||||
height: childrenRect.height
|
||||
|
||||
Label
|
||||
{
|
||||
id: materialLabel
|
||||
text:
|
||||
{
|
||||
if(printCoreConfiguration != undefined && printCoreConfiguration.activeMaterial != undefined)
|
||||
{
|
||||
return printCoreConfiguration.activeMaterial.name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
elide: Text.ElideRight
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: printCoreLabel
|
||||
text:
|
||||
{
|
||||
if(printCoreConfiguration != undefined && printCoreConfiguration.hotendID != undefined)
|
||||
{
|
||||
return printCoreConfiguration.hotendID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
anchors.top: materialLabel.bottom
|
||||
elide: Text.ElideRight
|
||||
width: parent.width
|
||||
opacity: 0.6
|
||||
font: UM.Theme.getFont("default")
|
||||
}
|
||||
}
|
||||
}
|
378
plugins/UM3NetworkPrinting/resources/qml/PrintJobInfoBlock.qml
Normal file
|
@ -0,0 +1,378 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
import UM 1.3 as UM
|
||||
|
||||
|
||||
Item
|
||||
{
|
||||
id: base
|
||||
property var printJob: null
|
||||
property var shadowRadius: 5
|
||||
function getPrettyTime(time)
|
||||
{
|
||||
return OutputDevice.formatDuration(time)
|
||||
}
|
||||
|
||||
UM.I18nCatalog
|
||||
{
|
||||
id: catalog
|
||||
name: "cura"
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: background
|
||||
anchors
|
||||
{
|
||||
top: parent.top
|
||||
topMargin: 3
|
||||
left: parent.left
|
||||
leftMargin: base.shadowRadius
|
||||
rightMargin: base.shadowRadius
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
bottomMargin: base.shadowRadius
|
||||
}
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: DropShadow
|
||||
{
|
||||
radius: base.shadowRadius
|
||||
verticalOffset: 2
|
||||
color: "#3F000000" // 25% shadow
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
// Content on the left of the infobox
|
||||
anchors
|
||||
{
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
right: parent.horizontalCenter
|
||||
margins: 2 * UM.Theme.getSize("default_margin").width
|
||||
rightMargin: UM.Theme.getSize("default_margin").width
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: printJobName
|
||||
text: printJob.name
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
width: parent.width
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: ownerName
|
||||
anchors.top: printJobName.bottom
|
||||
text: printJob.owner
|
||||
font: UM.Theme.getFont("default")
|
||||
opacity: 0.6
|
||||
width: parent.width
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Image
|
||||
{
|
||||
id: printJobPreview
|
||||
source: printJob.previewImageUrl
|
||||
anchors.top: ownerName.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: totalTimeLabel.bottom
|
||||
width: height
|
||||
opacity: printJob.state == "error" ? 0.5 : 1.0
|
||||
}
|
||||
|
||||
UM.RecolorImage
|
||||
{
|
||||
id: statusImage
|
||||
anchors.centerIn: printJobPreview
|
||||
source: printJob.state == "error" ? "../svg/aborted-icon.svg" : ""
|
||||
visible: source != ""
|
||||
width: 0.5 * printJobPreview.width
|
||||
height: 0.5 * printJobPreview.height
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
color: "black"
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: totalTimeLabel
|
||||
opacity: 0.6
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
font: UM.Theme.getFont("default")
|
||||
text: printJob != null ? getPrettyTime(printJob.timeTotal) : ""
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
// Content on the right side of the infobox.
|
||||
anchors
|
||||
{
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
left: parent.horizontalCenter
|
||||
right: parent.right
|
||||
margins: 2 * UM.Theme.getSize("default_margin").width
|
||||
leftMargin: UM.Theme.getSize("default_margin").width
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: targetPrinterLabel
|
||||
elide: Text.ElideRight
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
text:
|
||||
{
|
||||
if(printJob.assignedPrinter == null)
|
||||
{
|
||||
if(printJob.state == "error")
|
||||
{
|
||||
return catalog.i18nc("@label", "Waiting for: Unavailable printer")
|
||||
}
|
||||
return catalog.i18nc("@label", "Waiting for: First available")
|
||||
}
|
||||
else
|
||||
{
|
||||
return catalog.i18nc("@label", "Waiting for: ") + printJob.assignedPrinter.name
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
anchors
|
||||
{
|
||||
left: parent.left
|
||||
right: contextButton.left
|
||||
rightMargin: UM.Theme.getSize("default_margin").width
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function switchPopupState()
|
||||
{
|
||||
popup.visible ? popup.close() : popup.open()
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
id: contextButton
|
||||
text: "\u22EE" //Unicode; Three stacked points.
|
||||
font.pixelSize: 25
|
||||
width: 35
|
||||
height: width
|
||||
anchors
|
||||
{
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
}
|
||||
hoverEnabled: true
|
||||
|
||||
background: Rectangle
|
||||
{
|
||||
opacity: contextButton.down || contextButton.hovered ? 1 : 0
|
||||
width: contextButton.width
|
||||
height: contextButton.height
|
||||
radius: 0.5 * width
|
||||
color: UM.Theme.getColor("viewport_background")
|
||||
}
|
||||
|
||||
onClicked: parent.switchPopupState()
|
||||
}
|
||||
|
||||
Popup
|
||||
{
|
||||
// TODO Change once updating to Qt5.10 - The 'opened' property is in 5.10 but the behavior is now implemented with the visible property
|
||||
id: popup
|
||||
clip: true
|
||||
closePolicy: Popup.CloseOnPressOutsideParent
|
||||
x: parent.width - width
|
||||
y: contextButton.height
|
||||
width: 160
|
||||
height: contentItem.height + 2 * padding
|
||||
visible: false
|
||||
|
||||
transformOrigin: Popup.Top
|
||||
contentItem: Item
|
||||
{
|
||||
width: popup.width - 2 * popup.padding
|
||||
height: childrenRect.height + 15
|
||||
Button
|
||||
{
|
||||
id: sendToTopButton
|
||||
text: catalog.i18nc("@label", "Move to top")
|
||||
onClicked:
|
||||
{
|
||||
OutputDevice.sendJobToTop(printJob.key)
|
||||
popup.close()
|
||||
}
|
||||
width: parent.width
|
||||
enabled: OutputDevice.queuedPrintJobs[0].key != printJob.key
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
hoverEnabled: true
|
||||
background: Rectangle
|
||||
{
|
||||
opacity: sendToTopButton.down || sendToTopButton.hovered ? 1 : 0
|
||||
color: UM.Theme.getColor("viewport_background")
|
||||
}
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
id: deleteButton
|
||||
text: catalog.i18nc("@label", "Delete")
|
||||
onClicked:
|
||||
{
|
||||
OutputDevice.deleteJobFromQueue(printJob.key)
|
||||
popup.close()
|
||||
}
|
||||
width: parent.width
|
||||
anchors.top: sendToTopButton.bottom
|
||||
hoverEnabled: true
|
||||
background: Rectangle
|
||||
{
|
||||
opacity: deleteButton.down || deleteButton.hovered ? 1 : 0
|
||||
color: UM.Theme.getColor("viewport_background")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
background: Item
|
||||
{
|
||||
width: popup.width
|
||||
height: popup.height
|
||||
|
||||
DropShadow
|
||||
{
|
||||
anchors.fill: pointedRectangle
|
||||
radius: 5
|
||||
color: "#3F000000" // 25% shadow
|
||||
source: pointedRectangle
|
||||
transparentBorder: true
|
||||
verticalOffset: 2
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: pointedRectangle
|
||||
width: parent.width -10
|
||||
height: parent.height -10
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: point
|
||||
height: 13
|
||||
width: 13
|
||||
color: UM.Theme.getColor("setting_control")
|
||||
transform: Rotation { angle: 45}
|
||||
anchors.right: bloop.right
|
||||
y: 1
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: bloop
|
||||
color: UM.Theme.getColor("setting_control")
|
||||
width: parent.width
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit: Transition
|
||||
{
|
||||
// This applies a default NumberAnimation to any changes a state change makes to x or y properties
|
||||
NumberAnimation { property: "visible"; duration: 75; }
|
||||
}
|
||||
enter: Transition
|
||||
{
|
||||
// This applies a default NumberAnimation to any changes a state change makes to x or y properties
|
||||
NumberAnimation { property: "visible"; duration: 75; }
|
||||
}
|
||||
|
||||
onClosed: visible = false
|
||||
onOpened: visible = true
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
id: printerFamilyPills
|
||||
spacing: 0.5 * UM.Theme.getSize("default_margin").width
|
||||
anchors
|
||||
{
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: extrudersInfo.top
|
||||
bottomMargin: UM.Theme.getSize("default_margin").height
|
||||
}
|
||||
height: childrenRect.height
|
||||
Repeater
|
||||
{
|
||||
model: printJob.compatibleMachineFamilies
|
||||
|
||||
delegate: PrinterFamilyPill
|
||||
{
|
||||
text: modelData
|
||||
color: UM.Theme.getColor("viewport_background")
|
||||
padding: 3
|
||||
}
|
||||
}
|
||||
}
|
||||
// PrintCore && Material config
|
||||
Row
|
||||
{
|
||||
id: extrudersInfo
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
anchors
|
||||
{
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
height: childrenRect.height
|
||||
|
||||
spacing: UM.Theme.getSize("default_margin").width
|
||||
|
||||
PrintCoreConfiguration
|
||||
{
|
||||
id: leftExtruderInfo
|
||||
width: Math.round(parent.width / 2)
|
||||
printCoreConfiguration: printJob.configuration.extruderConfigurations[0]
|
||||
}
|
||||
|
||||
PrintCoreConfiguration
|
||||
{
|
||||
id: rightExtruderInfo
|
||||
width: Math.round(parent.width / 2)
|
||||
printCoreConfiguration: printJob.configuration.extruderConfigurations[1]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
color: UM.Theme.getColor("viewport_background")
|
||||
width: 2
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: UM.Theme.getSize("default_margin").height
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.4
|
||||
import UM 1.2 as UM
|
||||
|
||||
Item
|
||||
{
|
||||
property alias color: background.color
|
||||
property alias text: familyNameLabel.text
|
||||
property var padding: 0
|
||||
implicitHeight: familyNameLabel.contentHeight + 2 * padding // Apply the padding to top and bottom.
|
||||
implicitWidth: familyNameLabel.contentWidth + implicitHeight // The extra height is added to ensure the radius doesn't cut something off.
|
||||
Rectangle
|
||||
{
|
||||
id: background
|
||||
height: parent.height
|
||||
width: parent.width
|
||||
color: parent.color
|
||||
anchors.right: parent.right
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
radius: 0.5 * height
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: familyNameLabel
|
||||
anchors.centerIn: parent
|
||||
text: ""
|
||||
}
|
||||
}
|
|
@ -7,6 +7,8 @@ import UM 1.3 as UM
|
|||
|
||||
Item
|
||||
{
|
||||
property var camera: null
|
||||
|
||||
Rectangle
|
||||
{
|
||||
anchors.fill:parent
|
||||
|
@ -17,7 +19,7 @@ Item
|
|||
MouseArea
|
||||
{
|
||||
anchors.fill: parent
|
||||
onClicked: OutputDevice.setActivePrinter(null)
|
||||
onClicked: OutputDevice.setActiveCamera(null)
|
||||
z: 0
|
||||
}
|
||||
|
||||
|
@ -32,7 +34,7 @@ Item
|
|||
width: 20 * screenScaleFactor
|
||||
height: 20 * screenScaleFactor
|
||||
|
||||
onClicked: OutputDevice.setActivePrinter(null)
|
||||
onClicked: OutputDevice.setActiveCamera(null)
|
||||
|
||||
style: ButtonStyle
|
||||
{
|
||||
|
@ -65,23 +67,24 @@ Item
|
|||
{
|
||||
if(visible)
|
||||
{
|
||||
if(OutputDevice.activePrinter != null && OutputDevice.activePrinter.camera != null)
|
||||
if(camera != null)
|
||||
{
|
||||
OutputDevice.activePrinter.camera.start()
|
||||
camera.start()
|
||||
}
|
||||
} else
|
||||
{
|
||||
if(OutputDevice.activePrinter != null && OutputDevice.activePrinter.camera != null)
|
||||
if(camera != null)
|
||||
{
|
||||
OutputDevice.activePrinter.camera.stop()
|
||||
camera.stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
source:
|
||||
{
|
||||
if(OutputDevice.activePrinter != null && OutputDevice.activePrinter.camera != null && OutputDevice.activePrinter.camera.latestImage)
|
||||
if(camera != null && camera.latestImage != null)
|
||||
{
|
||||
return OutputDevice.activePrinter.camera.latestImage;
|
||||
return camera.latestImage;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
@ -92,7 +95,7 @@ Item
|
|||
anchors.fill: cameraImage
|
||||
onClicked:
|
||||
{
|
||||
OutputDevice.setActivePrinter(null)
|
||||
OutputDevice.setActiveCamera(null)
|
||||
}
|
||||
z: 1
|
||||
}
|
1
plugins/UM3NetworkPrinting/resources/svg/UM3-icon.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 46.16 48"><defs><style>.cls-1{fill:#9a9a9a;}</style></defs><title>UM3-icon</title><g id="Symbols"><g id="system_overview_inactive" data-name="system overview inactive"><path id="Shape" class="cls-1" d="M18.4,12.2h9.26c.1,0,.1,0,.2-.2l1.73-4.27a.22.22,0,0,0-.2-.2H16.67a.22.22,0,0,0-.2.2L18.2,12C18.3,12.2,18.3,12.2,18.4,12.2Z"/><path id="Shape-2" data-name="Shape" class="cls-1" d="M38.33,35.08H7.72a.48.48,0,0,0-.5.51V37a.48.48,0,0,0,.5.51H38.44a.48.48,0,0,0,.5-.51V35.59A.64.64,0,0,0,38.33,35.08Z"/><path id="Shape-3" data-name="Shape" class="cls-1" d="M0,0V48H3.76a2.86,2.86,0,0,1,2.13-1H40.27a2.86,2.86,0,0,1,2.13,1h3.76V0ZM41.28,37a2.83,2.83,0,0,1-2.84,2.84H7.72A2.84,2.84,0,0,1,4.88,37V5.49a.65.65,0,0,1,.61-.61H40.67a.65.65,0,0,1,.61.61Z"/></g></g></svg>
|
After Width: | Height: | Size: 847 B |
1
plugins/UM3NetworkPrinting/resources/svg/UM3x-icon.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 46.16 58"><defs><style>.cls-1{fill:#9a9a9a;}</style></defs><title>UM3x-icon</title><g id="Symbols"><g id="system_overview_inactive" data-name="system overview inactive"><path id="Shape" class="cls-1" d="M18.4,12.2h9.26c.1,0,.1,0,.2-.2l1.73-4.27a.22.22,0,0,0-.2-.2H16.67a.22.22,0,0,0-.2.2L18.2,12C18.3,12.2,18.3,12.2,18.4,12.2Z"/><path id="Shape-2" data-name="Shape" class="cls-1" d="M38.33,45.08H7.72a.48.48,0,0,0-.5.51V47a.48.48,0,0,0,.5.51H38.44a.48.48,0,0,0,.5-.51V45.59A.64.64,0,0,0,38.33,45.08Z"/><path id="Shape-3" data-name="Shape" class="cls-1" d="M0,0V58H3.76a2.86,2.86,0,0,1,2.13-1H40.27a2.86,2.86,0,0,1,2.13,1h3.76V0ZM41.28,35.32V47a2.83,2.83,0,0,1-2.84,2.84H7.72A2.84,2.84,0,0,1,4.88,47V5.49a.65.65,0,0,1,.61-.61H40.67a.65.65,0,0,1,.61.61Z"/></g></g></svg>
|
After Width: | Height: | Size: 854 B |
1
plugins/UM3NetworkPrinting/resources/svg/UMs5-icon.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 58 58"><defs><style>.cls-1{fill:none;}.cls-2{fill:#9a9a9a;}</style></defs><title>UMs5-icon</title><path class="cls-1" d="M33.83,12.33c-.1.2-.1.2-.2.2H24.37c-.1,0-.1,0-.2-.2L22.44,8.06a.22.22,0,0,1,.2-.2H35.36a.22.22,0,0,1,.2.2Z"/><path class="cls-2" d="M35.36,7.86H22.64a.22.22,0,0,0-.2.2l1.73,4.27c.1.2.1.2.2.2h9.26c.1,0,.1,0,.2-.2l1.73-4.27A.22.22,0,0,0,35.36,7.86Z"/><path class="cls-2" d="M0,0V58H3.75a2.85,2.85,0,0,1,2.12-1H52.13a2.85,2.85,0,0,1,2.12,1H58V0ZM37.5,53.82a1.5,1.5,0,0,1-1.5,1.5H22a1.5,1.5,0,0,1-1.5-1.5v-4a1.5,1.5,0,0,1,1.5-1.5H36a1.5,1.5,0,0,1,1.5,1.5Zm15.63-18.5V47a2.83,2.83,0,0,1-2.83,2.84H38.5v0a2.5,2.5,0,0,0-2.5-2.5H22a2.5,2.5,0,0,0-2.5,2.5v0H7.7A2.83,2.83,0,0,1,4.87,47V5.49a.65.65,0,0,1,.6-.61H52.53a.65.65,0,0,1,.6.61Z"/></svg>
|
After Width: | Height: | Size: 842 B |
|
@ -0,0 +1 @@
|
|||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><title>aborted-icon</title><path d="M16,0A16,16,0,1,0,32,16,16,16,0,0,0,16,0Zm1.69,28.89a13,13,0,1,1,11.2-11.2A13,13,0,0,1,17.69,28.89Z"/><polygon points="20.6 9.28 16 13.88 11.4 9.28 9.28 11.4 13.88 16 9.28 20.6 11.4 22.72 16 18.12 20.6 22.72 22.72 20.6 18.12 16 22.72 11.4 20.6 9.28"/></svg>
|
After Width: | Height: | Size: 386 B |
Before Width: | Height: | Size: 844 B After Width: | Height: | Size: 844 B |
|
@ -0,0 +1 @@
|
|||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><title>approved-icon</title><path d="M16,29A13,13,0,1,0,3,16,13,13,0,0,0,16,29ZM8,14.59l6,5.3L23.89,9l2.22,2L14.18,24.11,6,16.83Z" fill="none"/><path d="M16,32A16,16,0,1,0,0,16,16,16,0,0,0,16,32ZM16,3A13,13,0,1,1,3,16,13,13,0,0,1,16,3Z"/><polygon points="26.11 11.01 23.89 8.99 13.96 19.89 8 14.59 6 16.83 14.18 24.11 26.11 11.01"/></svg>
|
After Width: | Height: | Size: 431 B |
Before Width: | Height: | Size: 311 B After Width: | Height: | Size: 311 B |
Before Width: | Height: | Size: 438 B After Width: | Height: | Size: 438 B |
Before Width: | Height: | Size: 194 B After Width: | Height: | Size: 194 B |
1
plugins/UM3NetworkPrinting/resources/svg/paused-icon.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><title>paused-icon</title><path d="M16,0A16,16,0,1,0,32,16,16,16,0,0,0,16,0Zm0,29A13,13,0,1,1,29,16,13,13,0,0,1,16,29Z"/><rect x="11.5" y="9" width="3" height="14"/><rect x="17.5" y="9" width="3" height="14"/></svg>
|
After Width: | Height: | Size: 308 B |
|
@ -0,0 +1 @@
|
|||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><title>warning-icon</title><path d="M18.09,1.31A2.35,2.35,0,0,0,16,0a2.31,2.31,0,0,0-2.09,1.31L.27,28.44A2.49,2.49,0,0,0,.11,30.3a2.38,2.38,0,0,0,1.16,1.42A2.33,2.33,0,0,0,2.36,32H29.64A2.4,2.4,0,0,0,32,29.57a2.55,2.55,0,0,0-.27-1.14ZM3.34,29,16,3.83,28.66,29Z"/><polygon points="13.94 25.19 13.94 25.19 13.94 25.19 13.94 25.19"/><polygon points="14.39 21.68 17.61 21.68 18.11 11.85 13.89 11.85 14.39 21.68"/><path d="M16.06,23.08a2.19,2.19,0,0,0-1.56,3.66,2.14,2.14,0,0,0,1.56.55,2.06,2.06,0,0,0,1.54-.55,2.1,2.1,0,0,0,.55-1.55,2.17,2.17,0,0,0-.53-1.55A2.05,2.05,0,0,0,16.06,23.08Z"/></svg>
|
After Width: | Height: | Size: 684 B |
|
@ -4,19 +4,21 @@
|
|||
from typing import Any, cast, Optional, Set, Tuple, Union
|
||||
|
||||
from UM.FileHandler.FileHandler import FileHandler
|
||||
from UM.FileHandler.FileWriter import FileWriter #To choose based on the output file mode (text vs. binary).
|
||||
from UM.FileHandler.WriteFileJob import WriteFileJob #To call the file writer asynchronously.
|
||||
from UM.FileHandler.FileWriter import FileWriter # To choose based on the output file mode (text vs. binary).
|
||||
from UM.FileHandler.WriteFileJob import WriteFileJob # To call the file writer asynchronously.
|
||||
from UM.Logger import Logger
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Mesh.MeshWriter import MeshWriter # For typing
|
||||
|
||||
from UM.Message import Message
|
||||
from UM.Qt.Duration import Duration, DurationFormat
|
||||
from UM.OutputDevice import OutputDeviceError #To show that something went wrong when writing.
|
||||
from UM.Scene.SceneNode import SceneNode #For typing.
|
||||
from UM.Version import Version #To check against firmware versions for support.
|
||||
from UM.OutputDevice import OutputDeviceError # To show that something went wrong when writing.
|
||||
from UM.Scene.SceneNode import SceneNode # For typing.
|
||||
from UM.Version import Version # To check against firmware versions for support.
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
|
||||
from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel
|
||||
from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState
|
||||
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
|
||||
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
|
||||
|
@ -27,14 +29,14 @@ from .ClusterUM3PrinterOutputController import ClusterUM3PrinterOutputController
|
|||
from .SendMaterialJob import SendMaterialJob
|
||||
|
||||
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
|
||||
from PyQt5.QtGui import QDesktopServices
|
||||
from PyQt5.QtGui import QDesktopServices, QImage
|
||||
from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QObject
|
||||
|
||||
from time import time
|
||||
from datetime import datetime
|
||||
from typing import Optional, Dict, List, Set
|
||||
from typing import Optional, Dict, List
|
||||
|
||||
import io #To create the correct buffers for sending data to the printer.
|
||||
import io # To create the correct buffers for sending data to the printer.
|
||||
import json
|
||||
import os
|
||||
|
||||
|
@ -44,6 +46,7 @@ i18n_catalog = i18nCatalog("cura")
|
|||
class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
printJobsChanged = pyqtSignal()
|
||||
activePrinterChanged = pyqtSignal()
|
||||
activeCameraChanged = pyqtSignal()
|
||||
|
||||
# This is a bit of a hack, as the notify can only use signals that are defined by the class that they are in.
|
||||
# Inheritance doesn't seem to work. Tying them together does work, but i'm open for better suggestions.
|
||||
|
@ -59,24 +62,24 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
|
||||
self._print_jobs = [] # type: List[PrintJobOutputModel]
|
||||
|
||||
self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ClusterMonitorItem.qml")
|
||||
self._control_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ClusterControlItem.qml")
|
||||
self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/ClusterMonitorItem.qml")
|
||||
self._control_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/ClusterControlItem.qml")
|
||||
|
||||
# See comments about this hack with the clusterPrintersChanged signal
|
||||
self.printersChanged.connect(self.clusterPrintersChanged)
|
||||
|
||||
self._accepts_commands = True #type: bool
|
||||
self._accepts_commands = True # type: bool
|
||||
|
||||
# Cluster does not have authentication, so default to authenticated
|
||||
self._authentication_state = AuthState.Authenticated
|
||||
|
||||
self._error_message = None #type: Optional[Message]
|
||||
self._write_job_progress_message = None #type: Optional[Message]
|
||||
self._progress_message = None #type: Optional[Message]
|
||||
self._error_message = None # type: Optional[Message]
|
||||
self._write_job_progress_message = None # type: Optional[Message]
|
||||
self._progress_message = None # type: Optional[Message]
|
||||
|
||||
self._active_printer = None # type: Optional[PrinterOutputModel]
|
||||
|
||||
self._printer_selection_dialog = None #type: QObject
|
||||
self._printer_selection_dialog = None # type: QObject
|
||||
|
||||
self.setPriority(3) # Make sure the output device gets selected above local file output
|
||||
self.setName(self._id)
|
||||
|
@ -87,32 +90,35 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
|
||||
self._printer_uuid_to_unique_name_mapping = {} # type: Dict[str, str]
|
||||
|
||||
self._finished_jobs = [] # type: List[PrintJobOutputModel]
|
||||
self._finished_jobs = [] # type: List[PrintJobOutputModel]
|
||||
|
||||
self._cluster_size = int(properties.get(b"cluster_size", 0))
|
||||
self._cluster_size = int(properties.get(b"cluster_size", 0)) # type: int
|
||||
|
||||
self._latest_reply_handler = None #type: Optional[QNetworkReply]
|
||||
self._latest_reply_handler = None # type: Optional[QNetworkReply]
|
||||
self._sending_job = None
|
||||
|
||||
self._active_camera = None # type: Optional[NetworkCamera]
|
||||
|
||||
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None:
|
||||
self.writeStarted.emit(self)
|
||||
|
||||
self.sendMaterialProfiles()
|
||||
|
||||
#Formats supported by this application (file types that we can actually write).
|
||||
# Formats supported by this application (file types that we can actually write).
|
||||
if file_handler:
|
||||
file_formats = file_handler.getSupportedFileTypesWrite()
|
||||
else:
|
||||
file_formats = CuraApplication.getInstance().getMeshFileHandler().getSupportedFileTypesWrite()
|
||||
|
||||
global_stack = CuraApplication.getInstance().getGlobalContainerStack()
|
||||
#Create a list from the supported file formats string.
|
||||
# Create a list from the supported file formats string.
|
||||
if not global_stack:
|
||||
Logger.log("e", "Missing global stack!")
|
||||
return
|
||||
|
||||
machine_file_formats = global_stack.getMetaDataEntry("file_formats").split(";")
|
||||
machine_file_formats = [file_type.strip() for file_type in machine_file_formats]
|
||||
#Exception for UM3 firmware version >=4.4: UFP is now supported and should be the preferred file format.
|
||||
# Exception for UM3 firmware version >=4.4: UFP is now supported and should be the preferred file format.
|
||||
if "application/x-ufp" not in machine_file_formats and Version(self.firmwareVersion) >= Version("4.4"):
|
||||
machine_file_formats = ["application/x-ufp"] + machine_file_formats
|
||||
|
||||
|
@ -125,7 +131,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
raise OutputDeviceError.WriteRequestFailedError(i18n_catalog.i18nc("@info:status", "There are no file formats available to write with!"))
|
||||
preferred_format = file_formats[0]
|
||||
|
||||
#Just take the first file format available.
|
||||
# Just take the first file format available.
|
||||
if file_handler is not None:
|
||||
writer = file_handler.getWriterByMimeType(cast(str, preferred_format["mime_type"]))
|
||||
else:
|
||||
|
@ -135,29 +141,30 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
Logger.log("e", "Unexpected error when trying to get the FileWriter")
|
||||
return
|
||||
|
||||
#This function pauses with the yield, waiting on instructions on which printer it needs to print with.
|
||||
# This function pauses with the yield, waiting on instructions on which printer it needs to print with.
|
||||
if not writer:
|
||||
Logger.log("e", "Missing file or mesh writer!")
|
||||
return
|
||||
self._sending_job = self._sendPrintJob(writer, preferred_format, nodes)
|
||||
self._sending_job.send(None) #Start the generator.
|
||||
if self._sending_job is not None:
|
||||
self._sending_job.send(None) # Start the generator.
|
||||
|
||||
if len(self._printers) > 1: #We need to ask the user.
|
||||
self._spawnPrinterSelectionDialog()
|
||||
is_job_sent = True
|
||||
else: #Just immediately continue.
|
||||
self._sending_job.send("") #No specifically selected printer.
|
||||
is_job_sent = self._sending_job.send(None)
|
||||
if len(self._printers) > 1: # We need to ask the user.
|
||||
self._spawnPrinterSelectionDialog()
|
||||
is_job_sent = True
|
||||
else: # Just immediately continue.
|
||||
self._sending_job.send("") # No specifically selected printer.
|
||||
is_job_sent = self._sending_job.send(None)
|
||||
|
||||
def _spawnPrinterSelectionDialog(self):
|
||||
if self._printer_selection_dialog is None:
|
||||
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "PrintWindow.qml")
|
||||
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/PrintWindow.qml")
|
||||
self._printer_selection_dialog = CuraApplication.getInstance().createQmlComponent(path, {"OutputDevice": self})
|
||||
if self._printer_selection_dialog is not None:
|
||||
self._printer_selection_dialog.show()
|
||||
|
||||
@pyqtProperty(int, constant=True)
|
||||
def clusterSize(self):
|
||||
def clusterSize(self) -> int:
|
||||
return self._cluster_size
|
||||
|
||||
## Allows the user to choose a printer to print with from the printer
|
||||
|
@ -165,7 +172,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
# \param target_printer The name of the printer to target.
|
||||
@pyqtSlot(str)
|
||||
def selectPrinter(self, target_printer: str = "") -> None:
|
||||
self._sending_job.send(target_printer)
|
||||
if self._sending_job is not None:
|
||||
self._sending_job.send(target_printer)
|
||||
|
||||
@pyqtSlot()
|
||||
def cancelPrintSelection(self) -> None:
|
||||
|
@ -214,8 +222,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
|
||||
job.start()
|
||||
|
||||
yield True #Return that we had success!
|
||||
yield #To prevent having to catch the StopIteration exception.
|
||||
yield True # Return that we had success!
|
||||
yield # To prevent having to catch the StopIteration exception.
|
||||
|
||||
def _sendPrintJobWaitOnWriteJobFinished(self, job: WriteFileJob) -> None:
|
||||
if self._write_job_progress_message:
|
||||
|
@ -240,7 +248,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
|
||||
file_name = CuraApplication.getInstance().getPrintInformation().jobName + "." + preferred_format["extension"]
|
||||
|
||||
output = stream.getvalue() #Either str or bytes depending on the output mode.
|
||||
output = stream.getvalue() # Either str or bytes depending on the output mode.
|
||||
if isinstance(stream, io.StringIO):
|
||||
output = cast(str, output).encode("utf-8")
|
||||
output = cast(bytes, output)
|
||||
|
@ -253,6 +261,10 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
def activePrinter(self) -> Optional[PrinterOutputModel]:
|
||||
return self._active_printer
|
||||
|
||||
@pyqtProperty(QObject, notify=activeCameraChanged)
|
||||
def activeCamera(self) -> Optional[NetworkCamera]:
|
||||
return self._active_camera
|
||||
|
||||
@pyqtSlot(QObject)
|
||||
def setActivePrinter(self, printer: Optional[PrinterOutputModel]) -> None:
|
||||
if self._active_printer != printer:
|
||||
|
@ -261,6 +273,19 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
self._active_printer = printer
|
||||
self.activePrinterChanged.emit()
|
||||
|
||||
@pyqtSlot(QObject)
|
||||
def setActiveCamera(self, camera: Optional[NetworkCamera]) -> None:
|
||||
if self._active_camera != camera:
|
||||
if self._active_camera:
|
||||
self._active_camera.stop()
|
||||
|
||||
self._active_camera = camera
|
||||
|
||||
if self._active_camera:
|
||||
self._active_camera.start()
|
||||
|
||||
self.activeCameraChanged.emit()
|
||||
|
||||
def _onPostPrintJobFinished(self, reply: QNetworkReply) -> None:
|
||||
if self._progress_message:
|
||||
self._progress_message.hide()
|
||||
|
@ -279,8 +304,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
|
||||
# If successfully sent:
|
||||
if bytes_sent == bytes_total:
|
||||
# Show a confirmation to the user so they know the job was sucessful and provide the option to switch to the
|
||||
# monitor tab.
|
||||
# Show a confirmation to the user so they know the job was sucessful and provide the option to switch to
|
||||
# the monitor tab.
|
||||
self._success_message = Message(
|
||||
i18n_catalog.i18nc("@info:status", "Print job was successfully sent to the printer."),
|
||||
lifetime=5, dismissable=True,
|
||||
|
@ -329,7 +354,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
|
||||
@pyqtProperty("QVariantList", notify = printJobsChanged)
|
||||
def queuedPrintJobs(self) -> List[PrintJobOutputModel]:
|
||||
return [print_job for print_job in self._print_jobs if print_job.state == "queued"]
|
||||
return [print_job for print_job in self._print_jobs if print_job.state == "queued" or print_job.state == "error"]
|
||||
|
||||
@pyqtProperty("QVariantList", notify = printJobsChanged)
|
||||
def activePrintJobs(self) -> List[PrintJobOutputModel]:
|
||||
|
@ -348,6 +373,10 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
result.append({"machine_type": machine_type, "count": str(printer_count[machine_type])})
|
||||
return result
|
||||
|
||||
@pyqtProperty("QVariantList", notify=clusterPrintersChanged)
|
||||
def printers(self):
|
||||
return self._printers
|
||||
|
||||
@pyqtSlot(int, result = str)
|
||||
def formatDuration(self, seconds: int) -> str:
|
||||
return Duration(seconds).getDisplayString(DurationFormat.Format.Short)
|
||||
|
@ -364,6 +393,19 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
datetime_completed = datetime.fromtimestamp(current_time + time_remaining)
|
||||
return (datetime_completed.strftime("%a %b ") + "{day}".format(day=datetime_completed.day)).upper()
|
||||
|
||||
@pyqtSlot(str)
|
||||
def sendJobToTop(self, print_job_uuid: str) -> None:
|
||||
# This function is part of the output device (and not of the printjob output model) as this type of operation
|
||||
# is a modification of the cluster queue and not of the actual job.
|
||||
data = "{\"to_position\": 0}"
|
||||
self.put("print_jobs/{uuid}/move_to_position".format(uuid = print_job_uuid), data, on_finished=None)
|
||||
|
||||
@pyqtSlot(str)
|
||||
def deleteJobFromQueue(self, print_job_uuid: str) -> None:
|
||||
# This function is part of the output device (and not of the printjob output model) as this type of operation
|
||||
# is a modification of the cluster queue and not of the actual job.
|
||||
self.delete("print_jobs/{uuid}".format(uuid = print_job_uuid), on_finished=None)
|
||||
|
||||
def _printJobStateChanged(self) -> None:
|
||||
username = self._getUserName()
|
||||
|
||||
|
@ -392,11 +434,26 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
super().connect()
|
||||
self.sendMaterialProfiles()
|
||||
|
||||
def _onGetPreviewImageFinished(self, reply: QNetworkReply) -> None:
|
||||
reply_url = reply.url().toString()
|
||||
|
||||
uuid = reply_url[reply_url.find("print_jobs/")+len("print_jobs/"):reply_url.rfind("/preview_image")]
|
||||
|
||||
print_job = findByKey(self._print_jobs, uuid)
|
||||
if print_job:
|
||||
image = QImage()
|
||||
image.loadFromData(reply.readAll())
|
||||
print_job.updatePreviewImage(image)
|
||||
|
||||
def _update(self) -> None:
|
||||
super()._update()
|
||||
self.get("printers/", on_finished = self._onGetPrintersDataFinished)
|
||||
self.get("print_jobs/", on_finished = self._onGetPrintJobsFinished)
|
||||
|
||||
for print_job in self._print_jobs:
|
||||
if print_job.getPreviewImage() is None:
|
||||
self.get("print_jobs/{uuid}/preview_image".format(uuid=print_job.key), on_finished=self._onGetPreviewImageFinished)
|
||||
|
||||
def _onGetPrintJobsFinished(self, reply: QNetworkReply) -> None:
|
||||
if not checkValidGetReply(reply):
|
||||
return
|
||||
|
@ -407,16 +464,19 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
|
||||
print_jobs_seen = []
|
||||
job_list_changed = False
|
||||
for print_job_data in result:
|
||||
for idx, print_job_data in enumerate(result):
|
||||
print_job = findByKey(self._print_jobs, print_job_data["uuid"])
|
||||
|
||||
if print_job is None:
|
||||
print_job = self._createPrintJobModel(print_job_data)
|
||||
job_list_changed = True
|
||||
elif not job_list_changed:
|
||||
# Check if the order of the jobs has changed since the last check
|
||||
if self._print_jobs.index(print_job) != idx:
|
||||
job_list_changed = True
|
||||
|
||||
self._updatePrintJob(print_job, print_job_data)
|
||||
|
||||
if print_job.state != "queued": # Print job should be assigned to a printer.
|
||||
if print_job.state != "queued" and print_job.state != "error": # Print job should be assigned to a printer.
|
||||
if print_job.state in ["failed", "finished", "aborted", "none"]:
|
||||
# Print job was already completed, so don't attach it to a printer.
|
||||
printer = None
|
||||
|
@ -437,6 +497,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
job_list_changed = job_list_changed or self._removeJob(removed_job)
|
||||
|
||||
if job_list_changed:
|
||||
# Override the old list with the new list (either because jobs were removed / added or order changed)
|
||||
self._print_jobs = print_jobs_seen
|
||||
self.printJobsChanged.emit() # Do a single emit for all print job changes.
|
||||
|
||||
def _onGetPrintersDataFinished(self, reply: QNetworkReply) -> None:
|
||||
|
@ -478,16 +540,59 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
def _createPrintJobModel(self, data: Dict[str, Any]) -> PrintJobOutputModel:
|
||||
print_job = PrintJobOutputModel(output_controller=ClusterUM3PrinterOutputController(self),
|
||||
key=data["uuid"], name= data["name"])
|
||||
|
||||
configuration = ConfigurationModel()
|
||||
extruders = [ExtruderConfigurationModel(position = idx) for idx in range(0, self._number_of_extruders)]
|
||||
for index in range(0, self._number_of_extruders):
|
||||
try:
|
||||
extruder_data = data["configuration"][index]
|
||||
except IndexError:
|
||||
continue
|
||||
extruder = extruders[int(data["configuration"][index]["extruder_index"])]
|
||||
extruder.setHotendID(extruder_data.get("print_core_id", ""))
|
||||
extruder.setMaterial(self._createMaterialOutputModel(extruder_data.get("material", {})))
|
||||
|
||||
configuration.setExtruderConfigurations(extruders)
|
||||
print_job.updateConfiguration(configuration)
|
||||
print_job.setCompatibleMachineFamilies(data.get("compatible_machine_families", []))
|
||||
print_job.stateChanged.connect(self._printJobStateChanged)
|
||||
self._print_jobs.append(print_job)
|
||||
return print_job
|
||||
|
||||
def _updatePrintJob(self, print_job: PrintJobOutputModel, data: Dict[str, Any]) -> None:
|
||||
print_job.updateTimeTotal(data["time_total"])
|
||||
print_job.updateTimeElapsed(data["time_elapsed"])
|
||||
print_job.updateState(data["status"])
|
||||
impediments_to_printing = data.get("impediments_to_printing", [])
|
||||
print_job.updateOwner(data["owner"])
|
||||
|
||||
status_set_by_impediment = False
|
||||
for impediment in impediments_to_printing:
|
||||
if impediment["severity"] == "UNFIXABLE":
|
||||
status_set_by_impediment = True
|
||||
print_job.updateState("error")
|
||||
break
|
||||
|
||||
if not status_set_by_impediment:
|
||||
print_job.updateState(data["status"])
|
||||
|
||||
|
||||
def _createMaterialOutputModel(self, material_data) -> MaterialOutputModel:
|
||||
containers = ContainerRegistry.getInstance().findInstanceContainers(type="material", GUID=material_data["guid"])
|
||||
if containers:
|
||||
color = containers[0].getMetaDataEntry("color_code")
|
||||
brand = containers[0].getMetaDataEntry("brand")
|
||||
material_type = containers[0].getMetaDataEntry("material")
|
||||
name = containers[0].getName()
|
||||
else:
|
||||
Logger.log("w",
|
||||
"Unable to find material with guid {guid}. Using data as provided by cluster".format(
|
||||
guid=material_data["guid"]))
|
||||
color = material_data["color"]
|
||||
brand = material_data["brand"]
|
||||
material_type = material_data["material"]
|
||||
name = "Empty" if material_data["material"] == "empty" else "Unknown"
|
||||
return MaterialOutputModel(guid=material_data["guid"], type=material_type,
|
||||
brand=brand, color=color, name=name)
|
||||
|
||||
def _updatePrinter(self, printer: PrinterOutputModel, data: Dict[str, Any]) -> None:
|
||||
# For some unknown reason the cluster wants UUID for everything, except for sending a job directly to a printer.
|
||||
# Then we suddenly need the unique name. So in order to not have to mess up all the other code, we save a mapping.
|
||||
|
@ -523,24 +628,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
|
||||
material_data = extruder_data["material"]
|
||||
if extruder.activeMaterial is None or extruder.activeMaterial.guid != material_data["guid"]:
|
||||
containers = ContainerRegistry.getInstance().findInstanceContainers(type="material",
|
||||
GUID=material_data["guid"])
|
||||
if containers:
|
||||
color = containers[0].getMetaDataEntry("color_code")
|
||||
brand = containers[0].getMetaDataEntry("brand")
|
||||
material_type = containers[0].getMetaDataEntry("material")
|
||||
name = containers[0].getName()
|
||||
else:
|
||||
Logger.log("w",
|
||||
"Unable to find material with guid {guid}. Using data as provided by cluster".format(
|
||||
guid=material_data["guid"]))
|
||||
color = material_data["color"]
|
||||
brand = material_data["brand"]
|
||||
material_type = material_data["material"]
|
||||
name = "Empty" if material_data["material"] == "empty" else "Unknown"
|
||||
|
||||
material = MaterialOutputModel(guid=material_data["guid"], type=material_type,
|
||||
brand=brand, color=color, name=name)
|
||||
material = self._createMaterialOutputModel(material_data)
|
||||
extruder.updateActiveMaterial(material)
|
||||
|
||||
def _removeJob(self, job: PrintJobOutputModel) -> bool:
|
||||
|
@ -568,6 +656,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
job = SendMaterialJob(device = self)
|
||||
job.run()
|
||||
|
||||
|
||||
def loadJsonFromReply(reply: QNetworkReply) -> Optional[List[Dict[str, Any]]]:
|
||||
try:
|
||||
result = json.loads(bytes(reply.readAll()).decode("utf-8"))
|
||||
|
@ -586,8 +675,8 @@ def checkValidGetReply(reply: QNetworkReply) -> bool:
|
|||
return True
|
||||
|
||||
|
||||
def findByKey(list: List[Union[PrintJobOutputModel, PrinterOutputModel]], key: str) -> Optional[PrintJobOutputModel]:
|
||||
for item in list:
|
||||
def findByKey(lst: List[Union[PrintJobOutputModel, PrinterOutputModel]], key: str) -> Optional[PrintJobOutputModel]:
|
||||
for item in lst:
|
||||
if item.key == key:
|
||||
return item
|
||||
return None
|
|
@ -6,8 +6,6 @@ from cura.PrinterOutput.PrinterOutputController import PrinterOutputController
|
|||
MYPY = False
|
||||
if MYPY:
|
||||
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
|
||||
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
|
||||
|
||||
|
||||
class ClusterUM3PrinterOutputController(PrinterOutputController):
|
||||
def __init__(self, output_device):
|
|
@ -24,7 +24,7 @@ class DiscoverUM3Action(MachineAction):
|
|||
|
||||
def __init__(self) -> None:
|
||||
super().__init__("DiscoverUM3Action", catalog.i18nc("@action","Connect via Network"))
|
||||
self._qml_url = "DiscoverUM3Action.qml"
|
||||
self._qml_url = "resources/qml/DiscoverUM3Action.qml"
|
||||
|
||||
self._network_plugin = None #type: Optional[UM3OutputDevicePlugin]
|
||||
|
||||
|
@ -174,7 +174,7 @@ class DiscoverUM3Action(MachineAction):
|
|||
plugin_path = PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting")
|
||||
if not plugin_path:
|
||||
return
|
||||
path = os.path.join(plugin_path, "UM3InfoComponents.qml")
|
||||
path = os.path.join(plugin_path, "resources/qml/UM3InfoComponents.qml")
|
||||
self.__additional_components_view = CuraApplication.getInstance().createQmlComponent(path, {"manager": self})
|
||||
if not self.__additional_components_view:
|
||||
Logger.log("w", "Could not create ui components for UM3.")
|
|
@ -76,7 +76,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
|
||||
self.setIconName("print")
|
||||
|
||||
self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "MonitorItem.qml")
|
||||
self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/MonitorItem.qml")
|
||||
|
||||
self._output_controller = LegacyUM3PrinterOutputController(self)
|
||||
|
|
@ -326,7 +326,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
if self._firmware_name is None:
|
||||
self.sendCommand("M115")
|
||||
|
||||
if (b"ok " in line and b"T:" in line) or b"ok T:" in line or line.startswith(b"T:") or b"ok B:" in line or line.startswith(b"B:"): # Temperature message. 'T:' for extruder and 'B:' for bed
|
||||
if (b"ok " in line and b"T:" in line) or line.startswith(b"T:") or b"ok B:" in line or line.startswith(b"B:"): # Temperature message. 'T:' for extruder and 'B:' for bed
|
||||
extruder_temperature_matches = re.findall(b"T(\d*): ?([\d\.]+) ?\/?([\d\.]+)?", line)
|
||||
# Update all temperature values
|
||||
matched_extruder_nrs = []
|
||||
|
|
|
@ -60,8 +60,8 @@ _RENAMED_MATERIAL_PROFILES = {
|
|||
}
|
||||
|
||||
## Upgrades configurations from the state they were in at version 3.4 to the
|
||||
# state they should be in at version 4.0.
|
||||
class VersionUpgrade34to40(VersionUpgrade):
|
||||
# state they should be in at version 3.5.
|
||||
class VersionUpgrade34to35(VersionUpgrade):
|
||||
|
||||
## Gets the version number from a CFG file in Uranium's 3.3 format.
|
||||
#
|
|
@ -1,9 +1,9 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from . import VersionUpgrade34to40
|
||||
from . import VersionUpgrade34to35
|
||||
|
||||
upgrade = VersionUpgrade34to40.VersionUpgrade34to40()
|
||||
upgrade = VersionUpgrade34to35.VersionUpgrade34to35()
|
||||
|
||||
|
||||
def getMetaData():
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"name": "Version Upgrade 3.4 to 4.0",
|
||||
"name": "Version Upgrade 3.4 to 3.5",
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.0",
|
||||
"description": "Upgrades configurations from Cura 3.4 to Cura 4.0.",
|
||||
"description": "Upgrades configurations from Cura 3.4 to Cura 3.5.",
|
||||
"api": 5,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
|
@ -4,12 +4,12 @@
|
|||
import configparser #To parse the resulting config files.
|
||||
import pytest #To register tests with.
|
||||
|
||||
import VersionUpgrade34to40 #The module we're testing.
|
||||
import VersionUpgrade34to35 #The module we're testing.
|
||||
|
||||
## Creates an instance of the upgrader to test with.
|
||||
@pytest.fixture
|
||||
def upgrader():
|
||||
return VersionUpgrade34to40.VersionUpgrade34to40()
|
||||
return VersionUpgrade34to35.VersionUpgrade34to35()
|
||||
|
||||
test_upgrade_version_nr_data = [
|
||||
("Empty config file",
|
|
@ -269,7 +269,7 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
# Find all hotend sub-profiles corresponding to this material and machine and add them to this profile.
|
||||
buildplate_dict = {} # type: Dict[str, Any]
|
||||
for variant_name, variant_dict in machine_variant_map[definition_id].items():
|
||||
variant_type = variant_dict["variant_node"].metadata["hardware_type"]
|
||||
variant_type = variant_dict["variant_node"].getMetaDataEntry("hardware_type", str(VariantType.NOZZLE))
|
||||
variant_type = VariantType(variant_type)
|
||||
if variant_type == VariantType.NOZZLE:
|
||||
# The hotend identifier is not the containers name, but its "name".
|
||||
|
|