sgtlib 3.3.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. StructuralGT/__init__.py +31 -0
  2. StructuralGT/apps/__init__.py +0 -0
  3. StructuralGT/apps/cli_main.py +258 -0
  4. StructuralGT/apps/gui_main.py +69 -0
  5. StructuralGT/apps/gui_mcw/__init__.py +0 -0
  6. StructuralGT/apps/gui_mcw/checkbox_model.py +91 -0
  7. StructuralGT/apps/gui_mcw/controller.py +1073 -0
  8. StructuralGT/apps/gui_mcw/image_provider.py +74 -0
  9. StructuralGT/apps/gui_mcw/imagegrid_model.py +75 -0
  10. StructuralGT/apps/gui_mcw/qthread_worker.py +102 -0
  11. StructuralGT/apps/gui_mcw/table_model.py +79 -0
  12. StructuralGT/apps/gui_mcw/tree_model.py +154 -0
  13. StructuralGT/apps/sgt_qml/CenterMainContent.qml +19 -0
  14. StructuralGT/apps/sgt_qml/LeftContent.qml +48 -0
  15. StructuralGT/apps/sgt_qml/MainWindow.qml +762 -0
  16. StructuralGT/apps/sgt_qml/RightLoggingPanel.qml +125 -0
  17. StructuralGT/apps/sgt_qml/assets/icons/.DS_Store +0 -0
  18. StructuralGT/apps/sgt_qml/assets/icons/back_icon.png +0 -0
  19. StructuralGT/apps/sgt_qml/assets/icons/brightness_icon.png +0 -0
  20. StructuralGT/apps/sgt_qml/assets/icons/cancel_icon.png +0 -0
  21. StructuralGT/apps/sgt_qml/assets/icons/crop_icon.png +0 -0
  22. StructuralGT/apps/sgt_qml/assets/icons/edit_icon.png +0 -0
  23. StructuralGT/apps/sgt_qml/assets/icons/graph_icon.png +0 -0
  24. StructuralGT/apps/sgt_qml/assets/icons/hide_panel.png +0 -0
  25. StructuralGT/apps/sgt_qml/assets/icons/next_icon.png +0 -0
  26. StructuralGT/apps/sgt_qml/assets/icons/notify_icon.png +0 -0
  27. StructuralGT/apps/sgt_qml/assets/icons/rescale_icon.png +0 -0
  28. StructuralGT/apps/sgt_qml/assets/icons/show_panel.png +0 -0
  29. StructuralGT/apps/sgt_qml/assets/icons/square_icon.png +0 -0
  30. StructuralGT/apps/sgt_qml/assets/icons/undo_icon.png +0 -0
  31. StructuralGT/apps/sgt_qml/components/ImageFilters.qml +82 -0
  32. StructuralGT/apps/sgt_qml/components/ImageProperties.qml +112 -0
  33. StructuralGT/apps/sgt_qml/components/ProjectNav.qml +127 -0
  34. StructuralGT/apps/sgt_qml/widgets/BinaryFilterWidget.qml +151 -0
  35. StructuralGT/apps/sgt_qml/widgets/BrightnessControlWidget.qml +103 -0
  36. StructuralGT/apps/sgt_qml/widgets/CreateProjectWidget.qml +112 -0
  37. StructuralGT/apps/sgt_qml/widgets/GTWidget.qml +94 -0
  38. StructuralGT/apps/sgt_qml/widgets/GraphComputeWidget.qml +77 -0
  39. StructuralGT/apps/sgt_qml/widgets/GraphExtractWidget.qml +175 -0
  40. StructuralGT/apps/sgt_qml/widgets/GraphPropertyWidget.qml +77 -0
  41. StructuralGT/apps/sgt_qml/widgets/ImageFilterWidget.qml +137 -0
  42. StructuralGT/apps/sgt_qml/widgets/ImagePropertyWidget.qml +78 -0
  43. StructuralGT/apps/sgt_qml/widgets/ImageViewWidget.qml +585 -0
  44. StructuralGT/apps/sgt_qml/widgets/MenuBarWidget.qml +137 -0
  45. StructuralGT/apps/sgt_qml/widgets/MicroscopyPropertyWidget.qml +80 -0
  46. StructuralGT/apps/sgt_qml/widgets/ProjectWidget.qml +141 -0
  47. StructuralGT/apps/sgt_qml/widgets/RescaleControlWidget.qml +83 -0
  48. StructuralGT/apps/sgt_qml/widgets/RibbonWidget.qml +406 -0
  49. StructuralGT/apps/sgt_qml/widgets/StatusBarWidget.qml +173 -0
  50. StructuralGT/compute/__init__.py +0 -0
  51. StructuralGT/compute/c_lang/include/sgt_base.h +21 -0
  52. StructuralGT/compute/graph_analyzer.py +1499 -0
  53. StructuralGT/entrypoints.py +49 -0
  54. StructuralGT/imaging/__init__.py +0 -0
  55. StructuralGT/imaging/base_image.py +403 -0
  56. StructuralGT/imaging/image_processor.py +780 -0
  57. StructuralGT/modules.py +29 -0
  58. StructuralGT/networks/__init__.py +0 -0
  59. StructuralGT/networks/fiber_network.py +490 -0
  60. StructuralGT/networks/graph_skeleton.py +425 -0
  61. StructuralGT/networks/sknw_mod.py +199 -0
  62. StructuralGT/utils/__init__.py +0 -0
  63. StructuralGT/utils/config_loader.py +244 -0
  64. StructuralGT/utils/configs.ini +97 -0
  65. StructuralGT/utils/progress_update.py +67 -0
  66. StructuralGT/utils/sgt_utils.py +291 -0
  67. sgtlib-3.3.9.dist-info/METADATA +789 -0
  68. sgtlib-3.3.9.dist-info/RECORD +72 -0
  69. sgtlib-3.3.9.dist-info/WHEEL +5 -0
  70. sgtlib-3.3.9.dist-info/entry_points.txt +3 -0
  71. sgtlib-3.3.9.dist-info/licenses/LICENSE +674 -0
  72. sgtlib-3.3.9.dist-info/top_level.txt +1 -0
@@ -0,0 +1,74 @@
1
+ from PIL import Image, ImageQt # Import ImageQt for conversion
2
+ from PySide6.QtGui import QPixmap
3
+ from PySide6.QtQuick import QQuickImageProvider
4
+
5
+
6
+ class ImageProvider(QQuickImageProvider):
7
+
8
+ def __init__(self, img_controller):
9
+ super().__init__(QQuickImageProvider.ImageType.Pixmap)
10
+ self.pixmap = QPixmap()
11
+ self.img_controller = img_controller
12
+ self.img_controller.changeImageSignal.connect(self.handle_change_image)
13
+
14
+ def handle_change_image(self):
15
+ if len(self.img_controller.sgt_objs) > 0:
16
+ img_cv = None
17
+ sgt_obj = self.img_controller.get_selected_sgt_obj()
18
+ ntwk_p = sgt_obj.ntwk_p
19
+ sel_img_batch = ntwk_p.get_selected_batch()
20
+ if sel_img_batch.current_view == "binary":
21
+ ntwk_p.apply_img_filters(filter_type=2)
22
+ bin_images = [obj.img_bin for obj in sel_img_batch.images]
23
+ if self.img_controller.is_img_3d():
24
+ self.img_controller.img3dGridModel.reset_data(bin_images, sel_img_batch.selected_images)
25
+ else:
26
+ # 2D, Do not use if 3D
27
+ img_cv = bin_images[0]
28
+ elif sel_img_batch.current_view == "processed":
29
+ ntwk_p.apply_img_filters(filter_type=1)
30
+ mod_images = [obj.img_mod for obj in sel_img_batch.images]
31
+ if self.img_controller.is_img_3d():
32
+ self.img_controller.img3dGridModel.reset_data(mod_images, sel_img_batch.selected_images)
33
+ else:
34
+ # 2D, Do not use if 3D
35
+ img_cv = mod_images[0]
36
+ elif sel_img_batch.current_view == "graph":
37
+ # If any is None, start the task
38
+ if sel_img_batch.graph_obj.img_ntwk is None:
39
+ self.img_controller.run_extract_graph()
40
+ # Wait for the task to finish
41
+ return
42
+ else:
43
+ net_images = [sel_img_batch.graph_obj.img_ntwk]
44
+ self.img_controller.img3dGridModel.reset_data(net_images, sel_img_batch.selected_images)
45
+ img_cv = net_images[0]
46
+ else:
47
+ # Original
48
+ images = [obj.img_2d for obj in sel_img_batch.images]
49
+ if self.img_controller.is_img_3d():
50
+ self.img_controller.img3dGridModel.reset_data(images, sel_img_batch.selected_images)
51
+ else:
52
+ # 2D, Do not use if 3D
53
+ img_cv = images[0]
54
+
55
+ if img_cv is not None:
56
+ # Create Pixmap image
57
+ img = Image.fromarray(img_cv)
58
+ self.pixmap = ImageQt.toqpixmap(img)
59
+
60
+ # Reset graph/image configs with selected values - reloads QML
61
+ self.img_controller.update_graph_models(sgt_obj)
62
+
63
+ # Save changes to the project data file
64
+ if len(self.img_controller.sgt_objs.items()) <= 10:
65
+ self.img_controller.save_project_data()
66
+
67
+ # Acknowledge the image load and send the signal to update QML
68
+ self.img_controller.img_loaded = True
69
+ self.img_controller.imageChangedSignal.emit()
70
+ else:
71
+ self.img_controller.img_loaded = False
72
+
73
+ def requestPixmap(self, img_id, requested_size, size):
74
+ return self.pixmap
@@ -0,0 +1,75 @@
1
+ from PySide6.QtCore import Qt, QAbstractListModel
2
+ from ...utils.sgt_utils import img_to_base64
3
+
4
+
5
+ class ImageGridModel(QAbstractListModel):
6
+ IdRole = Qt.ItemDataRole.UserRole + 1
7
+ SelectedRole = Qt.ItemDataRole.UserRole + 20
8
+ ImageRole = Qt.ItemDataRole.UserRole + 21
9
+
10
+ def __init__(self, img_lst: list, selected_images: set, parent=None):
11
+ super().__init__(parent)
12
+ if len(img_lst) == 0:
13
+ self._image_data = []
14
+ return
15
+ self._image_data = [{
16
+ "id": i,
17
+ "image": img_to_base64(img_lst[i]) if img_lst[i] is not None else "",
18
+ "selected": 1 if i in selected_images else 0
19
+ } for i in range(len(img_lst))]
20
+
21
+ def rowCount(self, parent=None):
22
+ return len(self._image_data)
23
+
24
+ def data(self, index, role=Qt.ItemDataRole.DisplayRole):
25
+ if not index.isValid() or index.row() >= len(self._image_data):
26
+ return None
27
+
28
+ item = self._image_data[index.row()]
29
+ if item["image"] is None:
30
+ return None
31
+
32
+ if role == self.IdRole:
33
+ return item["id"]
34
+ elif role == self.ImageRole:
35
+ return item["image"]
36
+ elif role == self.SelectedRole:
37
+ return item["selected"]
38
+ return ""
39
+
40
+ def setData(self, index, value, role=Qt.ItemDataRole.DisplayRole):
41
+ if not index.isValid() or index.row() >= len(self._image_data):
42
+ return False
43
+
44
+ if role == self.SelectedRole:
45
+ self._image_data[index.row()]["selected"] = value
46
+ self.dataChanged.emit(index, index, [role])
47
+ return True
48
+ if role == self.ImageRole:
49
+ self._image_data[index.row()]["image"] = value
50
+ self.dataChanged.emit(index, index, [role])
51
+ return True
52
+ return False
53
+
54
+ def reset_data(self, new_data: list, selected_images: set):
55
+ """ Resets the data to be displayed. """
56
+ if new_data is None:
57
+ return
58
+ self.beginResetModel()
59
+
60
+ self._image_data = [{
61
+ "id": i,
62
+ "image": img_to_base64(new_data[i]) if new_data[i] is not None else "",
63
+ "selected": 1 if i in selected_images else 0
64
+ } for i in range(len(new_data))]
65
+
66
+ self.endResetModel()
67
+ self.dataChanged.emit(self.index(0, 0), self.index(len(new_data), 0),
68
+ [self.IdRole, self.ImageRole, self.SelectedRole])
69
+
70
+ def roleNames(self):
71
+ return {
72
+ self.IdRole: b"id",
73
+ self.ImageRole: b"image",
74
+ self.SelectedRole: b"selected",
75
+ }
@@ -0,0 +1,102 @@
1
+ import logging
2
+ from PySide6.QtCore import QObject, QThread, Signal
3
+ from ...compute.graph_analyzer import GraphAnalyzer
4
+ from ...utils.sgt_utils import AbortException
5
+
6
+
7
+
8
+ class QThreadWorker(QThread):
9
+ def __init__(self, func, args, parent=None):
10
+ super().__init__(parent)
11
+ self.func = func # Store function reference
12
+ self.args = args # Store arguments
13
+
14
+ def run(self):
15
+ if self.func:
16
+ self.func(*self.args) # Call function with arguments
17
+
18
+
19
+ class WorkerTask (QObject):
20
+
21
+ inProgressSignal = Signal(int, str) # progress-value (0-100), progress-message (str)
22
+ taskFinishedSignal = Signal(bool, object) # success/fail (True/False), result (object)
23
+
24
+ def __init__(self):
25
+ super().__init__()
26
+
27
+ def update_progress(self, value, msg):
28
+ """
29
+ Send the update_progress signal to all listeners.
30
+ Progress-value (0-100), progress-message (str)
31
+ Args:
32
+ value: progress value (0-100), (-1, if it is an error), (101, if it is the nav-control message)
33
+ msg: progress message (str)
34
+
35
+ Returns:
36
+
37
+ """
38
+ self.inProgressSignal.emit(value, msg)
39
+
40
+ def task_apply_img_filters(self, ntwk_p):
41
+ """"""
42
+ try:
43
+ ntwk_p.add_listener(self.update_progress)
44
+ ntwk_p.apply_img_filters()
45
+ ntwk_p.remove_listener(self.update_progress)
46
+ self.taskFinishedSignal.emit(True, ntwk_p)
47
+ except Exception as err:
48
+ logging.exception("Error: %s", err, extra={'user': 'SGT Logs'})
49
+ # self.abort = True
50
+ self.update_progress(-1, "Error encountered! Try again")
51
+ # Emit failure signal (aborted)
52
+ self.taskFinishedSignal.emit(False, ["Apply Filters Failed", "Fatal error while applying filters! "
53
+ "Change filter settings and try again; "
54
+ "Or, Close the app and try again."])
55
+
56
+ def task_extract_graph(self, ntwk_p):
57
+ """"""
58
+ try:
59
+ ntwk_p.abort = False
60
+ ntwk_p.add_listener(self.update_progress)
61
+ ntwk_p.apply_img_filters()
62
+ ntwk_p.build_graph_network()
63
+ if ntwk_p.abort:
64
+ raise AbortException("Process aborted")
65
+ ntwk_p.remove_listener(self.update_progress)
66
+ self.taskFinishedSignal.emit(True, ntwk_p)
67
+ except AbortException as err:
68
+ logging.exception("Task Aborted: %s", err, extra={'user': 'SGT Logs'})
69
+ # Clean up listeners before exiting
70
+ ntwk_p.remove_listener(self.update_progress)
71
+ # Emit failure signal (aborted)
72
+ self.taskFinishedSignal.emit(False, ["Extract Graph Aborted", "Graph extraction aborted due to error! "
73
+ "Change image filters and/or graph settings "
74
+ "and try again. If error persists then close "
75
+ "the app and try again."])
76
+ except Exception as err:
77
+ logging.exception("Error: %s", err, extra={'user': 'SGT Logs'})
78
+ self.update_progress(-1, "Error encountered! Try again")
79
+ # Clean up listeners before exiting
80
+ ntwk_p.remove_listener(self.update_progress)
81
+ # Emit failure signal (aborted)
82
+ self.taskFinishedSignal.emit(False, ["Extract Graph Failed", "Graph extraction aborted due to error! "
83
+ "Change image filters and/or graph settings "
84
+ "and try again. If error persists then close "
85
+ "the app and try again."])
86
+
87
+ def task_compute_gt(self, sgt_obj):
88
+ """"""
89
+ success, new_sgt = GraphAnalyzer.safe_run_analyzer(sgt_obj, self.update_progress)
90
+ if success:
91
+ self.taskFinishedSignal.emit(False, new_sgt)
92
+ else:
93
+ self.taskFinishedSignal.emit(False, ["SGT Computations Failed", "Fatal error occurred while computing GT parameters. Change image filters and/or graph settings and try again. If error persists then close the app and try again."])
94
+
95
+ def task_compute_multi_gt(self, sgt_objs):
96
+ """"""
97
+ new_sgt_objs = GraphAnalyzer.safe_run_multi_analyzer(sgt_objs, self.update_progress)
98
+ if new_sgt_objs is not None:
99
+ self.taskFinishedSignal.emit(True, sgt_objs)
100
+ else:
101
+ msg = "Either task was aborted by user or a fatal error occurred while computing GT parameters. Change image filters and/or graph settings and try again. If error persists then close the app and try again."
102
+ self.taskFinishedSignal.emit(False, ["SGT Computations Aborted/Failed", msg])
@@ -0,0 +1,79 @@
1
+ from PySide6.QtCore import QAbstractTableModel, QModelIndex, Qt
2
+
3
+
4
+
5
+ # Define a simple table model
6
+ class TableModel(QAbstractTableModel):
7
+ SelectedRole = Qt.ItemDataRole.UserRole + 20
8
+
9
+ def __init__(self, data, parent=None):
10
+ super().__init__(parent)
11
+ self.itemData = data
12
+ self.imageCache = {}
13
+ self.selected_index = -1 # To track the selected row
14
+
15
+ def rowCount(self, parent=QModelIndex()):
16
+ return len(self.itemData) if self.itemData else 0
17
+
18
+ def columnCount(self, parent=QModelIndex()):
19
+ return len(self.itemData[0]) if self.itemData else 0
20
+
21
+ def data(self, index, role=Qt.ItemDataRole.DisplayRole):
22
+ if not index.isValid() or not (0 <= index.row() < self.rowCount()):
23
+ return None
24
+ if role == Qt.ItemDataRole.DisplayRole:
25
+ return self.itemData[index.row()][index.column()]
26
+ elif role == Qt.ItemDataRole.DecorationRole:
27
+ if len(self.imageCache) <= 0:
28
+ return None
29
+ image_name = self.itemData[index.row()][index.column()]
30
+ return self.imageCache[image_name]
31
+ elif role == self.SelectedRole:
32
+ return index.row() == self.selected_index # True if selected
33
+ return None
34
+
35
+ def set_selected(self, row):
36
+ if 0 <= row < len(self.itemData):
37
+ old_index = self.selected_index
38
+ self.selected_index = row
39
+ if old_index != -1:
40
+ self.dataChanged.emit(self.index(old_index, 0), self.index(old_index, 0), [self.SelectedRole])
41
+ self.dataChanged.emit(self.index(row, 0), self.index(row, 0), [self.SelectedRole])
42
+
43
+ def reset_data(self, item_data):
44
+ self.beginResetModel()
45
+ self.imageCache = {}
46
+ self.itemData = item_data
47
+ self.endResetModel()
48
+ self.dataChanged.emit(self.index(0, 0), self.index(len(self.itemData) - 1, 0))
49
+
50
+ def update_data(self, img_list, img_cache):
51
+ """Updates model with new images from analyze_objs"""
52
+ self.beginResetModel()
53
+ if img_list is None or img_cache is None:
54
+ # No data to add/update
55
+ self.itemData = []
56
+ self.imageCache = {}
57
+ self.endResetModel()
58
+ # Emit dataChanged signal to notify QML
59
+ self.dataChanged.emit(self.index(0, 0), self.index(len(self.itemData) - 1, 0))
60
+ return
61
+
62
+ start_row = len(self.itemData)
63
+ self.itemData = img_list
64
+ self.imageCache = img_cache
65
+ self.endResetModel()
66
+ # Emit dataChanged signal to notify QML
67
+ self.dataChanged.emit(self.index(start_row, 0), self.index(len(self.itemData) - 1, 0))
68
+
69
+ def roleNames(self):
70
+ return {
71
+ Qt.ItemDataRole.DisplayRole: b"text",
72
+ Qt.ItemDataRole.DecorationRole: b"thumbnail",
73
+ self.SelectedRole: b"selected",
74
+ }
75
+
76
+ def headerData(self, section, orientation, role=Qt.ItemDataRole.DisplayRole):
77
+ if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
78
+ return f"Column {section + 1}"
79
+ return super().headerData(section, orientation, role)
@@ -0,0 +1,154 @@
1
+ from PySide6.QtCore import Qt, QAbstractItemModel, QModelIndex
2
+
3
+
4
+ class TreeItem:
5
+ """ Represents a single item in the tree. """
6
+ def __init__(self, data, parent=None):
7
+ self._data = data # Dictionary of data (id, text, value)
8
+ self._parent = parent
9
+ self.children = []
10
+
11
+ def append_child(self, child):
12
+ """ Adds a child node to this item. """
13
+ self.children.append(child)
14
+
15
+ def child(self, row):
16
+ """ Returns the child at the specified row. """
17
+ return self.children[row] if row < len(self.children) else None
18
+
19
+ def child_count(self):
20
+ """ Returns the number of children. """
21
+ return len(self.children)
22
+
23
+ def column_count(self):
24
+ """ Returns the number of columns (3: id, text, value). """
25
+ return len(self._data)
26
+
27
+ def data(self, column):
28
+ """ Returns the data for a specific column. """
29
+ if column == 0:
30
+ return self._data.get("id", "")
31
+ elif column == 1:
32
+ return self._data.get("text", "")
33
+ elif column == 2:
34
+ return self._data.get("value", 0)
35
+ return None
36
+
37
+ def set_data(self, column, value):
38
+ """ Sets the data for a specific column. """
39
+ if column == 0:
40
+ self._data["id"] = value
41
+ elif column == 1:
42
+ self._data["text"] = value
43
+ elif column == 2:
44
+ self._data["value"] = value
45
+ return True
46
+
47
+ def parent(self):
48
+ """ Returns the parent of this item. """
49
+ return self._parent
50
+
51
+ def row(self):
52
+ """ Returns this item's index in its parent's children list. """
53
+ if self._parent:
54
+ return self._parent.children.index(self)
55
+ return 0
56
+
57
+
58
+ class TreeModel(QAbstractItemModel):
59
+ """ QAbstractItemModel for displaying hierarchical data in QML. """
60
+ # Qt.UserRole
61
+ IdRole = Qt.ItemDataRole.UserRole + 1
62
+ TextRole = Qt.ItemDataRole.UserRole + 2
63
+ ValueRole = Qt.ItemDataRole.UserRole + 3
64
+
65
+ def __init__(self, data, parent=None):
66
+ super().__init__(parent)
67
+ self._rootItem = TreeItem({"id": "root", "text": "Root", "value": None})
68
+ self.setup_model_data(data, self._rootItem)
69
+ # self.
70
+
71
+ def setup_model_data(self, data_list, parent):
72
+ """ Populates the model with data from a list of dictionaries. """
73
+ for item_data in data_list:
74
+ item = TreeItem(item_data, parent)
75
+ parent.append_child(item)
76
+ if 'items' in item_data:
77
+ self.setup_model_data(item_data['items'], item)
78
+
79
+ def rowCount(self, parent=QModelIndex()):
80
+ """ Returns the children count of the given parent. """
81
+ if not parent.isValid():
82
+ return self._rootItem.child_count()
83
+ return parent.internalPointer().child_count()
84
+
85
+ def columnCount(self, parent=QModelIndex()):
86
+ """ Returns the number of columns (fixed at 3). """
87
+ return 1
88
+
89
+ def data(self, index, role=Qt.ItemDataRole.DisplayRole):
90
+ """ Returns the data to be displayed. """
91
+ if not index.isValid():
92
+ return None
93
+ item = index.internalPointer()
94
+ # if role == Qt.DisplayRole:
95
+ # return item.data(index.column())
96
+ if role == self.IdRole:
97
+ return item.data(0) # id
98
+ elif role == self.TextRole:
99
+ return item.data(1) # text
100
+ elif role == self.ValueRole:
101
+ return item.data(2) # value
102
+ return None
103
+
104
+ def setData(self, index, value, role=Qt.ItemDataRole.EditRole):
105
+ if not index.isValid():
106
+ return False
107
+ item = index.internalPointer()
108
+ col = 2 # for valueRole
109
+ if item.set_data(col, value):
110
+ self.dataChanged.emit(index, index, [role])
111
+ return True
112
+ return False
113
+
114
+ def reset_data(self, new_data):
115
+ """ Resets the data to be displayed. """
116
+ self.beginResetModel()
117
+ self._rootItem = TreeItem({"id": "root", "text": "Root", "value": None})
118
+ self.setup_model_data(new_data, self._rootItem)
119
+ self.endResetModel()
120
+ self.dataChanged.emit(self.index(0,0), self.index(len(new_data), 0),
121
+ [self.IdRole, self.TextRole, self.ValueRole])
122
+
123
+ def parent(self, index=QModelIndex()):
124
+ """ Returns the parent index of a given child index. """
125
+ if not index.isValid():
126
+ return QModelIndex()
127
+
128
+ item = index.internalPointer()
129
+ parent_item = item.parent()
130
+
131
+ if parent_item == self._rootItem:
132
+ return QModelIndex()
133
+
134
+ return self.createIndex(parent_item.row(), 0, parent_item)
135
+
136
+ def index(self, row, column, parent=QModelIndex()):
137
+ """ Returns the index of a given row and column. """
138
+ if not self.hasIndex(row, column, parent):
139
+ return QModelIndex()
140
+
141
+ parent_item = self._rootItem if not parent.isValid() else parent.internalPointer()
142
+ child_item = parent_item.child(row)
143
+
144
+ if child_item:
145
+ return self.createIndex(row, column, child_item)
146
+
147
+ return QModelIndex()
148
+
149
+ def roleNames(self):
150
+ return {
151
+ self.IdRole: b"id",
152
+ self.TextRole: b"text",
153
+ self.ValueRole: b"value",
154
+ }
@@ -0,0 +1,19 @@
1
+ import QtQuick
2
+ import QtQuick.Controls
3
+ import QtQuick.Layouts
4
+ import "widgets"
5
+
6
+ Rectangle {
7
+ width: parent.width
8
+ height: parent.height
9
+ color: "#f0f0f0"
10
+
11
+ GridLayout {
12
+ anchors.fill: parent
13
+ columns: 1
14
+
15
+ ImageViewWidget{}
16
+
17
+ }
18
+
19
+ }
@@ -0,0 +1,48 @@
1
+ import QtQuick
2
+ import QtQuick.Controls
3
+ import QtQuick.Layouts
4
+ import "components"
5
+
6
+ Rectangle {
7
+ width: 300
8
+ height: parent.height
9
+ color: "#f0f0f0"
10
+ border.color: "#c0c0c0"
11
+
12
+ ColumnLayout {
13
+ anchors.fill: parent
14
+
15
+ TabBar {
16
+ id: tabBar
17
+ currentIndex: 2
18
+ Layout.fillWidth: true
19
+ TabButton { text: "Project" }
20
+ TabButton { text: "Properties" }
21
+ TabButton { text: "Filters" }
22
+ }
23
+
24
+ StackLayout {
25
+ id: stackLayout
26
+ //width: parent.width
27
+ Layout.fillWidth: true
28
+ currentIndex: tabBar.currentIndex
29
+
30
+
31
+ ProjectNav{}
32
+
33
+ ImageProperties{}
34
+
35
+ ImageFilters{}
36
+
37
+
38
+ }
39
+ }
40
+
41
+ Connections {
42
+ target: mainController
43
+
44
+ function onProjectOpenedSignal(name) {
45
+ tabBar.currentIndex = 0;
46
+ }
47
+ }
48
+ }