Merge branch 'feature_intent' of github.com:Ultimaker/Cura into feature_intent_container_tree

This commit is contained in:
Jaime van Kessel 2019-08-27 14:18:41 +02:00
commit d1a8ce54a1
52 changed files with 774 additions and 290 deletions

View file

@ -12,9 +12,10 @@ import json
import ssl
import urllib.request
import urllib.error
import shutil
from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, Qt, QUrl
import certifi
from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, QUrl
from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QTextEdit, QGroupBox, QCheckBox, QPushButton
from PyQt5.QtGui import QDesktopServices
@ -22,7 +23,6 @@ from UM.Application import Application
from UM.Logger import Logger
from UM.View.GL.OpenGL import OpenGL
from UM.i18n import i18nCatalog
from UM.Platform import Platform
from UM.Resources import Resources
catalog = i18nCatalog("cura")
@ -352,11 +352,13 @@ class CrashHandler:
# Convert data to bytes
binary_data = json.dumps(self.data).encode("utf-8")
# CURA-6698 Create an SSL context and use certifi CA certificates for verification.
context = ssl.SSLContext(protocol=ssl.PROTOCOL_TLSv1_2)
context.load_verify_locations(cafile = certifi.where())
# Submit data
kwoptions = {"data": binary_data, "timeout": 5}
if Platform.isOSX():
kwoptions["context"] = ssl._create_unverified_context()
kwoptions = {"data": binary_data,
"timeout": 5,
"context": context}
Logger.log("i", "Sending crash report info to [%s]...", self.crash_url)
if not self.has_started:

View file

@ -75,7 +75,7 @@ class DiscoveredPrinter(QObject):
def readableMachineType(self) -> str:
from cura.CuraApplication import CuraApplication
machine_manager = CuraApplication.getInstance().getMachineManager()
# In LocalClusterOutputDevice, when it updates a printer information, it updates the machine type using the field
# In NetworkOutputDevice, when it updates a printer information, it updates the machine type using the field
# "machine_variant", and for some reason, it's not the machine type ID/codename/... but a human-readable string
# like "Ultimaker 3". The code below handles this case.
if self._hasHumanReadableMachineTypeName(self._machine_type):

View file

@ -2,15 +2,19 @@
# Cura is released under the terms of the LGPLv3 or higher.
import json
import webbrowser
from datetime import datetime, timedelta
import os
from typing import Optional, TYPE_CHECKING
from urllib.parse import urlencode
import requests.exceptions
from PyQt5.QtCore import QUrl
from PyQt5.QtGui import QDesktopServices
from UM.Logger import Logger
from UM.Message import Message
from UM.Platform import Platform
from UM.Signal import Signal
from cura.OAuth2.LocalAuthorizationServer import LocalAuthorizationServer
@ -163,7 +167,7 @@ class AuthorizationService:
})
# Open the authorization page in a new browser window.
webbrowser.open_new("{}?{}".format(self._auth_url, query_string))
QDesktopServices.openUrl(QUrl("{}?{}".format(self._auth_url, query_string)))
# Start a local web server to receive the callback URL on.
self._server.start(verification_code)

View file

@ -25,7 +25,7 @@ class ExtruderConfigurationModel(QObject):
return self._position
def setMaterial(self, material: Optional[MaterialOutputModel]) -> None:
if self._hotend_id != material:
if self._material != material:
self._material = material
self.extruderConfigurationChanged.emit()
@ -33,7 +33,7 @@ class ExtruderConfigurationModel(QObject):
def activeMaterial(self) -> Optional[MaterialOutputModel]:
return self._material
@pyqtProperty(QObject, fset=setMaterial, notify=extruderConfigurationChanged)
@pyqtProperty(QObject, fset = setMaterial, notify = extruderConfigurationChanged)
def material(self) -> Optional[MaterialOutputModel]:
return self._material

View file

@ -58,6 +58,14 @@ class PrinterConfigurationModel(QObject):
return False
return self._printer_type != ""
def hasAnyMaterialLoaded(self) -> bool:
if not self.isValid():
return False
for configuration in self._extruder_configurations:
if configuration.activeMaterial and configuration.activeMaterial.type != "empty":
return True
return False
def __str__(self):
message_chunks = []
message_chunks.append("Printer type: " + self._printer_type)

View file

@ -7,6 +7,7 @@ from UM.Math.Vector import Vector
from cura.PrinterOutput.Peripheral import Peripheral
from cura.PrinterOutput.Models.PrinterConfigurationModel import PrinterConfigurationModel
from cura.PrinterOutput.Models.ExtruderOutputModel import ExtruderOutputModel
from UM.Logger import Logger
if TYPE_CHECKING:
from cura.PrinterOutput.Models.PrintJobOutputModel import PrintJobOutputModel
@ -37,7 +38,7 @@ class PrinterOutputModel(QObject):
self._controller = output_controller
self._controller.canUpdateFirmwareChanged.connect(self._onControllerCanUpdateFirmwareChanged)
self._extruders = [ExtruderOutputModel(printer = self, position = i) for i in range(number_of_extruders)]
self._printer_configuration = PrinterConfigurationModel() # Indicates the current configuration setup in this printer
self._active_printer_configuration = PrinterConfigurationModel() # Indicates the current configuration setup in this printer
self._head_position = Vector(0, 0, 0)
self._active_print_job = None # type: Optional[PrintJobOutputModel]
self._firmware_version = firmware_version
@ -47,8 +48,10 @@ class PrinterOutputModel(QObject):
self._buildplate = ""
self._peripherals = [] # type: List[Peripheral]
self._printer_configuration.extruderConfigurations = [extruder.extruderConfiguration for extruder in
self._extruders]
self._active_printer_configuration.extruderConfigurations = [extruder.extruderConfiguration for extruder in
self._extruders]
self._active_printer_configuration.configurationChanged.connect(self.configurationChanged)
self._available_printer_configurations = [] # type: List[PrinterConfigurationModel]
self._camera_url = QUrl() # type: QUrl
@ -81,7 +84,7 @@ class PrinterOutputModel(QObject):
def updateType(self, printer_type: str) -> None:
if self._printer_type != printer_type:
self._printer_type = printer_type
self._printer_configuration.printerType = self._printer_type
self._active_printer_configuration.printerType = self._printer_type
self.typeChanged.emit()
self.configurationChanged.emit()
@ -92,7 +95,7 @@ class PrinterOutputModel(QObject):
def updateBuildplate(self, buildplate: str) -> None:
if self._buildplate != buildplate:
self._buildplate = buildplate
self._printer_configuration.buildplateConfiguration = self._buildplate
self._active_printer_configuration.buildplateConfiguration = self._buildplate
self.buildplateChanged.emit()
self.configurationChanged.emit()
@ -290,18 +293,18 @@ class PrinterOutputModel(QObject):
def _onControllerCanUpdateFirmwareChanged(self) -> None:
self.canUpdateFirmwareChanged.emit()
# Returns the configuration (material, variant and buildplate) of the current printer
# Returns the active configuration (material, variant and buildplate) of the current printer
@pyqtProperty(QObject, notify = configurationChanged)
def printerConfiguration(self) -> Optional[PrinterConfigurationModel]:
if self._printer_configuration.isValid():
return self._printer_configuration
if self._active_printer_configuration.isValid():
return self._active_printer_configuration
return None
peripheralsChanged = pyqtSignal()
@pyqtProperty(str, notify = peripheralsChanged)
def peripherals(self) -> str:
return ", ".join(*[peripheral.name for peripheral in self._peripherals])
return ", ".join([peripheral.name for peripheral in self._peripherals])
def addPeripheral(self, peripheral: Peripheral) -> None:
self._peripherals.append(peripheral)
@ -309,4 +312,29 @@ class PrinterOutputModel(QObject):
def removePeripheral(self, peripheral: Peripheral) -> None:
self._peripherals.remove(peripheral)
self.peripheralsChanged.emit()
self.peripheralsChanged.emit()
availableConfigurationsChanged = pyqtSignal()
# The availableConfigurations are configuration options that a printer can switch to, but doesn't currently have
# active (eg; Automatic tool changes, material loaders, etc).
@pyqtProperty("QVariantList", notify = availableConfigurationsChanged)
def availableConfigurations(self) -> List[PrinterConfigurationModel]:
return self._available_printer_configurations
def addAvailableConfiguration(self, new_configuration: PrinterConfigurationModel) -> None:
if new_configuration not in self._available_printer_configurations:
self._available_printer_configurations.append(new_configuration)
self.availableConfigurationsChanged.emit()
def removeAvailableConfiguration(self, config_to_remove: PrinterConfigurationModel) -> None:
try:
self._available_printer_configurations.remove(config_to_remove)
except ValueError:
Logger.log("w", "Unable to remove configuration that isn't in the list of available configurations")
else:
self.availableConfigurationsChanged.emit()
def setAvailableConfigurations(self, new_configurations: List[PrinterConfigurationModel]) -> None:
self._available_printer_configurations = new_configurations
self.availableConfigurationsChanged.emit()

View file

@ -220,20 +220,25 @@ class PrinterOutputDevice(QObject, OutputDevice):
return self._unique_configurations
def _updateUniqueConfigurations(self) -> None:
self._unique_configurations = sorted(
{printer.printerConfiguration for printer in self._printers if printer.printerConfiguration is not None},
key=lambda config: config.printerType,
)
self.uniqueConfigurationsChanged.emit()
all_configurations = set()
for printer in self._printers:
if printer.printerConfiguration is not None and printer.printerConfiguration.hasAnyMaterialLoaded():
all_configurations.add(printer.printerConfiguration)
all_configurations.update(printer.availableConfigurations)
new_configurations = sorted(all_configurations, key = lambda config: config.printerType or "")
if new_configurations != self._unique_configurations:
self._unique_configurations = new_configurations
self.uniqueConfigurationsChanged.emit()
# Returns the unique configurations of the printers within this output device
@pyqtProperty("QStringList", notify = uniqueConfigurationsChanged)
def uniquePrinterTypes(self) -> List[str]:
return list(sorted(set([configuration.printerType for configuration in self._unique_configurations])))
return list(sorted(set([configuration.printerType or "" for configuration in self._unique_configurations])))
def _onPrintersChanged(self) -> None:
for printer in self._printers:
printer.configurationChanged.connect(self._updateUniqueConfigurations)
printer.availableConfigurationsChanged.connect(self._updateUniqueConfigurations)
# At this point there may be non-updated configurations
self._updateUniqueConfigurations()

View file

@ -9,7 +9,6 @@ from typing import Any, cast, Dict, Optional, List, Union
from PyQt5.QtWidgets import QMessageBox
from UM.Decorators import override
from UM.PluginObject import PluginObject
from UM.Settings.ContainerFormatError import ContainerFormatError
from UM.Settings.Interfaces import ContainerInterface
from UM.Settings.ContainerRegistry import ContainerRegistry
@ -21,7 +20,6 @@ from UM.Logger import Logger
from UM.Message import Message
from UM.Platform import Platform
from UM.PluginRegistry import PluginRegistry # For getting the possible profile writers to write with.
from UM.Util import parseBool
from UM.Resources import Resources
from cura.ReaderWriters.ProfileWriter import ProfileWriter
@ -29,6 +27,7 @@ from . import ExtruderStack
from . import GlobalStack
import cura.CuraApplication
from cura.Settings.cura_empty_instance_containers import empty_quality_container
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
from cura.ReaderWriters.ProfileReader import NoProfileException, ProfileReader
@ -389,13 +388,33 @@ class CuraContainerRegistry(ContainerRegistry):
# successfully imported but then fail to show up.
quality_manager = cura.CuraApplication.CuraApplication.getInstance()._quality_manager
quality_group_dict = quality_manager.getQualityGroupsForMachineDefinition(global_stack)
if quality_type not in quality_group_dict:
# "not_supported" profiles can be imported.
if quality_type != empty_quality_container.getMetaDataEntry("quality_type") and quality_type not in quality_group_dict:
return catalog.i18nc("@info:status", "Could not find a quality type {0} for the current configuration.", quality_type)
ContainerRegistry.getInstance().addContainer(profile)
return None
@override(ContainerRegistry)
def saveDirtyContainers(self) -> None:
# Lock file for "more" atomically loading and saving to/from config dir.
with self.lockFile():
# Save base files first
for instance in self.findDirtyContainers(container_type=InstanceContainer):
if instance.getMetaDataEntry("removed"):
continue
if instance.getId() == instance.getMetaData().get("base_file"):
self.saveContainer(instance)
for instance in self.findDirtyContainers(container_type=InstanceContainer):
if instance.getMetaDataEntry("removed"):
continue
self.saveContainer(instance)
for stack in self.findContainerStacks():
self.saveContainer(stack)
## Gets a list of profile writer plugins
# \return List of tuples of (plugin_id, meta_data).
def _getIOPlugins(self, io_type):