Fix material model update and signal recursion

CURA-6904
This commit is contained in:
Lipu Fei 2019-10-21 09:53:12 +02:00
parent cd0dbb3902
commit 5602c71ec7
3 changed files with 29 additions and 15 deletions

View file

@ -1,9 +1,9 @@
# Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Optional, Dict, Set
from typing import Dict, Set
from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty
from PyQt5.QtCore import Qt, QTimer, pyqtSignal, pyqtProperty
from UM.Qt.ListModel import ListModel
@ -38,14 +38,25 @@ class BaseMaterialsModel(ListModel):
self._extruder_stack = None
self._enabled = True
# CURA-6904
# Updating the material model requires information from material nodes and containers. We use a timer here to
# make sure that an update function call will not be directly invoked by an event. Because the triggered event
# can be caused in the middle of a XMLMaterial loading, and the material container we try to find may not be
# in the system yet. This will cause an infinite recursion of (1) trying to load a material, (2) trying to
# update the material model, (3) cannot find the material container, load it, (4) repeat #1.
self._update_timer = QTimer()
self._update_timer.setInterval(100)
self._update_timer.setSingleShot(True)
self._update_timer.timeout.connect(self._update)
# Update the stack and the model data when the machine changes
self._machine_manager.globalContainerChanged.connect(self._updateExtruderStack)
self._updateExtruderStack()
# Update this model when switching machines, when adding materials or changing their metadata.
self._machine_manager.activeStackChanged.connect(self._update)
self._machine_manager.activeStackChanged.connect(self._onChanged)
ContainerTree.getInstance().materialsChanged.connect(self._materialsListChanged)
self._application.getMaterialManagementModel().favoritesChanged.connect(self._update)
self._application.getMaterialManagementModel().favoritesChanged.connect(self._onChanged)
self.addRoleName(Qt.UserRole + 1, "root_material_id")
self.addRoleName(Qt.UserRole + 2, "id")
@ -64,14 +75,17 @@ class BaseMaterialsModel(ListModel):
self.addRoleName(Qt.UserRole + 15, "container_node")
self.addRoleName(Qt.UserRole + 16, "is_favorite")
def _onChanged(self) -> None:
self._update_timer.start()
def _updateExtruderStack(self):
global_stack = self._machine_manager.activeMachine
if global_stack is None:
return
if self._extruder_stack is not None:
self._extruder_stack.pyqtContainersChanged.disconnect(self._update)
self._extruder_stack.approximateMaterialDiameterChanged.disconnect(self._update)
self._extruder_stack.pyqtContainersChanged.disconnect(self._onChanged)
self._extruder_stack.approximateMaterialDiameterChanged.disconnect(self._onChanged)
try:
self._extruder_stack = global_stack.extruderList[self._extruder_position]
@ -79,10 +93,10 @@ class BaseMaterialsModel(ListModel):
self._extruder_stack = None
if self._extruder_stack is not None:
self._extruder_stack.pyqtContainersChanged.connect(self._update)
self._extruder_stack.approximateMaterialDiameterChanged.connect(self._update)
self._extruder_stack.pyqtContainersChanged.connect(self._onChanged)
self._extruder_stack.approximateMaterialDiameterChanged.connect(self._onChanged)
# Force update the model when the extruder stack changes
self._update()
self._onChanged()
def setExtruderPosition(self, position: int):
if self._extruder_stack is None or self._extruder_position != position:
@ -99,7 +113,7 @@ class BaseMaterialsModel(ListModel):
self._enabled = enabled
if self._enabled:
# ensure the data is there again.
self._update()
self._onChanged()
self.enabledChanged.emit()
@pyqtProperty(bool, fset = setEnabled, notify = enabledChanged)
@ -119,12 +133,12 @@ class BaseMaterialsModel(ListModel):
return
if material.variant.machine.container_id != global_stack.definition.getId():
return
self._update()
self._onChanged()
## Triggered when the list of favorite materials is changed.
def _favoritesChanged(self, material_base_file: str) -> None:
if material_base_file in self._available_materials:
self._update()
self._onChanged()
## This is an abstract method that needs to be implemented by the specific
# models themselves.