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

This commit is contained in:
Ghostkeeper 2018-04-17 11:36:45 +02:00
commit 4c75d78f26
No known key found for this signature in database
GPG key ID: 5252B696FB5E7C7A
10 changed files with 63 additions and 48 deletions

View file

@ -501,11 +501,6 @@ class CuraApplication(QtApplication):
def getStaticVersion(cls): def getStaticVersion(cls):
return CuraVersion return CuraVersion
## Handle removing the unneeded plugins
# \sa PluginRegistry
def _removePlugins(self):
self._plugin_registry.removePlugins()
## Handle loading of all plugin types (and the backend explicitly) ## Handle loading of all plugin types (and the backend explicitly)
# \sa PluginRegistry # \sa PluginRegistry
def _loadPlugins(self): def _loadPlugins(self):

34
cura/Utils/Threading.py Normal file
View file

@ -0,0 +1,34 @@
import threading
from cura.CuraApplication import CuraApplication
#
# HACK:
#
# In project loading, when override the existing machine is selected, the stacks and containers that are correctly
# active in the system will be overridden at runtime. Because the project loading is done in a different thread than
# the Qt thread, something else can kick in the middle of the process. One of them is the rendering. It will access
# the current stacks and container, which have not completely been updated yet, so Cura will crash in this case.
#
# This "@call_on_qt_thread" decorator makes sure that a function will always be called on the Qt thread (blocking).
# It is applied to the read() function of project loading so it can be guaranteed that only after the project loading
# process is completely done, everything else that needs to occupy the QT thread will be executed.
#
class InterCallObject:
def __init__(self):
self.finish_event = threading.Event()
self.result = None
def call_on_qt_thread(func):
def _call_on_qt_thread_wrapper(*args, **kwargs):
def _handle_call(ico, *args, **kwargs):
ico.result = func(*args, **kwargs)
ico.finish_event.set()
inter_call_object = InterCallObject()
new_args = tuple([inter_call_object] + list(args)[:])
CuraApplication.getInstance().callLater(_handle_call, *new_args, **kwargs)
inter_call_object.finish_event.wait()
return inter_call_object.result
return _call_on_qt_thread_wrapper

0
cura/Utils/__init__.py Normal file
View file

View file

@ -4,7 +4,6 @@
from configparser import ConfigParser from configparser import ConfigParser
import zipfile import zipfile
import os import os
import threading
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
@ -27,43 +26,13 @@ from cura.Settings.ExtruderStack import ExtruderStack
from cura.Settings.GlobalStack import GlobalStack from cura.Settings.GlobalStack import GlobalStack
from cura.Settings.CuraContainerStack import _ContainerIndexes from cura.Settings.CuraContainerStack import _ContainerIndexes
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
from cura.Utils.Threading import call_on_qt_thread
from .WorkspaceDialog import WorkspaceDialog from .WorkspaceDialog import WorkspaceDialog
i18n_catalog = i18nCatalog("cura") i18n_catalog = i18nCatalog("cura")
#
# HACK:
#
# In project loading, when override the existing machine is selected, the stacks and containers that are correctly
# active in the system will be overridden at runtime. Because the project loading is done in a different thread than
# the Qt thread, something else can kick in the middle of the process. One of them is the rendering. It will access
# the current stacks and container, which have not completely been updated yet, so Cura will crash in this case.
#
# This "@call_on_qt_thread" decorator makes sure that a function will always be called on the Qt thread (blocking).
# It is applied to the read() function of project loading so it can be guaranteed that only after the project loading
# process is completely done, everything else that needs to occupy the QT thread will be executed.
#
class InterCallObject:
def __init__(self):
self.finish_event = threading.Event()
self.result = None
def call_on_qt_thread(func):
def _call_on_qt_thread_wrapper(*args, **kwargs):
def _handle_call(ico, *args, **kwargs):
ico.result = func(*args, **kwargs)
ico.finish_event.set()
inter_call_object = InterCallObject()
new_args = tuple([inter_call_object] + list(args)[:])
CuraApplication.getInstance().callLater(_handle_call, *new_args, **kwargs)
inter_call_object.finish_event.wait()
return inter_call_object.result
return _call_on_qt_thread_wrapper
class ContainerInfo: class ContainerInfo:
def __init__(self, file_name: str, serialized: str, parser: ConfigParser): def __init__(self, file_name: str, serialized: str, parser: ConfigParser):
self.file_name = file_name self.file_name = file_name

View file

@ -6,16 +6,18 @@ from io import StringIO
import zipfile import zipfile
from UM.Application import Application from UM.Application import Application
from UM.Logger import Logger
from UM.Preferences import Preferences from UM.Preferences import Preferences
from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Workspace.WorkspaceWriter import WorkspaceWriter from UM.Workspace.WorkspaceWriter import WorkspaceWriter
from cura.Utils.Threading import call_on_qt_thread
class ThreeMFWorkspaceWriter(WorkspaceWriter): class ThreeMFWorkspaceWriter(WorkspaceWriter):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
@call_on_qt_thread
def write(self, stream, nodes, mode=WorkspaceWriter.OutputMode.BinaryMode): def write(self, stream, nodes, mode=WorkspaceWriter.OutputMode.BinaryMode):
application = Application.getInstance() application = Application.getInstance()
machine_manager = application.getMachineManager() machine_manager = application.getMachineManager()

View file

@ -121,7 +121,7 @@ class CuraEngineBackend(QObject, Backend):
self._slice_start_time = None self._slice_start_time = None
self._is_disabled = False self._is_disabled = False
Preferences.getInstance().addPreference("general/auto_slice", True) Preferences.getInstance().addPreference("general/auto_slice", False)
self._use_timer = False self._use_timer = False
# When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired. # When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired.

View file

@ -49,6 +49,13 @@ class ModelChecker(QObject, Extension):
warning_size_xy = 150 #The horizontal size of a model that would be too large when dealing with shrinking materials. warning_size_xy = 150 #The horizontal size of a model that would be too large when dealing with shrinking materials.
warning_size_z = 100 #The vertical size of a model that would be too large when dealing with shrinking materials. warning_size_z = 100 #The vertical size of a model that would be too large when dealing with shrinking materials.
# This function can be triggered in the middle of a machine change, so do not proceed if the machine change
# has not done yet.
global_container_stack = Application.getInstance().getGlobalContainerStack()
if global_container_stack is None:
Application.getInstance().callLater(lambda: self.onChanged.emit())
return False
material_shrinkage = self._getMaterialShrinkage() material_shrinkage = self._getMaterialShrinkage()
warning_nodes = [] warning_nodes = []
@ -56,6 +63,13 @@ class ModelChecker(QObject, Extension):
# Check node material shrinkage and bounding box size # Check node material shrinkage and bounding box size
for node in self.sliceableNodes(): for node in self.sliceableNodes():
node_extruder_position = node.callDecoration("getActiveExtruderPosition") node_extruder_position = node.callDecoration("getActiveExtruderPosition")
# This function can be triggered in the middle of a machine change, so do not proceed if the machine change
# has not done yet.
if str(node_extruder_position) not in global_container_stack.extruders:
Application.getInstance().callLater(lambda: self.onChanged.emit())
return False
if material_shrinkage[node_extruder_position] > shrinkage_threshold: if material_shrinkage[node_extruder_position] > shrinkage_threshold:
bbox = node.getBoundingBox() bbox = node.getBoundingBox()
if bbox.width >= warning_size_xy or bbox.depth >= warning_size_xy or bbox.height >= warning_size_z: if bbox.width >= warning_size_xy or bbox.depth >= warning_size_xy or bbox.height >= warning_size_z:
@ -63,11 +77,11 @@ class ModelChecker(QObject, Extension):
self._caution_message.setText(catalog.i18nc( self._caution_message.setText(catalog.i18nc(
"@info:status", "@info:status",
"Some models may not be printed optimally due to object size and chosen material for models: {model_names}.\n" "<p>One or more 3D models may not print optimally due to the model size and material configuration:</p>\n"
"Tips that may be useful to improve the print quality:\n" "<p>{model_names}</p>\n"
"1) Use rounded corners.\n" "<p>Find out how to ensure the best possible print quality and reliability.</p>\n"
"2) Turn the fan off (only if there are no tiny details on the model).\n" "<p><a href=\"https://ultimaker.com/3D-model-assistant\">View print quality guide</a></p>"
"3) Use a different material.").format(model_names = ", ".join([n.getName() for n in warning_nodes]))) ).format(model_names = ", ".join([n.getName() for n in warning_nodes])))
return len(warning_nodes) > 0 return len(warning_nodes) > 0
@ -92,9 +106,8 @@ class ModelChecker(QObject, Extension):
Logger.log("d", "Model checker view created.") Logger.log("d", "Model checker view created.")
@pyqtProperty(bool, notify = onChanged) @pyqtProperty(bool, notify = onChanged)
def runChecks(self): def hasWarnings(self):
danger_shrinkage = self.checkObjectsForShrinkage() danger_shrinkage = self.checkObjectsForShrinkage()
return any((danger_shrinkage, )) #If any of the checks fail, show the warning button. return any((danger_shrinkage, )) #If any of the checks fail, show the warning button.
@pyqtSlot() @pyqtSlot()

View file

@ -18,7 +18,7 @@ Button
UM.I18nCatalog{id: catalog; name:"cura"} UM.I18nCatalog{id: catalog; name:"cura"}
visible: manager.runChecks visible: manager.hasWarnings
tooltip: catalog.i18nc("@info:tooltip", "Some things could be problematic in this print. Click to see tips for adjustment.") tooltip: catalog.i18nc("@info:tooltip", "Some things could be problematic in this print. Click to see tips for adjustment.")
onClicked: manager.showWarnings() onClicked: manager.showWarnings()

View file

@ -15,6 +15,7 @@ Window {
id: base id: base
title: catalog.i18nc("@title:tab", "Plugins"); title: catalog.i18nc("@title:tab", "Plugins");
modality: Qt.ApplicationModal
width: 800 * screenScaleFactor width: 800 * screenScaleFactor
height: 640 * screenScaleFactor height: 640 * screenScaleFactor
minimumWidth: 350 * screenScaleFactor minimumWidth: 350 * screenScaleFactor

View file

@ -51,7 +51,8 @@ Menu
MenuItem MenuItem
{ {
text: model.name text: model.name
checkable: true checkable: model.available
enabled: model.available
checked: Cura.MachineManager.activeQualityOrQualityChangesName == model.name checked: Cura.MachineManager.activeQualityOrQualityChangesName == model.name
exclusiveGroup: group exclusiveGroup: group
onTriggered: Cura.MachineManager.setQualityChangesGroup(model.quality_changes_group) onTriggered: Cura.MachineManager.setQualityChangesGroup(model.quality_changes_group)