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.
- StructuralGT/__init__.py +31 -0
- StructuralGT/apps/__init__.py +0 -0
- StructuralGT/apps/cli_main.py +258 -0
- StructuralGT/apps/gui_main.py +69 -0
- StructuralGT/apps/gui_mcw/__init__.py +0 -0
- StructuralGT/apps/gui_mcw/checkbox_model.py +91 -0
- StructuralGT/apps/gui_mcw/controller.py +1073 -0
- StructuralGT/apps/gui_mcw/image_provider.py +74 -0
- StructuralGT/apps/gui_mcw/imagegrid_model.py +75 -0
- StructuralGT/apps/gui_mcw/qthread_worker.py +102 -0
- StructuralGT/apps/gui_mcw/table_model.py +79 -0
- StructuralGT/apps/gui_mcw/tree_model.py +154 -0
- StructuralGT/apps/sgt_qml/CenterMainContent.qml +19 -0
- StructuralGT/apps/sgt_qml/LeftContent.qml +48 -0
- StructuralGT/apps/sgt_qml/MainWindow.qml +762 -0
- StructuralGT/apps/sgt_qml/RightLoggingPanel.qml +125 -0
- StructuralGT/apps/sgt_qml/assets/icons/.DS_Store +0 -0
- StructuralGT/apps/sgt_qml/assets/icons/back_icon.png +0 -0
- StructuralGT/apps/sgt_qml/assets/icons/brightness_icon.png +0 -0
- StructuralGT/apps/sgt_qml/assets/icons/cancel_icon.png +0 -0
- StructuralGT/apps/sgt_qml/assets/icons/crop_icon.png +0 -0
- StructuralGT/apps/sgt_qml/assets/icons/edit_icon.png +0 -0
- StructuralGT/apps/sgt_qml/assets/icons/graph_icon.png +0 -0
- StructuralGT/apps/sgt_qml/assets/icons/hide_panel.png +0 -0
- StructuralGT/apps/sgt_qml/assets/icons/next_icon.png +0 -0
- StructuralGT/apps/sgt_qml/assets/icons/notify_icon.png +0 -0
- StructuralGT/apps/sgt_qml/assets/icons/rescale_icon.png +0 -0
- StructuralGT/apps/sgt_qml/assets/icons/show_panel.png +0 -0
- StructuralGT/apps/sgt_qml/assets/icons/square_icon.png +0 -0
- StructuralGT/apps/sgt_qml/assets/icons/undo_icon.png +0 -0
- StructuralGT/apps/sgt_qml/components/ImageFilters.qml +82 -0
- StructuralGT/apps/sgt_qml/components/ImageProperties.qml +112 -0
- StructuralGT/apps/sgt_qml/components/ProjectNav.qml +127 -0
- StructuralGT/apps/sgt_qml/widgets/BinaryFilterWidget.qml +151 -0
- StructuralGT/apps/sgt_qml/widgets/BrightnessControlWidget.qml +103 -0
- StructuralGT/apps/sgt_qml/widgets/CreateProjectWidget.qml +112 -0
- StructuralGT/apps/sgt_qml/widgets/GTWidget.qml +94 -0
- StructuralGT/apps/sgt_qml/widgets/GraphComputeWidget.qml +77 -0
- StructuralGT/apps/sgt_qml/widgets/GraphExtractWidget.qml +175 -0
- StructuralGT/apps/sgt_qml/widgets/GraphPropertyWidget.qml +77 -0
- StructuralGT/apps/sgt_qml/widgets/ImageFilterWidget.qml +137 -0
- StructuralGT/apps/sgt_qml/widgets/ImagePropertyWidget.qml +78 -0
- StructuralGT/apps/sgt_qml/widgets/ImageViewWidget.qml +585 -0
- StructuralGT/apps/sgt_qml/widgets/MenuBarWidget.qml +137 -0
- StructuralGT/apps/sgt_qml/widgets/MicroscopyPropertyWidget.qml +80 -0
- StructuralGT/apps/sgt_qml/widgets/ProjectWidget.qml +141 -0
- StructuralGT/apps/sgt_qml/widgets/RescaleControlWidget.qml +83 -0
- StructuralGT/apps/sgt_qml/widgets/RibbonWidget.qml +406 -0
- StructuralGT/apps/sgt_qml/widgets/StatusBarWidget.qml +173 -0
- StructuralGT/compute/__init__.py +0 -0
- StructuralGT/compute/c_lang/include/sgt_base.h +21 -0
- StructuralGT/compute/graph_analyzer.py +1499 -0
- StructuralGT/entrypoints.py +49 -0
- StructuralGT/imaging/__init__.py +0 -0
- StructuralGT/imaging/base_image.py +403 -0
- StructuralGT/imaging/image_processor.py +780 -0
- StructuralGT/modules.py +29 -0
- StructuralGT/networks/__init__.py +0 -0
- StructuralGT/networks/fiber_network.py +490 -0
- StructuralGT/networks/graph_skeleton.py +425 -0
- StructuralGT/networks/sknw_mod.py +199 -0
- StructuralGT/utils/__init__.py +0 -0
- StructuralGT/utils/config_loader.py +244 -0
- StructuralGT/utils/configs.ini +97 -0
- StructuralGT/utils/progress_update.py +67 -0
- StructuralGT/utils/sgt_utils.py +291 -0
- sgtlib-3.3.9.dist-info/METADATA +789 -0
- sgtlib-3.3.9.dist-info/RECORD +72 -0
- sgtlib-3.3.9.dist-info/WHEEL +5 -0
- sgtlib-3.3.9.dist-info/entry_points.txt +3 -0
- sgtlib-3.3.9.dist-info/licenses/LICENSE +674 -0
- sgtlib-3.3.9.dist-info/top_level.txt +1 -0
StructuralGT/__init__.py
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
"""
|
2
|
+
**StructuralGT**
|
3
|
+
|
4
|
+
A software package for performing Graph Theory on microscopic TEM images. This software is a \
|
5
|
+
modified version of StructuralGT by Drew A. Vecchio: https://github.com/drewvecchio/StructuralGT.
|
6
|
+
|
7
|
+
Copyright (C) 2025, the Regents of the University of Michigan.
|
8
|
+
|
9
|
+
This program is free software: you can redistribute it and/or modify \
|
10
|
+
it under the terms of the GNU General Public License as published by \
|
11
|
+
the Free Software Foundation, either version 3 of the License, or \
|
12
|
+
(at your option) any later version. This program is distributed in \
|
13
|
+
the hope that it will be useful, but WITHOUT ANY WARRANTY; without \
|
14
|
+
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. \
|
15
|
+
See the GNU General Public License for more details. You should have received a copy \
|
16
|
+
of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
Development Lead: Dickson Owuor
|
19
|
+
|
20
|
+
Contributors: Nicholas A. Kotov
|
21
|
+
|
22
|
+
Contact email: owuordickson@gmail.com
|
23
|
+
"""
|
24
|
+
|
25
|
+
|
26
|
+
# Project Details
|
27
|
+
__version__ = "3.3.9"
|
28
|
+
__install_version__ = "3.3.9"
|
29
|
+
__title__ = f"StructuralGT (v{__version__})"
|
30
|
+
__author__ = "Dickson Owuor"
|
31
|
+
__credits__ = "The Regents of the University of Michigan"
|
File without changes
|
@@ -0,0 +1,258 @@
|
|
1
|
+
# SPDX-License-Identifier: GNU GPL v3
|
2
|
+
|
3
|
+
"""
|
4
|
+
Terminal interface implementations
|
5
|
+
"""
|
6
|
+
|
7
|
+
import os
|
8
|
+
import sys
|
9
|
+
import logging
|
10
|
+
from optparse import OptionParser
|
11
|
+
|
12
|
+
from ..utils.sgt_utils import verify_path, AbortException
|
13
|
+
from ..utils.config_loader import strict_read_config_file
|
14
|
+
from ..imaging.image_processor import ImageProcessor, ALLOWED_IMG_EXTENSIONS
|
15
|
+
from ..compute.graph_analyzer import GraphAnalyzer
|
16
|
+
|
17
|
+
logger = logging.getLogger("SGT App")
|
18
|
+
# logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s", stream=sys.stdout)
|
19
|
+
|
20
|
+
class TerminalApp:
|
21
|
+
"""Exposes the terminal interface for StructuralGT."""
|
22
|
+
|
23
|
+
def __init__(self, config_path: str):
|
24
|
+
"""
|
25
|
+
Exposes methods for running StructuralGT tasks
|
26
|
+
:param config_path: the path to the configuration file
|
27
|
+
"""
|
28
|
+
# Create graph objects
|
29
|
+
self.config_file = config_path
|
30
|
+
self.allow_auto_scale = True
|
31
|
+
self.sgt_objs = {}
|
32
|
+
|
33
|
+
def create_sgt_object(self, img_path, out_dir):
|
34
|
+
"""
|
35
|
+
A function that processes a selected image file and creates an analyzer object with default configurations.
|
36
|
+
|
37
|
+
Args:
|
38
|
+
img_path (str): file path to image
|
39
|
+
out_dir (str): file path to the output folder.
|
40
|
+
|
41
|
+
Returns:
|
42
|
+
"""
|
43
|
+
|
44
|
+
success, result = verify_path(img_path)
|
45
|
+
if success:
|
46
|
+
img_path = result
|
47
|
+
if out_dir != "":
|
48
|
+
path_ok, new_path = verify_path(out_dir)
|
49
|
+
out_dir = new_path if path_ok else ""
|
50
|
+
else:
|
51
|
+
logging.info(result, extra={'user': 'SGT Logs'})
|
52
|
+
return False
|
53
|
+
|
54
|
+
# Create an SGT object as a GraphAnalyzer object.
|
55
|
+
try:
|
56
|
+
ntwk_p, img_file = ImageProcessor.create_imp_object(img_path, out_dir, self.config_file, self.allow_auto_scale)
|
57
|
+
sgt_obj = GraphAnalyzer(ntwk_p)
|
58
|
+
self.sgt_objs[img_file] = sgt_obj
|
59
|
+
return True
|
60
|
+
except Exception as err:
|
61
|
+
logging.exception("File Error: %s", err, extra={'user': 'SGT Logs'})
|
62
|
+
return False
|
63
|
+
|
64
|
+
def get_selected_sgt_obj(self, obj_index: int = 0):
|
65
|
+
"""
|
66
|
+
Retrieve the SGT object at a specified index.
|
67
|
+
Args:
|
68
|
+
obj_index: index of the SGT object to retrieve
|
69
|
+
"""
|
70
|
+
try:
|
71
|
+
keys_list = list(self.sgt_objs.keys())
|
72
|
+
key_at_index = keys_list[obj_index]
|
73
|
+
sgt_obj = self.sgt_objs[key_at_index]
|
74
|
+
return sgt_obj
|
75
|
+
except IndexError:
|
76
|
+
logging.info("No Image Error: Please import/add an image.", extra={'user': 'SGT Logs'})
|
77
|
+
return None
|
78
|
+
|
79
|
+
def add_single_image(self, image_path, output_dir):
|
80
|
+
"""
|
81
|
+
Verify and validate an image path, use it to create an SGT object
|
82
|
+
|
83
|
+
:param image_path: image path to be processed
|
84
|
+
:param output_dir: output directory for saving output files
|
85
|
+
:return: bool result of SGT object creation
|
86
|
+
"""
|
87
|
+
is_created = self.create_sgt_object(image_path, output_dir)
|
88
|
+
if not is_created:
|
89
|
+
logging.info("Fatal Error: Unable to create SGT object", extra={'user': 'SGT Logs'})
|
90
|
+
return is_created
|
91
|
+
|
92
|
+
def add_multiple_images(self, img_dir_path, output_dir):
|
93
|
+
"""
|
94
|
+
Verify and validate multiple image paths, use each to create an SGT object.
|
95
|
+
"""
|
96
|
+
|
97
|
+
success, result = verify_path(img_dir_path)
|
98
|
+
if success:
|
99
|
+
img_dir_path = result
|
100
|
+
else:
|
101
|
+
logging.info(result, extra={'user': 'SGT Logs'})
|
102
|
+
return False
|
103
|
+
|
104
|
+
files = os.listdir(img_dir_path)
|
105
|
+
files = sorted(files)
|
106
|
+
for a_file in files:
|
107
|
+
allowed_extensions = tuple(ext[1:] if ext.startswith('*.') else ext for ext in ALLOWED_IMG_EXTENSIONS)
|
108
|
+
if a_file.endswith(allowed_extensions):
|
109
|
+
img_path = os.path.join(str(img_dir_path), a_file)
|
110
|
+
self.create_sgt_object(img_path, output_dir)
|
111
|
+
|
112
|
+
if len(self.sgt_objs) <= 0:
|
113
|
+
logging.info("File Error: Files have to be either .tif .png .jpg .jpeg", extra={'user': 'SGT Logs'})
|
114
|
+
return False
|
115
|
+
else:
|
116
|
+
return True
|
117
|
+
|
118
|
+
def task_extract_graph(self, selected_index: int = 0):
|
119
|
+
""""""
|
120
|
+
sgt_obj = self.get_selected_sgt_obj(obj_index=selected_index)
|
121
|
+
ntwk_p = sgt_obj.ntwk_p
|
122
|
+
try:
|
123
|
+
ntwk_p.abort = False
|
124
|
+
ntwk_p.add_listener(TerminalApp.update_progress)
|
125
|
+
ntwk_p.apply_img_filters()
|
126
|
+
ntwk_p.build_graph_network()
|
127
|
+
ntwk_p.remove_listener(TerminalApp.update_progress)
|
128
|
+
if ntwk_p.abort:
|
129
|
+
raise AbortException("Process aborted")
|
130
|
+
TerminalApp.update_progress(100, "Graph successfully extracted!")
|
131
|
+
return ntwk_p
|
132
|
+
except AbortException as err:
|
133
|
+
logging.exception("Task Aborted: %s", err, extra={'user': 'SGT Logs'})
|
134
|
+
# Clean up listeners before exiting
|
135
|
+
ntwk_p.remove_listener(TerminalApp.update_progress)
|
136
|
+
# Emit failure signal (aborted)
|
137
|
+
msg = "Graph extraction aborted due to error! Change image filters and/or graph settings and try again. If error persists then close the app and try again"
|
138
|
+
logging.info(f"Extract Graph Aborted: {msg}", extra={'user': 'SGT Logs'})
|
139
|
+
return None
|
140
|
+
|
141
|
+
def task_compute_gt(self, selected_index: int = 0):
|
142
|
+
""""""
|
143
|
+
sgt_obj = self.get_selected_sgt_obj(obj_index=selected_index)
|
144
|
+
success, new_sgt = GraphAnalyzer.safe_run_analyzer(sgt_obj, TerminalApp.update_progress)
|
145
|
+
if success:
|
146
|
+
GraphAnalyzer.write_to_pdf(new_sgt, TerminalApp.update_progress)
|
147
|
+
return new_sgt
|
148
|
+
else:
|
149
|
+
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."
|
150
|
+
logging.info(f"SGT Computations Failed: {msg}", extra={'user': 'SGT Logs'})
|
151
|
+
return None
|
152
|
+
|
153
|
+
def task_compute_multi_gt(self):
|
154
|
+
""""""
|
155
|
+
new_sgt_objs = GraphAnalyzer.safe_run_multi_analyzer(self.sgt_objs, TerminalApp.update_progress)
|
156
|
+
if new_sgt_objs is None:
|
157
|
+
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."
|
158
|
+
logging.info(f"SGT Computations Failed: {msg}", extra={'user': 'SGT Logs'})
|
159
|
+
return new_sgt_objs
|
160
|
+
|
161
|
+
@staticmethod
|
162
|
+
def update_progress(progress_val, msg):
|
163
|
+
"""
|
164
|
+
Simple method to display progress updates.
|
165
|
+
|
166
|
+
Args:
|
167
|
+
progress_val (int): progress value
|
168
|
+
msg (str): progress message
|
169
|
+
Returns:
|
170
|
+
None:
|
171
|
+
"""
|
172
|
+
|
173
|
+
if 0 <= progress_val <= 100:
|
174
|
+
print(f"{progress_val} %: {msg}")
|
175
|
+
logging.info(f"{progress_val} %: {msg}", extra={'user': 'SGT Logs'})
|
176
|
+
elif progress_val > 100:
|
177
|
+
print(f"{msg}")
|
178
|
+
logging.info(f"{msg}", extra={'user': 'SGT Logs'})
|
179
|
+
else:
|
180
|
+
print(f"Error: {msg}")
|
181
|
+
logging.exception(f"{msg}", extra={'user': 'SGT Logs'})
|
182
|
+
|
183
|
+
@classmethod
|
184
|
+
def execute(cls):
|
185
|
+
"""Initializes and starts the terminal/CMD the StructuralGT application."""
|
186
|
+
|
187
|
+
# Retrieve user settings
|
188
|
+
opt_parser = OptionParser()
|
189
|
+
opt_parser.add_option('-f', '--inputFile',
|
190
|
+
dest='img_path',
|
191
|
+
help='path to image file',
|
192
|
+
default="",
|
193
|
+
type='string')
|
194
|
+
opt_parser.add_option('-d', '--inputDir',
|
195
|
+
dest='img_dir_path',
|
196
|
+
help='path to folder containing images',
|
197
|
+
default="",
|
198
|
+
type='string')
|
199
|
+
opt_parser.add_option('-o', '--outputDir',
|
200
|
+
dest='output_dir',
|
201
|
+
help='path to folder for saving output files. If not provided, output files will be saved in input dir.',
|
202
|
+
default="",
|
203
|
+
type='string')
|
204
|
+
opt_parser.add_option('-s', '--allowAutoScale',
|
205
|
+
dest='auto_scale',
|
206
|
+
help='allow automatic scaling of images',
|
207
|
+
default=1,
|
208
|
+
type='int')
|
209
|
+
opt_parser.add_option('-t', '--runTask',
|
210
|
+
dest='run_task',
|
211
|
+
help='you can run the following tasks: (1) extract graph; (2) compute GT metrics.',
|
212
|
+
default=2,
|
213
|
+
type='int')
|
214
|
+
opt_parser.add_option('-c', '--config',
|
215
|
+
dest='config_file',
|
216
|
+
help='path to config file',
|
217
|
+
default="",
|
218
|
+
type='string')
|
219
|
+
# opt_parser.add_option('-m', '--runMultiGT',
|
220
|
+
# dest='run_multi_gt',
|
221
|
+
# help='run compute GT parameters on multiple images',
|
222
|
+
# default=0,
|
223
|
+
# type='int')
|
224
|
+
# opt_parser.add_option('-i', '--selectedImgIndex',
|
225
|
+
# dest='sel_img_idx',
|
226
|
+
# help='index of selected image',
|
227
|
+
# default=0,
|
228
|
+
# type='int')
|
229
|
+
(cfg, args) = opt_parser.parse_args()
|
230
|
+
cfg.auto_scale = bool(cfg.auto_scale)
|
231
|
+
# cfg.run_multi_gt = bool(cfg.run_multi_gt)
|
232
|
+
|
233
|
+
# Create Terminal App
|
234
|
+
term_app = cls(cfg.config_file)
|
235
|
+
|
236
|
+
# 1. Verify config file
|
237
|
+
config_file_ok = strict_read_config_file(cfg.config_file, term_app.update_progress)
|
238
|
+
if not config_file_ok:
|
239
|
+
sys.exit('Usage: StructuralGT-cli -f datasets/InVitroBioFilm.png -c datasets/sgt_configs.ini -t 2 -o results/')
|
240
|
+
|
241
|
+
# 2. Get images and process them
|
242
|
+
if cfg.img_path != "":
|
243
|
+
term_app.add_single_image(cfg.img_path, cfg.output_dir)
|
244
|
+
elif cfg.img_dir_path != "":
|
245
|
+
term_app.add_multiple_images(cfg.img_dir_path, cfg.output_dir)
|
246
|
+
else:
|
247
|
+
term_app.update_progress(-1, "No image path/image folder provided! System will exit.")
|
248
|
+
sys.exit('System exit')
|
249
|
+
|
250
|
+
# 3. Execute specific task
|
251
|
+
if cfg.run_task == 1:
|
252
|
+
term_app.task_extract_graph()
|
253
|
+
elif cfg.run_task == 2:
|
254
|
+
run_multi_gt = True if cfg.img_dir_path != "" else False
|
255
|
+
term_app.task_compute_multi_gt() if run_multi_gt else term_app.task_compute_gt()
|
256
|
+
else:
|
257
|
+
term_app.update_progress(-1, "Invalid GT task selected! System will exit.")
|
258
|
+
sys.exit('System exit')
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# SPDX-License-Identifier: GNU GPL v3
|
2
|
+
|
3
|
+
"""
|
4
|
+
Pyside6 implementation of StructuralGT user interface.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import os
|
8
|
+
import sys
|
9
|
+
from PySide6.QtCore import QObject
|
10
|
+
from PySide6.QtWidgets import QApplication
|
11
|
+
from PySide6.QtQml import QQmlApplicationEngine
|
12
|
+
|
13
|
+
from .gui_mcw.controller import MainController
|
14
|
+
from .gui_mcw.image_provider import ImageProvider
|
15
|
+
|
16
|
+
|
17
|
+
class MainWindow(QObject):
|
18
|
+
def __init__(self):
|
19
|
+
super().__init__()
|
20
|
+
self.app = QApplication(sys.argv)
|
21
|
+
self.ui_engine = QQmlApplicationEngine()
|
22
|
+
|
23
|
+
# Register Controller for Dynamic Updates
|
24
|
+
controller = MainController(qml_app=self.app)
|
25
|
+
# Register Image Provider
|
26
|
+
self.image_provider = ImageProvider(controller)
|
27
|
+
|
28
|
+
# Test Image
|
29
|
+
# img_path = "../../../../../datasets/InVitroBioFilm.png"
|
30
|
+
# controller.imageChangedSignal.emit(0, img_path)
|
31
|
+
|
32
|
+
# Set Models in QML Context
|
33
|
+
self.ui_engine.rootContext().setContextProperty("imgThumbnailModel", controller.imgThumbnailModel)
|
34
|
+
self.ui_engine.rootContext().setContextProperty("imagePropsModel", controller.imagePropsModel)
|
35
|
+
self.ui_engine.rootContext().setContextProperty("graphPropsModel", controller.graphPropsModel)
|
36
|
+
self.ui_engine.rootContext().setContextProperty("graphComputeModel", controller.graphComputeModel)
|
37
|
+
self.ui_engine.rootContext().setContextProperty("microscopyPropsModel", controller.microscopyPropsModel)
|
38
|
+
|
39
|
+
self.ui_engine.rootContext().setContextProperty("gteTreeModel", controller.gteTreeModel)
|
40
|
+
self.ui_engine.rootContext().setContextProperty("gtcListModel", controller.gtcListModel)
|
41
|
+
self.ui_engine.rootContext().setContextProperty("exportGraphModel", controller.exportGraphModel)
|
42
|
+
self.ui_engine.rootContext().setContextProperty("imgBatchModel", controller.imgBatchModel)
|
43
|
+
self.ui_engine.rootContext().setContextProperty("imgControlModel", controller.imgControlModel)
|
44
|
+
self.ui_engine.rootContext().setContextProperty("imgBinFilterModel", controller.imgBinFilterModel)
|
45
|
+
self.ui_engine.rootContext().setContextProperty("imgFilterModel", controller.imgFilterModel)
|
46
|
+
self.ui_engine.rootContext().setContextProperty("imgScaleOptionModel", controller.imgScaleOptionModel)
|
47
|
+
self.ui_engine.rootContext().setContextProperty("saveImgModel", controller.saveImgModel)
|
48
|
+
self.ui_engine.rootContext().setContextProperty("img3dGridModel", controller.img3dGridModel)
|
49
|
+
self.ui_engine.rootContext().setContextProperty("mainController", controller)
|
50
|
+
self.ui_engine.addImageProvider("imageProvider", self.image_provider)
|
51
|
+
|
52
|
+
# Load UI
|
53
|
+
# Get the directory of the current script
|
54
|
+
qml_dir = os.path.dirname(os.path.abspath(__file__))
|
55
|
+
qml_name = 'sgt_qml/MainWindow.qml'
|
56
|
+
qml_path = os.path.join(qml_dir, qml_name)
|
57
|
+
self.ui_engine.load(qml_path)
|
58
|
+
if not self.ui_engine.rootObjects():
|
59
|
+
sys.exit(-1)
|
60
|
+
|
61
|
+
|
62
|
+
def pyside_app():
|
63
|
+
"""
|
64
|
+
Initialize and run the PySide GUI application.
|
65
|
+
Returns:
|
66
|
+
|
67
|
+
"""
|
68
|
+
main_window = MainWindow()
|
69
|
+
sys.exit(main_window.app.exec())
|
File without changes
|
@@ -0,0 +1,91 @@
|
|
1
|
+
from PySide6.QtCore import Qt, QAbstractListModel, QModelIndex, QPersistentModelIndex
|
2
|
+
|
3
|
+
# Define a simple QAbstractListModel
|
4
|
+
class CheckBoxModel(QAbstractListModel):
|
5
|
+
IdRole = Qt.ItemDataRole.UserRole + 1
|
6
|
+
TypeRole = Qt.ItemDataRole.UserRole + 2
|
7
|
+
TextRole = Qt.ItemDataRole.UserRole + 3
|
8
|
+
ValueRole = Qt.ItemDataRole.UserRole + 4
|
9
|
+
DataIdRole = Qt.ItemDataRole.UserRole + 5
|
10
|
+
DataValueRole = Qt.ItemDataRole.UserRole + 6
|
11
|
+
MinValueRole = Qt.ItemDataRole.UserRole + 7
|
12
|
+
MaxValueRole = Qt.ItemDataRole.UserRole + 8
|
13
|
+
StepSizeRole = Qt.ItemDataRole.UserRole + 9
|
14
|
+
VisibleRole = Qt.ItemDataRole.UserRole + 10
|
15
|
+
|
16
|
+
def __init__(self, data, parent=None):
|
17
|
+
super().__init__(parent)
|
18
|
+
self.list_data = data
|
19
|
+
|
20
|
+
def rowCount(self, parent=None):
|
21
|
+
return len(self.list_data)
|
22
|
+
|
23
|
+
def data(self, index, role=Qt.ItemDataRole.DisplayRole):
|
24
|
+
if not index.isValid() or index.row() >= len(self.list_data):
|
25
|
+
return None
|
26
|
+
item = self.list_data[index.row()]
|
27
|
+
if role == self.IdRole:
|
28
|
+
return item["id"]
|
29
|
+
elif role == self.TypeRole:
|
30
|
+
return item["type"]
|
31
|
+
elif role == self.TextRole:
|
32
|
+
return item["text"]
|
33
|
+
elif role == self.ValueRole:
|
34
|
+
return item["value"]
|
35
|
+
elif role == self.DataIdRole:
|
36
|
+
return item["dataId"]
|
37
|
+
elif role == self.DataValueRole:
|
38
|
+
return item["dataValue"]
|
39
|
+
elif role == self.MinValueRole:
|
40
|
+
return item["minValue"]
|
41
|
+
elif role == self.MaxValueRole:
|
42
|
+
return item["maxValue"]
|
43
|
+
elif role == self.StepSizeRole:
|
44
|
+
return item["stepSize"]
|
45
|
+
elif role == self.VisibleRole:
|
46
|
+
return item["visible"]
|
47
|
+
return None
|
48
|
+
|
49
|
+
def setData(self, index, value, role=Qt.ItemDataRole.DisplayRole):
|
50
|
+
"""
|
51
|
+
|
52
|
+
Args:
|
53
|
+
index (QModelIndex | QPersistentModelIndex):
|
54
|
+
value (int|float):
|
55
|
+
role (int):
|
56
|
+
"""
|
57
|
+
if not index.isValid() or index.row() >= len(self.list_data):
|
58
|
+
return False
|
59
|
+
|
60
|
+
if role == self.ValueRole:
|
61
|
+
self.list_data[index.row()]["value"] = value
|
62
|
+
self.dataChanged.emit(index, index, [role])
|
63
|
+
return True
|
64
|
+
if role == self.DataValueRole:
|
65
|
+
self.list_data[index.row()]["dataValue"] = value
|
66
|
+
self.dataChanged.emit(index, index, [role])
|
67
|
+
return True
|
68
|
+
return False
|
69
|
+
|
70
|
+
def reset_data(self, new_data):
|
71
|
+
self.beginResetModel()
|
72
|
+
self.list_data = new_data
|
73
|
+
self.endResetModel()
|
74
|
+
self.dataChanged.emit(self.index(0,0), self.index(len(new_data), 0),
|
75
|
+
[self.IdRole, self.TypeRole, self.TextRole, self.ValueRole, self.DataIdRole,
|
76
|
+
self.DataValueRole, self.MinValueRole, self.MaxValueRole, self.StepSizeRole,
|
77
|
+
self.VisibleRole])
|
78
|
+
|
79
|
+
def roleNames(self):
|
80
|
+
return {
|
81
|
+
self.IdRole: b"id",
|
82
|
+
self.TypeRole: b"type",
|
83
|
+
self.TextRole: b"text",
|
84
|
+
self.ValueRole: b"value",
|
85
|
+
self.DataIdRole: b"dataId",
|
86
|
+
self.DataValueRole: b"dataValue",
|
87
|
+
self.MinValueRole: b"minValue",
|
88
|
+
self.MaxValueRole: b"maxValue",
|
89
|
+
self.StepSizeRole: b"stepSize",
|
90
|
+
self.VisibleRole: b"visible"
|
91
|
+
}
|