Merge branch '3.0'

Conflict in ClusterControlItem.qml where there was a new element in Master that wasn't present in 3.0. That element wasn't referenced anywhere else so I just removed it.
This commit is contained in:
Ghostkeeper 2017-10-02 17:40:24 +02:00
commit 1d7792861d
No known key found for this signature in database
GPG Key ID: C5F96EE2BC0F7E75
20 changed files with 221 additions and 73 deletions

View File

@ -304,6 +304,7 @@ class MachineManager(QObject):
quality.nameChanged.connect(self._onQualityNameChanged)
self._active_container_stack = self._global_container_stack
self.activeStackChanged.emit()
self._error_check_timer.start()

View File

@ -370,7 +370,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
## Overrides an ExtruderStack in the given GlobalStack and returns the new ExtruderStack.
def _overrideExtruderStack(self, global_stack, extruder_file_content):
# get extruder position first
# Get extruder position first
extruder_config = configparser.ConfigParser()
extruder_config.read_string(extruder_file_content)
if not extruder_config.has_option("metadata", "position"):
@ -378,10 +378,9 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
Logger.log("e", "Could not find 'metadata/position' in extruder stack file")
raise RuntimeError(msg)
extruder_position = extruder_config.get("metadata", "position")
extruder_stack = global_stack.extruders[extruder_position]
# override the given extruder stack
# Override the given extruder stack
extruder_stack.deserialize(extruder_file_content)
# return the new ExtruderStack
@ -699,7 +698,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
extruder_file_content = archive.open(extruder_stack_file, "r").read().decode("utf-8")
if self._resolve_strategies["machine"] == "override":
# deserialize new extruder stack over the current ones
if global_stack.getProperty("machine_extruder_count", "value") > 1:
# deserialize new extruder stack over the current ones (if any)
stack = self._overrideExtruderStack(global_stack, extruder_file_content)
elif self._resolve_strategies["machine"] == "new":
@ -732,7 +732,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# Create a new definition_changes container if it was empty
if stack.definitionChanges == self._container_registry.getEmptyInstanceContainer():
stack.setDefinitionChanges(CuraStackBuilder.createDefinitionChangesContainer(stack, stack._id + "_settings"))
if global_stack.getProperty("machine_extruder_count", "value") > 1:
extruder_stacks.append(stack)
except:
Logger.logException("w", "We failed to serialize the stack. Trying to clean up.")

View File

@ -6,11 +6,13 @@ import Cura 1.0 as Cura
Component
{
Item
Rectangle
{
id: base
property var manager: Cura.MachineManager.printerOutputDevices[0]
anchors.fill: parent
color: UM.Theme.getColor("viewport_background")
property var lineColor: "#DCDCDC" // TODO: Should be linked to theme.
property var cornerRadius: 4 * screenScaleFactor // TODO: Should be linked to theme.
@ -27,24 +29,17 @@ Component
id: activePrintersLabel
font: UM.Theme.getFont("large")
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.top: parent.top
text: Cura.MachineManager.printerOutputDevices[0].name
}
Label
{
id: printerGroupLabel
anchors.top: activePrintersLabel.bottom
text: catalog.i18nc("@label", "Printer Group").toUpperCase()
anchors.horizontalCenter: parent.horizontalCenter
font: UM.Theme.getFont("very_small")
opacity: 0.65
}
Rectangle
{
id: printJobArea
border.width: UM.Theme.getSize("default_lining").width
border.color: lineColor
anchors.top: printerGroupLabel.bottom
anchors.top: activePrintersLabel.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width

View File

@ -11,7 +11,7 @@ Component
{
width: maximumWidth
height: maximumHeight
color: "#FFFFFF" // TODO; Should not be hardcoded.
color: UM.Theme.getColor("viewport_background")
property var emphasisColor: "#44c0ff" //TODO: should be linked to theme.
property var lineColor: "#DCDCDC" // TODO: Should be linked to theme.

View File

@ -13,10 +13,12 @@ from PyQt5.QtGui import QDesktopServices
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply
from PyQt5.QtQml import QQmlComponent, QQmlContext
from UM.Application import Application
from UM.Decorators import override
from UM.Logger import Logger
from UM.Message import Message
from UM.OutputDevice import OutputDeviceError
from UM.i18n import i18nCatalog
from UM.Qt.Duration import Duration, DurationFormat
from . import NetworkPrinterOutputDevice
@ -44,6 +46,7 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte
else:
name = key
self._authentication_state = NetworkPrinterOutputDevice.AuthState.Authenticated # The printer is always authenticated
self._plugin_path = plugin_path
self.setName(name)
@ -110,6 +113,20 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte
temporary_translation3 = i18n_catalog.i18n("{printer_name} has finished printing '{job_name}'. Please collect the print and confirm clearing the build plate.") #When finished.
temporary_translation4 = i18n_catalog.i18n("{printer_name} is reserved to print '{job_name}'. Please change the printer's configuration to match the job, for it to start printing.") #When configuration changed.
## No authentication, so requestAuthentication should do exactly nothing
@pyqtSlot()
def requestAuthentication(self, message_id = None, action_id = "Retry"):
pass # Cura Connect doesn't do any authorization
def setAuthenticationState(self, auth_state):
self._authentication_state = NetworkPrinterOutputDevice.AuthState.Authenticated # The printer is always authenticated
def _verifyAuthentication(self):
pass
def _checkAuthentication(self):
Logger.log("d", "_checkAuthentication Cura Connect - nothing to be done")
@pyqtProperty(QObject, notify=selectedPrinterChanged)
def controlItem(self):
# TODO: Probably not the nicest way to do this. This needs to be done better at some point in time.
@ -173,18 +190,19 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte
def _requestClusterStatus(self):
# TODO: Handle timeout. We probably want to know if the cluster is still reachable or not.
url = QUrl(self._api_base_uri + "print_jobs/")
print_jobs_request = QNetworkRequest(url)
self._addUserAgentHeader(print_jobs_request)
self._manager.get(print_jobs_request)
# See _finishedPrintJobsRequest()
url = QUrl(self._api_base_uri + "printers/")
printers_request = QNetworkRequest(url)
self._addUserAgentHeader(printers_request)
self._manager.get(printers_request)
# See _finishedPrintersRequest()
if self._printers: # if printers is not empty
url = QUrl(self._api_base_uri + "print_jobs/")
print_jobs_request = QNetworkRequest(url)
self._addUserAgentHeader(print_jobs_request)
self._manager.get(print_jobs_request)
# See _finishedPrintJobsRequest()
def _finishedPrintJobsRequest(self, reply):
try:
json_data = json.loads(bytes(reply.readAll()).decode("utf-8"))
@ -275,7 +293,10 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte
self._file_name = "%s.gcode.gz" % file_name
self._showProgressMessage()
self._request = self._buildSendPrintJobHttpRequest(require_printer_name)
new_request = self._buildSendPrintJobHttpRequest(require_printer_name)
if new_request is None or self._stage != OutputStage.uploading:
return
self._request = new_request
self._reply = self._manager.post(self._request, self._multipart)
self._reply.uploadProgress.connect(self._onUploadProgress)
# See _finishedPostPrintJobRequest()
@ -294,7 +315,7 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte
gcode = getattr(Application.getInstance().getController().getScene(), "gcode_list")
compressed_gcode = self._compressGcode(gcode)
if compressed_gcode is None:
return # User aborted print, so stop trying.
return None # User aborted print, so stop trying.
part.setBody(compressed_gcode)
self._multipart.append(part)
@ -331,7 +352,7 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte
for line in gcode:
if not self._compressing_print:
self._progress_message.hide()
return # Stop trying to zip, abort was called.
return None # Stop trying to zip, abort was called.
batched_line += line
# if the gcode was read from a gcode file, self._gcode will be a list of all lines in that file.
# Compressing line by line in this case is extremely slow, so we need to batch them.
@ -489,7 +510,8 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte
printer_name = self.__getPrinterNameFromUuid(print_job["assigned_to"])
if printer_name is None:
printer_name = i18n_catalog.i18nc("@info:status", "Unknown printer")
# don't report on yet unknown printers
continue
message_text = (i18n_catalog.i18n("{printer_name} is reserved to print '{job_name}'. Please change the printer's configuration to match the job, for it to start printing.")
.format(printer_name=printer_name, job_name=print_job["name"]))
@ -625,9 +647,7 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte
request.setRawHeader(b"User-agent", b"CuraPrintClusterOutputDevice Plugin")
def _cleanupRequest(self):
self._reply = None
self._request = None
self._multipart = None
self._stage = OutputStage.ready
self._file_name = None
@ -680,8 +700,11 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte
Logger.log("d", "User aborted sending print to remote.")
self._progress_message.hide()
self._compressing_print = False
self._stage = OutputStage.ready
if self._reply:
self._reply.abort()
self._reply = None
self._stage = OutputStage.ready
Application.getInstance().showPrintMonitor.emit(False)
@pyqtSlot(int, result=str)
def formatDuration(self, seconds):
return Duration(seconds).getDisplayString(DurationFormat.Format.Short)

View File

@ -335,8 +335,11 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
if self._image_reply:
try:
self._image_reply.abort()
try:
self._image_reply.downloadProgress.disconnect(self._onStreamDownloadProgress)
except TypeError:
pass #The signal was never connected.
self._image_reply.abort()
except RuntimeError:
pass # It can happen that the wrapped c++ object is already deleted.
self._image_reply = None
@ -427,7 +430,7 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
Logger.log("d", "Requestion authentication for %s due to action %s" % (self._key, action_id))
self._authentication_failed_message.hide()
self._not_authenticated_message.hide()
self._authentication_state = AuthState.NotAuthenticated
self.setAuthenticationState(AuthState.NotAuthenticated)
self._authentication_counter = 0
self._authentication_requested_message.setProgress(0)
self._authentication_id = None
@ -614,7 +617,7 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
# Reset authentication state
self._authentication_requested_message.hide()
self._authentication_state = AuthState.NotAuthenticated
self.setAuthenticationState(AuthState.NotAuthenticated)
self._authentication_counter = 0
self._authentication_timer.stop()

View File

@ -14,5 +14,58 @@ Button {
tooltip: catalog.i18nc("@info:tooltip", "Opens the print jobs page with your default web browser.")
text: catalog.i18nc("@action:button", "View print jobs")
style: UM.Theme.styles.sidebar_action_button
// 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 { }
}
}

View File

@ -14,14 +14,7 @@ Rectangle
function getPrettyTime(time)
{
var hours = Math.floor(time / 3600)
time -= hours * 3600
var minutes = Math.floor(time / 60);
time -= minutes * 60
var seconds = Math.floor(time);
var finalTime = strPadLeft(hours, "0", 2) + ':' + strPadLeft(minutes,'0',2)+ ':' + strPadLeft(seconds,'0',2);
return finalTime;
return OutputDevice.formatDuration(time)
}
function formatPrintJobPercent(printJob)
@ -37,6 +30,23 @@ Rectangle
return Math.min(100, Math.round(printJob.time_elapsed / printJob.time_total * 100)) + "%";
}
function printerStatusText(printer)
{
switch (printer.status)
{
case "pre_print":
return catalog.i18nc("@label", "Preparing to print")
case "printing":
return catalog.i18nc("@label:status", "Printing");
case "idle":
return catalog.i18nc("@label:status", "Available");
case "unreachable": // TODO: new string
case "maintenance": // TODO: new string
case "unknown":
default:
return catalog.i18nc("@label", "Unknown");
}
}
id: printerDelegate
property var printer
@ -143,14 +153,14 @@ Rectangle
anchors.right: printProgressArea.left
anchors.rightMargin: UM.Theme.getSize("default_margin").width
color: emphasisColor
UM.RecolorImage
Image
{
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
width: 40 * screenScaleFactor
height: width
anchors.right: parent.right
anchors.rightMargin: parent.rightMargin
source: "camera-icon.svg"
width: sourceSize.width
height: sourceSize.height * width / sourceSize.width
color: "white"
}
}
@ -217,6 +227,9 @@ Rectangle
//border.color: lineColor
height: 40 * screenScaleFactor
anchors.left: parent.left
property var showPercent: {
return printJob != null && (["printing", "post_print", "pre_print", "sent_to_printer"].indexOf(printJob.status) !== -1);
}
Label
{
@ -232,7 +245,7 @@ Rectangle
return catalog.i18nc("@label:status", "Disabled");
}
if(printJob != null)
if ((printJob != null) && ((printer.status === "pre_print") || (printer.status === "printing")))
{
switch (printJob.status)
{
@ -263,16 +276,17 @@ Rectangle
case "aborted":
return catalog.i18nc("@label:status", "Print aborted");
default:
return "";
return printerStatusText(printer);
}
}
return catalog.i18nc("@label:status", "Available");
return printerStatusText(printer);
}
elide: Text.ElideRight
font: UM.Theme.getFont("small")
}
Label
{
id: progressText
@ -282,10 +296,44 @@ Rectangle
anchors.top: statusText.top
text: formatPrintJobPercent(printJob)
visible: printJob != null && (["printing", "post_print", "pre_print", "sent_to_printer"].indexOf(printJob.status) !== -1)
visible: printProgressTitleBar.showPercent
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.enabled)
{
return "blocked-icon.svg";
}
if (printJob != null)
{
if(printJob.status === "queued")
{
if (printJob.configuration_changes_required != null && printJob.configuration_changes_required.length !== 0)
{
return "action-required-icon.svg";
}
}
else if (printJob.status === "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
@ -308,7 +356,7 @@ Rectangle
width: parent.width - 2 * UM.Theme.getSize("default_margin").width
visible: showExtended
visible: printProgressArea.showExtended
Label // Status detail
{

View File

@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24">
<defs>
<path id="a" d="M13.774 1.412l9.706 18.665A2 2 0 0 1 21.706 23H2.294A2 2 0 0 1 .52 20.077l9.706-18.665a2 2 0 0 1 3.548 0z"/>
</defs>
<g fill="none" fill-rule="evenodd">
<use fill="#FFF" xlink:href="#a"/>
<path stroke="#000" stroke-width="2.4" d="M12.71 1.966a.8.8 0 0 0-1.42 0L1.584 20.631a.8.8 0 0 0 .71 1.169h19.412a.8.8 0 0 0 .71-1.17L12.71 1.967z"/>
<path fill="#000" d="M13.144 14.995h-2.29L10.5 8h2.998l-.354 6.995zm-2.612 2.502c0-.475.13-.844.388-1.108.258-.263.633-.395 1.125-.395.488 0 .857.132 1.108.395.251.264.377.633.377 1.108 0 .47-.13.836-.39 1.1-.261.263-.626.395-1.095.395-.473 0-.843-.132-1.111-.396-.268-.263-.402-.63-.402-1.1z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 844 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="#000" fill-rule="evenodd" d="M6 10h12v4H6v-4zm6 14c6.627 0 12-5.373 12-12S18.627 0 12 0 0 5.373 0 12s5.373 12 12 12z"/>
</svg>

After

Width:  |  Height:  |  Size: 227 B

View File

@ -1,3 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="21" height="14" viewBox="0 0 21 14">
<path fill="none" fill-rule="evenodd" stroke="#464B4E" stroke-width="1.5" d="M19.295 2.83L16.25 4.31V2c0-.69-.56-1.25-1.25-1.25H2C1.31.75.75 1.31.75 2v10c0 .69.56 1.25 1.25 1.25h13c.69 0 1.25-.56 1.25-1.25V9.69l3.045 1.48a.85.85 0 0 0 .367.08c.355 0 .584-.181.584-.31V3.06c0-.026-.011-.058-.04-.096-.16-.206-.592-.289-.911-.134z" opacity=".85"/>
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48">
<g fill="none" fill-rule="evenodd">
<!-- <rect width="48" height="48" fill="#00A6EC" rx="24"/>-->
<path stroke="#FFF" stroke-width="2.5" d="M32.75 16.25h-19.5v15.5h19.5v-4.51l3.501 1.397c.181.072.405.113.638.113.333 0 .627-.081.81-.2.036-.024.048-.028.051-.011V18.487c-.26-.23-.976-.332-1.499-.124L32.75 19.76v-3.51z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 441 B

After

Width:  |  Height:  |  Size: 438 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="none" fill-rule="evenodd" stroke="#000" stroke-width="3" d="M3 10.71L10.068 17 21 5"/>
</svg>

After

Width:  |  Height:  |  Size: 194 B

View File

@ -14,6 +14,7 @@
"platform_texture": "Ultimaker2backplate.png",
"platform_offset": [9, 0, 0],
"has_materials": false,
"has_machine_quality": true,
"first_start_actions": ["UM2UpgradeSelection"],
"supported_actions":["UM2UpgradeSelection", "UpgradeFirmware"]
},

View File

@ -386,8 +386,6 @@ UM.MainWindow
visible: opacity > 0
opacity: base.showPrintMonitor ? 1 : 0
Behavior on opacity { NumberAnimation { duration: 100; } }
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.AllButtons
@ -818,7 +816,7 @@ UM.MainWindow
Connections
{
target: Printer
target: CuraApplication
onShowDiscardOrKeepProfileChanges:
{
discardOrKeepProfileChangesDialog.show()

View File

@ -76,7 +76,7 @@ Item
property var totalTicks: 0
property var availableTotalTicks: 0
property var activeQualityId: 0
property var activeQualityIndex: 0
property var qualitySliderStepWidth: 0
property var qualitySliderAvailableMin : 0
@ -97,7 +97,7 @@ Item
// Set selected value
if (Cura.MachineManager.activeQualityId == qualityItem.id) {
qualityModel.activeQualityId = i
qualityModel.activeQualityIndex = i
}
// Set min available
@ -144,11 +144,15 @@ Item
// check, the ticks count cannot be less than zero
if(Cura.ProfilesModel.rowCount() != 0)
{
qualityModel.totalTicks = Cura.ProfilesModel.rowCount() - 1 // minus one, because slider starts from 0
}
else
{
qualityModel.totalTicks = 0
}
}
}
Text
{
@ -264,7 +268,7 @@ Item
maximumValue: qualityModel.qualitySliderAvailableMax >= 0 ? qualityModel.qualitySliderAvailableMax : 0
stepSize: 1
value: qualityModel.activeQualityId
value: qualityModel.activeQualityIndex
width: qualityModel.qualitySliderStepWidth * qualityModel.availableTotalTicks
@ -292,10 +296,11 @@ Item
}
onValueChanged: {
if(Cura.MachineManager.activeMachine != null)
// Only change if an active machine is set and the slider is visible at all.
if(Cura.MachineManager.activeMachine != null && visible)
{
//Prevent updating during view initializing. Trigger only if the value changed by user
if(qualitySlider.value != qualityModel.activeQualityId)
if(qualitySlider.value != qualityModel.activeQualityIndex)
{
//start updating with short delay
qualitySliderChangeTimer.start();

View File

@ -16,7 +16,7 @@
"secondary": [241, 242, 242, 255],
"topbar_background_color": [0, 0, 0, 0],
"topbar_background_color_monitoring": [39, 44, 48, 255],
"topbar_background_color_monitoring": [0, 0, 0, 0],
"topbar_button_text_active": [255, 255, 255, 255],
"topbar_button_text_inactive": [128, 128, 128, 255],

View File

@ -68,7 +68,7 @@
"secondary": [245, 245, 245, 255],
"topbar_background_color": [255, 255, 255, 0],
"topbar_background_color_monitoring": [255, 255, 255, 255],
"topbar_background_color_monitoring": [255, 255, 255, 0],
"topbar_button_text_active": [0, 0, 0, 255],
"topbar_button_text_inactive": [128, 128, 128, 255],

View File

@ -1,6 +1,8 @@
#Todo: Write tests
import pytest
# QtApplication needs to be imported first to prevent import errors.
from UM.Qt.QtApplication import QtApplication
from cura.MachineAction import MachineAction
from cura.MachineActionManager import MachineActionManager, NotUniqueMachineActionError, UnknownMachineActionError