Skip to content

Commit

Permalink
chore: Flatten work
Browse files Browse the repository at this point in the history
  • Loading branch information
phil65 committed Jul 5, 2023
1 parent 3587f7a commit e5874c0
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 69 deletions.
2 changes: 1 addition & 1 deletion docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
* [SliceMapRoleProxyModel](features/proxymodels/slicemaproleproxymodel.md)
* [Reshape / Styling proxies](features/reshapeproxies.md)
* [MeltProxyModel](features/proxymodels/meltproxymodel.md)
* [FlattenedTreeProxyModel](features/proxymodels/flattenedtreeproxymodel.md)
* [FlattenTreeProxyModel](features/proxymodels/flattentreeproxymodel.md)
* [ColumnJoinerProxyModel](features/proxymodels/columnjoinerproxymodel.md)
* [ColumnOrderProxyModel](features/proxymodels/columnorderproxymodel.md)
* [TableToListProxyModel](features/proxymodels/tabletolistproxymodel.md)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@ table.show()
```
or
```py
proxy = custom_models.FlattenedTreeProxyModel()
proxy = custom_models.FlattenTreeProxyModel()
proxy.set_source_model(model)
table.set_model(proxy)
table.show()
```
<figure markdown>
![Image title](../../images/flattenedtreeproxymodel.png)
<figcaption>FlattenedTreeProxyModel</figcaption>
![Image title](../../images/flattentreeproxymodel.png)
<figcaption>FlattenTreeProxyModel</figcaption>
</figure>

### API

::: prettyqt.custom_models.FlattenedTreeProxyModel
::: prettyqt.custom_models.FlattenTreeProxyModel

### Qt Properties

Expand Down
2 changes: 1 addition & 1 deletion docs/features/reshapeproxies.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
| Proxy | Description |
| -----------------------------------------------------|------------------------------------------|
|[TableToListProxyModel](tabletolistproxymodel.md) | Stack all columns into one single column |
|[FlattenedTreeProxyModel](flattenedtreeproxymodel.md) | Moves all rows up to the root level. |
|[FlattenTreeProxyModel](flattentreeproxymodel.md) | Moves all rows up to the root level. |
|[MeltProxyModel](meltproxymodel.md) |Unpivot a Table from wide to long format. |
|[ColumnOrderProxyModel](columnorderproxymodel.md) |Reorder columns and hide columns. |
|[ChangeHeadersProxyModel](changeheadersproxymodel.md) | Change horizontal / vertical headers. |
Expand Down
Binary file added docs/images/flattentreeproxymodel_path.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions prettyqt/custom_models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
# Reshape proxies

from .proxies.tabletolistproxymodel import TableToListProxyModel
from .proxies.flattenedtreeproxymodel import FlattenedTreeProxyModel
from .proxies.flattentreeproxymodel import FlattenTreeProxyModel
from .proxies.meltproxymodel import MeltProxyModel
from .proxies.columnorderproxymodel import ColumnOrderProxyModel

Expand Down Expand Up @@ -134,7 +134,7 @@
"SliceCheckableProxyModel",
"SliceCheckableTreeProxyModel",
"TableToListProxyModel",
"FlattenedTreeProxyModel",
"FlattenTreeProxyModel",
"MeltProxyModel",
"ColumnOrderProxyModel",
"SliceAppearanceProxyModel",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,20 @@
from __future__ import annotations

import enum

from prettyqt import constants, core


class FlatteningMode(enum.IntEnum):
Default = 1
InternalNodesDisabled = 2
LeavesOnly = 4


class DisplayMode(enum.IntEnum):
Path = 1
Title = 2


class FlattenedTreeProxyModel(core.AbstractProxyModel):
class FlattenTreeProxyModel(core.AbstractProxyModel):
ID = "flatten_tree"

FlatteningMode = FlatteningMode
core.Enum(FlatteningMode)

DisplayMode = DisplayMode
core.Enum(DisplayMode)

def __init__(self, parent: widgets.QWidget | None = None, **kwargs):
super().__init__(parent, **kwargs)
self._leaves_only = False
self._show_path = False
self._source_column = 0
self._flattening_mode = FlatteningMode.Default
self._display_mode = DisplayMode.Path
self.PATH_SEPARATOR = " / "
self._source_root_index = core.ModelIndex()
self._source_key: list[tuple[int, ...]] = []
self._source_offset: dict[tuple[int, ...], int] = {}
super().__init__(parent, **kwargs)

def setSourceModel(self, model: core.QAbstractItemModel):
if (old_model := self.sourceModel()) is not None:
Expand Down Expand Up @@ -65,22 +47,22 @@ def set_root_index(self, root_index: core.ModelIndex):
def get_root_index(self) -> core.ModelIndex:
return self._source_root_index

def set_flattening_mode(self, mode: FlattenedTreeProxyModel.FlatteningMode):
if mode != self._flattening_mode:
def set_leaves_only(self, leaves_only: bool):
if leaves_only != self._leaves_only:
with self.reset_model():
self._flattening_mode = mode
self._leaves_only = leaves_only
self._update_mapping()

def get_flattening_mode(self) -> FlattenedTreeProxyModel.FlatteningMode:
return self._flattening_mode
def is_leaves_only(self) -> bool:
return self._leaves_only

def set_display_mode(self, mode: FlattenedTreeProxyModel.DisplayMode):
if mode != self._display_mode:
def set_show_path(self, show: bool):
if show != self._show_path:
with self.reset_model():
self._display_mode = mode
self._show_path = show

def get_display_mode(self) -> FlattenedTreeProxyModel.DisplayMode:
return self._display_mode
def is_path_shown(self) -> bool:
return self._show_path

def mapFromSource(self, source_index: core.ModelIndex) -> core.ModelIndex:
if not source_index.isValid():
Expand All @@ -103,7 +85,7 @@ def index(
return (
core.ModelIndex()
if parent.isValid()
else self.createIndex(row, column, ptr=row) # object=row)
else self.createIndex(row, column, row) # object=row)
)

def parent(self, child=None) -> core.ModelIndex:
Expand All @@ -119,25 +101,25 @@ def columnCount(self, parent: core.ModelIndex | None = None) -> int:

def flags(self, index: core.ModelIndex) -> constants.ItemFlag:
flags = super().flags(index)
if self._flattening_mode != self.FlatteningMode.InternalNodesDisabled:
return flags
index = self.mapToSource(index)
model = self.sourceModel()
enabled = flags & constants.ItemFlag.ItemIsEnabled
if model is not None and model.rowCount(index) > 0 and enabled:
flags ^= constants.ItemFlag.ItemIsEnabled
return flags
# this would disable non-leave items
# index = self.mapToSource(index)
# model = self.sourceModel()
# enabled = flags & constants.ItemFlag.ItemIsEnabled
# if model is not None and model.rowCount(index) > 0 and enabled:
# flags ^= constants.ItemFlag.ItemIsEnabled
# return flags

def data(
self,
index: core.ModelIndex,
role: constants.ItemDataRole = constants.DISPLAY_ROLE,
):
if role == constants.DISPLAY_ROLE and self._display_mode == DisplayMode.Path:
if role == constants.DISPLAY_ROLE and self._show_path:
index = self.mapToSource(index)
model = self.sourceModel()
path = model.get_breadcrumbs_path(index)
return " / ".join(str(i) for i in path)
return self.PATH_SEPARATOR.join(str(i) for i in path)
return super().data(index, role)

def _update_mapping(self):
Expand All @@ -148,11 +130,12 @@ def _update_mapping(self):

def create_mapping(model, index: core.ModelIndex, key_path: tuple[int, ...]):
if (rowcount := model.rowCount(index)) > 0:
if self._flattening_mode != self.FlatteningMode.LeavesOnly:
if not self._leaves_only:
self._source_offset[key_path] = len(self._source_offset)
self._source_key.append(key_path)
for i in range(rowcount):
create_mapping(model, model.index(i, 0, index), (*key_path, i))
child = model.index(i, 0, index)
create_mapping(model, child, (*key_path, i))
else:
self._source_offset[key_path] = len(self._source_offset)
self._source_key.append(key_path)
Expand All @@ -176,24 +159,22 @@ def _on_row_move(
with self.reset_model():
self._update_mapping()

flatteningMode = core.Property(
FlatteningMode,
get_flattening_mode,
set_flattening_mode,
)

displayMode = core.Property(
DisplayMode,
get_display_mode,
set_display_mode,
)
leaves_only = core.Property(bool, is_leaves_only, set_leaves_only)
show_path = core.Property(bool, is_path_shown, set_show_path)


if __name__ == "__main__":
from prettyqt import debugging, widgets
from prettyqt import custom_models, widgets

app = widgets.app()
table = debugging.example_tree()
table = widgets.TreeView()
source_model = custom_models.ParentClassTreeModel(widgets.Frame)
table.set_model(source_model)
table.expandAll()
table.proxifier.flatten()
table.show()
table.resize(600, 500)
table.h_header.resize_sections("stretch")
table.set_icon("mdi6.table-pivot")
table.set_title("Flatten")
app.exec()
16 changes: 13 additions & 3 deletions prettyqt/utils/proxifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,13 +357,23 @@ def transpose(self) -> core.TransposeProxyModel:
self._widget.set_model(proxy)
return proxy

def flatten(self) -> custom_models.FlattenedTreeProxyModel:
"""Wraps model in a Proxy which flattens tree structures."""
def flatten(
self, show_path: bool = False, leaves_only: bool = False
) -> custom_models.FlattenTreeProxyModel:
"""Wraps model in a Proxy which flattens tree to one column.
Arguments:
show_path: Whether the first column should show the full tree path.
leaves_only: whether the proxied model should return only tree leaves.
"""
# ss = """QTreeView::branch{border-image: url(none.png);}"""
# self._widget.set_stylesheet(ss)
from prettyqt import custom_models

proxy = custom_models.FlattenedTreeProxyModel(parent=self._widget)
proxy = custom_models.FlattenTreeProxyModel(
parent=self._widget, show_path=show_path, leaves_only=leaves_only
)
proxy.setSourceModel(self._widget.model())
self._widget.set_model(proxy)
return proxy
Expand Down

0 comments on commit e5874c0

Please sign in to comment.