mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 22:47:29 -06:00
Merge branch 'master' into layer_view3_cleanup
This commit is contained in:
commit
abe7a8e9a3
42 changed files with 12101 additions and 89 deletions
9
cura/BlockSlicingDecorator.py
Normal file
9
cura/BlockSlicingDecorator.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||
|
||||
|
||||
class BlockSlicingDecorator(SceneNodeDecorator):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def isBlockSlicing(self):
|
||||
return True
|
|
@ -538,8 +538,11 @@ class BuildVolume(SceneNode):
|
|||
prime_tower_size = self._global_container_stack.getProperty("prime_tower_size", "value")
|
||||
machine_width = self._global_container_stack.getProperty("machine_width", "value")
|
||||
machine_depth = self._global_container_stack.getProperty("machine_depth", "value")
|
||||
prime_tower_x = self._global_container_stack.getProperty("prime_tower_position_x", "value") - machine_width / 2 #Offset by half machine_width and _depth to put the origin in the front-left.
|
||||
prime_tower_y = - self._global_container_stack.getProperty("prime_tower_position_y", "value") + machine_depth / 2
|
||||
prime_tower_x = self._global_container_stack.getProperty("prime_tower_position_x", "value")
|
||||
prime_tower_y = - self._global_container_stack.getProperty("prime_tower_position_y", "value")
|
||||
if not self._global_container_stack.getProperty("machine_center_is_zero", "value"):
|
||||
prime_tower_x = prime_tower_x - machine_width / 2 #Offset by half machine_width and _depth to put the origin in the front-left.
|
||||
prime_tower_y = prime_tower_y + machine_depth / 2
|
||||
|
||||
prime_tower_area = Polygon([
|
||||
[prime_tower_x - prime_tower_size, prime_tower_y - prime_tower_size],
|
||||
|
@ -570,8 +573,17 @@ class BuildVolume(SceneNode):
|
|||
machine_width = self._global_container_stack.getProperty("machine_width", "value")
|
||||
machine_depth = self._global_container_stack.getProperty("machine_depth", "value")
|
||||
for extruder in used_extruders:
|
||||
prime_x = extruder.getProperty("extruder_prime_pos_x", "value") - machine_width / 2 #Offset by half machine_width and _depth to put the origin in the front-left.
|
||||
prime_y = machine_depth / 2 - extruder.getProperty("extruder_prime_pos_y", "value")
|
||||
prime_x = extruder.getProperty("extruder_prime_pos_x", "value")
|
||||
prime_y = - extruder.getProperty("extruder_prime_pos_y", "value")
|
||||
|
||||
#Ignore extruder prime position if it is not set
|
||||
if prime_x == 0 and prime_y == 0:
|
||||
result[extruder.getId()] = []
|
||||
continue
|
||||
|
||||
if not self._global_container_stack.getProperty("machine_center_is_zero", "value"):
|
||||
prime_x = prime_x - machine_width / 2 #Offset by half machine_width and _depth to put the origin in the front-left.
|
||||
prime_y = prime_x + machine_depth / 2
|
||||
|
||||
prime_polygon = Polygon.approximatedCircle(PRIME_CLEARANCE)
|
||||
prime_polygon = prime_polygon.translate(prime_x, prime_y)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from PyQt5.QtCore import QObject, pyqtSlot, QUrl
|
||||
from PyQt5.QtCore import QObject, QUrl
|
||||
from PyQt5.QtGui import QDesktopServices
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
|
||||
from UM.Event import CallFunctionEvent
|
||||
from UM.Application import Application
|
||||
|
|
|
@ -19,6 +19,8 @@ from UM.SaveFile import SaveFile
|
|||
from UM.Scene.Selection import Selection
|
||||
from UM.Scene.GroupDecorator import GroupDecorator
|
||||
from UM.Settings.Validator import Validator
|
||||
from UM.Message import Message
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
|
||||
from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
|
||||
|
@ -26,13 +28,13 @@ from UM.Operations.GroupedOperation import GroupedOperation
|
|||
from UM.Operations.SetTransformOperation import SetTransformOperation
|
||||
from UM.Operations.TranslateOperation import TranslateOperation
|
||||
from cura.SetParentOperation import SetParentOperation
|
||||
from cura.SliceableObjectDecorator import SliceableObjectDecorator
|
||||
from cura.BlockSlicingDecorator import BlockSlicingDecorator
|
||||
|
||||
from UM.Settings.SettingDefinition import SettingDefinition, DefinitionPropertyType
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.SettingFunction import SettingFunction
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
from . import PlatformPhysics
|
||||
from . import BuildVolume
|
||||
from . import CameraAnimation
|
||||
|
@ -45,7 +47,8 @@ from . import MachineActionManager
|
|||
|
||||
import cura.Settings
|
||||
|
||||
from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
|
||||
from PyQt5.QtCore import QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
from PyQt5.QtGui import QColor, QIcon
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
|
||||
|
@ -135,6 +138,9 @@ class CuraApplication(QtApplication):
|
|||
}
|
||||
)
|
||||
|
||||
self._currently_loading_files = []
|
||||
self._non_sliceable_extensions = []
|
||||
|
||||
self._machine_action_manager = MachineActionManager.MachineActionManager()
|
||||
self._machine_manager = None # This is initialized on demand.
|
||||
self._setting_inheritance_manager = None
|
||||
|
@ -212,7 +218,7 @@ class CuraApplication(QtApplication):
|
|||
Preferences.getInstance().addPreference("cura/recent_files", "")
|
||||
Preferences.getInstance().addPreference("cura/categories_expanded", "")
|
||||
Preferences.getInstance().addPreference("cura/jobname_prefix", True)
|
||||
Preferences.getInstance().addPreference("view/center_on_select", True)
|
||||
Preferences.getInstance().addPreference("view/center_on_select", False)
|
||||
Preferences.getInstance().addPreference("mesh/scale_to_fit", True)
|
||||
Preferences.getInstance().addPreference("mesh/scale_tiny_meshes", True)
|
||||
Preferences.getInstance().addPreference("cura/dialog_on_project_save", True)
|
||||
|
@ -581,9 +587,12 @@ class CuraApplication(QtApplication):
|
|||
def updatePlatformActivity(self, node = None):
|
||||
count = 0
|
||||
scene_bounding_box = None
|
||||
is_block_slicing_node = False
|
||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||
if type(node) is not SceneNode or not node.getMeshData():
|
||||
if type(node) is not SceneNode or (not node.getMeshData() and not node.callDecoration("getLayerData")):
|
||||
continue
|
||||
if node.callDecoration("isBlockSlicing"):
|
||||
is_block_slicing_node = True
|
||||
|
||||
count += 1
|
||||
if not scene_bounding_box:
|
||||
|
@ -593,6 +602,10 @@ class CuraApplication(QtApplication):
|
|||
if other_bb is not None:
|
||||
scene_bounding_box = scene_bounding_box + node.getBoundingBox()
|
||||
|
||||
print_information = self.getPrintInformation()
|
||||
if print_information:
|
||||
print_information.setPreSliced(is_block_slicing_node)
|
||||
|
||||
if not scene_bounding_box:
|
||||
scene_bounding_box = AxisAlignedBox.Null
|
||||
|
||||
|
@ -713,7 +726,7 @@ class CuraApplication(QtApplication):
|
|||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||
if type(node) is not SceneNode:
|
||||
continue
|
||||
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
||||
if (not node.getMeshData() and not node.callDecoration("getLayerData")) and not node.callDecoration("isGroup"):
|
||||
continue # Node that doesnt have a mesh and is not a group.
|
||||
if node.getParent() and node.getParent().callDecoration("isGroup"):
|
||||
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||
|
@ -1008,3 +1021,78 @@ class CuraApplication(QtApplication):
|
|||
@pyqtSlot(str)
|
||||
def log(self, msg):
|
||||
Logger.log("d", msg)
|
||||
|
||||
@pyqtSlot(QUrl)
|
||||
def readLocalFile(self, file):
|
||||
if not file.isValid():
|
||||
return
|
||||
|
||||
scene = self.getController().getScene()
|
||||
|
||||
for node in DepthFirstIterator(scene.getRoot()):
|
||||
if node.callDecoration("isBlockSlicing"):
|
||||
self.deleteAll()
|
||||
break
|
||||
|
||||
f = file.toLocalFile()
|
||||
extension = os.path.splitext(f)[1]
|
||||
filename = os.path.basename(f)
|
||||
if len(self._currently_loading_files) > 0:
|
||||
# If a non-slicable file is already being loaded, we prevent loading of any further non-slicable files
|
||||
if extension.lower() in self._non_sliceable_extensions:
|
||||
message = Message(
|
||||
self._i18n_catalog.i18nc("@info:status",
|
||||
"Only one G-code file can be loaded at a time. Skipped importing {0}",
|
||||
filename))
|
||||
message.show()
|
||||
return
|
||||
# If file being loaded is non-slicable file, then prevent loading of any other files
|
||||
extension = os.path.splitext(self._currently_loading_files[0])[1]
|
||||
if extension.lower() in self._non_sliceable_extensions:
|
||||
message = Message(
|
||||
self._i18n_catalog.i18nc("@info:status",
|
||||
"Can't open any other file if G-code is loading. Skipped importing {0}",
|
||||
filename))
|
||||
message.show()
|
||||
return
|
||||
|
||||
self._currently_loading_files.append(f)
|
||||
if extension in self._non_sliceable_extensions:
|
||||
self.deleteAll()
|
||||
|
||||
job = ReadMeshJob(f)
|
||||
job.finished.connect(self._readMeshFinished)
|
||||
job.start()
|
||||
|
||||
def _readMeshFinished(self, job):
|
||||
nodes = job.getResult()
|
||||
filename = job.getFileName()
|
||||
self._currently_loading_files.remove(filename)
|
||||
|
||||
for node in nodes:
|
||||
node.setSelectable(True)
|
||||
node.setName(os.path.basename(filename))
|
||||
|
||||
extension = os.path.splitext(filename)[1]
|
||||
if extension.lower() in self._non_sliceable_extensions:
|
||||
self.getController().setActiveView("LayerView")
|
||||
view = self.getController().getActiveView()
|
||||
view.resetLayerData()
|
||||
view.setLayer(9999999)
|
||||
view.calculateMaxLayers()
|
||||
|
||||
block_slicing_decorator = BlockSlicingDecorator()
|
||||
node.addDecorator(block_slicing_decorator)
|
||||
else:
|
||||
sliceable_decorator = SliceableObjectDecorator()
|
||||
node.addDecorator(sliceable_decorator)
|
||||
|
||||
scene = self.getController().getScene()
|
||||
|
||||
op = AddSceneNodeOperation(node, scene.getRoot())
|
||||
op.push()
|
||||
|
||||
scene.sceneChanged.emit(node)
|
||||
|
||||
def addNonSliceableExtension(self, extension):
|
||||
self._non_sliceable_extensions.append(extension)
|
||||
|
|
13
cura/GCodeListDecorator.py
Normal file
13
cura/GCodeListDecorator.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||
|
||||
|
||||
class GCodeListDecorator(SceneNodeDecorator):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._gcode_list = []
|
||||
|
||||
def getGCodeList(self):
|
||||
return self._gcode_list
|
||||
|
||||
def setGCodeList(self, list):
|
||||
self._gcode_list = list
|
|
@ -6,7 +6,8 @@ from UM.PluginRegistry import PluginRegistry # So MachineAction can be added as
|
|||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSlot
|
||||
from PyQt5.QtCore import QObject
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
|
||||
## Raised when trying to add an unknown machine action as a required action
|
||||
class UnknownMachineActionError(Exception):
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot
|
||||
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Qt.Duration import Duration
|
||||
|
@ -13,6 +14,9 @@ import math
|
|||
import os.path
|
||||
import unicodedata
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
## A class for processing and calculating minimum, current and maximum print time as well as managing the job name
|
||||
#
|
||||
# This class contains all the logic relating to calculation and slicing for the
|
||||
|
@ -49,6 +53,8 @@ class PrintInformation(QObject):
|
|||
self._material_lengths = []
|
||||
self._material_weights = []
|
||||
|
||||
self._pre_sliced = False
|
||||
|
||||
self._backend = Application.getInstance().getBackend()
|
||||
if self._backend:
|
||||
self._backend.printDurationMessage.connect(self._onPrintDurationMessage)
|
||||
|
@ -61,6 +67,16 @@ class PrintInformation(QObject):
|
|||
|
||||
currentPrintTimeChanged = pyqtSignal()
|
||||
|
||||
preSlicedChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(bool, notify=preSlicedChanged)
|
||||
def preSliced(self):
|
||||
return self._pre_sliced
|
||||
|
||||
def setPreSliced(self, pre_sliced):
|
||||
self._pre_sliced = pre_sliced
|
||||
self.preSlicedChanged.emit()
|
||||
|
||||
@pyqtProperty(Duration, notify = currentPrintTimeChanged)
|
||||
def currentPrintTime(self):
|
||||
return self._current_print_time
|
||||
|
@ -122,7 +138,9 @@ class PrintInformation(QObject):
|
|||
def createJobName(self, base_name):
|
||||
base_name = self._stripAccents(base_name)
|
||||
self._setAbbreviatedMachineName()
|
||||
if Preferences.getInstance().getValue("cura/jobname_prefix"):
|
||||
if self._pre_sliced:
|
||||
return catalog.i18nc("@label", "Pre-sliced file {0}", base_name)
|
||||
elif Preferences.getInstance().getValue("cura/jobname_prefix"):
|
||||
return self._abbr_machine + "_" + base_name
|
||||
else:
|
||||
return base_name
|
||||
|
|
|
@ -220,6 +220,9 @@ class QualityManager:
|
|||
material_ids = set()
|
||||
for material_instance in material_containers:
|
||||
if material_instance is not None:
|
||||
# Add the parent material too.
|
||||
for basic_material in self._getBasicMaterials(material_instance):
|
||||
material_ids.add(basic_material.getId())
|
||||
material_ids.add(material_instance.getId())
|
||||
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**criteria)
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
import os.path
|
||||
import urllib
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal, QUrl, QVariant
|
||||
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, QUrl, QVariant
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
import UM.PluginRegistry
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import pyqtSignal, pyqtProperty, pyqtSlot, QObject, QVariant #For communicating data and events to Qt.
|
||||
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant #For communicating data and events to Qt.
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
|
||||
import UM.Application #To get the global container stack to find the current machine.
|
||||
import UM.Logger
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal
|
||||
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
from UM import Util
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal
|
||||
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
import UM.Settings
|
||||
from UM.Application import Application
|
||||
import cura.Settings
|
||||
|
|
12
cura/SliceableObjectDecorator.py
Normal file
12
cura/SliceableObjectDecorator.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||
|
||||
|
||||
class SliceableObjectDecorator(SceneNodeDecorator):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def isSliceable(self):
|
||||
return True
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
return type(self)()
|
37
docs/How_to_use_the_flame_graph_profiler.md
Normal file
37
docs/How_to_use_the_flame_graph_profiler.md
Normal file
|
@ -0,0 +1,37 @@
|
|||
|
||||
How to Profile Cura and See What It is Doing
|
||||
============================================
|
||||
Cura has a simple flame graph profiler available as a plugin which can be used to see what Cura is doing as it runs and how much time it takes. A flame graph profile shows its output as a timeline and stacks of "blocks" which represent parts of the code and are stacked up to show call depth. These often form little peaks which look like flames. It is a simple yet powerful way to visualise the activity of a program.
|
||||
|
||||
|
||||
Setting up and installing the profiler
|
||||
--------------------------------------
|
||||
|
||||
The profiler plugin is kept outside of the Cura source code here: https://github.com/sedwards2009/cura-big-flame-graph
|
||||
|
||||
To install it do:
|
||||
|
||||
* Use `git clone https://github.com/sedwards2009/cura-big-flame-graph.git` to grab a copy of the code.
|
||||
* Copy the `BigFlameGraph` directory into the `plugins` directory in your local Cura.
|
||||
* Set the `URANIUM_FLAME_PROFILER` environment variable to something before starting Cura. This flags to the profiler code in Cura to activate and insert the needed hooks into the code.
|
||||
|
||||
|
||||
Using the profiler
|
||||
------------------
|
||||
To open the profiler go to the Extensions menu and select "Start BFG" from the "Big Flame Graph" menu. A page will open up in your default browser. This is the profiler UI. Click on "Record" to start recording, go to Cura and perform an action and then back in the profiler click on "Stop". The results should now load in.
|
||||
|
||||
The time scale is at the top of the window. The blocks should be read as meaning the blocks at the bottom call the blocks which are stacked on top of them. Hover the mouse to get more detailed information about a block such as the name of the code involved and its duration. Use the zoom buttons or mouse wheel to zoom in. The display can be panned by dragging with the left mouse button.
|
||||
|
||||
Note: The profiler front-end itself is quite "heavy" (ok, not optimised). It runs much better in Google Chrome or Chromium than Firefox. It is also a good idea to keep recording sessions short for the same reason.
|
||||
|
||||
|
||||
What the Profiler Sees
|
||||
----------------------
|
||||
The profiler doesn't capture every function call in Cura. It hooks into a number of important systems which give a good picture of activity without too much run time overhead. The most important system is Uranium's signal mechanism and PyQt5 slots. Functions which are called via the signal mechanism are recorded and thier names appear in the results. PyQt5 slots appear in the results with the prefix `[SLOT]`.
|
||||
|
||||
Note that not all slots are captured. Only those slots which belong to classes which use the `pyqtSlot` decorator from the `UM.FlameProfiler` module.
|
||||
|
||||
|
||||
Manually adding profiling code to more detail
|
||||
---------------------------------------------
|
||||
It is also possible to manually add decorators to methods to make them appear in the profiler results. The `UM.FlameProfiler` module contains the `profile` decorator which can be applied to methods. There is also a `profileCall` context manager which can be used with Python's `with` statement to measure a block of code. `profileCall` takes one argument, a label to use in the results.
|
|
@ -1,22 +1,22 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.Mesh.MeshReader import MeshReader
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||
import os.path
|
||||
import zipfile
|
||||
|
||||
from UM.Job import Job
|
||||
from UM.Logger import Logger
|
||||
from UM.Math.Matrix import Matrix
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||
from UM.Mesh.MeshReader import MeshReader
|
||||
from UM.Scene.GroupDecorator import GroupDecorator
|
||||
import UM.Application
|
||||
from UM.Job import Job
|
||||
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
||||
from UM.Application import Application
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
from cura.QualityManager import QualityManager
|
||||
|
||||
import os.path
|
||||
import zipfile
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
|
||||
try:
|
||||
import xml.etree.cElementTree as ET
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import Qt, QUrl, pyqtSignal, pyqtSlot, QObject, pyqtProperty, QCoreApplication
|
||||
from PyQt5.QtCore import Qt, QUrl, pyqtSignal, QObject, pyqtProperty, QCoreApplication
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
from PyQt5.QtQml import QQmlComponent, QQmlContext
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Application import Application
|
||||
|
|
|
@ -12,6 +12,8 @@ from UM.PluginRegistry import PluginRegistry
|
|||
from UM.Resources import Resources
|
||||
from UM.Settings.Validator import ValidatorState #To find if a setting is in an error state. We can't slice then.
|
||||
from UM.Platform import Platform
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
|
||||
|
||||
import cura.Settings
|
||||
|
||||
|
@ -69,6 +71,8 @@ class CuraEngineBackend(Backend):
|
|||
self._scene = Application.getInstance().getController().getScene()
|
||||
self._scene.sceneChanged.connect(self._onSceneChanged)
|
||||
|
||||
self._pause_slicing = False
|
||||
|
||||
# Workaround to disable layer view processing if layer view is not active.
|
||||
self._layer_view_active = False
|
||||
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
|
||||
|
@ -150,6 +154,8 @@ class CuraEngineBackend(Backend):
|
|||
## Perform a slice of the scene.
|
||||
def slice(self):
|
||||
Logger.log("d", "Starting slice job...")
|
||||
if self._pause_slicing:
|
||||
return
|
||||
self._slice_start_time = time()
|
||||
if not self._enabled or not self._global_container_stack: # We shouldn't be slicing.
|
||||
# try again in a short time
|
||||
|
@ -183,6 +189,17 @@ class CuraEngineBackend(Backend):
|
|||
self._start_slice_job.start()
|
||||
self._start_slice_job.finished.connect(self._onStartSliceCompleted)
|
||||
|
||||
|
||||
def pauseSlicing(self):
|
||||
self.close()
|
||||
self._pause_slicing = True
|
||||
self.backendStateChange.emit(BackendState.Disabled)
|
||||
|
||||
def continueSlicing(self):
|
||||
if self._pause_slicing:
|
||||
self._pause_slicing = False
|
||||
self.backendStateChange.emit(BackendState.NotStarted)
|
||||
|
||||
## Terminate the engine process.
|
||||
def _terminate(self):
|
||||
self._slicing = False
|
||||
|
@ -298,6 +315,19 @@ class CuraEngineBackend(Backend):
|
|||
if source is self._scene.getRoot():
|
||||
return
|
||||
|
||||
should_pause = False
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
if node.callDecoration("isBlockSlicing"):
|
||||
should_pause = True
|
||||
gcode_list = node.callDecoration("getGCodeList")
|
||||
if gcode_list is not None:
|
||||
self._scene.gcode_list = gcode_list
|
||||
|
||||
if should_pause:
|
||||
self.pauseSlicing()
|
||||
else:
|
||||
self.continueSlicing()
|
||||
|
||||
if source.getMeshData() is None:
|
||||
return
|
||||
|
||||
|
|
303
plugins/GCodeReader/GCodeReader.py
Normal file
303
plugins/GCodeReader/GCodeReader.py
Normal file
|
@ -0,0 +1,303 @@
|
|||
# Copyright (c) 2016 Aleph Objects, Inc.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Logger import Logger
|
||||
from UM.Math.AxisAlignedBox import AxisAlignedBox
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Mesh.MeshReader import MeshReader
|
||||
from UM.Message import Message
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
from cura import LayerDataBuilder
|
||||
from cura import LayerDataDecorator
|
||||
from cura.LayerPolygon import LayerPolygon
|
||||
from cura.GCodeListDecorator import GCodeListDecorator
|
||||
|
||||
import numpy
|
||||
import math
|
||||
import re
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
# Class for loading and parsing G-code files
|
||||
class GCodeReader(MeshReader):
|
||||
def __init__(self):
|
||||
super(GCodeReader, self).__init__()
|
||||
self._supported_extensions = [".gcode", ".g"]
|
||||
Application.getInstance().hideMessageSignal.connect(self._onHideMessage)
|
||||
self._cancelled = False
|
||||
self._message = None
|
||||
self._clearValues()
|
||||
self._scene_node = None
|
||||
self._position = namedtuple('Position', ['x', 'y', 'z', 'e'])
|
||||
|
||||
def _clearValues(self):
|
||||
self._extruder = 0
|
||||
self._layer_type = LayerPolygon.Inset0Type
|
||||
self._layer = 0
|
||||
self._previous_z = 0
|
||||
self._layer_data_builder = LayerDataBuilder.LayerDataBuilder()
|
||||
self._center_is_zero = False
|
||||
|
||||
@staticmethod
|
||||
def _getValue(line, code):
|
||||
n = line.find(code)
|
||||
if n < 0:
|
||||
return None
|
||||
n += len(code)
|
||||
pattern = re.compile("[;\s]")
|
||||
match = pattern.search(line, n)
|
||||
m = match.start() if match is not None else -1
|
||||
try:
|
||||
if m < 0:
|
||||
return line[n:]
|
||||
return line[n:m]
|
||||
except:
|
||||
return None
|
||||
|
||||
def _getInt(self, line, code):
|
||||
value = self._getValue(line, code)
|
||||
try:
|
||||
return int(value)
|
||||
except:
|
||||
return None
|
||||
|
||||
def _getFloat(self, line, code):
|
||||
value = self._getValue(line, code)
|
||||
try:
|
||||
return float(value)
|
||||
except:
|
||||
return None
|
||||
|
||||
def _onHideMessage(self, message):
|
||||
if message == self._message:
|
||||
self._cancelled = True
|
||||
|
||||
@staticmethod
|
||||
def _getNullBoundingBox():
|
||||
return AxisAlignedBox(minimum=Vector(0, 0, 0), maximum=Vector(10, 10, 10))
|
||||
|
||||
def _createPolygon(self, current_z, path):
|
||||
countvalid = 0
|
||||
for point in path:
|
||||
if point[3] > 0:
|
||||
countvalid += 1
|
||||
if countvalid < 2:
|
||||
return False
|
||||
try:
|
||||
self._layer_data_builder.addLayer(self._layer)
|
||||
self._layer_data_builder.setLayerHeight(self._layer, path[0][2])
|
||||
self._layer_data_builder.setLayerThickness(self._layer, math.fabs(current_z - self._previous_z))
|
||||
this_layer = self._layer_data_builder.getLayer(self._layer)
|
||||
except ValueError:
|
||||
return False
|
||||
count = len(path)
|
||||
line_types = numpy.empty((count - 1, 1), numpy.int32)
|
||||
line_widths = numpy.empty((count - 1, 1), numpy.float32)
|
||||
# TODO: need to calculate actual line width based on E values
|
||||
line_widths[:, 0] = 0.4
|
||||
points = numpy.empty((count, 3), numpy.float32)
|
||||
i = 0
|
||||
for point in path:
|
||||
points[i, 0] = point[0]
|
||||
points[i, 1] = point[2]
|
||||
points[i, 2] = -point[1]
|
||||
if i > 0:
|
||||
line_types[i - 1] = point[3]
|
||||
if point[3] in [LayerPolygon.MoveCombingType, LayerPolygon.MoveRetractionType]:
|
||||
line_widths[i - 1] = 0.2
|
||||
i += 1
|
||||
|
||||
this_poly = LayerPolygon(self._layer_data_builder, self._extruder, line_types, points, line_widths)
|
||||
this_poly.buildCache()
|
||||
|
||||
this_layer.polygons.append(this_poly)
|
||||
return True
|
||||
|
||||
def _gCode0(self, position, params, path):
|
||||
x, y, z, e = position
|
||||
x = params.x if params.x is not None else x
|
||||
y = params.y if params.y is not None else y
|
||||
z_changed = False
|
||||
if params.z is not None:
|
||||
if z != params.z:
|
||||
z_changed = True
|
||||
self._previous_z = z
|
||||
z = params.z
|
||||
if params.e is not None:
|
||||
if params.e > e[self._extruder]:
|
||||
path.append([x, y, z, self._layer_type]) # extrusion
|
||||
else:
|
||||
path.append([x, y, z, LayerPolygon.MoveRetractionType]) # retraction
|
||||
e[self._extruder] = params.e
|
||||
else:
|
||||
path.append([x, y, z, LayerPolygon.MoveCombingType])
|
||||
if z_changed:
|
||||
if not self._is_layers_in_file:
|
||||
if len(path) > 1 and z > 0:
|
||||
if self._createPolygon(z, path):
|
||||
self._layer += 1
|
||||
path.clear()
|
||||
else:
|
||||
path.clear()
|
||||
return self._position(x, y, z, e)
|
||||
|
||||
def _gCode28(self, position, params, path):
|
||||
return self._position(
|
||||
params.x if params.x is not None else position.x,
|
||||
params.y if params.y is not None else position.y,
|
||||
0,
|
||||
position.e)
|
||||
|
||||
def _gCode92(self, position, params, path):
|
||||
if params.e is not None:
|
||||
position.e[self._extruder] = params.e
|
||||
return self._position(
|
||||
params.x if params.x is not None else position.x,
|
||||
params.y if params.y is not None else position.y,
|
||||
params.z if params.z is not None else position.z,
|
||||
position.e)
|
||||
|
||||
_gCode1 = _gCode0
|
||||
|
||||
def _processGCode(self, G, line, position, path):
|
||||
func = getattr(self, "_gCode%s" % G, None)
|
||||
x = self._getFloat(line, "X")
|
||||
y = self._getFloat(line, "Y")
|
||||
z = self._getFloat(line, "Z")
|
||||
e = self._getFloat(line, "E")
|
||||
if func is not None:
|
||||
if (x is not None and x < 0) or (y is not None and y < 0):
|
||||
self._center_is_zero = True
|
||||
params = self._position(x, y, z, e)
|
||||
return func(position, params, path)
|
||||
return position
|
||||
|
||||
def _processTCode(self, T, line, position, path):
|
||||
self._extruder = T
|
||||
if self._extruder + 1 > len(position.e):
|
||||
position.e.extend([0] * (self._extruder - len(position.e) + 1))
|
||||
if not self._is_layers_in_file:
|
||||
if len(path) > 1 and position[2] > 0:
|
||||
if self._createPolygon(position[2], path):
|
||||
self._layer += 1
|
||||
path.clear()
|
||||
else:
|
||||
path.clear()
|
||||
return position
|
||||
|
||||
_type_keyword = ";TYPE:"
|
||||
_layer_keyword = ";LAYER:"
|
||||
|
||||
def read(self, file_name):
|
||||
Logger.log("d", "Preparing to load %s" % file_name)
|
||||
self._cancelled = False
|
||||
|
||||
scene_node = SceneNode()
|
||||
scene_node.getBoundingBox = self._getNullBoundingBox # Manually set bounding box, because mesh doesn't have mesh data
|
||||
|
||||
glist = []
|
||||
self._is_layers_in_file = False
|
||||
|
||||
|
||||
Logger.log("d", "Opening file %s" % file_name)
|
||||
|
||||
with open(file_name, "r") as file:
|
||||
file_lines = 0
|
||||
current_line = 0
|
||||
for line in file:
|
||||
file_lines += 1
|
||||
glist.append(line)
|
||||
if not self._is_layers_in_file and line[:len(self._layer_keyword)] == self._layer_keyword:
|
||||
self._is_layers_in_file = True
|
||||
file.seek(0)
|
||||
|
||||
file_step = max(math.floor(file_lines / 100), 1)
|
||||
|
||||
self._clearValues()
|
||||
|
||||
self._message = Message(catalog.i18nc("@info:status", "Parsing G-code"), lifetime=0)
|
||||
self._message.setProgress(0)
|
||||
self._message.show()
|
||||
|
||||
Logger.log("d", "Parsing %s" % file_name)
|
||||
|
||||
current_position = self._position(0, 0, 0, [0])
|
||||
current_path = []
|
||||
|
||||
for line in file:
|
||||
if self._cancelled:
|
||||
Logger.log("d", "Parsing %s cancelled" % file_name)
|
||||
return None
|
||||
current_line += 1
|
||||
if current_line % file_step == 0:
|
||||
self._message.setProgress(math.floor(current_line / file_lines * 100))
|
||||
if len(line) == 0:
|
||||
continue
|
||||
if line.find(self._type_keyword) == 0:
|
||||
type = line[len(self._type_keyword):].strip()
|
||||
if type == "WALL-INNER":
|
||||
self._layer_type = LayerPolygon.InsetXType
|
||||
elif type == "WALL-OUTER":
|
||||
self._layer_type = LayerPolygon.Inset0Type
|
||||
elif type == "SKIN":
|
||||
self._layer_type = LayerPolygon.SkinType
|
||||
elif type == "SKIRT":
|
||||
self._layer_type = LayerPolygon.SkirtType
|
||||
elif type == "SUPPORT":
|
||||
self._layer_type = LayerPolygon.SupportType
|
||||
elif type == "FILL":
|
||||
self._layer_type = LayerPolygon.InfillType
|
||||
if self._is_layers_in_file and line[:len(self._layer_keyword)] == self._layer_keyword:
|
||||
try:
|
||||
layer_number = int(line[len(self._layer_keyword):])
|
||||
self._createPolygon(current_position[2], current_path)
|
||||
current_path.clear()
|
||||
self._layer = layer_number
|
||||
except:
|
||||
pass
|
||||
if line[0] == ";":
|
||||
continue
|
||||
|
||||
G = self._getInt(line, "G")
|
||||
if G is not None:
|
||||
current_position = self._processGCode(G, line, current_position, current_path)
|
||||
T = self._getInt(line, "T")
|
||||
if T is not None:
|
||||
current_position = self._processTCode(T, line, current_position, current_path)
|
||||
|
||||
if not self._is_layers_in_file and len(current_path) > 1 and current_position[2] > 0:
|
||||
if self._createPolygon(current_position[2], current_path):
|
||||
self._layer += 1
|
||||
current_path.clear()
|
||||
|
||||
layer_mesh = self._layer_data_builder.build()
|
||||
decorator = LayerDataDecorator.LayerDataDecorator()
|
||||
decorator.setLayerData(layer_mesh)
|
||||
scene_node.addDecorator(decorator)
|
||||
|
||||
gcode_list_decorator = GCodeListDecorator()
|
||||
gcode_list_decorator.setGCodeList(glist)
|
||||
scene_node.addDecorator(gcode_list_decorator)
|
||||
|
||||
Logger.log("d", "Finished parsing %s" % file_name)
|
||||
self._message.hide()
|
||||
|
||||
if self._layer == 0:
|
||||
Logger.log("w", "File %s doesn't contain any valid layers" % file_name)
|
||||
|
||||
settings = Application.getInstance().getGlobalContainerStack()
|
||||
machine_width = settings.getProperty("machine_width", "value")
|
||||
machine_depth = settings.getProperty("machine_depth", "value")
|
||||
|
||||
if not self._center_is_zero:
|
||||
scene_node.setPosition(Vector(-machine_width / 2, 0, machine_depth / 2))
|
||||
|
||||
Logger.log("d", "Loaded %s" % file_name)
|
||||
|
||||
return scene_node
|
33
plugins/GCodeReader/__init__.py
Normal file
33
plugins/GCodeReader/__init__.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
# Copyright (c) 2016 Aleph Objects, Inc.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from . import GCodeReader
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
def getMetaData():
|
||||
return {
|
||||
"plugin": {
|
||||
"name": i18n_catalog.i18nc("@label", "G-code Reader"),
|
||||
"author": "Victor Larchenko",
|
||||
"version": "1.0",
|
||||
"description": i18n_catalog.i18nc("@info:whatsthis", "Allows loading and displaying G-code files."),
|
||||
"api": 3
|
||||
},
|
||||
"mesh_reader": [
|
||||
{
|
||||
"extension": "gcode",
|
||||
"description": i18n_catalog.i18nc("@item:inlistbox", "G-code File")
|
||||
},
|
||||
{
|
||||
"extension": "g",
|
||||
"description": i18n_catalog.i18nc("@item:inlistbox", "G File")
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def register(app):
|
||||
app.addNonSliceableExtension(".gcode")
|
||||
app.addNonSliceableExtension(".g")
|
||||
return { "mesh_reader": GCodeReader.GCodeReader() }
|
|
@ -4,9 +4,9 @@
|
|||
import os
|
||||
import threading
|
||||
|
||||
from PyQt5.QtCore import Qt, QUrl, pyqtSignal, pyqtSlot, QObject
|
||||
from PyQt5.QtCore import Qt, QUrl, pyqtSignal, QObject
|
||||
from PyQt5.QtQml import QQmlComponent, QQmlContext
|
||||
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
from UM.Application import Application
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Logger import Logger
|
||||
|
|
|
@ -71,10 +71,11 @@ class LayerPass(RenderPass):
|
|||
tool_handle_batch = RenderBatch(self._tool_handle_shader, type = RenderBatch.RenderType.Overlay)
|
||||
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
|
||||
if isinstance(node, ToolHandle):
|
||||
tool_handle_batch.addItem(node.getWorldTransformation(), mesh = node.getSolidMesh())
|
||||
|
||||
elif isinstance(node, SceneNode) and node.getMeshData() and node.isVisible():
|
||||
elif isinstance(node, SceneNode) and (node.getMeshData() or node.callDecoration("isBlockSlicing")) and node.isVisible():
|
||||
layer_data = node.callDecoration("getLayerData")
|
||||
if not layer_data:
|
||||
continue
|
||||
|
|
|
@ -135,7 +135,7 @@ class LayerView(View):
|
|||
continue
|
||||
|
||||
if not node.render(renderer):
|
||||
if node.getMeshData() and node.isVisible():
|
||||
if (node.getMeshData()) and node.isVisible():
|
||||
renderer.queueNode(node, transparent = True, shader = self._ghost_shader)
|
||||
|
||||
def setLayer(self, value):
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty
|
||||
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
from UM.Application import Application
|
||||
|
||||
import LayerView
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtCore import pyqtProperty, pyqtSignal
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
|
||||
from cura.MachineAction import MachineAction
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Uranium is released under the terms of the AGPLv3 or higher.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import platform
|
||||
from UM.Platform import Platform
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
@ -18,15 +18,15 @@ def getMetaData():
|
|||
}
|
||||
|
||||
def register(app):
|
||||
if platform.system() == "Windows":
|
||||
if Platform.isWindows():
|
||||
from . import WindowsRemovableDrivePlugin
|
||||
return { "output_device": WindowsRemovableDrivePlugin.WindowsRemovableDrivePlugin() }
|
||||
elif platform.system() == "Darwin":
|
||||
elif Platform.isOSX():
|
||||
from . import OSXRemovableDrivePlugin
|
||||
return { "output_device": OSXRemovableDrivePlugin.OSXRemovableDrivePlugin() }
|
||||
elif platform.system() == "Linux":
|
||||
elif Platform.isLinux():
|
||||
from . import LinuxRemovableDrivePlugin
|
||||
return { "output_device": LinuxRemovableDrivePlugin.LinuxRemovableDrivePlugin() }
|
||||
else:
|
||||
Logger.log("e", "Unsupported system %s, no removable device hotplugging support available.", platform.system())
|
||||
Logger.log("e", "Unsupported system, thus no removable device hotplugging support available.")
|
||||
return { }
|
||||
|
|
|
@ -43,8 +43,8 @@ class Stk500v2(ispBase.IspBase):
|
|||
|
||||
self.serial.flushInput()
|
||||
self.serial.flushOutput()
|
||||
try:
|
||||
if self.sendMessage([0x10, 0xc8, 0x64, 0x19, 0x20, 0x00, 0x53, 0x03, 0xac, 0x53, 0x00, 0x00]) != [0x10, 0x00]:
|
||||
self.close()
|
||||
raise ispBase.IspError("Failed to enter programming mode")
|
||||
|
||||
self.sendMessage([0x06, 0x80, 0x00, 0x00, 0x00])
|
||||
|
@ -52,6 +52,9 @@ class Stk500v2(ispBase.IspBase):
|
|||
self._has_checksum = True
|
||||
else:
|
||||
self._has_checksum = False
|
||||
except ispBase.IspError:
|
||||
self.close()
|
||||
raise
|
||||
self.serial.timeout = 5
|
||||
|
||||
def close(self):
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from cura.MachineAction import MachineAction
|
||||
from cura.PrinterOutputDevice import PrinterOutputDevice
|
||||
|
||||
from PyQt5.QtCore import pyqtSlot
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.i18n import i18nCatalog
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
# Contributed by Seva Alekseyev <sevaa@nih.gov> with National Institutes of Health, 2016
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.Mesh.MeshReader import MeshReader
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||
from math import pi, sin, cos, sqrt
|
||||
|
||||
import numpy
|
||||
|
||||
from UM.Job import Job
|
||||
from UM.Logger import Logger
|
||||
from UM.Math.Matrix import Matrix
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||
from UM.Mesh.MeshReader import MeshReader
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Job import Job
|
||||
from math import pi, sin, cos, sqrt
|
||||
import numpy
|
||||
|
||||
try:
|
||||
import xml.etree.cElementTree as ET
|
||||
|
|
58
resources/definitions/101Hero.def.json
Normal file
58
resources/definitions/101Hero.def.json
Normal file
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"id": "101Hero",
|
||||
"version": 2,
|
||||
"name": "101Hero",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "rikky",
|
||||
"manufacturer": "101Hero",
|
||||
"category": "Other",
|
||||
"machine_extruder_trains":
|
||||
{
|
||||
"0": "fdmextruder"
|
||||
},
|
||||
"file_formats": "text/x-gcode",
|
||||
"supports_usb_connection": true
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_name": { "default_value": "101Hero" },
|
||||
"machine_shape": { "default_value": "elliptic"},
|
||||
"machine_heated_bed": { "default_value": false },
|
||||
"machine_width": { "default_value": 149.86 },
|
||||
"machine_depth": { "default_value": 149.86 },
|
||||
"machine_height": { "default_value": 99.822 },
|
||||
"machine_center_is_zero": { "default_value": true },
|
||||
"layer_height": { "default_value": 0.2 },
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"material_diameter": { "default_value": 1.75 },
|
||||
"machine_nozzle_heat_up_speed": { "default_value": 2 },
|
||||
"machine_nozzle_cool_down_speed": { "default_value": 2 },
|
||||
"machine_head_with_fans_polygon": {
|
||||
"default_value": [
|
||||
[ 0, 0 ],
|
||||
[ 0, 0 ],
|
||||
[ 0, 0 ],
|
||||
[ 0, 0 ]
|
||||
]
|
||||
},
|
||||
"speed_print": { "default_value": 14 },
|
||||
"speed_travel": { "value": "speed_print" },
|
||||
"speed_infill": { "default_value": 14 },
|
||||
"speed_wall": { "value": "speed_print * 0.7" },
|
||||
"speed_topbottom": { "value": "speed_print * 0.7" },
|
||||
"speed_layer_0": { "value": "speed_print * 0.7" },
|
||||
"gantry_height": { "default_value": 0 },
|
||||
"retraction_speed": { "default_value" : 10 },
|
||||
"retraction_amount": { "default_value" : 2.5 },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
|
||||
"machine_start_gcode": {
|
||||
"default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 Z0 ;home Z\nG1 Z15.0 F840\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F840\n;Put printing message on LCD screen\nM117 Printing...\n"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit\nG1 Z0.5 E-5 F840 ;move Z up a bit and retract even more\nG28 X0 Y0 ;home X/Y, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -890,6 +890,21 @@
|
|||
"default_value": "lines",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"top_bottom_pattern_0":
|
||||
{
|
||||
"label": "Bottom Pattern Initial Layer",
|
||||
"description": "The pattern on the bottom of the print on the first layer.",
|
||||
"type": "enum",
|
||||
"options":
|
||||
{
|
||||
"lines": "Lines",
|
||||
"concentric": "Concentric",
|
||||
"zigzag": "Zig Zag"
|
||||
},
|
||||
"default_value": "lines",
|
||||
"value": "top_bottom_pattern",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"wall_0_inset":
|
||||
{
|
||||
"label": "Outer Wall Inset",
|
||||
|
@ -2800,7 +2815,7 @@
|
|||
"support_z_distance":
|
||||
{
|
||||
"label": "Support Z Distance",
|
||||
"description": "Distance from the top/bottom of the support structure to the print. This gap provides clearance to remove the supports after the model is printed. This value is rounded down to a multiple of the layer height.",
|
||||
"description": "Distance from the top/bottom of the support structure to the print. This gap provides clearance to remove the supports after the model is printed. This value is rounded up to a multiple of the layer height.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"minimum_value": "0",
|
||||
|
@ -3113,8 +3128,8 @@
|
|||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value_warning": "machine_width",
|
||||
"minimum_value_warning": "machine_width / -2 if machine_center_is_zero else 0",
|
||||
"maximum_value_warning": "machine_width / 2 if machine_center_is_zero else machine_width",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"enabled": false
|
||||
|
@ -3126,8 +3141,8 @@
|
|||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value_warning": "machine_depth",
|
||||
"minimum_value_warning": "machine_depth / -2 if machine_center_is_zero else 0",
|
||||
"maximum_value_warning": "machine_depth / 2 if machine_center_is_zero else machine_depth",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"enabled": false
|
||||
|
@ -3776,8 +3791,8 @@
|
|||
"default_value": 200,
|
||||
"minimum_value_warning": "-1000",
|
||||
"maximum_value_warning": "1000",
|
||||
"maximum_value": "machine_width",
|
||||
"minimum_value": "resolveOrValue('prime_tower_size')",
|
||||
"maximum_value": "machine_width / 2 if machine_center_is_zero else machine_width",
|
||||
"minimum_value": "resolveOrValue('prime_tower_size') - machine_width / 2 if machine_center_is_zero else resolveOrValue('prime_tower_size')",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
|
@ -3793,6 +3808,8 @@
|
|||
"maximum_value_warning": "1000",
|
||||
"maximum_value": "machine_depth - resolveOrValue('prime_tower_size')",
|
||||
"minimum_value": "0",
|
||||
"maximum_value": "machine_depth / 2 - resolveOrValue('prime_tower_size') if machine_center_is_zero else machine_depth - resolveOrValue('prime_tower_size')",
|
||||
"minimum_value": "machine_depth / -2 if machine_center_is_zero else 0",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
|
@ -3804,6 +3821,7 @@
|
|||
"unit": "%",
|
||||
"enabled": "resolveOrValue('prime_tower_enable')",
|
||||
"default_value": 100,
|
||||
"value": "material_flow",
|
||||
"minimum_value": "0.0001",
|
||||
"minimum_value_warning": "50",
|
||||
"maximum_value_warning": "150",
|
||||
|
|
|
@ -26,10 +26,10 @@
|
|||
"machine_nozzle_cool_down_speed": { "default_value": 2 },
|
||||
"machine_head_with_fans_polygon": {
|
||||
"default_value": [
|
||||
[ 55, -20 ],
|
||||
[ 55, 99999 ],
|
||||
[ -49, 99999 ],
|
||||
[ -49, -20 ]
|
||||
[-49, 20],
|
||||
[-49, -99999],
|
||||
[55, 20],
|
||||
[55, -99999]
|
||||
]
|
||||
},
|
||||
"gantry_height": { "default_value": 99999 },
|
||||
|
|
3544
resources/i18n/ru/cura.po
Normal file
3544
resources/i18n/ru/cura.po
Normal file
File diff suppressed because it is too large
Load diff
2417
resources/i18n/ru/fdmextruder.def.json.po
Normal file
2417
resources/i18n/ru/fdmextruder.def.json.po
Normal file
File diff suppressed because it is too large
Load diff
5394
resources/i18n/ru/fdmprinter.def.json.po
Normal file
5394
resources/i18n/ru/fdmprinter.def.json.po
Normal file
File diff suppressed because it is too large
Load diff
|
@ -269,16 +269,16 @@ UM.MainWindow
|
|||
if(drop.urls.length > 0)
|
||||
{
|
||||
// Import models
|
||||
var imported_model = -1;
|
||||
for(var i in drop.urls)
|
||||
{
|
||||
// There is no endsWith in this version of JS...
|
||||
if ((drop.urls[i].length <= 12) || (drop.urls[i].substring(drop.urls[i].length-12) !== ".curaprofile")) {
|
||||
// Drop an object
|
||||
UM.MeshFileHandler.readLocalFile(drop.urls[i]);
|
||||
if (i == drop.urls.length - 1)
|
||||
Printer.readLocalFile(drop.urls[i]);
|
||||
if (imported_model == -1)
|
||||
{
|
||||
var meshName = backgroundItem.getMeshName(drop.urls[i].toString());
|
||||
backgroundItem.hasMesh(decodeURIComponent(meshName));
|
||||
imported_model = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -297,6 +297,11 @@ UM.MainWindow
|
|||
}
|
||||
messageDialog.open()
|
||||
}
|
||||
if (imported_model != -1)
|
||||
{
|
||||
var meshName = backgroundItem.getMeshName(drop.urls[imported_model].toString())
|
||||
backgroundItem.hasMesh(decodeURIComponent(meshName))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -406,7 +411,8 @@ UM.MainWindow
|
|||
iconSource: UM.Theme.getIcon("viewmode");
|
||||
|
||||
style: UM.Theme.styles.tool_button;
|
||||
tooltip: '';
|
||||
tooltip: "";
|
||||
enabled: !PrintInformation.preSliced
|
||||
menu: ViewMenu { }
|
||||
}
|
||||
|
||||
|
@ -731,16 +737,13 @@ UM.MainWindow
|
|||
|
||||
for(var i in fileUrls)
|
||||
{
|
||||
UM.MeshFileHandler.readLocalFile(fileUrls[i])
|
||||
Printer.readLocalFile(fileUrls[i])
|
||||
}
|
||||
|
||||
if (i == fileUrls.length - 1)
|
||||
{
|
||||
var meshName = backgroundItem.getMeshName(fileUrls.toString())
|
||||
var meshName = backgroundItem.getMeshName(fileUrls[0].toString())
|
||||
backgroundItem.hasMesh(decodeURIComponent(meshName))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections
|
||||
{
|
||||
|
|
|
@ -26,7 +26,7 @@ Menu
|
|||
return (index + 1) + ". " + path.slice(path.lastIndexOf("/") + 1);
|
||||
}
|
||||
onTriggered: {
|
||||
UM.MeshFileHandler.readLocalFile(modelData);
|
||||
Printer.readLocalFile(modelData);
|
||||
var meshName = backgroundItem.getMeshName(modelData.toString())
|
||||
backgroundItem.hasMesh(decodeURIComponent(meshName))
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ Menu
|
|||
{
|
||||
title: catalog.i18nc("@title:menu menubar:toplevel", "&View");
|
||||
id: menu
|
||||
enabled: !PrintInformation.preSliced
|
||||
Instantiator
|
||||
{
|
||||
model: UM.ViewModel { }
|
||||
|
|
|
@ -97,6 +97,7 @@ UM.PreferencesPage
|
|||
append({ text: "Français", code: "fr" })
|
||||
append({ text: "Italiano", code: "it" })
|
||||
append({ text: "Nederlands", code: "nl" })
|
||||
append({ text: "Русский", code: "ru" })
|
||||
append({ text: "Türkçe", code: "tr" })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@ Rectangle {
|
|||
return catalog.i18nc("@label:PrintjobStatus %1 is target operation","Ready to %1").arg(UM.OutputDeviceManager.activeDeviceShortDescription);
|
||||
case 4:
|
||||
return catalog.i18nc("@label:PrintjobStatus", "Unable to Slice");
|
||||
case 5:
|
||||
return catalog.i18nc("@label:PrintjobStatus", "Slicing unavailable");
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
@ -104,7 +106,7 @@ Rectangle {
|
|||
id: saveToButton
|
||||
|
||||
tooltip: UM.OutputDeviceManager.activeDeviceDescription;
|
||||
enabled: base.backendState == 3 && base.activity == true
|
||||
enabled: (base.backendState == 3 || base.backendState == 5) && base.activity == true
|
||||
height: UM.Theme.getSize("save_button_save_to_button").height
|
||||
|
||||
anchors.top: parent.top
|
||||
|
@ -179,7 +181,7 @@ Rectangle {
|
|||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
width: UM.Theme.getSize("save_button_save_to_button").height
|
||||
height: UM.Theme.getSize("save_button_save_to_button").height
|
||||
enabled: base.backendState == 3 && base.activity == true
|
||||
enabled: (base.backendState == 3 || base.backendState == 5) && base.activity == true
|
||||
visible: devicesModel.deviceCount > 1
|
||||
|
||||
|
||||
|
|
|
@ -167,7 +167,7 @@ Item
|
|||
id: definitionsModel;
|
||||
containerId: Cura.MachineManager.activeDefinitionId
|
||||
visibilityHandler: UM.SettingPreferenceVisibilityHandler { }
|
||||
exclude: ["machine_settings", "command_line_settings", "infill_mesh", "infill_mesh_order"] // TODO: infill_mesh settigns are excluded hardcoded, but should be based on the fact that settable_globally, settable_per_meshgroup and settable_per_extruder are false.
|
||||
exclude: ["machine_settings", "command_line_settings", "infill_mesh", "infill_mesh_order", "support_mesh", "anti_overhang_mesh"] // TODO: infill_mesh settigns are excluded hardcoded, but should be based on the fact that settable_globally, settable_per_meshgroup and settable_per_extruder are false.
|
||||
expanded: Printer.expandedCategories
|
||||
onExpandedChanged:
|
||||
{
|
||||
|
|
|
@ -16,6 +16,7 @@ Rectangle
|
|||
|
||||
property int currentModeIndex;
|
||||
property bool monitoringPrint: false
|
||||
property bool hideSettings: PrintInformation.preSliced
|
||||
Connections
|
||||
{
|
||||
target: Printer
|
||||
|
@ -270,7 +271,7 @@ Rectangle
|
|||
Rectangle {
|
||||
id: headerSeparator
|
||||
width: parent.width
|
||||
visible: !monitoringPrint
|
||||
visible: !monitoringPrint && !hideSettings
|
||||
height: visible ? UM.Theme.getSize("sidebar_lining").height : 0
|
||||
color: UM.Theme.getColor("sidebar_lining")
|
||||
anchors.top: header.bottom
|
||||
|
@ -288,7 +289,7 @@ Rectangle
|
|||
|
||||
Label {
|
||||
id: settingsModeLabel
|
||||
text: catalog.i18nc("@label:listbox", "Print Setup");
|
||||
text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified");
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width;
|
||||
anchors.top: headerSeparator.bottom
|
||||
|
@ -308,7 +309,7 @@ Rectangle
|
|||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.top: headerSeparator.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
visible: !monitoringPrint
|
||||
visible: !monitoringPrint && !hideSettings
|
||||
Component{
|
||||
id: wizardDelegate
|
||||
Button {
|
||||
|
@ -384,7 +385,7 @@ Rectangle
|
|||
height: settingsModeSelection.height
|
||||
width: visible ? height : 0
|
||||
|
||||
visible: !monitoringPrint && modesListModel.get(base.currentModeIndex) != undefined && modesListModel.get(base.currentModeIndex).showFilterButton
|
||||
visible: !monitoringPrint && !hideSettings && modesListModel.get(base.currentModeIndex) != undefined && modesListModel.get(base.currentModeIndex).showFilterButton
|
||||
opacity: visible ? 1 : 0
|
||||
|
||||
onClicked: sidebarContents.currentItem.toggleFilterField()
|
||||
|
@ -432,7 +433,7 @@ Rectangle
|
|||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.left: base.left
|
||||
anchors.right: base.right
|
||||
visible: !monitoringPrint
|
||||
visible: !monitoringPrint && !hideSettings
|
||||
|
||||
delegate: StackViewDelegate
|
||||
{
|
||||
|
|
|
@ -164,7 +164,7 @@ Column
|
|||
id: variantRow
|
||||
|
||||
height: UM.Theme.getSize("sidebar_setup").height
|
||||
visible: (Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials) && !sidebar.monitoringPrint
|
||||
visible: (Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials) && !sidebar.monitoringPrint && !sidebar.hideSettings
|
||||
|
||||
anchors
|
||||
{
|
||||
|
@ -261,7 +261,7 @@ Column
|
|||
{
|
||||
id: globalProfileRow
|
||||
height: UM.Theme.getSize("sidebar_setup").height
|
||||
visible: !sidebar.monitoringPrint
|
||||
visible: !sidebar.monitoringPrint && !sidebar.hideSettings
|
||||
|
||||
anchors
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue