mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-08-08 06:23:59 -06:00
Merge branch 'master' into output_device
* master: (22 commits) Added grouping action LayerData now works by using decorators Do not cause "dictionary changed size during iteration" errors when changing view Adds an idle-state for the layerview slider Adds an idle-state for the safebutton Sets the platform activity on true when a model is loaded Sets the platform activity on true when a model is loaded Create functions that get & set platform activity Send M104 to set the temperature to 0 Do not store files that fail to load in recent files feat: infill wipe dist; wireframe restructure & renaming; bugfix: draft_shield_height inherit_function Also add the parent class' command line arguments Only process the layer data if the layer view is active. Write to the correct variable so bed temperature is properly updated Correct a copy-paste error in getConnectionList Properly close all open USB connections on shut down Catch errors when trying to close the connection thread Enlarge the zone where you can use scrolling to look trough the layer mode. included retraction_extra_prime_amount tiniest detail concerning the padding of the layer count label ...
This commit is contained in:
commit
a8c36282fb
16 changed files with 426 additions and 86 deletions
|
@ -37,6 +37,12 @@ class CuraEngineBackend(Backend):
|
|||
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 = None
|
||||
|
||||
self._settings = None
|
||||
Application.getInstance().activeMachineChanged.connect(self._onActiveMachineChanged)
|
||||
self._onActiveMachineChanged()
|
||||
|
@ -150,7 +156,7 @@ class CuraEngineBackend(Backend):
|
|||
obj = msg.objects.add()
|
||||
obj.id = id(object)
|
||||
|
||||
verts = numpy.array(mesh_data.getVertices(), copy=True)
|
||||
verts = numpy.array(mesh_data.getVertices())
|
||||
verts[:,[1,2]] = verts[:,[2,1]]
|
||||
verts[:,1] *= -1
|
||||
obj.vertices = verts.tostring()
|
||||
|
@ -188,8 +194,11 @@ class CuraEngineBackend(Backend):
|
|||
|
||||
def _onSlicedObjectListMessage(self, message):
|
||||
if self._save_polygons:
|
||||
job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(message)
|
||||
job.start()
|
||||
if self._layer_view_active:
|
||||
job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(message)
|
||||
job.start()
|
||||
else :
|
||||
self._stored_layer_data = message
|
||||
|
||||
def _onProgressMessage(self, message):
|
||||
if message.amount >= 0.99:
|
||||
|
@ -248,3 +257,14 @@ class CuraEngineBackend(Backend):
|
|||
def _onToolOperationStopped(self, tool):
|
||||
self._enabled = True
|
||||
self._onChanged()
|
||||
|
||||
def _onActiveViewChanged(self):
|
||||
if Application.getInstance().getController().getActiveView():
|
||||
view = Application.getInstance().getController().getActiveView()
|
||||
if view.getPluginId() == "LayerView":
|
||||
self._layer_view_active = True
|
||||
if self._stored_layer_data:
|
||||
job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(self._stored_layer_data)
|
||||
job.start()
|
||||
else:
|
||||
self._layer_view_active = False
|
||||
|
|
12
plugins/CuraEngineBackend/LayerDataDecorator.py
Normal file
12
plugins/CuraEngineBackend/LayerDataDecorator.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||
|
||||
class LayerDataDecorator(SceneNodeDecorator):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._layer_data = None
|
||||
|
||||
def getLayerData(self):
|
||||
return self._layer_data
|
||||
|
||||
def setLayerData(self, layer_data):
|
||||
self._layer_data = layer_data
|
|
@ -11,6 +11,7 @@ from UM.Message import Message
|
|||
from UM.i18n import i18nCatalog
|
||||
|
||||
from . import LayerData
|
||||
from . import LayerDataDecorator
|
||||
|
||||
import numpy
|
||||
import struct
|
||||
|
@ -22,21 +23,22 @@ class ProcessSlicedObjectListJob(Job):
|
|||
super().__init__()
|
||||
self._message = message
|
||||
self._scene = Application.getInstance().getController().getScene()
|
||||
|
||||
self._progress = None
|
||||
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
|
||||
|
||||
def run(self):
|
||||
if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
|
||||
self._progress = Message(catalog.i18nc("Layers View mode", "Layers"), 0, False, 0)
|
||||
self._progress.show()
|
||||
|
||||
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
|
||||
|
||||
objectIdMap = {}
|
||||
new_node = SceneNode()
|
||||
## Put all nodes in a dict identified by ID
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
if type(node) is SceneNode and node.getMeshData():
|
||||
if hasattr(node.getMeshData(), "layerData"):
|
||||
if node.callDecoration("getLayerData"):
|
||||
#if hasattr(node.getMeshData(), "layerData"):
|
||||
self._scene.getRoot().removeChild(node)
|
||||
else:
|
||||
objectIdMap[id(node)] = node
|
||||
|
@ -83,14 +85,18 @@ class ProcessSlicedObjectListJob(Job):
|
|||
|
||||
# We are done processing all the layers we got from the engine, now create a mesh out of the data
|
||||
layerData.build()
|
||||
mesh.layerData = layerData
|
||||
|
||||
if self._progress:
|
||||
self._progress.setProgress(100)
|
||||
|
||||
|
||||
#Add layerdata decorator to scene node to indicate that the node has layerdata
|
||||
decorator = LayerDataDecorator.LayerDataDecorator()
|
||||
decorator.setLayerData(layerData)
|
||||
new_node.addDecorator(decorator)
|
||||
|
||||
new_node.setMeshData(mesh)
|
||||
new_node.setParent(self._scene.getRoot())
|
||||
|
||||
|
||||
view = Application.getInstance().getController().getActiveView()
|
||||
if view.getPluginId() == "LayerView":
|
||||
view.resetLayerData()
|
||||
|
|
|
@ -27,9 +27,13 @@ class LayerView(View):
|
|||
self._max_layers = 10
|
||||
self._current_layer_num = 10
|
||||
self._current_layer_mesh = None
|
||||
self._activity = False
|
||||
|
||||
self._solid_layers = 5
|
||||
|
||||
def getActivity(self):
|
||||
return self._activity
|
||||
|
||||
def getCurrentLayer(self):
|
||||
return self._current_layer_num
|
||||
|
||||
|
@ -64,10 +68,8 @@ class LayerView(View):
|
|||
if node.getMeshData() and node.isVisible():
|
||||
if Selection.isSelected(node):
|
||||
renderer.queueNode(node, material = self._selection_material, transparent = True)
|
||||
|
||||
try:
|
||||
layer_data = node.getMeshData().layerData
|
||||
except AttributeError:
|
||||
layer_data = node.callDecoration("getLayerData")
|
||||
if not layer_data:
|
||||
continue
|
||||
|
||||
# Render all layers below a certain number as line mesh instead of vertices.
|
||||
|
@ -114,13 +116,14 @@ class LayerView(View):
|
|||
|
||||
self._current_layer_mesh = None
|
||||
self.currentLayerNumChanged.emit()
|
||||
|
||||
|
||||
currentLayerNumChanged = Signal()
|
||||
|
||||
|
||||
def calculateMaxLayers(self):
|
||||
scene = self.getController().getScene()
|
||||
renderer = self.getRenderer()
|
||||
if renderer and self._material:
|
||||
self._activity = True
|
||||
renderer.setRenderSelection(False)
|
||||
self._old_max_layers = self._max_layers
|
||||
## Recalculate num max layers
|
||||
|
@ -128,21 +131,25 @@ class LayerView(View):
|
|||
for node in DepthFirstIterator(scene.getRoot()):
|
||||
if not node.render(renderer):
|
||||
if node.getMeshData() and node.isVisible():
|
||||
try:
|
||||
layer_data = node.getMeshData().layerData
|
||||
except AttributeError:
|
||||
|
||||
layer_data = node.callDecoration("getLayerData")
|
||||
if not layer_data:
|
||||
continue
|
||||
|
||||
if new_max_layers < len(layer_data.getLayers()):
|
||||
new_max_layers = len(layer_data.getLayers()) - 1
|
||||
|
||||
if new_max_layers > 0 and new_max_layers != self._old_max_layers:
|
||||
self._max_layers = new_max_layers
|
||||
self.maxLayersChanged.emit()
|
||||
self._current_layer_num = self._max_layers
|
||||
|
||||
# This makes sure we update the current layer
|
||||
self.setLayer(int(self._max_layers * (self._current_layer_num / self._old_max_layers)))
|
||||
|
||||
self.setLayer(int(self._max_layers))
|
||||
self.currentLayerNumChanged.emit()
|
||||
|
||||
maxLayersChanged = Signal()
|
||||
currentLayerNumChanged = Signal()
|
||||
|
||||
## Hackish way to ensure the proxy is already created, which ensures that the layerview.qml is already created
|
||||
# as this caused some issues.
|
||||
|
|
|
@ -15,9 +15,11 @@ Item
|
|||
|
||||
Slider
|
||||
{
|
||||
id: slider
|
||||
width: 10
|
||||
height: 250
|
||||
anchors.right : parent.right
|
||||
anchors.rightMargin: UM.Theme.sizes.slider_layerview_margin.width
|
||||
orientation: Qt.Vertical
|
||||
minimumValue: 0;
|
||||
maximumValue: UM.LayerView.numLayers;
|
||||
|
@ -26,6 +28,31 @@ Item
|
|||
value: UM.LayerView.currentLayer
|
||||
onValueChanged: UM.LayerView.setCurrentLayer(value)
|
||||
|
||||
style: UM.Theme.styles.slider;
|
||||
style: UM.Theme.styles.layerViewSlider
|
||||
}
|
||||
Rectangle {
|
||||
anchors.right: parent.right
|
||||
y: -UM.Theme.sizes.slider_layerview_background_extension.height
|
||||
z: slider.z - 1
|
||||
width: UM.Theme.sizes.button.width
|
||||
height: UM.Theme.sizes.slider_layerview_background_extension.height
|
||||
color: UM.Theme.colors.slider_text_background
|
||||
}
|
||||
UM.AngledCornerRectangle {
|
||||
anchors.right : parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
z: slider.z - 1
|
||||
cornerSize: UM.Theme.sizes.default_margin.width;
|
||||
width: UM.Theme.sizes.slider_layerview_background.width
|
||||
height: slider.height + UM.Theme.sizes.default_margin.height * 2
|
||||
color: UM.Theme.colors.slider_text_background
|
||||
MouseArea {
|
||||
id: sliderMouseArea
|
||||
property double manualStepSize: slider.maximumValue / 11
|
||||
anchors.fill: parent
|
||||
onWheel: {
|
||||
slider.value = wheel.angleDelta.y < 0 ? slider.value - sliderMouseArea.manualStepSize : slider.value + sliderMouseArea.manualStepSize
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,13 @@ class LayerViewProxy(QObject):
|
|||
|
||||
currentLayerChanged = pyqtSignal()
|
||||
maxLayersChanged = pyqtSignal()
|
||||
|
||||
activityChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(bool, notify = activityChanged)
|
||||
def getLayerActivity(self):
|
||||
active_view = self._controller.getActiveView()
|
||||
return active_view.getActivity()
|
||||
|
||||
@pyqtProperty(int, notify = maxLayersChanged)
|
||||
def numLayers(self):
|
||||
active_view = self._controller.getActiveView()
|
||||
|
@ -30,9 +36,13 @@ class LayerViewProxy(QObject):
|
|||
active_view = self._controller.getActiveView()
|
||||
if type(active_view) == LayerView.LayerView.LayerView:
|
||||
active_view.setLayer(layer_num)
|
||||
|
||||
def _layerActivityChanged(self):
|
||||
self.activityChanged.emit()
|
||||
|
||||
def _onLayerChanged(self):
|
||||
self.currentLayerChanged.emit()
|
||||
self._layerActivityChanged()
|
||||
|
||||
def _onMaxLayersChanged(self):
|
||||
self.maxLayersChanged.emit()
|
||||
|
|
|
@ -469,7 +469,7 @@ class PrinterConnection(SignalEmitter):
|
|||
|
||||
# Turn of temperatures
|
||||
self._sendCommand("M140 S0")
|
||||
self._sendCommand("M109 S0")
|
||||
self._sendCommand("M104 S0")
|
||||
self._is_printing = False
|
||||
|
||||
## Check if the process did not encounter an error yet.
|
||||
|
|
|
@ -46,6 +46,8 @@ class USBPrinterManager(QObject, SignalEmitter, Extension):
|
|||
## Add menu item to top menu of the application.
|
||||
self.setMenuName("Firmware")
|
||||
self.addMenuItem(i18n_catalog.i18n("Update Firmware"), self.updateAllFirmware)
|
||||
|
||||
Application.getInstance().applicationShuttingDown.connect(self._onApplicationShuttingDown)
|
||||
|
||||
pyqtError = pyqtSignal(str, arguments = ["error"])
|
||||
processingProgress = pyqtSignal(float, arguments = ["amount"])
|
||||
|
@ -170,7 +172,7 @@ class USBPrinterManager(QObject, SignalEmitter, Extension):
|
|||
|
||||
## Callback for bed temperature change
|
||||
def onBedTemperature(self, serial_port,temperature):
|
||||
self._bed_temperature = temperature
|
||||
self._bed_temp = temperature
|
||||
self.pyqtBedTemperature.emit(temperature)
|
||||
|
||||
## Callback for error
|
||||
|
@ -280,15 +282,19 @@ class USBPrinterManager(QObject, SignalEmitter, Extension):
|
|||
i = 0
|
||||
while True:
|
||||
values = winreg.EnumValue(key, i)
|
||||
if not base_list or "USBSER" in values[0]:
|
||||
if not only_list_usb or "USBSER" in values[0]:
|
||||
base_list += [values[1]]
|
||||
i += 1
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
if base_list:
|
||||
base_list = base_list + glob.glob("/dev/ttyUSB*") + glob.glob("/dev/ttyACM*") + glob.glob("/dev/cu.usb*")
|
||||
base_list = filter(lambda s: "Bluetooth" not in s, base_list) # Filter because mac sometimes puts them in the list
|
||||
else:
|
||||
base_list = base_list + glob.glob("/dev/ttyUSB*") + glob.glob("/dev/ttyACM*") + glob.glob("/dev/cu.*") + glob.glob("/dev/tty.usb*") + glob.glob("/dev/rfcomm*") + glob.glob("/dev/serial/by-id/*")
|
||||
if only_list_usb:
|
||||
base_list = base_list + glob.glob("/dev/ttyUSB*") + glob.glob("/dev/ttyACM*") + glob.glob("/dev/cu.usb*")
|
||||
base_list = filter(lambda s: "Bluetooth" not in s, base_list) # Filter because mac sometimes puts them in the list
|
||||
else:
|
||||
base_list = base_list + glob.glob("/dev/ttyUSB*") + glob.glob("/dev/ttyACM*") + glob.glob("/dev/cu.*") + glob.glob("/dev/tty.usb*") + glob.glob("/dev/rfcomm*") + glob.glob("/dev/serial/by-id/*")
|
||||
return base_list
|
||||
|
||||
def _onApplicationShuttingDown(self):
|
||||
for connection in self._printer_connections:
|
||||
connection.close()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue