Optimize model field replacements
This commit is contained in:
parent
68e344ae21
commit
fd8cf4ad8d
1
TODO.md
1
TODO.md
|
@ -148,7 +148,6 @@
|
|||
## Backend
|
||||
|
||||
- Saving the room settings
|
||||
- Optimize Model item replacement
|
||||
- Refetch profile after manual profile change, don't wait for a room event
|
||||
|
||||
- Better config file format
|
||||
|
|
|
@ -8,9 +8,7 @@ from typing import (
|
|||
|
||||
from blist import blist
|
||||
|
||||
from ..pyotherside_events import (
|
||||
ModelCleared, ModelItemDeleted, ModelItemInserted,
|
||||
)
|
||||
from ..pyotherside_events import ModelCleared, ModelItemDeleted, ModelItemSet
|
||||
from . import SyncId
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
@ -61,36 +59,49 @@ class Model(MutableMapping):
|
|||
|
||||
|
||||
def __setitem__(self, key, value: "ModelItem") -> None:
|
||||
"""Merge new item with an existing one if possible, else add it.
|
||||
|
||||
If an existing item with the passed `key` is found, its fields will be
|
||||
updated with the passed `ModelItem`'s fields.
|
||||
In other cases, the item is simply added to the model.
|
||||
|
||||
This also sets the `ModelItem.parent_model` hidden attributes on
|
||||
the passed item.
|
||||
"""
|
||||
|
||||
with self._write_lock:
|
||||
existing = self._data.get(key)
|
||||
new = value
|
||||
|
||||
if existing:
|
||||
for field in new.__dataclass_fields__: # type: ignore
|
||||
# The same shared item is in _sorted_data, no need to find
|
||||
# and modify it explicitely.
|
||||
setattr(existing, field, getattr(new, field))
|
||||
return
|
||||
# Collect changed fields
|
||||
|
||||
changed_fields = {}
|
||||
|
||||
for field in new.__dataclass_fields__: # type: ignore
|
||||
changed = True
|
||||
|
||||
if existing:
|
||||
changed = getattr(new, field) != getattr(existing, field)
|
||||
|
||||
if changed:
|
||||
changed_fields[field] = new.serialize_field(field)
|
||||
|
||||
# Set parent model on new item
|
||||
|
||||
if self.sync_id:
|
||||
new.parent_model = self
|
||||
|
||||
self._data[key] = new
|
||||
index = bisect(self._sorted_data, new)
|
||||
self._sorted_data.insert(index, new)
|
||||
# Insert into sorted data
|
||||
|
||||
if self.sync_id:
|
||||
ModelItemInserted(self.sync_id, index, new)
|
||||
index_then = None
|
||||
|
||||
if existing:
|
||||
index_then = self._sorted_data.index(existing)
|
||||
del self._sorted_data[index_then]
|
||||
|
||||
index_now = bisect(self._sorted_data, new)
|
||||
self._sorted_data.insert(index_now, new)
|
||||
|
||||
# Insert into dict data
|
||||
|
||||
self._data[key] = new
|
||||
|
||||
# Emit PyOtherSide event
|
||||
|
||||
if self.sync_id and (index_then != index_now or changed_fields):
|
||||
ModelItemSet(
|
||||
self.sync_id, index_then, index_now, changed_fields,
|
||||
)
|
||||
|
||||
|
||||
def __delitem__(self, key) -> None:
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional
|
||||
|
||||
from ..pyotherside_events import ModelItemFieldChanged
|
||||
from ..pyotherside_events import ModelItemSet
|
||||
from ..utils import serialize_value_for_qml
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
@ -38,17 +38,19 @@ class ModelItem:
|
|||
with self.parent_model._write_lock:
|
||||
super().__setattr__(name, value)
|
||||
|
||||
old_index = self.parent_model._sorted_data.index(self)
|
||||
if self.parent_model.sync_id:
|
||||
index_then = self.parent_model._sorted_data.index(self)
|
||||
|
||||
self.parent_model._sorted_data.sort()
|
||||
new_index = self.parent_model._sorted_data.index(self)
|
||||
|
||||
if self.parent_model.sync_id:
|
||||
ModelItemFieldChanged(
|
||||
index_now = self.parent_model._sorted_data.index(self)
|
||||
|
||||
ModelItemSet(
|
||||
self.parent_model.sync_id,
|
||||
old_index,
|
||||
new_index,
|
||||
name,
|
||||
self.serialize_field(name),
|
||||
index_then,
|
||||
index_now,
|
||||
{name: self.serialize_field(name)},
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import TYPE_CHECKING, Any, Optional
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional
|
||||
|
||||
import pyotherside
|
||||
|
||||
|
@ -9,7 +9,6 @@ from .utils import serialize_value_for_qml
|
|||
|
||||
if TYPE_CHECKING:
|
||||
from .models import SyncId
|
||||
from .models.model_item import ModelItem
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -70,21 +69,12 @@ class ModelEvent(PyOtherSideEvent):
|
|||
|
||||
|
||||
@dataclass
|
||||
class ModelItemInserted(ModelEvent):
|
||||
"""Indicate a `ModelItem` insertion into a `Backend` `Model`."""
|
||||
class ModelItemSet(ModelEvent):
|
||||
"""Indicate `ModelItem` insert or field changes in a `Backend` `Model`."""
|
||||
|
||||
index: int = field()
|
||||
item: "ModelItem" = field()
|
||||
|
||||
|
||||
@dataclass
|
||||
class ModelItemFieldChanged(ModelEvent):
|
||||
"""Indicate a `ModelItem`'s field value change in a `Backend` `Model`."""
|
||||
|
||||
item_index_then: int = field()
|
||||
item_index_now: int = field()
|
||||
changed_field: str = field()
|
||||
field_value: Any = field()
|
||||
index_then: Optional[int] = field()
|
||||
index_now: int = field()
|
||||
fields: Dict[str, Any] = field()
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
|
@ -49,23 +49,24 @@ QtObject {
|
|||
}
|
||||
|
||||
|
||||
function onModelItemInserted(syncId, index, item) {
|
||||
// print("insert", syncId, index, item)
|
||||
ModelStore.get(syncId).insert(index, item)
|
||||
}
|
||||
function onModelItemSet(syncId, indexThen, indexNow, changedFields){
|
||||
if (indexThen === undefined) {
|
||||
print("insert", syncId, indexThen, indexNow,
|
||||
JSON.stringify(changedFields))
|
||||
ModelStore.get(syncId).insert(indexNow, changedFields)
|
||||
|
||||
|
||||
function onModelItemFieldChanged(syncId, oldIndex, newIndex, field, value){
|
||||
// print("change", syncId, oldIndex, newIndex, field, value)
|
||||
const model = ModelStore.get(syncId)
|
||||
model.setProperty(oldIndex, field, value)
|
||||
|
||||
if (oldIndex !== newIndex) model.move(oldIndex, newIndex, 1)
|
||||
} else {
|
||||
print("set", syncId, indexThen, indexNow,
|
||||
JSON.stringify(changedFields))
|
||||
const model = ModelStore.get(syncId)
|
||||
model.set(indexThen, changedFields)
|
||||
if (indexThen !== indexNow) model.move(indexThen, indexNow, 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onModelItemDeleted(syncId, index) {
|
||||
// print("del", syncId, index)
|
||||
// print("delete", syncId, index)
|
||||
ModelStore.get(syncId).remove(index)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user