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