diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 18d1bde57e..e90dfd70d3 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -409,6 +409,14 @@ class CuraApplication(QtApplication): else: self.exit(0) + ## Signal to connect preferences action in QML + showPreferencesWindow = pyqtSignal() + + ## Show the preferences window + @pyqtSlot() + def showPreferences(self): + self.showPreferencesWindow.emit() + ## A reusable dialogbox # showMessageBox = pyqtSignal(str, str, str, str, int, int, arguments = ["title", "text", "informativeText", "detailedText", "buttons", "icon"]) @@ -683,7 +691,7 @@ class CuraApplication(QtApplication): self.setMainQml(Resources.getPath(self.ResourceTypes.QmlFiles, "Cura.qml")) self._qml_import_paths.append(Resources.getPath(self.ResourceTypes.QmlFiles)) - run_without_gui = self.getCommandLineOption("headless", False) or self.getCommandLineOption("invisible", False) + run_without_gui = self.getCommandLineOption("headless", False) if not run_without_gui: self.initializeEngine() controller.setActiveStage("PrepareStage") diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index 23197dac24..471e54dba6 100755 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -76,7 +76,8 @@ class PlatformPhysics: if not node.getDecorator(ConvexHullDecorator): node.addDecorator(ConvexHullDecorator()) - if Preferences.getInstance().getValue("physics/automatic_push_free"): + # only push away objects if this node is a printing mesh + if not node.callDecoration("isNonPrintingMesh") and Preferences.getInstance().getValue("physics/automatic_push_free"): # Check for collisions between convex hulls for other_node in BreadthFirstIterator(root): # Ignore root, ourselves and anything that is not a normal SceneNode. @@ -98,6 +99,9 @@ class PlatformPhysics: if other_node in transformed_nodes: continue # Other node is already moving, wait for next pass. + if other_node.callDecoration("isNonPrintingMesh"): + continue + overlap = (0, 0) # Start loop with no overlap current_overlap_checks = 0 # Continue to check the overlap until we no longer find one. diff --git a/plugins/SimulationView/SimulationView.py b/plugins/SimulationView/SimulationView.py index 44f472129f..6fc362725e 100644 --- a/plugins/SimulationView/SimulationView.py +++ b/plugins/SimulationView/SimulationView.py @@ -4,6 +4,7 @@ import sys from PyQt5.QtCore import Qt +from PyQt5.QtGui import QOpenGLContext from PyQt5.QtWidgets import QApplication from UM.Application import Application @@ -13,6 +14,7 @@ from UM.Logger import Logger from UM.Math.Color import Color from UM.Mesh.MeshBuilder import MeshBuilder from UM.Message import Message +from UM.Platform import Platform from UM.PluginRegistry import PluginRegistry from UM.Preferences import Preferences from UM.Resources import Resources @@ -24,6 +26,7 @@ from UM.View.GL.OpenGLContext import OpenGLContext from UM.View.View import View from UM.i18n import i18nCatalog from cura.ConvexHullNode import ConvexHullNode +from cura.CuraApplication import CuraApplication from .NozzleNode import NozzleNode from .SimulationPass import SimulationPass @@ -414,6 +417,23 @@ class SimulationView(View): return True if event.type == Event.ViewActivateEvent: + # FIX: on Max OS X, somehow QOpenGLContext.currentContext() can become None during View switching. + # This can happen when you do the following steps: + # 1. Start Cura + # 2. Load a model + # 3. Switch to Custom mode + # 4. Select the model and click on the per-object tool icon + # 5. Switch view to Layer view or X-Ray + # 6. Cura will very likely crash + # It seems to be a timing issue that the currentContext can somehow be empty, but I have no clue why. + # This fix tries to reschedule the view changing event call on the Qt thread again if the current OpenGL + # context is None. + if Platform.isOSX(): + if QOpenGLContext.currentContext() is None: + Logger.log("d", "current context of OpenGL is empty on Mac OS X, will try to create shaders later") + CuraApplication.getInstance().callLater(lambda e=event: self.event(e)) + return + # Make sure the SimulationPass is created layer_pass = self.getSimulationPass() self.getRenderer().addRenderPass(layer_pass) diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 508d174cf2..753e00091b 100755 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -39,19 +39,26 @@ class SliceInfo(Extension): Preferences.getInstance().addPreference("info/send_slice_info", True) Preferences.getInstance().addPreference("info/asked_send_slice_info", False) - if not Preferences.getInstance().getValue("info/asked_send_slice_info") and Preferences.getInstance().getValue("info/send_slice_info"): - self.send_slice_info_message = Message(catalog.i18nc("@info", "Cura collects anonymised slicing statistics. You can disable this in the preferences."), + if not Preferences.getInstance().getValue("info/asked_send_slice_info"): + self.send_slice_info_message = Message(catalog.i18nc("@info", "Cura collects anonymized usage statistics."), lifetime = 0, dismissable = False, title = catalog.i18nc("@info:title", "Collecting Data")) - self.send_slice_info_message.addAction("Dismiss", catalog.i18nc("@action:button", "Dismiss"), None, "") + self.send_slice_info_message.addAction("Dismiss", name = catalog.i18nc("@action:button", "Allow"), icon = None, + description = catalog.i18nc("@action:tooltip", "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing.")) + self.send_slice_info_message.addAction("Disable", name = catalog.i18nc("@action:button", "Disable"), icon = None, + description = catalog.i18nc("@action:tooltip", "Don't allow Cura to send anonymized usage statistics. You can enable it again in the preferences."), button_style = Message.ActionButtonStyle.LINK) self.send_slice_info_message.actionTriggered.connect(self.messageActionTriggered) self.send_slice_info_message.show() + ## Perform action based on user input. + # Note that clicking "Disable" won't actually disable the data sending, but rather take the user to preferences where they can disable it. def messageActionTriggered(self, message_id, action_id): - self.send_slice_info_message.hide() Preferences.getInstance().setValue("info/asked_send_slice_info", True) + if action_id == "Disable": + CuraApplication.getInstance().showPreferences() + self.send_slice_info_message.hide() def _onWriteStarted(self, output_device): try: diff --git a/plugins/XRayView/XRayView.py b/plugins/XRayView/XRayView.py index 35509a9715..0c4035c62d 100644 --- a/plugins/XRayView/XRayView.py +++ b/plugins/XRayView/XRayView.py @@ -2,10 +2,13 @@ # Cura is released under the terms of the LGPLv3 or higher. import os.path +from PyQt5.QtGui import QOpenGLContext from UM.Application import Application +from UM.Logger import Logger from UM.Math.Color import Color from UM.PluginRegistry import PluginRegistry +from UM.Platform import Platform from UM.Event import Event from UM.View.View import View from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator @@ -13,6 +16,8 @@ from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator from UM.View.RenderBatch import RenderBatch from UM.View.GL.OpenGL import OpenGL +from cura.CuraApplication import CuraApplication + from . import XRayPass ## View used to display a see-through version of objects with errors highlighted. @@ -52,6 +57,23 @@ class XRayView(View): def event(self, event): if event.type == Event.ViewActivateEvent: + # FIX: on Max OS X, somehow QOpenGLContext.currentContext() can become None during View switching. + # This can happen when you do the following steps: + # 1. Start Cura + # 2. Load a model + # 3. Switch to Custom mode + # 4. Select the model and click on the per-object tool icon + # 5. Switch view to Layer view or X-Ray + # 6. Cura will very likely crash + # It seems to be a timing issue that the currentContext can somehow be empty, but I have no clue why. + # This fix tries to reschedule the view changing event call on the Qt thread again if the current OpenGL + # context is None. + if Platform.isOSX(): + if QOpenGLContext.currentContext() is None: + Logger.log("d", "current context of OpenGL is empty on Mac OS X, will try to create shaders later") + CuraApplication.getInstance().callLater(lambda e = event: self.event(e)) + return + if not self._xray_pass: # Currently the RenderPass constructor requires a size > 0 # This should be fixed in RenderPass's constructor. diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 91098bbb29..efe956939b 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -528,6 +528,12 @@ UM.MainWindow onTriggered: preferences.visible = true } + Connections + { + target: CuraApplication + onShowPreferencesWindow: preferences.visible = true + } + MessageDialog { id: newProjectDialog