This commit is contained in:
2025-11-23 23:31:18 +08:00
parent d60cdc52fd
commit 9f7667a475
710 changed files with 252869 additions and 6 deletions

View File

@@ -0,0 +1,144 @@
import uuid
from copy import deepcopy
from collections import defaultdict
from .pyside import QtCore
from .shape import Shape
from .templates import PICKER
from .undo import UndoManager
from .stack import count_panels
class PickerDocument(QtCore.QObject):
shapes_changed = QtCore.Signal()
# origin: str ["editor"|"picker"], key: str
general_option_changed = QtCore.Signal(str, str)
data_changed = QtCore.Signal()
changed = QtCore.Signal()
def __init__(self, data):
super(PickerDocument, self).__init__()
self.data = data
self.filename = None
self.modified_state = False
self.undo_manager = UndoManager(self.data)
self.shapes = []
self.shapes_by_panel = {}
self.shapes_by_id = {}
self.shapes_by_layer = {}
self.generate_shapes()
self.shapes_changed.connect(self.emit_change)
self.general_option_changed.connect(self.emit_change)
self.data_changed.connect(self.emit_change)
self.shapes_changed.connect(self.emit_change)
def emit_change(self, *_):
"""
Signal allways emitted when any data of the model changed.
"""
self.changed.emit()
@staticmethod
def create():
data = {
'general': deepcopy(PICKER),
'shapes': []}
return PickerDocument(data)
def record_undo(self):
self.undo_manager.set_data_modified(self.data)
self.modified_state = True
def undo(self):
if self.undo_manager.undo():
self.data = self.undo_manager.data
self.generate_shapes()
self.data_changed.emit()
self.modified_state = True
def redo(self):
if self.undo_manager.redo():
self.data = self.undo_manager.data
self.generate_shapes()
self.data_changed.emit()
self.modified_state = True
def panel_count(self):
return count_panels(self.data['general']['panels'])
def set_shapes_data(self, data):
self.data['shapes'] = data
self.generate_shapes()
def generate_shapes(self):
self.shapes = [Shape(options) for options in self.data['shapes']]
self.sync_shapes_caches()
def sync_shapes_caches(self):
self.shapes_by_panel = defaultdict(list)
self.shapes_by_id = {}
self.shapes_by_layer = defaultdict(list)
for shape in self.shapes:
self.shapes_by_panel[shape.options['panel']].append(shape)
self.shapes_by_id[shape.options['id']] = shape
layer = shape.options['visibility_layer']
if layer:
self.shapes_by_layer[layer].append(shape)
def add_shapes(self, shapes_data, prepend=False, hierarchize=False):
for options in shapes_data:
options['id'] = str(uuid.uuid4())
options['children'] = []
shapes = []
parent_shape = None
for options in shapes_data:
shape = Shape(options)
shapes.append(shape)
if parent_shape and hierarchize:
parent_shape.options['children'].append(shape.options['id'])
parent_shape = shape
if prepend:
for shape in reversed(shapes):
self.shapes.insert(0, shape)
self.data['shapes'].insert(0, shape.options)
else:
self.shapes.extend(shapes)
self.data['shapes'].extend(shapes_data)
self.sync_shapes_caches()
return shapes
def remove_shapes(self, shapes):
removed_ids = [shape.options['id'] for shape in shapes]
self.data['shapes'] = [
s for s in self.data['shapes'] if s['id'] not in removed_ids]
self.generate_shapes()
def all_children(self, id_):
if id_ not in self.shapes_by_id:
return []
shape = self.shapes_by_id[id_]
result = []
to_visit = [id_]
visited = set()
while to_visit:
current_id = to_visit.pop(0)
if current_id in visited:
continue
visited.add(current_id)
shape = self.shapes_by_id.get(current_id)
if shape:
result.append(shape)
children = shape.options.get('children', [])
to_visit.extend(c for c in children if c not in visited)
return result