Optimize model field replacements

This commit is contained in:
miruka 2020-04-07 11:58:26 -04:00
parent 68e344ae21
commit fd8cf4ad8d
5 changed files with 64 additions and 61 deletions

View File

@ -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

View File

@ -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:

View File

@ -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)},
)

View File

@ -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

View File

@ -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)
}