mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-08-10 07:15:03 -06:00
Merge branch 'feature_intent' of github.com:Ultimaker/Cura into feature_intent_container_tree
This commit is contained in:
commit
d1a8ce54a1
52 changed files with 774 additions and 290 deletions
|
@ -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:
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue