def __init__(self): Resources.addResourcePath(os.path.join(QtApplication.getInstallPrefix(), "share", "cura")) if not hasattr(sys, "frozen"): Resources.addResourcePath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..")) super().__init__(name = "cura", version = "master") self.setRequiredPlugins([ "CuraEngineBackend", "MeshView", "LayerView", "STLReader", "SelectionTool", "CameraTool", "GCodeWriter", "LocalFileStorage" ]) self._physics = None self._volume = None self._platform = None self._output_devices = {} self._print_information = None self._i18n_catalog = None self.activeMachineChanged.connect(self._onActiveMachineChanged) Preferences.getInstance().addPreference("cura/active_machine", "") Preferences.getInstance().addPreference("cura/active_mode", "simple")
def _onActiveMachineChanged(self): machine = self.getActiveMachine() if machine: Preferences.getInstance().setValue("cura/active_machine", machine.getName()) self._volume.setWidth(machine.getSettingValueByKey("machine_width")) self._volume.setHeight(machine.getSettingValueByKey("machine_height")) self._volume.setDepth(machine.getSettingValueByKey("machine_depth")) disallowed_areas = machine.getSettingValueByKey("machine_disallowed_areas") areas = [] if disallowed_areas: for area in disallowed_areas: polygon = [] polygon.append(Vector(area[0][0], 0.2, area[0][1])) polygon.append(Vector(area[1][0], 0.2, area[1][1])) polygon.append(Vector(area[2][0], 0.2, area[2][1])) polygon.append(Vector(area[3][0], 0.2, area[3][1])) areas.append(polygon) self._volume.setDisallowedAreas(areas) self._volume.rebuild() if self.getController().getTool("ScaleTool"): self.getController().getTool("ScaleTool").setMaximumBounds(self._volume.getBoundingBox()) offset = machine.getSettingValueByKey("machine_platform_offset") if offset: self._platform.setPosition(Vector(offset[0], offset[1], offset[2])) else: self._platform.setPosition(Vector(0.0, 0.0, 0.0))
def __init__(self): super().__init__() self._scene = Application.getInstance().getController().getScene() self._yaw = 0 self._pitch = 0 self._origin = Vector(0, 0, 0) self._min_zoom = 1 self._max_zoom = 2000.0 self._manual_zoom = 200 self._rotate = False self._move = False self._dragged = False self._shift_is_active = None self._ctrl_is_active = None self._space_is_active = None self._start_drag = None self._start_y = None self._drag_distance = 0.05 Preferences.getInstance().addPreference("view/invert_zoom", False) self._invert_zoom = Preferences.getInstance().getValue("view/invert_zoom") Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
def __init__(self): super().__init__() self.addMenuItem(i18n_catalog.i18nc("@item:inmenu", "Check for Updates"), self.checkNewVersion) Preferences.getInstance().addPreference("info/automatic_update_check", True) if Preferences.getInstance().getValue("info/automatic_update_check"): self.checkNewVersion(True)
def __init__(self, parent = None): super().__init__(parent) self._current_print_time = Duration(None, self) self._material_lengths = [] self._material_weights = [] self._material_costs = [] self._pre_sliced = False self._backend = Application.getInstance().getBackend() if self._backend: self._backend.printDurationMessage.connect(self._onPrintDurationMessage) self._job_name = "" self._abbr_machine = "" Application.getInstance().globalContainerStackChanged.connect(self._setAbbreviatedMachineName) Application.getInstance().fileLoaded.connect(self.setJobName) Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged) self._active_material_container = None Application.getInstance().getMachineManager().activeMaterialChanged.connect(self._onActiveMaterialChanged) self._onActiveMaterialChanged() self._material_amounts = []
def __init__(self): super().__init__() Preferences.getInstance().addPreference("view/show_overhang", True) self._enabled_shader = None self._disabled_shader = None
def saveMachineInstances(self): if self._active_machine: Preferences.getInstance().setValue("machines/active_instance", self._active_machine.getName()) for instance in self._machine_instances: file_name = urllib.parse.quote_plus(instance.getName()) + ".cfg" instance.saveToFile(Resources.getStoragePath(Resources.MachineInstances, file_name))
def _onGlobalContainerChanged(self): if self._global_container_stack: self._global_container_stack.nameChanged.disconnect(self._onMachineNameChanged) self._global_container_stack.containersChanged.disconnect(self._onInstanceContainersChanged) self._global_container_stack.propertyChanged.disconnect(self._onPropertyChanged) material = self._global_container_stack.findContainer({"type": "material"}) material.nameChanged.disconnect(self._onMaterialNameChanged) quality = self._global_container_stack.findContainer({"type": "quality"}) quality.nameChanged.disconnect(self._onQualityNameChanged) self._global_container_stack = Application.getInstance().getGlobalContainerStack() self._active_container_stack = self._global_container_stack self.globalContainerChanged.emit() if self._global_container_stack: Preferences.getInstance().setValue("cura/active_machine", self._global_container_stack.getId()) self._global_container_stack.nameChanged.connect(self._onMachineNameChanged) self._global_container_stack.containersChanged.connect(self._onInstanceContainersChanged) self._global_container_stack.propertyChanged.connect(self._onPropertyChanged) material = self._global_container_stack.findContainer({"type": "material"}) material.nameChanged.connect(self._onMaterialNameChanged) quality = self._global_container_stack.findContainer({"type": "quality"}) quality.nameChanged.connect(self._onQualityNameChanged)
def __init__(self): super().__init__() self._shader = None self._selection_shader = None self._num_layers = 0 self._layer_percentage = 0 # what percentage of layers need to be shown (SLider gives value between 0 - 100) self._proxy = LayerViewProxy.LayerViewProxy() self._controller.getScene().getRoot().childrenChanged.connect(self._onSceneChanged) self._max_layers = 0 self._current_layer_num = 0 self._current_layer_mesh = None self._current_layer_jumps = None self._top_layers_job = None self._activity = False Preferences.getInstance().addPreference("view/top_layer_count", 1) Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged) self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count")) self._top_layer_timer = QTimer() self._top_layer_timer.setInterval(50) self._top_layer_timer.setSingleShot(True) self._top_layer_timer.timeout.connect(self._startUpdateTopLayers) self._busy = False
def __init__(self, parent = None, *args, **kwargs): super().__init__(parent = parent, *args, **kwargs) Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged) self._onPreferencesChanged("general/visible_settings") self.visibilityChanged.connect(self._onVisibilityChanged)
def __init__(self, engine, parent = None): super().__init__(parent) self._engine = engine self._styles = None self._path = "" self._icons = {} self._images = {} # Workaround for incorrect default font on Windows if sys.platform == "win32": default_font = QFont() default_font.setPointSize(9) QCoreApplication.instance().setFont(default_font) self._em_height = int(QFontMetrics(QCoreApplication.instance().font()).ascent()) self._em_width = self._em_height; self._initializeDefaults() Preferences.getInstance().addPreference("general/theme", Application.getInstance().getApplicationName()) try: theme_path = Resources.getPath(Resources.Themes, Preferences.getInstance().getValue("general/theme")) self.load(theme_path) except FileNotFoundError: Logger.log("e", "Could not find theme file.")
def test_loginAndLogout() -> None: preferences = Preferences() authorization_service = AuthorizationService(OAUTH_SETTINGS, preferences) authorization_service.onAuthenticationError.emit = MagicMock() authorization_service.onAuthStateChanged.emit = MagicMock() authorization_service.initialize() # Let the service think there was a successful response with patch.object(AuthorizationHelpers, "parseJWT", return_value=UserProfile()): authorization_service._onAuthStateChanged(SUCCESSFUL_AUTH_RESPONSE) # Ensure that the error signal was not triggered assert authorization_service.onAuthenticationError.emit.call_count == 0 # Since we said that it went right this time, validate that we got a signal. assert authorization_service.onAuthStateChanged.emit.call_count == 1 assert authorization_service.getUserProfile() is not None assert authorization_service.getAccessToken() == "beep" # Check that we stored the authentication data, so next time the user won't have to log in again. assert preferences.getValue("test/auth_data") is not None # We're logged in now, also check if logging out works authorization_service.deleteAuthData() assert authorization_service.onAuthStateChanged.emit.call_count == 2 assert authorization_service.getUserProfile() is None # Ensure the data is gone after we logged out. assert preferences.getValue("test/auth_data") == "{}"
def __init__(self, parent = None): super().__init__(parent) self._global_container_stack = None Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged) self._global_stack_valid = None self._onGlobalContainerChanged() ## When the global container is changed, active material probably needs to be updated. self.globalContainerChanged.connect(self.activeMaterialChanged) self.globalContainerChanged.connect(self.activeVariantChanged) self.globalContainerChanged.connect(self.activeQualityChanged) self._empty_variant_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_variant")[0] self._empty_material_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_material")[0] self._empty_quality_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_quality")[0] Preferences.getInstance().addPreference("cura/active_machine", "") active_machine_id = Preferences.getInstance().getValue("cura/active_machine") if active_machine_id != "": # An active machine was saved, so restore it. self.setActiveMachine(active_machine_id) pass
def __init__(self, **kwargs): plugin_path = "" if sys.platform == "win32": if hasattr(sys, "frozen"): plugin_path = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "PyQt5", "plugins") Logger.log("i", "Adding QT5 plugin path: %s" % (plugin_path)) QCoreApplication.addLibraryPath(plugin_path) else: import site for dir in site.getsitepackages(): QCoreApplication.addLibraryPath(os.path.join(dir, "PyQt5", "plugins")) elif sys.platform == "darwin": plugin_path = os.path.join(Application.getInstallPrefix(), "Resources", "plugins") if plugin_path: Logger.log("i", "Adding QT5 plugin path: %s" % (plugin_path)) QCoreApplication.addLibraryPath(plugin_path) os.environ["QSG_RENDER_LOOP"] = "basic" super().__init__(sys.argv, **kwargs) self._plugins_loaded = False #Used to determine when it's safe to use the plug-ins. self._main_qml = "main.qml" self._engine = None self._renderer = None self._main_window = None self._shutting_down = False self._qml_import_paths = [] self._qml_import_paths.append(os.path.join(os.path.dirname(sys.executable), "qml")) self._qml_import_paths.append(os.path.join(Application.getInstallPrefix(), "Resources", "qml")) self.setAttribute(Qt.AA_UseDesktopOpenGL) try: self._splash = self._createSplashScreen() except FileNotFoundError: self._splash = None else: self._splash.show() self.processEvents() signal.signal(signal.SIGINT, signal.SIG_DFL) # This is done here as a lot of plugins require a correct gl context. If you want to change the framework, # these checks need to be done in your <framework>Application.py class __init__(). i18n_catalog = i18nCatalog("uranium") self.showSplashMessage(i18n_catalog.i18nc("@info:progress", "Loading plugins...")) self._loadPlugins() self.parseCommandLine() Logger.log("i", "Command line arguments: %s", self._parsed_command_line) self._plugin_registry.checkRequiredPlugins(self.getRequiredPlugins()) self.showSplashMessage(i18n_catalog.i18nc("@info:progress", "Loading preferences...")) try: file = Resources.getPath(Resources.Preferences, self.getApplicationName() + ".cfg") Preferences.getInstance().readFromFile(file) except FileNotFoundError: pass
def setExpandedCategories(self, categories): categories = list(set(categories)) categories.sort() joined = ";".join(categories) if joined != Preferences.getInstance().getValue("cura/categories_expanded"): Preferences.getInstance().setValue("cura/categories_expanded", joined) self.expandedCategoriesChanged.emit()
def __init__(self): super().__init__() self._model = None self.setExposedProperties("Model", "SelectedIndex") Preferences.getInstance().preferenceChanged.connect(self._onPreferenceChanged) self._onPreferenceChanged("cura/active_mode")
def test_deserialize(): preferences = Preferences() path = Resources.getPath(Resources.Preferences, "preferences_test.cfg") with open(path, "r", encoding="utf-8") as f: preferences.deserialize(f.read()) assert preferences.getValue("general/foo") == "omgzomg" assert preferences.getValue("general/derp") == True
def _onPreferencesChanged(self, preference): if preference != "view/top_layer_count" and preference != "view/only_show_top_layers": return self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count")) self._only_show_top_layers = bool(Preferences.getInstance().getValue("view/only_show_top_layers")) self._startUpdateTopLayers()
def __init__(self): super().__init__() Preferences.getInstance().addPreference("view/show_overhang", False) Preferences.getInstance().preferenceChanged.connect(self._onPreferenceChanged) self._enabled_material = None self._disabled_material = None
def __init__(self): super().__init__() # Find out where the engine is located, and how it is called. This depends on how Cura is packaged and which OS we are running on. default_engine_location = os.path.join(Application.getInstallPrefix(), "bin", "CuraEngine") if hasattr(sys, "frozen"): default_engine_location = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "CuraEngine") if sys.platform == "win32": default_engine_location += ".exe" default_engine_location = os.path.abspath(default_engine_location) Preferences.getInstance().addPreference("backend/location", default_engine_location) self._scene = Application.getInstance().getController().getScene() self._scene.sceneChanged.connect(self._onSceneChanged) # Workaround to disable layer view processing if layer view is not active. self._layer_view_active = False Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged) self._onActiveViewChanged() self._stored_layer_data = [] #Triggers for when to (re)start slicing: self._global_container_stack = None Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged) self._onGlobalStackChanged() #When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired. #This timer will group them up, and only slice for the last setting changed signal. #TODO: Properly group propertyChanged signals by whether they are triggered by the same user interaction. self._change_timer = QTimer() self._change_timer.setInterval(500) self._change_timer.setSingleShot(True) self._change_timer.timeout.connect(self.slice) #Listeners for receiving messages from the back-end. self._message_handlers["cura.proto.Layer"] = self._onLayerMessage self._message_handlers["cura.proto.Progress"] = self._onProgressMessage self._message_handlers["cura.proto.GCodeLayer"] = self._onGCodeLayerMessage self._message_handlers["cura.proto.GCodePrefix"] = self._onGCodePrefixMessage self._message_handlers["cura.proto.ObjectPrintTime"] = self._onObjectPrintTimeMessage self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage self._start_slice_job = None self._slicing = False #Are we currently slicing? self._restart = False #Back-end is currently restarting? self._enabled = True #Should we be slicing? Slicing might be paused when, for instance, the user is dragging the mesh around. self._always_restart = True #Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness. self._process_layers_job = None #The currently active job to process layers, or None if it is not processing layers. self._error_message = None #Pop-up message that shows errors. self.backendQuit.connect(self._onBackendQuit) self.backendConnected.connect(self._onBackendConnected) #When a tool operation is in progress, don't slice. So we need to listen for tool operations. Application.getInstance().getController().toolOperationStarted.connect(self._onToolOperationStarted) Application.getInstance().getController().toolOperationStopped.connect(self._onToolOperationStopped)
def __init__(self): super().__init__() # Find out where the engine is located, and how it is called. This depends on how Cura is packaged and which OS we are running on. default_engine_location = os.path.join(Application.getInstallPrefix(), "bin", "CuraEngine") if hasattr(sys, "frozen"): default_engine_location = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "CuraEngine") if sys.platform == "win32": default_engine_location += ".exe" default_engine_location = os.path.abspath(default_engine_location) Preferences.getInstance().addPreference("backend/location", default_engine_location) self._scene = Application.getInstance().getController().getScene() self._scene.sceneChanged.connect(self._onSceneChanged) # Workaround to disable layer view processing if layer view is not active. self._layer_view_active = False Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged) self._onActiveViewChanged() self._stored_layer_data = [] # When there are current settings and machine instance is changed, there is no profile changed event. We should # pretend there is though. Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onActiveProfileChanged) self._profile = None Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onActiveProfileChanged) self._onActiveProfileChanged() self._change_timer = QTimer() self._change_timer.setInterval(500) self._change_timer.setSingleShot(True) self._change_timer.timeout.connect(self.slice) self._message_handlers["cura.proto.Layer"] = self._onLayerMessage self._message_handlers["cura.proto.Progress"] = self._onProgressMessage self._message_handlers["cura.proto.GCodeLayer"] = self._onGCodeLayerMessage self._message_handlers["cura.proto.GCodePrefix"] = self._onGCodePrefixMessage self._message_handlers["cura.proto.ObjectPrintTime"] = self._onObjectPrintTimeMessage self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage self._slicing = False self._start_slice_job = None self._restart = False self._enabled = True self._always_restart = True self._process_layers_job = None #The currently active job to process layers, or None if it is not processing layers. self._message = None self.backendQuit.connect(self._onBackendQuit) self.backendConnected.connect(self._onBackendConnected) Application.getInstance().getController().toolOperationStarted.connect(self._onToolOperationStarted) Application.getInstance().getController().toolOperationStopped.connect(self._onToolOperationStopped) Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onInstanceChanged)
def __init__(self): super().__init__() self._model = None self.setExposedProperties("SelectedObjectId","ContainerID") Preferences.getInstance().preferenceChanged.connect(self._onPreferenceChanged) Selection.selectionChanged.connect(self.propertyChanged) self._onPreferenceChanged("cura/active_mode")
def _onTimeout(self): self._saving = True # To prevent the save process from triggering another autosave. Logger.log("d", "Autosaving preferences, instances and profiles") Application.getInstance().saveSettings() Preferences.getInstance().writeToFile(Resources.getStoragePath(Resources.Preferences, Application.getInstance().getApplicationName() + ".cfg")) self._saving = False
def __init__(self): super().__init__() Preferences.getInstance().addPreference("view/show_overhang", True) self._enabled_shader = None self._disabled_shader = None self._extruders_model = cura.Settings.ExtrudersModel()
def __init__(self, **kwargs): plugin_path = "" if sys.platform == "win32": plugin_path = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "PyQt5", "plugins") Logger.log("i", "Adding QT5 plugin path: %s" % (plugin_path)) QCoreApplication.addLibraryPath(plugin_path) elif sys.platform == "darwin": plugin_path = os.path.join(Application.getInstallPrefix(), "Resources", "plugins") if plugin_path: Logger.log("i", "Adding QT5 plugin path: %s" % (plugin_path)) QCoreApplication.addLibraryPath(plugin_path) os.environ["QSG_RENDER_LOOP"] = "basic" super().__init__(sys.argv, **kwargs) self._main_qml = "main.qml" self._engine = None self._renderer = None self.setAttribute(Qt.AA_UseDesktopOpenGL) try: self._splash = QSplashScreen(QPixmap(Resources.getPath(Resources.ImagesLocation, self.getApplicationName() + ".png"))) except FileNotFoundError: self._splash = None else: self._splash.show() self.processEvents() signal.signal(signal.SIGINT, signal.SIG_DFL) # This is done here as a lot of plugins require a correct gl context. If you want to change the framework, # these checks need to be done in your <framework>Application.py class __init__(). i18n_catalog = i18nCatalog("uranium") self.showSplashMessage(i18n_catalog.i18nc("Splash screen message", "Loading plugins...")) self._loadPlugins() self._plugin_registry.checkRequiredPlugins(self.getRequiredPlugins()) self.showSplashMessage(i18n_catalog.i18nc("Splash screen message", "Loading machines...")) self.loadMachines() self.showSplashMessage(i18n_catalog.i18nc("Splash screen message", "Loading preferences...")) try: file = Resources.getPath(Resources.PreferencesLocation, self.getApplicationName() + ".cfg") Preferences.getInstance().readFromFile(file) except FileNotFoundError: pass self._translators = {} self.showSplashMessage(i18n_catalog.i18nc("Splash screen message", "Loading translations...")) self.loadQtTranslation("uranium_qt") self.loadQtTranslation(self.getApplicationName() + "_qt")
def __init__(self): super().__init__() self.addMenuItem(i18n_catalog.i18nc("@item:inmenu", "Check for Updates"), self.checkNewVersion) self._url = None Preferences.getInstance().addPreference("info/automatic_update_check", True) if Preferences.getInstance().getValue("info/automatic_update_check"): thread = Thread(target = self.checkNewVersion, args = (True,)) thread.daemon = True thread.start()
def _onGlobalContainerChanged(self): if self._global_container_stack: self._global_container_stack.containersChanged.disconnect(self._onInstanceContainersChanged) self._global_container_stack = Application.getInstance().getGlobalContainerStack() self.globalContainerChanged.emit() if self._global_container_stack: Preferences.getInstance().setValue("cura/active_machine", self._global_container_stack.getId()) self._global_container_stack.containersChanged.connect(self._onInstanceContainersChanged)
def saveVisibility(self): if not self._active_machine: Logger.log("w", "No active machine found when trying to save setting visibility") return visible_settings = self._active_machine.getMachineDefinition().getAllSettings(visible_only = True) visible_settings = map(lambda s: s.getKey(), visible_settings) preference = ",".join(visible_settings) Preferences.getInstance().setValue("machines/setting_visibility", preference)
def didAgree(self, user_choice): if user_choice: Logger.log("i", "User agreed to the user agreement") Preferences.getInstance().setValue("general/accepted_user_agreement", True) self._user_agreement_window.hide() else: Logger.log("i", "User did NOT agree to the user agreement") Preferences.getInstance().setValue("general/accepted_user_agreement", False) CuraApplication.getInstance().quit() CuraApplication.getInstance().setNeedToShowUserAgreement(False)
def __init__(self): super().__init__() Application.getInstance().getOutputDeviceManager().writeStarted.connect(self._onWriteStarted) 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"): self.send_slice_info_message = Message(catalog.i18nc("@info", "Cura automatically sends slice info. You can disable this in preferences"), lifetime = 0, dismissable = False) self.send_slice_info_message.addAction("Dismiss", catalog.i18nc("@action:button", "Dismiss"), None, "") self.send_slice_info_message.actionTriggered.connect(self.messageActionTriggered) self.send_slice_info_message.show()
def _updateJobName(self): if self._base_name == "": self._job_name = "" self.jobNameChanged.emit() return base_name = self._stripAccents(self._base_name) self._setAbbreviatedMachineName() if self._pre_sliced: self._job_name = catalog.i18nc("@label", "Pre-sliced file {0}", base_name) elif Preferences.getInstance().getValue("cura/jobname_prefix"): # Don't add abbreviation if it already has the exact same abbreviation. if base_name.startswith(self._abbr_machine + "_"): self._job_name = base_name else: self._job_name = self._abbr_machine + "_" + base_name else: self._job_name = base_name self.jobNameChanged.emit()
def test_setActivePreset(): preferences = Preferences() visibility_model = SettingVisibilityPresetsModel(preferences) visibility_model.activePresetChanged = MagicMock() # Ensure that we start off with basic (since we didn't change anyting just yet!) assert visibility_model.activePreset == "basic" # Everything should be the same. visibility_model.setActivePreset("basic") assert visibility_model.activePreset == "basic" assert visibility_model.activePresetChanged.emit.call_count == 0 # No events should be sent. # Change it to existing type (should work...) visibility_model.setActivePreset("advanced") assert visibility_model.activePreset == "advanced" assert visibility_model.activePresetChanged.emit.call_count == 1 # Change to unknown preset. Shouldn't do anything. visibility_model.setActivePreset("OMGZOMGNOPE") assert visibility_model.activePreset == "advanced" assert visibility_model.activePresetChanged.emit.call_count == 1
def _update(self, *args): nodes = [] filter_current_build_plate = Preferences.getInstance().getValue("view/filter_current_build_plate") active_build_plate_number = self._build_plate_number group_nr = 1 for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()): if not isinstance(node, SceneNode): continue if (not node.getMeshData() and not node.callDecoration("getLayerData")) and not node.callDecoration("isGroup"): continue if node.getParent() and node.getParent().callDecoration("isGroup"): continue # Grouped nodes don't need resetting as their parent (the group) is resetted) if not node.callDecoration("isSliceable") and not node.callDecoration("isGroup"): continue node_build_plate_number = node.callDecoration("getBuildPlateNumber") if filter_current_build_plate and node_build_plate_number != active_build_plate_number: continue if not node.callDecoration("isGroup"): name = node.getName() else: name = catalog.i18nc("@label", "Group #{group_nr}").format(group_nr = str(group_nr)) group_nr += 1 if hasattr(node, "isOutsideBuildArea"): is_outside_build_area = node.isOutsideBuildArea() else: is_outside_build_area = False nodes.append({ "name": name, "isSelected": Selection.isSelected(node), "isOutsideBuildArea": is_outside_build_area, "buildPlateNumber": node_build_plate_number, "node": node }) nodes = sorted(nodes, key=lambda n: n["name"]) self.setItems(nodes) self.itemsChanged.emit()
def initializeBeforePluginsAreLoaded(self): config_path = Resources.getConfigStoragePath() self._plugin_config_filename = os.path.join(os.path.abspath(config_path), "plugins.json") # Load the plugin info if exists if os.path.exists(self._plugin_config_filename): Logger.log("i", "Loading plugin configuration file '%s'", self._plugin_config_filename) with open(self._plugin_config_filename, "r", encoding = "utf-8") as f: data = json.load(f) self._disabled_plugins = data["disabled"] self._plugins_to_install = data["to_install"] self._plugins_to_remove = data["to_remove"] # Also load data from preferences, where the plugin info used to be saved preferences = Preferences.getInstance() disabled_plugins = preferences.getValue("general/disabled_plugins") disabled_plugins = disabled_plugins.split(",") if disabled_plugins else [] for plugin_id in disabled_plugins: if plugin_id not in self._disabled_plugins: self._disabled_plugins.append(plugin_id) plugins_to_remove = preferences.getValue("general/plugins_to_remove") plugins_to_remove = plugins_to_remove.split(",") if plugins_to_remove else [] for plugin_id in plugins_to_remove: if plugin_id not in self._plugins_to_remove: self._plugins_to_remove.append(plugin_id) # Remove plugins that need to be removed for plugin_id in self._plugins_to_remove: self._removePlugin(plugin_id) self._plugins_to_remove = [] if plugins_to_remove is not None: preferences.setValue("general/plugins_to_remove", "") self._savePluginData() # Install the plugins that need to be installed (overwrite existing) for plugin_id, plugin_info in self._plugins_to_install.items(): self._installPlugin(plugin_id, plugin_info["filename"]) self._plugins_to_install = dict() self._savePluginData()
def onSelectionChanged(self): if Selection.hasSelection(): if not self.getController().getActiveTool(): if self._previous_active_tool: self.getController().setActiveTool( self._previous_active_tool) self._previous_active_tool = None else: self.getController().setActiveTool("TranslateTool") if Preferences.getInstance().getValue("view/center_on_select"): self._camera_animation.setStart( self.getController().getTool("CameraTool").getOrigin()) self._camera_animation.setTarget( Selection.getSelectedObject(0).getWorldPosition()) self._camera_animation.start() else: if self.getController().getActiveTool(): self._previous_active_tool = self.getController( ).getActiveTool().getPluginId() self.getController().setActiveTool(None) else: self._previous_active_tool = None
def onSelectionChanged(self): if Selection.hasSelection(): if self.getController().getActiveTool(): # If the tool has been disabled by the new selection if not self.getController().getActiveTool().getEnabled(): # Default self.getController().setActiveTool("TranslateTool") else: if self._previous_active_tool: self.getController().setActiveTool(self._previous_active_tool) if not self.getController().getActiveTool().getEnabled(): self.getController().setActiveTool("TranslateTool") self._previous_active_tool = None else: # Default self.getController().setActiveTool("TranslateTool") if Preferences.getInstance().getValue("view/center_on_select"): self._center_after_select = True else: if self.getController().getActiveTool(): self._previous_active_tool = self.getController().getActiveTool().getPluginId() self.getController().setActiveTool(None)
def determineAutoSlicing(self): enable_timer = True if not Preferences.getInstance().getValue("general/auto_slice"): enable_timer = False for node in DepthFirstIterator(self._scene.getRoot()): if node.callDecoration("isBlockSlicing"): enable_timer = False self.backendStateChange.emit(BackendState.Disabled) gcode_list = node.callDecoration("getGCodeList") if gcode_list is not None: self._scene.gcode_dict[node.callDecoration("getBuildPlateNumber")] = gcode_list if self._use_timer == enable_timer: return self._use_timer if enable_timer: self.backendStateChange.emit(BackendState.NotStarted) self.enableTimer() return True else: self.disableTimer() return False
def reload(self): self._styles = None self._path = "" self._icons = {} self._images = {} Preferences.getInstance().addPreference("general/theme", Application.getInstance().default_theme) try: theme_path = Resources.getPath(Resources.Themes, Preferences.getInstance().getValue("general/theme")) self.load(theme_path) except FileNotFoundError: Logger.log("e", "Could not find theme file, resetting to the default theme.") # cannot the current theme, so go back to the default Preferences.getInstance().setValue("general/theme", Application.getInstance().default_theme) theme_path = Resources.getPath(Resources.Themes, Preferences.getInstance().getValue("general/theme")) self.load(theme_path)
def __init__(self, app_name): super().__init__() self._application_name = app_name self._machine_definitions = [] self._machine_instances = [] self._profiles = [] self._active_machine = None self._active_profile = None Preferences.getInstance().addPreference("machines/setting_visibility", "") Preferences.getInstance().addPreference("machines/active_instance", "") Preferences.getInstance().addPreference("machines/active_profile", "Normal Quality")
def __init__(self): super().__init__() if PrintModeManager._instance is not None: raise ValueError("Duplicate singleton creation") PrintModeManager._instance = self self._duplicated_nodes = [] self._scene = Application.getInstance().getController().getScene() #Settings which value needs to be handled when changing print_mode self._conflict_settings = { 'wall_extruder_nr', 'wall_0_extruder_nr', 'wall_x_extruder_nr', 'roofing_extruder_nr', 'top_bottom_extruder_nr', 'infill_extruder_nr', 'support_extruder_nr', 'support_infill_extruder_nr', 'support_extruder_nr_layer_0', 'support_interface_extruder_nr', 'support_roof_extruder_nr', 'support_bottom_extruder_nr', 'adhesion_extruder_nr', 'prime_tower_enable', 'ooze_shield_enabled', 'carve_multiple_volumes', 'retraction_max_count' } old_material_id = Preferences.getInstance().getValue( "cura/old_material") if Application.getInstance().getContainerRegistry().findContainers( id=old_material_id): self._old_material = Application.getInstance( ).getContainerRegistry().findContainers(id=old_material_id)[0] else: self._old_material = "" self._global_stack = None Application.getInstance().globalContainerStackChanged.connect( self._onGlobalStackChanged) self._onGlobalStackChanged() self.printModeChanged.connect(self._onPrintModeChanged) self._onPrintModeChanged()
def __init__(self): super().__init__() self._zero_conf = None self._zero_conf_browser = None # Because the model needs to be created in the same thread as the QMLEngine, we use a signal. self.addDeviceSignal.connect(self._onAddDevice) self.removeDeviceSignal.connect(self._onRemoveDevice) Application.getInstance().globalContainerStackChanged.connect(self.reCheckConnections) self._discovered_devices = {} self._network_manager = QNetworkAccessManager() self._network_manager.finished.connect(self._onNetworkRequestFinished) self._min_cluster_version = Version("4.0.0") self._api_version = "1" self._api_prefix = "/api/v" + self._api_version + "/" self._cluster_api_version = "1" self._cluster_api_prefix = "/cluster-api/v" + self._cluster_api_version + "/" # Get list of manual instances from preferences self._preferences = Preferences.getInstance() self._preferences.addPreference("um3networkprinting/manual_instances", "") # A comma-separated list of ip adresses or hostnames self._manual_instances = self._preferences.getValue("um3networkprinting/manual_instances").split(",") # The zero-conf service changed requests are handled in a separate thread, so we can re-schedule the requests # which fail to get detailed service info. # Any new or re-scheduled requests will be appended to the request queue, and the handling thread will pick # them up and process them. self._service_changed_request_queue = Queue() self._service_changed_request_event = Event() self._service_changed_request_thread = Thread(target=self._handleOnServiceChangedRequests, daemon=True) self._service_changed_request_thread.start()
def requestWrite(self, node, file_name=None, filter_by_machine=False): if self._writing: raise OutputDeviceError.DeviceBusyError() self.writeStarted.emit(self) mesh_writer = Application.getInstance().getMeshFileHandler( ).getWriterByMimeType("text/x-gcode") job = OctoprintUploadJob(mesh_writer, node) job.setFileName(file_name + ".gcode") job.progress.connect(self._onJobProgress) job.finished.connect(self._onWriteJobFinished) message = Message("Uploading {0} to {1}".format( job.getFileName(), Preferences.getInstance().getValue("octoprint/base_url")), 0, progress=-1) message.show() job._message = message self._writing = True job.start()
def _configurationNeedsUpdates(self): preferences = Preferences.getInstance() preferences.addPreference("blackbelt/setting_version", "0.0.0") # Get version information from plugin.json plugin_file_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "plugin.json") try: with open(plugin_file_path) as plugin_file: plugin_info = json.load(plugin_file) plugin_version = plugin_info["version"] except: Logger.log("w", "Could not determine BlackBelt plugin version") return False if Version(preferences.getValue( "blackbelt/setting_version")) < Version(plugin_version): Logger.log("d", "Setting BlackBelt version nr to %s" % plugin_version) preferences.setValue("blackbelt/setting_version", plugin_version) return True return False
def __init__(self): super().__init__() self._zero_conf = None self._browser = None self._instances = {} # Because the model needs to be created in the same thread as the QMLEngine, we use a signal. self.addInstanceSignal.connect(self.addInstance) self.removeInstanceSignal.connect(self.removeInstance) Application.getInstance().globalContainerStackChanged.connect(self.reCheckConnections) # Load custom instances from preferences self._preferences = Preferences.getInstance() self._preferences.addPreference("Repetier/manual_instances", "{}") try: self._manual_instances = json.loads(self._preferences.getValue("Repetier/manual_instances")) except ValueError: self._manual_instances = {} if not isinstance(self._manual_instances, dict): self._manual_instances = {} self._name_regex = re.compile("Repetier instance (\".*\"\.|on )(.*)\.")
def test_refreshAccessTokenFailed(): """ Test if the authentication is reset once the refresh token fails to refresh access. """ authorization_service = AuthorizationService(OAUTH_SETTINGS, Preferences()) authorization_service.initialize() def mock_refresh(self, refresh_token, callback): # Refreshing gives a valid token. callback(FAILED_AUTH_RESPONSE) mock_reply = Mock( ) # The response that the request should give, containing an error about it failing to authenticate. mock_reply.error = Mock( return_value=QNetworkReply.NetworkError.AuthenticationRequiredError ) # The reply is 403: Authentication required, meaning the server responded with a "Can't do that, Dave". http_mock = Mock() http_mock.get = lambda url, headers_dict, callback, error_callback: callback( mock_reply) http_mock.post = lambda url, data, headers_dict, callback, error_callback: callback( mock_reply) with patch( "UM.TaskManagement.HttpRequestManager.HttpRequestManager.readJSON", Mock( return_value={"error_description": "Mock a failed request!"})): with patch( "UM.TaskManagement.HttpRequestManager.HttpRequestManager.getInstance", MagicMock(return_value=http_mock)): authorization_service._storeAuthData(SUCCESSFUL_AUTH_RESPONSE) authorization_service.onAuthStateChanged.emit = MagicMock() with patch( "cura.OAuth2.AuthorizationHelpers.AuthorizationHelpers.getAccessTokenUsingRefreshToken", mock_refresh): authorization_service.refreshAccessToken() assert authorization_service.onAuthStateChanged.emit.called_with( False)
def __init__(self): super().__init__() self._zero_conf = None self._browser = None self._printers = {} self._cluster_printers_seen = { } # do not forget a cluster printer when we have seen one, to not 'downgrade' from Connect to legacy printer self._api_version = "1" self._api_prefix = "/api/v" + self._api_version + "/" self._cluster_api_version = "1" self._cluster_api_prefix = "/cluster-api/v" + self._cluster_api_version + "/" self._network_manager = QNetworkAccessManager() self._network_manager.finished.connect(self._onNetworkRequestFinished) # List of old printer names. This is used to ensure that a refresh of zeroconf does not needlessly forces # authentication requests. self._old_printers = [] # Because the model needs to be created in the same thread as the QMLEngine, we use a signal. self.addPrinterSignal.connect(self.addPrinter) self.removePrinterSignal.connect(self.removePrinter) Application.getInstance().globalContainerStackChanged.connect( self.reCheckConnections) # Get list of manual printers from preferences self._preferences = Preferences.getInstance() self._preferences.addPreference( "um3networkprinting/manual_instances", "") # A comma-separated list of ip adresses or hostnames self._manual_instances = self._preferences.getValue( "um3networkprinting/manual_instances").split(",") self._network_requests_buffer = { } # store api responses until data is complete
def test_preferenceChanged(): preferences = Preferences() # Set the visible_settings to something silly preferences.addPreference("general/visible_settings", "omgzomg") visibility_model = SettingVisibilityPresetsModel(preferences) visibility_model.activePresetChanged = MagicMock() assert visibility_model.activePreset == "custom" # This should make the model start at "custom assert visibility_model.activePresetChanged.emit.call_count == 0 basic_visibility = visibility_model.getVisibilityPresetById("basic") new_visibility_string = ";".join(basic_visibility.settings) preferences.setValue("general/visible_settings", new_visibility_string) # Fake a signal emit (since we didn't create the application, our own signals are not fired) visibility_model._onPreferencesChanged("general/visible_settings") # Set the visibility settings to basic assert visibility_model.activePreset == "basic" assert visibility_model.activePresetChanged.emit.call_count == 1
def __init__(self, engine, parent=None): super().__init__(parent) self._engine = engine self._styles = None self._path = "" self._icons = {} self._images = {} # Workaround for incorrect default font on Windows if sys.platform == "win32": default_font = QFont() default_font.setPointSize(9) QCoreApplication.instance().setFont(default_font) self._em_height = int( QFontMetrics(QCoreApplication.instance().font()).ascent()) self._em_width = self._em_height self._initializeDefaults() Preferences.getInstance().addPreference( "general/theme", Application.getInstance().default_theme) try: theme_path = Resources.getPath( Resources.Themes, Preferences.getInstance().getValue("general/theme")) self.load(theme_path) except FileNotFoundError: Logger.log( "e", "Could not find theme file, resetting to the default theme.") # cannot the current theme, so go back to the default Preferences.getInstance().setValue( "general/theme", Application.getInstance().default_theme) theme_path = Resources.getPath( Resources.Themes, Preferences.getInstance().getValue("general/theme")) self.load(theme_path)
def __init__(self, app_name): super().__init__() self._application_name = app_name self._machine_definitions = [] self._machine_instances = [] self._profiles = [] self._active_machine = None self._active_profile = None self._profile_readers = {} #Plugins that read profiles from file. self._profile_writers = {} #Plugins that write profiles to file. PluginRegistry.addType("profile_reader", self.addProfileReader) PluginRegistry.addType("profile_writer", self.addProfileWriter) Preferences.getInstance().addPreference("machines/setting_visibility", "") Preferences.getInstance().addPreference("machines/active_instance", "") Preferences.getInstance().addPreference("machines/active_profile", "Normal Quality")
def _onChangeTimerFinished(self): if not self._enabled: return root = self._controller.getScene().getRoot() # Keep a list of nodes that are moving. We use this so that we don't move two intersecting objects in the # same direction. transformed_nodes = [] # We try to shuffle all the nodes to prevent "locked" situations, where iteration B inverts iteration A. # By shuffling the order of the nodes, this might happen a few times, but at some point it will resolve. nodes = list(BreadthFirstIterator(root)) # Only check nodes inside build area. nodes = [ node for node in nodes if (hasattr(node, "_outside_buildarea") and not node._outside_buildarea) ] random.shuffle(nodes) for node in nodes: if node is root or not isinstance(node, SceneNode) or isinstance( node, DuplicatedNode) or node.getBoundingBox() is None: continue bbox = node.getBoundingBox() # Move it downwards if bottom is above platform move_vector = Vector() if Preferences.getInstance().getValue( "physics/automatic_drop_down") and not ( node.getParent() and node.getParent().callDecoration( "isGroup")) and node.isEnabled( ): #If an object is grouped, don't move it down z_offset = node.callDecoration( "getZOffset") if node.getDecorator( ZOffsetDecorator.ZOffsetDecorator) else 0 move_vector = move_vector.set(y=-bbox.bottom + z_offset) # If there is no convex hull for the node, start calculating it and continue. if not node.getDecorator(ConvexHullDecorator): node.addDecorator(ConvexHullDecorator()) # only push away objects if this node is a printing mesh if not node.callDecoration( "isNonPrintingMesh") and Preferences.getInstance( ).getValue("physics/automatic_push_free" ) and type(node) != DuplicatedNode: # Check for collisions between convex hulls for other_node in BreadthFirstIterator(root): # Ignore root, ourselves and anything that is not a normal SceneNode. if other_node is root or not issubclass( type(other_node), SceneNode ) or other_node is node or other_node.callDecoration( "getBuildPlateNumber") != node.callDecoration( "getBuildPlateNumber"): continue # Ignore collisions of a group with it's own children if other_node in node.getAllChildren( ) or node in other_node.getAllChildren(): continue # Ignore collisions within a group if other_node.getParent() and node.getParent() and ( other_node.getParent().callDecoration("isGroup") is not None or node.getParent().callDecoration("isGroup") is not None): continue # Ignore nodes that do not have the right properties set. if not other_node.callDecoration( "getConvexHull") or not other_node.getBoundingBox( ): continue 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. while overlap and current_overlap_checks < self._max_overlap_checks: current_overlap_checks += 1 head_hull = node.callDecoration("getConvexHullHead") if head_hull: # One at a time intersection. overlap = head_hull.translate( move_vector.x, move_vector.z).intersectsPolygon( other_node.callDecoration("getConvexHull")) if not overlap: other_head_hull = other_node.callDecoration( "getConvexHullHead") if other_head_hull: overlap = node.callDecoration( "getConvexHull").translate( move_vector.x, move_vector.z).intersectsPolygon( other_head_hull) if overlap: # Moving ensured that overlap was still there. Try anew! move_vector = move_vector.set( x=move_vector.x + overlap[0] * self._move_factor, z=move_vector.z + overlap[1] * self._move_factor) else: # Moving ensured that overlap was still there. Try anew! move_vector = move_vector.set( x=move_vector.x + overlap[0] * self._move_factor, z=move_vector.z + overlap[1] * self._move_factor) else: own_convex_hull = node.callDecoration( "getConvexHull") other_convex_hull = other_node.callDecoration( "getConvexHull") if own_convex_hull and other_convex_hull: overlap = own_convex_hull.translate( move_vector.x, move_vector.z).intersectsPolygon( other_convex_hull) if overlap: # Moving ensured that overlap was still there. Try anew! temp_move_vector = move_vector.set( x=move_vector.x + overlap[0] * self._move_factor, z=move_vector.z + overlap[1] * self._move_factor) # if the distance between two models less than 2mm then try to find a new factor if abs(temp_move_vector.x - overlap[0] ) < self._minimum_gap and abs( temp_move_vector.y - overlap[1]) < self._minimum_gap: temp_x_factor = ( abs(overlap[0]) + self._minimum_gap ) / overlap[0] if overlap[ 0] != 0 else 0 # find x move_factor, like (3.4 + 2) / 3.4 = 1.58 temp_y_factor = ( abs(overlap[1]) + self._minimum_gap ) / overlap[1] if overlap[ 1] != 0 else 0 # find y move_factor temp_scale_factor = temp_x_factor if abs( temp_x_factor) > abs( temp_y_factor ) else temp_y_factor move_vector = move_vector.set( x=move_vector.x + overlap[0] * temp_scale_factor, z=move_vector.z + overlap[1] * temp_scale_factor) else: move_vector = temp_move_vector else: # This can happen in some cases if the object is not yet done with being loaded. # Simply waiting for the next tick seems to resolve this correctly. overlap = None if not Vector.Null.equals(move_vector, epsilon=1e-5): transformed_nodes.append(node) op = PlatformPhysicsOperation.PlatformPhysicsOperation( node, move_vector) op.push() # After moving, we have to evaluate the boundary checks for nodes build_volume = Application.getInstance().getBuildVolume() build_volume.updateNodeBoundaryCheck()
def __init__(self): super().__init__() self._max_layers = 0 self._current_layer_num = 0 self._minimum_layer_num = 0 self._current_layer_mesh = None self._current_layer_jumps = None self._top_layers_job = None self._activity = False self._old_max_layers = 0 self._max_paths = 0 self._current_path_num = 0 self._minimum_path_num = 0 self.currentLayerNumChanged.connect(self._onCurrentLayerNumChanged) self._busy = False self._simulation_running = False self._ghost_shader = None self._layer_pass = None self._composite_pass = None self._old_layer_bindings = None self._simulationview_composite_shader = None self._old_composite_shader = None self._global_container_stack = None self._proxy = SimulationViewProxy() self._controller.getScene().getRoot().childrenChanged.connect(self._onSceneChanged) self._resetSettings() self._legend_items = None self._show_travel_moves = False self._nozzle_node = None Preferences.getInstance().addPreference("view/top_layer_count", 5) Preferences.getInstance().addPreference("view/only_show_top_layers", False) Preferences.getInstance().addPreference("view/force_layer_view_compatibility_mode", False) Preferences.getInstance().addPreference("layerview/layer_view_type", 0) Preferences.getInstance().addPreference("layerview/extruder_opacities", "") Preferences.getInstance().addPreference("layerview/show_travel_moves", False) Preferences.getInstance().addPreference("layerview/show_helpers", True) Preferences.getInstance().addPreference("layerview/show_skin", True) Preferences.getInstance().addPreference("layerview/show_infill", True) Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged) self._updateWithPreferences() self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count")) self._only_show_top_layers = bool(Preferences.getInstance().getValue("view/only_show_top_layers")) self._compatibility_mode = True # for safety self._wireprint_warning_message = Message(catalog.i18nc("@info:status", "Cura does not accurately display layers when Wire Printing is enabled"), title = catalog.i18nc("@info:title", "Simulation View"))
def requestWrite(self, nodes, file_name=None, limit_mimetypes=None, file_handler=None): if self._writing: raise OutputDeviceError.DeviceBusyError() dialog = QFileDialog() dialog.setWindowTitle(catalog.i18nc("@title:window", "Save to File")) dialog.setFileMode(QFileDialog.AnyFile) dialog.setAcceptMode(QFileDialog.AcceptSave) # Ensure platform never ask for overwrite confirmation since we do this ourselves dialog.setOption(QFileDialog.DontConfirmOverwrite) if sys.platform == "linux" and "KDE_FULL_SESSION" in os.environ: dialog.setOption(QFileDialog.DontUseNativeDialog) filters = [] mime_types = [] selected_filter = None last_used_type = Preferences.getInstance().getValue( "local_file/last_used_type") if not file_handler: file_handler = Application.getInstance().getMeshFileHandler() file_types = file_handler.getSupportedFileTypesWrite() file_types.sort(key=lambda k: k["description"]) if limit_mimetypes: file_types = list( filter(lambda i: i["mime_type"] in limit_mimetypes, file_types)) if len(file_types) == 0: Logger.log("e", "There are no file types available to write with!") raise OutputDeviceError.WriteRequestFailedError() for item in file_types: type_filter = "{0} (*.{1})".format(item["description"], item["extension"]) filters.append(type_filter) mime_types.append(item["mime_type"]) if last_used_type == item["mime_type"]: selected_filter = type_filter if file_name: file_name += "." + item["extension"] dialog.setNameFilters(filters) if selected_filter is not None: dialog.selectNameFilter(selected_filter) if file_name is not None: dialog.selectFile(file_name) stored_directory = Preferences.getInstance().getValue( "local_file/dialog_save_path") dialog.setDirectory(stored_directory) if not dialog.exec_(): raise OutputDeviceError.UserCanceledError() save_path = dialog.directory().absolutePath() Preferences.getInstance().setValue("local_file/dialog_save_path", save_path) selected_type = file_types[filters.index(dialog.selectedNameFilter())] Preferences.getInstance().setValue("local_file/last_used_type", selected_type["mime_type"]) file_name = dialog.selectedFiles()[0] if os.path.exists(file_name): result = QMessageBox.question( None, catalog.i18nc("@title:window", "File Already Exists"), catalog.i18nc( "@label", "The file <filename>{0}</filename> already exists. Are you sure you want to overwrite it?" ).format(file_name)) if result == QMessageBox.No: raise OutputDeviceError.UserCanceledError() self.writeStarted.emit(self) if file_handler: file_writer = file_handler.getWriter(selected_type["id"]) else: file_writer = Application.getInstance().getMeshFileHandler( ).getWriter(selected_type["id"]) try: mode = selected_type["mode"] if mode == MeshWriter.OutputMode.TextMode: Logger.log("d", "Writing to Local File %s in text mode", file_name) stream = open(file_name, "wt", encoding="utf-8") elif mode == MeshWriter.OutputMode.BinaryMode: Logger.log("d", "Writing to Local File %s in binary mode", file_name) stream = open(file_name, "wb") job = WriteFileJob(file_writer, stream, nodes, mode) job.setFileName(file_name) job.progress.connect(self._onJobProgress) job.finished.connect(self._onWriteJobFinished) message = Message( catalog.i18nc( "@info:progress", "Saving to <filename>{0}</filename>").format(file_name), 0, False, -1) message.show() job._message = message self._writing = True job.start() except PermissionError as e: Logger.log("e", "Permission denied when trying to write to %s: %s", file_name, str(e)) raise OutputDeviceError.PermissionDeniedError( catalog.i18nc( "@info:status", "Permission denied when trying to save <filename>{0}</filename>" ).format(file_name)) from e except OSError as e: Logger.log("e", "Operating system would not let us write to %s: %s", file_name, str(e)) raise OutputDeviceError.WriteRequestFailedError( catalog.i18nc( "@info:status", "Could not save to <filename>{0}</filename>: <message>{1}</message>" ).format()) from e
class Application: ## Init method # # \param name \type{string} The name of the application. # \param version \type{string} Version, formatted as major.minor.rev # \param build_type Additional version info on the type of build this is, such as "master". # \param is_debug_mode Whether to run in debug mode. def __init__(self, name: str, version: str, api_version: str, app_display_name: str = "", build_type: str = "", is_debug_mode: bool = False, **kwargs) -> None: if Application.__instance is not None: raise RuntimeError("Try to create singleton '%s' more than once" % self.__class__.__name__) Application.__instance = self super().__init__() # Call super to make multiple inheritance work. self._api_version = Version(api_version) # type: Version self._app_name = name #type: str self._app_display_name = app_display_name if app_display_name else name # type: str self._version = version #type: str self._build_type = build_type #type: str self._is_debug_mode = is_debug_mode #type: bool self._is_headless = False #type: bool self._use_external_backend = False #type: bool self._config_lock_filename = "{name}.lock".format( name=self._app_name) # type: str self._cli_args = None #type: argparse.Namespace self._cli_parser = argparse.ArgumentParser( prog=self._app_name, add_help=False) #type: argparse.ArgumentParser self._main_thread = threading.current_thread() #type: threading.Thread self.default_theme = self._app_name #type: str # Default theme is the application name self._default_language = "en_US" #type: str self.change_log_url = "https://github.com/Ultimaker/Uranium" # Where to find a more detailed description of the recent updates. self._preferences_filename = None #type: str self._preferences = None #type: Preferences self._extensions = [] #type: List[Extension] self._required_plugins = [] #type: List[str] self._package_manager_class = PackageManager # type: type self._package_manager = None # type: PackageManager self._plugin_registry = None #type: PluginRegistry self._container_registry_class = ContainerRegistry #type: type self._container_registry = None #type: ContainerRegistry self._global_container_stack = None #type: ContainerStack self._controller = None #type: Controller self._backend = None #type: Backend self._output_device_manager = None #type: OutputDeviceManager self._operation_stack = None #type: OperationStack self._visible_messages = [] #type: List[Message] self._message_lock = threading.Lock() #type: threading.Lock self._app_install_dir = self.getInstallPrefix() #type: str def getAPIVersion(self) -> "Version": return self._api_version # Adds the command line options that can be parsed by the command line parser. # Can be overridden to add additional command line options to the parser. def addCommandLineOptions(self) -> None: self._cli_parser.add_argument("--version", action="version", version="%(prog)s version: {0}".format( self._version)) self._cli_parser.add_argument( "--external-backend", action="store_true", default=False, help= "Use an externally started backend instead of starting it automatically. This is a debug feature to make it possible to run the engine with debug options enabled." ) self._cli_parser.add_argument('--headless', action='store_true', default=False, help="Hides all GUI elements.") self._cli_parser.add_argument( "--debug", action="store_true", default=False, help="Turn on the debug mode by setting this option.") def parseCliOptions(self) -> None: self._cli_args = self._cli_parser.parse_args() self._is_headless = self._cli_args.headless self._is_debug_mode = self._cli_args.debug or self._is_debug_mode self._use_external_backend = self._cli_args.external_backend # Performs initialization that must be done before start. def initialize(self) -> None: # For Ubuntu Unity this makes Qt use its own menu bar rather than pass it on to Unity. os.putenv("UBUNTU_MENUPROXY", "0") # Custom signal handling Signal._app = self Signal._signalQueue = self # Initialize Resources. Set the application name and version here because we can only know the actual info # after the __init__() has been called. Resources.ApplicationIdentifier = self._app_name Resources.ApplicationVersion = self._version Resources.addSearchPath( os.path.join(os.path.dirname(sys.executable), "resources")) Resources.addSearchPath( os.path.join(self._app_install_dir, "share", "uranium", "resources")) Resources.addSearchPath( os.path.join(self._app_install_dir, "Resources", "uranium", "resources")) Resources.addSearchPath( os.path.join(self._app_install_dir, "Resources", self._app_name, "resources")) if not hasattr(sys, "frozen"): Resources.addSearchPath( os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources")) i18nCatalog.setApplication(self) PluginRegistry.addType("backend", self.setBackend) PluginRegistry.addType("logger", Logger.addLogger) PluginRegistry.addType("extension", self.addExtension) self._preferences = Preferences() self._preferences.addPreference("general/language", self._default_language) self._preferences.addPreference("general/visible_settings", "") self._preferences.addPreference("general/plugins_to_remove", "") self._preferences.addPreference("general/disabled_plugins", "") self._controller = Controller(self) self._output_device_manager = OutputDeviceManager() self._operation_stack = OperationStack( self._controller) # type: OperationStack self._plugin_registry = PluginRegistry(self) #type: PluginRegistry self._plugin_registry.addPluginLocation( os.path.join(self._app_install_dir, "lib", "uranium")) self._plugin_registry.addPluginLocation( os.path.join(self._app_install_dir, "lib64", "uranium")) self._plugin_registry.addPluginLocation( os.path.join(self._app_install_dir, "lib32", "uranium")) self._plugin_registry.addPluginLocation( os.path.join(os.path.dirname(sys.executable), "plugins")) self._plugin_registry.addPluginLocation( os.path.join(self._app_install_dir, "Resources", "uranium", "plugins")) self._plugin_registry.addPluginLocation( os.path.join(self._app_install_dir, "Resources", self._app_name, "plugins")) # Locally installed plugins local_path = os.path.join( Resources.getStoragePath(Resources.Resources), "plugins") # Ensure the local plugins directory exists try: os.makedirs(local_path) except OSError: pass self._plugin_registry.addPluginLocation(local_path) if not hasattr(sys, "frozen"): self._plugin_registry.addPluginLocation( os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "plugins")) self._container_registry = self._container_registry_class(self) UM.Settings.InstanceContainer.setContainerRegistry( self._container_registry) UM.Settings.ContainerStack.setContainerRegistry( self._container_registry) # Initialize the package manager to remove and install scheduled packages. self._package_manager = self._package_manager_class(self) self.showMessageSignal.connect(self.showMessage) self.hideMessageSignal.connect(self.hideMessage) def startSplashWindowPhase(self) -> None: pass def startPostSplashWindowPhase(self) -> None: pass ## Run the main event loop. # This method should be re-implemented by subclasses to start the main event loop. # \exception NotImplementedError def run(self): self.addCommandLineOptions() self.parseCliOptions() self.initialize() self.startSplashWindowPhase() self.startPostSplashWindowPhase() def getContainerRegistry(self): return self._container_registry ## Get the lock filename def getApplicationLockFilename(self) -> str: return self._config_lock_filename ## Emitted when the application window was closed and we need to shut down the application applicationShuttingDown = Signal() showMessageSignal = Signal() hideMessageSignal = Signal() globalContainerStackChanged = Signal() workspaceLoaded = Signal() def setGlobalContainerStack(self, stack: "ContainerStack") -> None: if self._global_container_stack != stack: self._global_container_stack = stack self.globalContainerStackChanged.emit() def getGlobalContainerStack(self) -> Optional["ContainerStack"]: return self._global_container_stack def hideMessage(self, message: Message) -> None: raise NotImplementedError def showMessage(self, message: Message) -> None: raise NotImplementedError def showToastMessage(self, title: str, message: str) -> None: raise NotImplementedError ## Get the version of the application def getVersion(self) -> str: return self._version ## Get the build type of the application def getBuildType(self) -> str: return self._build_type def getIsDebugMode(self) -> bool: return self._is_debug_mode def getIsHeadLess(self) -> bool: return self._is_headless def getUseExternalBackend(self) -> bool: return self._use_external_backend visibleMessageAdded = Signal() ## Hide message by ID (as provided by built-in id function) def hideMessageById(self, message_id: int) -> None: # If a user and the application tries to close same message dialog simultaneously, message_id could become an empty # string, and then the application will raise an error when trying to do "int(message_id)". # So we check the message_id here. if not message_id: return found_message = None with self._message_lock: for message in self._visible_messages: if id(message) == int(message_id): found_message = message if found_message is not None: self.hideMessageSignal.emit(found_message) visibleMessageRemoved = Signal() ## Get list of all visible messages def getVisibleMessages(self) -> List[Message]: with self._message_lock: return self._visible_messages ## Function that needs to be overridden by child classes with a list of plugins it needs. def _loadPlugins(self) -> None: pass ## Get name of the application. # \returns app_name \type{string} def getApplicationName(self) -> str: return self._app_name def getApplicationDisplayName(self) -> str: return self._app_display_name ## Get the preferences. # \return preferences \type{Preferences} def getPreferences(self) -> Preferences: return self._preferences def savePreferences(self) -> None: if self._preferences_filename: self._preferences.writeToFile(self._preferences_filename) else: Logger.log("i", "Preferences filename not set. Unable to save file.") ## Get the currently used IETF language tag. # The returned tag is during runtime used to translate strings. # \returns Language tag. def getApplicationLanguage(self) -> str: language = os.getenv("URANIUM_LANGUAGE") if not language: language = self._preferences.getValue("general/language") if not language: language = os.getenv("LANGUAGE") if not language: language = self._default_language return language ## Application has a list of plugins that it *must* have. If it does not have these, it cannot function. # These plugins can not be disabled in any way. def getRequiredPlugins(self) -> List[str]: return self._required_plugins ## Set the plugins that the application *must* have in order to function. # \param plugin_names \type{list} List of strings with the names of the required plugins def setRequiredPlugins(self, plugin_names: List[str]) -> None: self._required_plugins = plugin_names ## Set the backend of the application (the program that does the heavy lifting). def setBackend(self, backend: "Backend") -> None: self._backend = backend ## Get the backend of the application (the program that does the heavy lifting). # \returns Backend \type{Backend} def getBackend(self) -> "Backend": return self._backend ## Get the PluginRegistry of this application. # \returns PluginRegistry \type{PluginRegistry} def getPluginRegistry(self) -> PluginRegistry: return self._plugin_registry ## Get the Controller of this application. # \returns Controller \type{Controller} def getController(self) -> Controller: return self._controller def getOperationStack(self) -> OperationStack: return self._operation_stack def getOutputDeviceManager(self) -> OutputDeviceManager: return self._output_device_manager ## Return an application-specific Renderer object. # \exception NotImplementedError def getRenderer(self) -> Renderer: raise NotImplementedError( "getRenderer must be implemented by subclasses.") ## Post a function event onto the event loop. # # This takes a CallFunctionEvent object and puts it into the actual event loop. # \exception NotImplementedError def functionEvent(self, event: CallFunctionEvent) -> None: raise NotImplementedError( "functionEvent must be implemented by subclasses.") ## Call a function the next time the event loop runs. # # You can't get the result of this function directly. It won't block. # \param function The function to call. # \param args The positional arguments to pass to the function. # \param kwargs The keyword arguments to pass to the function. def callLater(self, func: Callable[..., Any], *args, **kwargs) -> None: event = CallFunctionEvent(func, args, kwargs) self.functionEvent(event) ## Get the application's main thread. def getMainThread(self) -> threading.Thread: return self._main_thread def addExtension(self, extension: "Extension") -> None: self._extensions.append(extension) def getExtensions(self) -> List["Extension"]: return self._extensions @staticmethod def getInstallPrefix() -> str: if "python" in os.path.basename(sys.executable): return os.path.abspath( os.path.join(os.path.dirname(sys.argv[0]), "..")) else: return os.path.abspath( os.path.join(os.path.dirname(sys.executable), "..")) __instance = None # type: Application @classmethod def getInstance(cls, *args, **kwargs) -> "Application": return cls.__instance
def initialize(self) -> None: # For Ubuntu Unity this makes Qt use its own menu bar rather than pass it on to Unity. os.putenv("UBUNTU_MENUPROXY", "0") # Custom signal handling Signal._app = self Signal._signalQueue = self # Initialize Resources. Set the application name and version here because we can only know the actual info # after the __init__() has been called. Resources.ApplicationIdentifier = self._app_name Resources.ApplicationVersion = self._version Resources.addSearchPath( os.path.join(os.path.dirname(sys.executable), "resources")) Resources.addSearchPath( os.path.join(self._app_install_dir, "share", "uranium", "resources")) Resources.addSearchPath( os.path.join(self._app_install_dir, "Resources", "uranium", "resources")) Resources.addSearchPath( os.path.join(self._app_install_dir, "Resources", self._app_name, "resources")) if not hasattr(sys, "frozen"): Resources.addSearchPath( os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources")) i18nCatalog.setApplication(self) PluginRegistry.addType("backend", self.setBackend) PluginRegistry.addType("logger", Logger.addLogger) PluginRegistry.addType("extension", self.addExtension) self._preferences = Preferences() self._preferences.addPreference("general/language", self._default_language) self._preferences.addPreference("general/visible_settings", "") self._preferences.addPreference("general/plugins_to_remove", "") self._preferences.addPreference("general/disabled_plugins", "") self._controller = Controller(self) self._output_device_manager = OutputDeviceManager() self._operation_stack = OperationStack( self._controller) # type: OperationStack self._plugin_registry = PluginRegistry(self) #type: PluginRegistry self._plugin_registry.addPluginLocation( os.path.join(self._app_install_dir, "lib", "uranium")) self._plugin_registry.addPluginLocation( os.path.join(self._app_install_dir, "lib64", "uranium")) self._plugin_registry.addPluginLocation( os.path.join(self._app_install_dir, "lib32", "uranium")) self._plugin_registry.addPluginLocation( os.path.join(os.path.dirname(sys.executable), "plugins")) self._plugin_registry.addPluginLocation( os.path.join(self._app_install_dir, "Resources", "uranium", "plugins")) self._plugin_registry.addPluginLocation( os.path.join(self._app_install_dir, "Resources", self._app_name, "plugins")) # Locally installed plugins local_path = os.path.join( Resources.getStoragePath(Resources.Resources), "plugins") # Ensure the local plugins directory exists try: os.makedirs(local_path) except OSError: pass self._plugin_registry.addPluginLocation(local_path) if not hasattr(sys, "frozen"): self._plugin_registry.addPluginLocation( os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "plugins")) self._container_registry = self._container_registry_class(self) UM.Settings.InstanceContainer.setContainerRegistry( self._container_registry) UM.Settings.ContainerStack.setContainerRegistry( self._container_registry) # Initialize the package manager to remove and install scheduled packages. self._package_manager = self._package_manager_class(self) self.showMessageSignal.connect(self.showMessage) self.hideMessageSignal.connect(self.hideMessage)
def _onChangeTimerFinished(self): if not self._enabled: return root = self._controller.getScene().getRoot() for node in BreadthFirstIterator(root): if node is root or type(node) is not SceneNode: continue bbox = node.getBoundingBox() if not bbox or not bbox.isValid(): self._change_timer.start() continue build_volume_bounding_box = copy.deepcopy( self._build_volume.getBoundingBox()) build_volume_bounding_box.setBottom( -9001) # Ignore intersections with the bottom node._outside_buildarea = False # Mark the node as outside the build volume if the bounding box test fails. if build_volume_bounding_box.intersectsBox( bbox ) != AxisAlignedBox.IntersectionResult.FullIntersection: node._outside_buildarea = True # Move it downwards if bottom is above platform move_vector = Vector() if not (node.getParent() and node.getParent().callDecoration("isGroup") ): #If an object is grouped, don't move it down z_offset = node.callDecoration( "getZOffset") if node.getDecorator( ZOffsetDecorator.ZOffsetDecorator) else 0 if bbox.bottom > 0: move_vector.setY(-bbox.bottom + z_offset) elif bbox.bottom < z_offset: move_vector.setY((-bbox.bottom) - z_offset) #if not Float.fuzzyCompare(bbox.bottom, 0.0): # pass#move_vector.setY(-bbox.bottom) # If there is no convex hull for the node, start calculating it and continue. if not node.getDecorator(ConvexHullDecorator): node.addDecorator(ConvexHullDecorator()) if not node.callDecoration("getConvexHull"): if not node.callDecoration("getConvexHullJob"): job = ConvexHullJob.ConvexHullJob(node) job.start() node.callDecoration("setConvexHullJob", job) elif 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. if other_node is root or type( other_node) is not SceneNode or other_node is node: continue # Ignore colissions of a group with it's own children if other_node in node.getAllChildren( ) or node in other_node.getAllChildren(): continue # Ignore colissions within a group if other_node.getParent().callDecoration( "isGroup") is not None or node.getParent( ).callDecoration("isGroup") is not None: continue #if node.getParent().callDecoration("isGroup") is other_node.getParent().callDecoration("isGroup"): # continue # Ignore nodes that do not have the right properties set. if not other_node.callDecoration( "getConvexHull") or not other_node.getBoundingBox( ): continue # Check to see if the bounding boxes intersect. If not, we can ignore the node as there is no way the hull intersects. #if node.getBoundingBox().intersectsBox(other_node.getBoundingBox()) == AxisAlignedBox.IntersectionResult.NoIntersection: # continue # Get the overlap distance for both convex hulls. If this returns None, there is no intersection. try: head_hull = node.callDecoration("getConvexHullHead") if head_hull: overlap = head_hull.intersectsPolygon( other_node.callDecoration("getConvexHullHead")) if not overlap: other_head_hull = other_node.callDecoration( "getConvexHullHead") if other_head_hull: overlap = node.callDecoration( "getConvexHullHead").intersectsPolygon( other_head_hull) else: overlap = node.callDecoration( "getConvexHull").intersectsPolygon( other_node.callDecoration("getConvexHull")) except: overlap = None #It can sometimes occur that the caclulated convex hull has no size, in which case there is no overlap. if overlap is None: continue move_vector.setX(overlap[0] * 1.1) move_vector.setZ(overlap[1] * 1.1) convex_hull = node.callDecoration("getConvexHull") if convex_hull: if not convex_hull.isValid(): return # Check for collisions between disallowed areas and the object for area in self._build_volume.getDisallowedAreas(): overlap = convex_hull.intersectsPolygon(area) if overlap is None: continue node._outside_buildarea = True if move_vector != Vector(): op = PlatformPhysicsOperation.PlatformPhysicsOperation( node, move_vector) op.push()
def write(self, stream, nodes, mode=WorkspaceWriter.OutputMode.BinaryMode): application = Application.getInstance() machine_manager = application.getMachineManager() mesh_writer = application.getMeshFileHandler().getWriter("3MFWriter") if not mesh_writer: # We need to have the 3mf mesh writer, otherwise we can't save the entire workspace return False # Indicate that the 3mf mesh writer should not close the archive just yet (we still need to add stuff to it). mesh_writer.setStoreArchive(True) mesh_writer.write(stream, nodes, mode) archive = mesh_writer.getArchive() if archive is None: # This happens if there was no mesh data to write. archive = zipfile.ZipFile(stream, "w", compression=zipfile.ZIP_DEFLATED) global_stack = machine_manager.activeMachine # Add global container stack data to the archive. self._writeContainerToArchive(global_stack, archive) # Also write all containers in the stack to the file for container in global_stack.getContainers(): self._writeContainerToArchive(container, archive) # Check if the machine has extruders and save all that data as well. for extruder_stack in global_stack.extruders.values(): self._writeContainerToArchive(extruder_stack, archive) for container in extruder_stack.getContainers(): self._writeContainerToArchive(container, archive) # Write preferences to archive original_preferences = Application.getInstance().getPreferences( ) #Copy only the preferences that we use to the workspace. temp_preferences = Preferences() for preference in { "general/visible_settings", "steslicer/active_mode", "steslicer/categories_expanded" }: temp_preferences.addPreference(preference, None) temp_preferences.setValue( preference, original_preferences.getValue(preference)) preferences_string = StringIO() temp_preferences.writeToFile(preferences_string) preferences_file = zipfile.ZipInfo("SteSlicer/preferences.cfg") archive.writestr(preferences_file, preferences_string.getvalue()) # Save STE Slicer version version_file = zipfile.ZipInfo("SteSlicer/version.ini") version_config_parser = configparser.ConfigParser(interpolation=None) version_config_parser.add_section("versions") version_config_parser.set("versions", "steslicer_version", application.getVersion()) version_config_parser.set("versions", "build_type", application.getBuildType()) version_config_parser.set("versions", "is_debug_mode", str(application.getIsDebugMode())) version_file_string = StringIO() version_config_parser.write(version_file_string) archive.writestr(version_file, version_file_string.getvalue()) # Close the archive & reset states. archive.close() mesh_writer.setStoreArchive(False) return True
def __init__(self, name, version, **kwargs): if (Application._instance != None): raise ValueError("Duplicate singleton creation") # If the constructor is called and there is no instance, set the instance to self. # This is done because we can't make constructor private Application._instance = self self._application_name = name self._version = version os.putenv( "UBUNTU_MENUPROXY", "0" ) #For Ubuntu Unity this makes Qt use its own menu bar rather than pass it on to Unity. Signal._app = self Resources.ApplicationIdentifier = name i18nCatalog.setApplication(self) Resources.addSearchPath(os.path.dirname(sys.executable)) Resources.addSearchPath( os.path.join(Application.getInstallPrefix(), "share", "uranium")) Resources.addSearchPath( os.path.join(Application.getInstallPrefix(), "Resources", "uranium")) Resources.addSearchPath( os.path.join(Application.getInstallPrefix(), "Resources", self.getApplicationName())) if not hasattr(sys, "frozen"): Resources.addSearchPath( os.path.join(os.path.abspath(os.path.dirname(__file__)), "..")) self._main_thread = threading.current_thread() super().__init__( **kwargs) # Call super to make multiple inheritence work. self._renderer = None PluginRegistry.addType("backend", self.setBackend) PluginRegistry.addType("logger", Logger.addLogger) PluginRegistry.addType("extension", self.addExtension) preferences = Preferences.getInstance() preferences.addPreference("general/language", "en") try: preferences.readFromFile( Resources.getPath(Resources.Preferences, self._application_name + ".cfg")) except FileNotFoundError: pass self._controller = Controller(self) self._mesh_file_handler = MeshFileHandler() self._extensions = [] self._backend = None self._output_device_manager = OutputDeviceManager() self._machine_manager = MachineManager(self._application_name) self._required_plugins = [] self._operation_stack = OperationStack() self._plugin_registry = PluginRegistry.getInstance() self._plugin_registry.addPluginLocation( os.path.join(Application.getInstallPrefix(), "lib", "uranium")) self._plugin_registry.addPluginLocation( os.path.join(os.path.dirname(sys.executable), "plugins")) self._plugin_registry.addPluginLocation( os.path.join(Application.getInstallPrefix(), "Resources", "uranium", "plugins")) self._plugin_registry.addPluginLocation( os.path.join(Application.getInstallPrefix(), "Resources", self.getApplicationName(), "plugins")) # Locally installed plugins self._plugin_registry.addPluginLocation( os.path.join(Resources.getStoragePath(Resources.Resources), "plugins")) if not hasattr(sys, "frozen"): self._plugin_registry.addPluginLocation( os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "plugins")) self._plugin_registry.setApplication(self) self._parsed_command_line = None self.parseCommandLine() self._visible_messages = [] self._message_lock = threading.Lock() self.showMessageSignal.connect(self.showMessage) self.hideMessageSignal.connect(self.hideMessage)
def _onPreferenceChanged(self, preference): if preference == "cura/active_mode": self._advanced_mode = Preferences.getInstance().getValue(preference) == 1 self._updateEnabled()
def _onChangeTimerFinished(self): if not self._enabled: return root = self._controller.getScene().getRoot() # Keep a list of nodes that are moving. We use this so that we don't move two intersecting objects in the # same direction. transformed_nodes = [] group_nodes = [] # We try to shuffle all the nodes to prevent "locked" situations, where iteration B inverts iteration A. # By shuffling the order of the nodes, this might happen a few times, but at some point it will resolve. nodes = list(BreadthFirstIterator(root)) random.shuffle(nodes) for node in nodes: if node is root or type( node) is not SceneNode or node.getBoundingBox() is None: continue bbox = node.getBoundingBox() # Ignore intersections with the bottom build_volume_bounding_box = self._build_volume.getBoundingBox() if build_volume_bounding_box: # It's over 9000! build_volume_bounding_box = build_volume_bounding_box.set( bottom=-9001) else: # No bounding box. This is triggered when running Cura from command line with a model for the first time # In that situation there is a model, but no machine (and therefore no build volume. return node._outside_buildarea = False # Mark the node as outside the build volume if the bounding box test fails. if build_volume_bounding_box.intersectsBox( bbox ) != AxisAlignedBox.IntersectionResult.FullIntersection: node._outside_buildarea = True if node.callDecoration("isGroup"): group_nodes.append(node) # Keep list of affected group_nodes # Move it downwards if bottom is above platform move_vector = Vector() if Preferences.getInstance().getValue( "physics/automatic_drop_down") and not ( node.getParent() and node.getParent().callDecoration( "isGroup")) and node.isEnabled( ): #If an object is grouped, don't move it down z_offset = node.callDecoration( "getZOffset") if node.getDecorator( ZOffsetDecorator.ZOffsetDecorator) else 0 move_vector = move_vector.set(y=-bbox.bottom + z_offset) # If there is no convex hull for the node, start calculating it and continue. if not node.getDecorator(ConvexHullDecorator): node.addDecorator(ConvexHullDecorator()) if 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. if other_node is root or type( other_node) is not SceneNode or other_node is node: continue # Ignore collisions of a group with it's own children if other_node in node.getAllChildren( ) or node in other_node.getAllChildren(): continue # Ignore collisions within a group if other_node.getParent() and node.getParent() and ( other_node.getParent().callDecoration("isGroup") is not None or node.getParent().callDecoration("isGroup") is not None): continue # Ignore nodes that do not have the right properties set. if not other_node.callDecoration( "getConvexHull") or not other_node.getBoundingBox( ): continue if other_node in transformed_nodes: continue # Other node is already moving, wait for next pass. overlap = (0, 0) # Start loop with no overlap current_overlap_checks = 0 # Continue to check the overlap until we no longer find one. while overlap and current_overlap_checks < self._max_overlap_checks: current_overlap_checks += 1 head_hull = node.callDecoration("getConvexHullHead") if head_hull: # One at a time intersection. overlap = head_hull.translate( move_vector.x, move_vector.z).intersectsPolygon( other_node.callDecoration("getConvexHull")) if not overlap: other_head_hull = other_node.callDecoration( "getConvexHullHead") if other_head_hull: overlap = node.callDecoration( "getConvexHull").translate( move_vector.x, move_vector.z).intersectsPolygon( other_head_hull) if overlap: # Moving ensured that overlap was still there. Try anew! move_vector = move_vector.set( x=move_vector.x + overlap[0] * self._move_factor, z=move_vector.z + overlap[1] * self._move_factor) else: # Moving ensured that overlap was still there. Try anew! move_vector = move_vector.set( x=move_vector.x + overlap[0] * self._move_factor, z=move_vector.z + overlap[1] * self._move_factor) else: own_convex_hull = node.callDecoration( "getConvexHull") other_convex_hull = other_node.callDecoration( "getConvexHull") if own_convex_hull and other_convex_hull: overlap = own_convex_hull.translate( move_vector.x, move_vector.z).intersectsPolygon( other_convex_hull) if overlap: # Moving ensured that overlap was still there. Try anew! move_vector = move_vector.set( x=move_vector.x + overlap[0] * self._move_factor, z=move_vector.z + overlap[1] * self._move_factor) else: # This can happen in some cases if the object is not yet done with being loaded. # Simply waiting for the next tick seems to resolve this correctly. overlap = None convex_hull = node.callDecoration("getConvexHull") if convex_hull: if not convex_hull.isValid(): return # Check for collisions between disallowed areas and the object for area in self._build_volume.getDisallowedAreas(): overlap = convex_hull.intersectsPolygon(area) if overlap is None: continue node._outside_buildarea = True if not Vector.Null.equals(move_vector, epsilon=1e-5): transformed_nodes.append(node) op = PlatformPhysicsOperation.PlatformPhysicsOperation( node, move_vector) op.push() # Group nodes should override the _outside_buildarea property of their children. for group_node in group_nodes: for child_node in group_node.getAllChildren(): child_node._outside_buildarea = group_node._outside_buildarea
def run(self): super().run() if not self._result: self._result = [] # Scale down to maximum bounds size if that is available if hasattr(Application.getInstance().getController().getScene(), "_maximum_bounds"): for node in self._result: max_bounds = Application.getInstance().getController( ).getScene()._maximum_bounds node._resetAABB() build_bounds = node.getBoundingBox() if Preferences.getInstance().getValue( "mesh/scale_to_fit" ) == True or Preferences.getInstance().getValue( "mesh/scale_tiny_meshes") == True: scale_factor_width = max_bounds.width / build_bounds.width scale_factor_height = max_bounds.height / build_bounds.height scale_factor_depth = max_bounds.depth / build_bounds.depth scale_factor = min(scale_factor_width, scale_factor_depth, scale_factor_height) if Preferences.getInstance( ).getValue("mesh/scale_to_fit") == True and ( scale_factor_width < 1 or scale_factor_height < 1 or scale_factor_depth < 1 ): # Use scale factor to scale large object down # Ignore scaling on models which are less than 1.25 times bigger than the build volume ignore_factor = 1.25 if 1 / scale_factor < ignore_factor: Logger.log( "i", "Ignoring auto-scaling, because %.3d < %.3d" % (1 / scale_factor, ignore_factor)) scale_factor = 1 pass elif Preferences.getInstance().getValue( "mesh/scale_tiny_meshes") == True and ( scale_factor_width > 100 and scale_factor_height > 100 and scale_factor_depth > 100): # Round scale factor to lower factor of 10 to scale tiny object up (eg convert m to mm units) scale_factor = math.pow( 10, math.floor(math.log(scale_factor) / math.log(10))) else: scale_factor = 1 if scale_factor != 1: scale_vector = Vector(scale_factor, scale_factor, scale_factor) display_scale_factor = scale_factor * 100 scale_message = Message( i18n_catalog.i18nc( "@info:status", "Auto scaled object to {0}% of original size", ("%i" % display_scale_factor))) try: node.scale(scale_vector) scale_message.show() except Exception: Logger.logException( "e", "While auto-scaling an exception has been raised" )