Welcome to mirror list, hosted at ThFree Co, Russian Federation.

CuraSceneController.py « Scene « cura - github.com/Ultimaker/Cura.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 54f1cc56f2c4f88f3c3ec78459c09425b3a23733 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
from UM.Logger import Logger

from PyQt6.QtCore import Qt, pyqtSlot, QObject, QTimer
from PyQt6.QtWidgets import QApplication

from UM.Scene.Camera import Camera
from cura.UI.ObjectsModel import ObjectsModel
from cura.Machines.Models.MultiBuildPlateModel import MultiBuildPlateModel
from cura.Scene.CuraSceneNode import CuraSceneNode

from UM.Application import Application
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Scene.SceneNode import SceneNode
from UM.Scene.Selection import Selection
from UM.Signal import Signal


class CuraSceneController(QObject):
    activeBuildPlateChanged = Signal()

    def __init__(self, objects_model: ObjectsModel, multi_build_plate_model: MultiBuildPlateModel) -> None:
        super().__init__()

        self._objects_model = objects_model
        self._multi_build_plate_model = multi_build_plate_model
        self._active_build_plate = -1

        self._last_selected_index = 0
        self._max_build_plate = 1  # default
        self._change_timer = QTimer()
        self._change_timer.setInterval(100)
        self._change_timer.setSingleShot(True)
        self._change_timer.timeout.connect(self.updateMaxBuildPlate)
        Application.getInstance().getController().getScene().sceneChanged.connect(self.updateMaxBuildPlateDelayed)

    def updateMaxBuildPlateDelayed(self, *args):
        if args:
            source = args[0]
        else:
            source = None

        if not isinstance(source, SceneNode) or isinstance(source, Camera):
            return
        self._change_timer.start()

    def updateMaxBuildPlate(self, *args):
        global_stack = Application.getInstance().getGlobalContainerStack()
        if global_stack:
            scene_has_support_meshes = self._sceneHasSupportMeshes()  # TODO: see if this can be cached

            if scene_has_support_meshes != global_stack.getProperty("support_meshes_present", "value"):
                # Adjust the setting without having the setting value in an InstanceContainer
                setting_definitions = global_stack.definition.findDefinitions(key="support_meshes_present")
                if setting_definitions:
                    # Recreate the setting definition because the default_value is readonly
                    definition_dict = setting_definitions[0].serialize_to_dict()
                    definition_dict["enabled"] = False  # The enabled property has a value that would need to be evaluated
                    definition_dict["default_value"] = scene_has_support_meshes
                    relations = setting_definitions[0].relations  # Relations are wiped when deserializing from a dict
                    setting_definitions[0].deserialize(definition_dict)

                    # Restore relations and notify them that the setting has changed
                    for relation in relations:
                        setting_definitions[0].relations.append(relation)
                        global_stack.propertyChanged.emit(relation.target.key, "enabled")

        max_build_plate = self._calcMaxBuildPlate()
        changed = False
        if max_build_plate != self._max_build_plate:
            self._max_build_plate = max_build_plate
            changed = True
        if changed:
            self._multi_build_plate_model.setMaxBuildPlate(self._max_build_plate)
            build_plates = [{"name": "Build Plate %d" % (i + 1), "buildPlateNumber": i} for i in range(self._max_build_plate + 1)]
            self._multi_build_plate_model.setItems(build_plates)
            if self._active_build_plate > self._max_build_plate:
                build_plate_number = 0
                if self._last_selected_index >= 0:  # go to the buildplate of the item you last selected
                    item = self._objects_model.getItem(self._last_selected_index)
                    if "node" in item:
                        node = item["node"]
                        build_plate_number = node.callDecoration("getBuildPlateNumber")
                self.setActiveBuildPlate(build_plate_number)
            # self.buildPlateItemsChanged.emit()  # TODO: necessary after setItems?

    def _calcMaxBuildPlate(self):
        max_build_plate = 0
        for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()):
            if node.callDecoration("isSliceable"):
                build_plate_number = node.callDecoration("getBuildPlateNumber")
                if build_plate_number is None:
                    build_plate_number = 0
                max_build_plate = max(build_plate_number, max_build_plate)
        return max_build_plate

    def _sceneHasSupportMeshes(self):
        root = Application.getInstance().getController().getScene().getRoot()
        for node in root.getAllChildren():
            if isinstance(node, CuraSceneNode):
                per_mesh_stack = node.callDecoration("getStack")
                if per_mesh_stack and per_mesh_stack.getProperty("support_mesh", "value"):
                    return True
        return False

    @pyqtSlot(int)
    def changeSelection(self, index):
        """Either select or deselect an item"""

        modifiers = QApplication.keyboardModifiers()
        ctrl_is_active = modifiers & Qt.KeyboardModifier.ControlModifier
        shift_is_active = modifiers & Qt.KeyboardModifier.ShiftModifier

        if ctrl_is_active:
            item = self._objects_model.getItem(index)
            node = item["node"]
            if Selection.isSelected(node):
                Selection.remove(node)
            else:
                Selection.add(node)
        elif shift_is_active:
            polarity = 1 if index + 1 > self._last_selected_index else -1
            for i in range(self._last_selected_index, index + polarity, polarity):
                item = self._objects_model.getItem(i)
                node = item["node"]
                Selection.add(node)
        else:
            # Single select
            item = self._objects_model.getItem(index)
            node = item["node"]
            build_plate_number = node.callDecoration("getBuildPlateNumber")
            if build_plate_number is not None and build_plate_number != -1:
                self.setActiveBuildPlate(build_plate_number)
            Selection.clear()
            Selection.add(node)

        self._last_selected_index = index

    @pyqtSlot(int)
    def setActiveBuildPlate(self, nr):
        if nr == self._active_build_plate:
            return
        Logger.debug(f"Selected build plate: {nr}")
        self._active_build_plate = nr
        Selection.clear()

        self._multi_build_plate_model.setActiveBuildPlate(nr)
        self._objects_model.setActiveBuildPlate(nr)
        self.activeBuildPlateChanged.emit()

    @staticmethod
    def createCuraSceneController():
        objects_model = Application.getInstance().getObjectsModel()
        multi_build_plate_model = Application.getInstance().getMultiBuildPlateModel()
        return CuraSceneController(objects_model = objects_model, multi_build_plate_model = multi_build_plate_model)