Skip to content

Commit

Permalink
Merge pull request #414 from teksi/interlisUpdate
Browse files Browse the repository at this point in the history
Interlis update
  • Loading branch information
ponceta authored Sep 30, 2024
2 parents c7c7e36 + 6c46808 commit 44f630e
Show file tree
Hide file tree
Showing 7 changed files with 494 additions and 120 deletions.
49 changes: 33 additions & 16 deletions plugin/teksi_wastewater/interlis/gui/editors/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,33 +55,41 @@ def __init__(self, main_dialog, session, obj):
self.session = session
self.obj = obj

self._tree_widget_item = None

self.preprocess()

self.update_state()

@property
def listitem(self):
def tree_widget_item(self):
"""
The editor's listitem (created on the fly if needed)
The editor's QTreeWidgetItem (created on the fly if needed)
"""
if not hasattr(self, "_listitem"):
self._listitem = QTreeWidgetItem()
self._listitem.setCheckState(
0, Qt.Checked if self.initially_checked() else Qt.Unchecked
if self._tree_widget_item is None:
self.update_tree_widget_item()

return self._tree_widget_item

def update_tree_widget_item(self):
if self._tree_widget_item is None:
self._tree_widget_item = QTreeWidgetItem()
self._tree_widget_item.setCheckState(
self.main_dialog.Columns.NAME,
Qt.Checked if self.initially_checked() else Qt.Unchecked,
)
self.update_listitem()
return self._listitem

def update_listitem(self):
disp_id = str(
getattr(self.obj, "obj_id", getattr(self.obj, "value_en", "?"))
) # some elements may not have obj_id, such as value_lists
self.listitem.setText(0, getattr(self.obj, "identifier", disp_id))
self.listitem.setToolTip(0, disp_id)
self._tree_widget_item.setText(
self.main_dialog.Columns.NAME, getattr(self.obj, "identifier", disp_id)
)
self._tree_widget_item.setToolTip(self.main_dialog.Columns.NAME, disp_id)

self.listitem.setText(1, self.status)
self._tree_widget_item.setText(self.main_dialog.Columns.STATE, self.status)

self.listitem.setText(2, self.validity)
self._tree_widget_item.setText(self.main_dialog.Columns.VALIDITY, self.validity)
if self.status == Editor.EXISTING:
color = "lightgray"
elif self.validity == Editor.INVALID:
Expand All @@ -92,7 +100,9 @@ def update_listitem(self):
color = "lightgreen"
else:
color = "lightgray"
self.listitem.setBackground(2, QBrush(QColor(color)))
self._tree_widget_item.setBackground(
self.main_dialog.Columns.VALIDITY, QBrush(QColor(color))
)

@property
def widget(self):
Expand Down Expand Up @@ -133,12 +143,19 @@ def update_state(self):
self.status = Editor.NEW
elif obj_inspect.deleted:
self.status = Editor.DELETED
elif obj_inspect.modified:
self.status = Editor.MODIFIED
elif obj_inspect.persistent:
self.status = Editor.EXISTING
else:
self.status = Editor.UNKNOWN

# For modified use the session is_modified method (slower but more correct)
if (
self.status != Editor.NEW
and self.status != Editor.DELETED
and self.session.is_modified(self.obj)
):
self.status = Editor.MODIFIED

self.validate()

def validate(self):
Expand Down
17 changes: 0 additions & 17 deletions plugin/teksi_wastewater/interlis/gui/editors/damage_channel.py

This file was deleted.

142 changes: 109 additions & 33 deletions plugin/teksi_wastewater/interlis/gui/interlis_import_selection_dialog.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import os
import sys
from collections import defaultdict
from enum import IntEnum

from qgis.core import Qgis
from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtGui import QFont
from qgis.PyQt.QtGui import QBrush, QColor, QFont
from qgis.PyQt.QtWidgets import QDialog, QHeaderView, QMessageBox, QTreeWidgetItem
from qgis.PyQt.uic import loadUi
from qgis.utils import iface
from sqlalchemy import inspect
from sqlalchemy.orm import Session
from sqlalchemy.orm.attributes import get_history

from ...utils.qt_utils import OverrideCursor
from .editors.base import Editor
Expand All @@ -19,6 +21,17 @@


class InterlisImportSelectionDialog(QDialog):

class Columns(IntEnum):
NAME = 0
STATE = 1
VALIDITY = 2

class ColumnsDebug(IntEnum):
KEY = 0
VALUE = 1
VALUE_OLD = 2

def __init__(self, parent=None):
super().__init__(parent)
loadUi(
Expand All @@ -28,9 +41,17 @@ def __init__(self, parent=None):
self.accepted.connect(self.commit_session)
self.rejected.connect(self.rollback_session)

header = self.treeWidget.header()
header.setSectionResizeMode(QHeaderView.ResizeToContents)
header.setSectionResizeMode(0, QHeaderView.Stretch)
self.treeWidget.header().setSectionResizeMode(QHeaderView.ResizeToContents)
self.treeWidget.header().setSectionResizeMode(self.Columns.NAME, QHeaderView.Stretch)

self.debugTreeWidget.header().setSectionResizeMode(QHeaderView.ResizeToContents)
self.debugTreeWidget.header().setSectionResizeMode(
self.ColumnsDebug.KEY, QHeaderView.Interactive
)
self.debugTreeWidget.header().setSectionResizeMode(
self.ColumnsDebug.VALUE, QHeaderView.Interactive
)
self.debugTreeWidget.hideColumn(self.ColumnsDebug.VALUE_OLD)

def init_with_session(self, session: Session):
"""
Expand Down Expand Up @@ -66,22 +87,27 @@ def update_tree(self):
continue

if cls not in self.category_items:
self.category_items[cls].setText(0, cls.__name__)
self.category_items[cls].setCheckState(0, Qt.Checked)
self.category_items[cls].setText(self.Columns.NAME, cls.__name__)
self.category_items[cls].setCheckState(self.Columns.NAME, Qt.Checked)
self.category_items[cls].setFont(
0, QFont(QFont().defaultFamily(), weight=QFont.Weight.Bold)
self.Columns.NAME, QFont(QFont().defaultFamily(), weight=QFont.Weight.Bold)
)
self.treeWidget.addTopLevelItem(self.category_items[cls])

editor.update_listitem()
self.category_items[cls].addChild(editor.listitem)
editor.update_tree_widget_item()
self.category_items[cls].addChild(editor.tree_widget_item)

if editor.status != Editor.EXISTING:
self.category_items[cls].setText(self.Columns.STATE, "*")

if editor.validity != Editor.VALID:
self.treeWidget.expandItem(self.category_items[cls])

# Show counts
for cls, category_item in self.category_items.items():
category_item.setText(0, f"{cls.__name__} ({category_item.childCount()})")
category_item.setText(
self.Columns.NAME, f"{cls.__name__} ({category_item.childCount()})"
)

def item_changed(self, item, column):
"""
Expand All @@ -90,7 +116,7 @@ def item_changed(self, item, column):
(propagation to parent/children is disabled for now)
"""

checked = item.checkState(0) == Qt.Checked
checked = item.checkState(self.Columns.NAME) == Qt.Checked

# add or remove object from session
obj = self.get_obj_from_listitem(item)
Expand All @@ -100,47 +126,47 @@ def item_changed(self, item, column):
else:
self.session.expunge(obj)

checked_state = item.checkState(0)
checked_state = item.checkState(self.Columns.NAME)
if checked_state == Qt.PartiallyChecked:
return

# propagate to children
for child in [item.child(i) for i in range(item.childCount())]:
child.setCheckState(0, checked_state)
child.setCheckState(self.Columns.NAME, checked_state)

# propagate to parent
parent = item.parent()
if parent:
has_checked = False
has_unchecked = False
for sibling in [parent.child(i) for i in range(parent.childCount())]:
if sibling.checkState(0) == Qt.Checked:
if sibling.checkState(self.Columns.NAME) == Qt.Checked:
has_checked = True
if sibling.checkState(0) == Qt.Unchecked:
if sibling.checkState(self.Columns.NAME) == Qt.Unchecked:
has_unchecked = True
if has_checked and has_unchecked:
break

if has_checked and has_unchecked:
parent.setCheckState(0, Qt.PartiallyChecked)
parent.setCheckState(self.Columns.NAME, Qt.PartiallyChecked)
elif has_checked:
parent.setCheckState(0, Qt.Checked)
parent.setCheckState(self.Columns.NAME, Qt.Checked)
elif has_unchecked:
parent.setCheckState(0, Qt.Unchecked)
parent.setCheckState(self.Columns.NAME, Qt.Unchecked)
else:
# no children at all !!
parent.setCheckState(0, Qt.PartiallyChecked)
parent.setCheckState(self.Columns.NAME, Qt.PartiallyChecked)

def current_item_changed(self, current_item, previous_item):
"""
Calls refresh_widget_for_obj for the currently selected object
"""
for editor in self.editors.values():
if editor.listitem == current_item:
if editor.tree_widget_item == current_item:
self.refresh_editor(editor)
break
else:
self.debugTextEdit.clear()
self.debugTreeWidget.clear()
self.validityLabel.clear()
current_widget = self.stackedWidget.currentWidget()
if current_widget:
Expand All @@ -154,19 +180,58 @@ def refresh_editor(self, editor):
editor.update_state()

# Update the list item
editor.update_listitem()
editor.update_tree_widget_item()

# Update generic widget contents
self.debugTextEdit.clear()
self.validityLabel.clear()
self.debugTreeWidget.clear()

if editor.status == Editor.MODIFIED:
self.debugTreeWidget.showColumn(self.ColumnsDebug.VALUE_OLD)
else:
self.debugTreeWidget.hideColumn(self.ColumnsDebug.VALUE_OLD)

# Show all attributes in the debug text edit
treeWidgetItemAttributes = QTreeWidgetItem()
treeWidgetItemAttributes.setText(self.ColumnsDebug.KEY, "Attributes")
for attribute in inspect(editor.obj).mapper.column_attrs:
treeWidgetItemAttribute = QTreeWidgetItem()
treeWidgetItemAttribute.setText(self.ColumnsDebug.KEY, attribute.key)
val = getattr(editor.obj, attribute.key)
if val is not None:
treeWidgetItemAttribute.setText(self.ColumnsDebug.VALUE, f"{val}")

if editor.status == Editor.MODIFIED:

history = get_history(editor.obj, attribute.key)

value_old = None
if history.unchanged != ():
value_old = history.unchanged[0]

if history.deleted != ():
value_old = history.deleted[0]

if value_old is not None:
treeWidgetItemAttribute.setText(self.ColumnsDebug.VALUE_OLD, f"{value_old}")

if val != value_old:
brush = QBrush(QColor("orange"))
treeWidgetItemAttribute.setBackground(self.ColumnsDebug.KEY, brush)
treeWidgetItemAttribute.setBackground(self.ColumnsDebug.VALUE, brush)
treeWidgetItemAttribute.setBackground(self.ColumnsDebug.VALUE_OLD, brush)

treeWidgetItemAttributes.addChild(treeWidgetItemAttribute)

self.debugTreeWidget.addTopLevelItem(treeWidgetItemAttributes)
self.debugTreeWidget.expandItem(treeWidgetItemAttributes)

# Debug
treeWidgetItemDebug = QTreeWidgetItem()
treeWidgetItemDebug.setText(self.ColumnsDebug.KEY, "Debug")

# Show all attributes in the debug text edit
self.debugTextEdit.append("-- ATTRIBUTES --")
for c in inspect(editor.obj).mapper.column_attrs:
val = getattr(editor.obj, c.key)
self.debugTextEdit.append(f"{c.key}: {val}")
# Show sqlalchemy state in the debug text edit
self.debugTextEdit.append("-- SQLALCHEMY STATUS --")
sqlAlchemyStates = []
for status_name in [
"transient",
"pending",
Expand All @@ -177,9 +242,20 @@ def refresh_editor(self, editor):
"expired",
]:
if getattr(inspect(editor.obj), status_name):
self.debugTextEdit.append(f"{status_name} ")
self.debugTextEdit.append("-- DEBUG --")
self.debugTextEdit.append(repr(editor.obj))
sqlAlchemyStates.append(f"{status_name}")

treeWidgetItemSqlAlchemyState = QTreeWidgetItem()
treeWidgetItemSqlAlchemyState.setText(self.ColumnsDebug.KEY, "Sql Alchemy status")
treeWidgetItemSqlAlchemyState.setText(self.ColumnsDebug.VALUE, ", ".join(sqlAlchemyStates))
treeWidgetItemDebug.addChild(treeWidgetItemSqlAlchemyState)

treeWidgetItemEditorObject = QTreeWidgetItem()
treeWidgetItemEditorObject.setText(self.ColumnsDebug.KEY, "Editor object")
treeWidgetItemEditorObject.setText(self.ColumnsDebug.VALUE, repr(editor.obj))
treeWidgetItemDebug.addChild(treeWidgetItemEditorObject)

self.debugTreeWidget.addTopLevelItem(treeWidgetItemDebug)
self.debugTreeWidget.resizeColumnToContents(self.ColumnsDebug.KEY)

# Show the validity label
self.validityLabel.setText(editor.message)
Expand Down Expand Up @@ -226,6 +302,6 @@ def rollback_session(self):

def get_obj_from_listitem(self, listitem):
for obj, editor in self.editors.items():
if editor.listitem == listitem:
if editor.tree_widget_item == listitem:
return obj
return None
Loading

0 comments on commit 44f630e

Please sign in to comment.