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. # Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # 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 from UM.Qt.ListModel import ListModel
@ -38,14 +38,25 @@ class BaseMaterialsModel(ListModel):
self._extruder_stack = None self._extruder_stack = None
self._enabled = True 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 # Update the stack and the model data when the machine changes
self._machine_manager.globalContainerChanged.connect(self._updateExtruderStack) self._machine_manager.globalContainerChanged.connect(self._updateExtruderStack)
self._updateExtruderStack() self._updateExtruderStack()
# Update this model when switching machines, when adding materials or changing their metadata. # 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) 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 + 1, "root_material_id")
self.addRoleName(Qt.UserRole + 2, "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 + 15, "container_node")
self.addRoleName(Qt.UserRole + 16, "is_favorite") self.addRoleName(Qt.UserRole + 16, "is_favorite")
def _onChanged(self) -> None:
self._update_timer.start()
def _updateExtruderStack(self): def _updateExtruderStack(self):
global_stack = self._machine_manager.activeMachine global_stack = self._machine_manager.activeMachine
if global_stack is None: if global_stack is None:
return return
if self._extruder_stack is not None: if self._extruder_stack is not None:
self._extruder_stack.pyqtContainersChanged.disconnect(self._update) self._extruder_stack.pyqtContainersChanged.disconnect(self._onChanged)
self._extruder_stack.approximateMaterialDiameterChanged.disconnect(self._update) self._extruder_stack.approximateMaterialDiameterChanged.disconnect(self._onChanged)
try: try:
self._extruder_stack = global_stack.extruderList[self._extruder_position] self._extruder_stack = global_stack.extruderList[self._extruder_position]
@ -79,10 +93,10 @@ class BaseMaterialsModel(ListModel):
self._extruder_stack = None self._extruder_stack = None
if self._extruder_stack is not None: if self._extruder_stack is not None:
self._extruder_stack.pyqtContainersChanged.connect(self._update) self._extruder_stack.pyqtContainersChanged.connect(self._onChanged)
self._extruder_stack.approximateMaterialDiameterChanged.connect(self._update) self._extruder_stack.approximateMaterialDiameterChanged.connect(self._onChanged)
# Force update the model when the extruder stack changes # Force update the model when the extruder stack changes
self._update() self._onChanged()
def setExtruderPosition(self, position: int): def setExtruderPosition(self, position: int):
if self._extruder_stack is None or self._extruder_position != position: if self._extruder_stack is None or self._extruder_position != position:
@ -99,7 +113,7 @@ class BaseMaterialsModel(ListModel):
self._enabled = enabled self._enabled = enabled
if self._enabled: if self._enabled:
# ensure the data is there again. # ensure the data is there again.
self._update() self._onChanged()
self.enabledChanged.emit() self.enabledChanged.emit()
@pyqtProperty(bool, fset = setEnabled, notify = enabledChanged) @pyqtProperty(bool, fset = setEnabled, notify = enabledChanged)
@ -119,12 +133,12 @@ class BaseMaterialsModel(ListModel):
return return
if material.variant.machine.container_id != global_stack.definition.getId(): if material.variant.machine.container_id != global_stack.definition.getId():
return return
self._update() self._onChanged()
## Triggered when the list of favorite materials is changed. ## Triggered when the list of favorite materials is changed.
def _favoritesChanged(self, material_base_file: str) -> None: def _favoritesChanged(self, material_base_file: str) -> None:
if material_base_file in self._available_materials: 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 ## This is an abstract method that needs to be implemented by the specific
# models themselves. # models themselves.

View file

@ -9,14 +9,14 @@ class FavoriteMaterialsModel(BaseMaterialsModel):
def __init__(self, parent = None): def __init__(self, parent = None):
super().__init__(parent) super().__init__(parent)
cura.CuraApplication.CuraApplication.getInstance().getPreferences().preferenceChanged.connect(self._onFavoritesChanged) cura.CuraApplication.CuraApplication.getInstance().getPreferences().preferenceChanged.connect(self._onFavoritesChanged)
self._update() self._onChanged()
## Triggered when any preference changes, but only handles it when the list ## Triggered when any preference changes, but only handles it when the list
# of favourites is changed. # of favourites is changed.
def _onFavoritesChanged(self, preference_key: str) -> None: def _onFavoritesChanged(self, preference_key: str) -> None:
if preference_key != "cura/favorite_materials": if preference_key != "cura/favorite_materials":
return return
self._update() self._onChanged()
def _update(self): def _update(self):
if not self._canUpdate(): if not self._canUpdate():

View file

@ -7,7 +7,7 @@ class GenericMaterialsModel(BaseMaterialsModel):
def __init__(self, parent = None): def __init__(self, parent = None):
super().__init__(parent) super().__init__(parent)
self._update() self._onChanged()
def _update(self): def _update(self):
if not self._canUpdate(): if not self._canUpdate():