• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nens / ThreeDiToolbox / #2589

19 Sep 2025 08:50AM UTC coverage: 35.01% (-0.1%) from 35.146%
#2589

push

coveralls-python

web-flow
Merge 38792c162 into f6f4be1e7

62 of 260 new or added lines in 40 files covered. (23.85%)

6 existing lines in 5 files now uncovered.

4859 of 13879 relevant lines covered (35.01%)

0.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

25.69
/tool_fraction_analysis/fraction_graph_view.py
1
from qgis.core import Qgis
1✔
2
from qgis.core import QgsFeature
1✔
3
from qgis.core import QgsFeatureRequest
1✔
4
from qgis.core import QgsProject
1✔
5
from qgis.core import QgsVectorLayer
1✔
6
from qgis.gui import QgsRubberBand
1✔
7
from qgis.PyQt.QtCore import QSize
1✔
8
from qgis.PyQt.QtCore import Qt
1✔
9
from qgis.PyQt.QtWidgets import QComboBox
1✔
10
from qgis.PyQt.QtWidgets import QHBoxLayout
1✔
11
from qgis.PyQt.QtWidgets import QSizePolicy
1✔
12
from qgis.PyQt.QtWidgets import QSplitter
1✔
13
from qgis.PyQt.QtWidgets import QVBoxLayout
1✔
14
from qgis.PyQt.QtWidgets import QWidget
1✔
15
from threedi_results_analysis.threedi_plugin_model import ThreeDiPluginModel
1✔
16
from threedi_results_analysis.threedi_plugin_model import ThreeDiResultItem
1✔
17
from threedi_results_analysis.tool_fraction_analysis.fraction_model import FractionModel
1✔
18
from threedi_results_analysis.tool_fraction_analysis.fraction_plot import FractionPlot
1✔
19
from threedi_results_analysis.tool_fraction_analysis.fraction_table import FractionTable
1✔
20
from threedi_results_analysis.utils.constants import TOOLBOX_MESSAGE_TITLE
1✔
21
from threedi_results_analysis.utils.user_messages import messagebar_message
1✔
22

23
import logging
1✔
24

25

26
logger = logging.getLogger(__name__)
1✔
27

28

29
class FractionWidget(QWidget):
1✔
30
    def __init__(
1✔
31
        self,
32
        parent,
33
        model: ThreeDiPluginModel,
34
        iface
35
    ):
36
        super().__init__(parent)
×
37

38
        self.result_model = model
×
39
        self.parent = parent
×
40
        self.iface = iface
×
41
        self.fraction_model = FractionModel(self, self.result_model)
×
42
        self.fraction_model.itemChanged.connect(self.model_item_changed)
×
43
        self.setup_ui()
×
44
        self.clear()
×
45

46
    def model_item_changed(self, item):
1✔
47
        # a plot is toggled or color is changed
NEW
48
        if item.index().column() == 0:
×
NEW
49
            self.fraction_plot.item_checked(item)
×
NEW
50
        elif item.index().column() == 1:
×
NEW
51
            self.fraction_plot.item_color_changed(item)
×
52

53
    def clear(self):
1✔
54
        self.fraction_model.clear()
×
55
        self.fraction_plot.clear_plot()
×
56

57
        self.current_result_id = None
×
58
        self.current_layer = None
×
59
        self.current_substance_unit = None
×
60
        self.current_feature_id = None
×
61
        self.current_stacked = False
×
62
        self.current_volume = False
×
63

64
    def result_selected(self, result_item: ThreeDiResultItem, substance_units):
1✔
65
        self.current_result_id = result_item.id
×
66
        self.current_feature_id = None
×
67
        self.fraction_plot.clear_plot()
×
68
        self.fraction_model.set_fraction(result_item, substance_units)
×
69

70
    def highlight_feature(self, row):
1✔
71
        if self.current_feature_id and self.current_result_id and self.current_layer:
×
72
            result_item = self.result_model.get_result(self.current_result_id)
×
73
            for table_name, layer_id in result_item.parent().layer_ids.items():
×
74
                if self.current_layer == table_name:
×
75
                    # query layer for object
76
                    filt = u'"id" = {0}'.format(self.current_feature_id)
×
77
                    request = QgsFeatureRequest().setFilterExpression(filt)
×
78
                    lyr = QgsProject.instance().mapLayer(layer_id)
×
79
                    features = lyr.getFeatures(request)
×
80
                    for feature in features:
×
81
                        self.marker.setToGeometry(feature.geometry(), lyr)
×
82

83
    def highlight_plot(self, row):
1✔
NEW
84
        if self.current_feature_id and self.current_result_id and self.current_layer:
×
NEW
85
            self.fraction_plot.highlight_plot(row)
×
86

87
    def unhighlight_all_features(self):
1✔
NEW
88
        self.fraction_plot.unhighlight_plots()
×
UNCOV
89
        self.marker.reset()
×
90

91
    def setup_ui(self):
1✔
92
        mainLayout = QHBoxLayout(self)
×
93
        self.setLayout(mainLayout)
×
94
        splitterWidget = QSplitter(self)
×
95

96
        self.fraction_plot = FractionPlot(self, self.result_model, self.fraction_model)
×
NEW
97
        sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
×
98
        sizePolicy.setHorizontalStretch(1)
×
99
        sizePolicy.setVerticalStretch(1)
×
100
        sizePolicy.setHeightForWidth(self.fraction_plot.sizePolicy().hasHeightForWidth())
×
101
        self.fraction_plot.setSizePolicy(sizePolicy)
×
102
        self.fraction_plot.setMinimumSize(QSize(250, 250))
×
103
        splitterWidget.addWidget(self.fraction_plot)
×
104

105
        legendWidget = QWidget(self)
×
106
        vLayoutTable = QVBoxLayout(self)
×
107
        vLayoutTable.setMargin(0)
×
108
        legendWidget.setLayout(vLayoutTable)
×
109

110
        self.ts_units_combo_box = QComboBox(self)
×
111
        self.ts_units_combo_box.insertItems(0, ["hrs", "mins", "s"])
×
112
        self.ts_units_combo_box.currentIndexChanged.connect(self.time_units_change)
×
113
        vLayoutTable.addWidget(self.ts_units_combo_box)
×
114
        self.fraction_table = FractionTable(self)
×
115
        self.fraction_table.hoverEnterRow.connect(self.highlight_feature)
×
NEW
116
        self.fraction_table.hoverEnterRow.connect(self.highlight_plot)
×
117
        self.fraction_table.hoverExitAllRows.connect(self.unhighlight_all_features)
×
NEW
118
        sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
×
119
        sizePolicy.setHorizontalStretch(0)
×
120
        sizePolicy.setVerticalStretch(0)
×
121
        sizePolicy.setHeightForWidth(self.fraction_table.sizePolicy().hasHeightForWidth())
×
122
        self.fraction_table.setSizePolicy(sizePolicy)
×
123
        self.fraction_table.setMinimumSize(QSize(250, 0))
×
124
        self.fraction_table.setModel(self.fraction_model)
×
125
        vLayoutTable.addWidget(self.fraction_table)
×
126
        splitterWidget.addWidget(legendWidget)
×
127
        mainLayout.addWidget(splitterWidget)
×
128
        mainLayout.setContentsMargins(0, 0, 0, 0)
×
129

130
        self.marker = QgsRubberBand(self.iface.mapCanvas())
×
NEW
131
        self.marker.setColor(Qt.GlobalColor.red)
×
132
        self.marker.setWidth(2)
×
133

134
    def time_units_change(self):
1✔
135
        self.fraction_plot.setLabel("bottom", "Time", self.ts_units_combo_box.currentText())
×
136
        if self.current_feature_id and self.current_substance_unit:
×
137
            self.fraction_plot.fraction_selected(self.current_feature_id, self.current_substance_unit, self.ts_units_combo_box.currentText(), self.current_stacked, self.current_volume)
×
138

139
    def substance_units_change(self, substance_unit):
1✔
140
        self.current_substance_unit = substance_unit
×
141
        if self.current_result_id:
×
142
            self.fraction_model.set_fraction(self.result_model.get_result(self.current_result_id), self.current_substance_unit)
×
143
        if self.current_feature_id:
×
144
            self.fraction_plot.fraction_selected(self.current_feature_id, self.current_substance_unit, self.ts_units_combo_box.currentText(), self.current_stacked, self.current_volume)
×
145

146
    def stacked_changed(self, check_state):
1✔
NEW
147
        self.current_stacked = (check_state == Qt.CheckState.Checked)
×
148
        if self.current_feature_id:
×
149
            self.fraction_plot.fraction_selected(self.current_feature_id, self.current_substance_unit, self.ts_units_combo_box.currentText(), self.current_stacked, self.current_volume)
×
150

151
    def volume_changed(self, check_state):
1✔
NEW
152
        self.current_volume = (check_state == Qt.CheckState.Checked)
×
153
        if self.current_feature_id:
×
154
            self.fraction_plot.fraction_selected(self.current_feature_id, self.current_substance_unit, self.ts_units_combo_box.currentText(), self.current_stacked, self.current_volume)
×
155

156
    def feature_selected(self, layer: QgsVectorLayer, feature: QgsFeature) -> bool:
1✔
157
        if not layer.objectName() in ("node", "cell"):
×
158
            msg = """Please select results from either the 'nodes' or 'cells' layer."""
×
NEW
159
            messagebar_message(TOOLBOX_MESSAGE_TITLE, msg, Qgis.MessageLevel.Warning, 5.0)
×
160
            return False
×
161

162
        if len(self.result_model.get_results(checked_only=False)) == 0:
×
163
            logger.warning("No results loaded")
×
164
            return False
×
165

166
        assert layer.dataProvider().name() in ["memory", "ogr"]
×
167
        new_idx = feature["id"]
×
168
        self.current_feature_id = new_idx
×
169
        result_items = self.result_model.get_results(checked_only=False)
×
170
        for result_item in result_items:
×
171
            # Check whether this layer belongs to the selected grid
172
            if layer.id() in result_item.parent().layer_ids.values():
×
173
                self.fraction_plot.fraction_selected(new_idx, self.current_substance_unit, self.ts_units_combo_box.currentText(), self.current_stacked, self.current_volume)
×
174
                self.current_result_id = result_item.id
×
175
                self.current_layer = layer.objectName()
×
176
                break
×
177

178
        return True
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc