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

Open-MSS / MSS / 14727107325

29 Apr 2025 08:45AM UTC coverage: 69.875% (-2.5%) from 72.411%
14727107325

push

github

web-flow
Reintroduce coverage upload to coveralls (#2811)

14474 of 20714 relevant lines covered (69.88%)

0.7 hits per line

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

75.56
/mslib/msui/multiple_flightpath_dockwidget.py
1
# -*- coding: utf-8 -*-
2
"""
3

4
    mslib.multiple_flightpath_dockwidget
5
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6

7
    Control Widget to configure multiple flightpath on topview.
8

9
    This file is part of MSS.
10

11
    :copyright: Copyright 2022 Jatin Jain
12
    :copyright: Copyright 2022-2025 by the MSS team, see AUTHORS.
13
    :license: APACHE-2.0, see LICENSE for details.
14

15
    Licensed under the Apache License, Version 2.0 (the "License");
16
    you may not use this file except in compliance with the License.
17
    You may obtain a copy of the License at
18

19
       http://www.apache.org/licenses/LICENSE-2.0
20

21
    Unless required by applicable law or agreed to in writing, software
22
    distributed under the License is distributed on an "AS IS" BASIS,
23
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24
    See the License for the specific language governing permissions and
25
    limitations under the License.
26
"""
27
import random
1✔
28
import requests
1✔
29
import json
1✔
30
from PyQt5 import QtWidgets, QtGui, QtCore
1✔
31
from mslib.msui.qt5 import ui_multiple_flightpath_dockwidget as ui
1✔
32
from mslib.msui import flighttrack as ft
1✔
33
import mslib.msui.msui_mainwindow as msui_mainwindow
1✔
34
from mslib.utils.verify_user_token import verify_user_token
1✔
35
from mslib.utils.qt import Worker
1✔
36
from mslib.utils.config import config_loader
1✔
37
from urllib.parse import urljoin
1✔
38
from mslib.utils.colordialog import CustomColorDialog
1✔
39

40

41
class QMscolabOperationsListWidgetItem(QtWidgets.QListWidgetItem):
1✔
42
    """
43
    """
44

45
    def __init__(self, flighttrack_model, op_id: int, parent=None, user_type=QtWidgets.QListWidgetItem.UserType):
1✔
46
        view_name = flighttrack_model.name
1✔
47
        super().__init__(
1✔
48
            view_name, parent, user_type
49
        )
50
        self.parent = parent
1✔
51
        self.flighttrack_model = flighttrack_model
1✔
52
        self.op_id = op_id
1✔
53

54

55
class MultipleFlightpath:
1✔
56
    """
57
    Represent a Multiple FLightpath
58
    """
59

60
    def __init__(self, mapcanvas, wp, linewidth=2.0, color='blue', line_transparency=1.0, line_style="solid"):
1✔
61
        self.map = mapcanvas
1✔
62
        self.flightlevel = None
1✔
63
        self.comments = ''
1✔
64
        self.patches = []
1✔
65
        self.waypoints = wp
1✔
66
        self.linewidth = linewidth
1✔
67
        self.line_transparency = line_transparency
1✔
68
        self.line_style = line_style
1✔
69
        self.color = color
1✔
70
        self.draw()
1✔
71

72
    def draw_line(self, x, y):
1✔
73
        self.patches.append(self.map.plot(x, y, color=self.color, linewidth=self.linewidth,
1✔
74
                                          alpha=self.line_transparency, linestyle=self.line_style))
75

76
    def compute_xy(self, lon, lat):
1✔
77
        x, y = self.map.gcpoints_path(lon, lat)
1✔
78
        return x, y
1✔
79

80
    def get_lonlat(self):
1✔
81
        lon = []
1✔
82
        lat = []
1✔
83
        for i in range(len(self.waypoints)):
1✔
84
            lat.append(self.waypoints[i][0])
1✔
85
            lon.append(self.waypoints[i][1])
1✔
86
        return lat, lon
1✔
87

88
    def update(self, linewidth=None, color=None, line_transparency=None, line_style=None):
1✔
89
        if linewidth is not None:
1✔
90
            self.linewidth = linewidth
×
91
        if color is not None:
1✔
92
            self.color = color
1✔
93
        if line_transparency is not None:
1✔
94
            self.line_transparency = line_transparency
×
95
        if line_style is not None:
1✔
96
            self.line_style = line_style
×
97

98
        for patch in self.patches:  # allows dynamic updating of the flight path's appearance without needing to
1✔
99
            # remove and redraw the entire path
100
            for elem in patch:
1✔
101
                elem.set_linewidth(self.linewidth)
1✔
102
                elem.set_color(self.color)
1✔
103
                elem.set_alpha(self.line_transparency)
1✔
104
                elem.set_linestyle(self.line_style)
1✔
105
        self.map.ax.figure.canvas.draw()
1✔
106

107
    def draw(self):
1✔
108
        lat, lon = self.get_lonlat()
1✔
109
        x, y = self.compute_xy(lon, lat)
1✔
110
        self.draw_line(x, y)
1✔
111
        self.map.ax.figure.canvas.draw()
1✔
112

113
    def remove(self):
1✔
114
        for patch in self.patches:
1✔
115
            for elem in patch:
1✔
116
                elem.remove()
1✔
117
        self.patches = []
1✔
118
        self.map.ax.figure.canvas.draw()
1✔
119

120

121
class MultipleFlightpathControlWidget(QtWidgets.QWidget, ui.Ui_MultipleViewWidget):
1✔
122
    """
123
    This class provides the interface for plotting Multiple Flighttracks
124
    on the TopView canvas.
125
    """
126

127
    # ToDO: Make a new parent class with all the functions in this class and inherit them
128
    #  in MultipleFlightpathControlWidget and MultipleFlightpathOperations classes.
129

130
    signal_parent_closes = QtCore.pyqtSignal()
1✔
131

132
    custom_colors = [
1✔
133
        (128, 0, 0), (195, 31, 89), (245, 151, 87), (253, 228, 66), (0, 0, 255),
134
        (96, 195, 110), (101, 216, 242), (164, 70, 190), (241, 90, 234), (185, 186, 187),
135
        (230, 25, 75), (210, 207, 148), (53, 110, 51), (245, 130, 49), (44, 44, 44),
136
        (0, 0, 117), (154, 99, 36), (128, 128, 0), (0, 0, 0)
137
        # Add more colors as needed
138
    ]
139

140
    # Define the mapping from combo box text to line style codes
141
    line_styles = {
1✔
142
        "Solid": '-',
143
        "Dashed": '--',
144
        "Dotted": ':',
145
        "Dash-dot": '-.'
146
    }
147

148
    # Reverse dictionary
149
    line_styles_reverse = {v: k for k, v in line_styles.items()}
1✔
150

151
    def __init__(self, parent=None, view=None, listFlightTracks=None,
1✔
152
                 listOperationsMSC=None, category=None, activeFlightTrack=None, active_op_id=None,
153
                 mscolab_server_url=None, token=None):
154
        super().__init__(parent)
1✔
155
        # ToDO: Remove all patches, on closing dockwidget.
156
        self.ui = parent
1✔
157
        self.setupUi(self)
1✔
158
        self.view = view  # canvas
1✔
159
        self.flight_path = None  # flightpath object
1✔
160
        self.dict_flighttrack = {}  # Dictionary of flighttrack data: patch,color,wp_model
1✔
161
        self.used_colors = []  # Instance variable to track used colors
1✔
162
        self.active_flight_track = activeFlightTrack
1✔
163
        self.active_op_id = active_op_id
1✔
164
        self.msc_category = category  # object of active category
1✔
165
        self.listOperationsMSC = listOperationsMSC
1✔
166
        self.listFlightTracks = listFlightTracks
1✔
167
        self.mscolab_server_url = mscolab_server_url
1✔
168
        self.token = token
1✔
169
        self.flightpath_dict = {}
1✔
170
        if self.ui is not None:
1✔
171
            ft_settings_dict = self.ui.getView().get_settings()
1✔
172
            self.color = ft_settings_dict["colour_ft_vertices"]
1✔
173
        else:
174
            self.color = self.get_random_color()
×
175
        self.obb = []
1✔
176

177
        self.operations = None
1✔
178
        self.operation_list = False
1✔
179
        self.flighttrack_list = True
1✔
180

181
        # Set flags
182
        # ToDo: Use invented constants for initialization.
183
        self.flighttrack_added = False
1✔
184
        self.flighttrack_activated = False
1✔
185
        self.color_change = False
1✔
186
        self.change_linewidth = False
1✔
187
        self.change_line_transparency = False
1✔
188
        self.change_line_style = False
1✔
189
        self.dsbx_linewidth.setValue(2.0)
1✔
190
        self.hsTransparencyControl.setValue(100)
1✔
191
        self.cbLineStyle.addItems(["Solid", "Dashed", "Dotted", "Dash-dot"])  # Item added in the list
1✔
192
        self.cbLineStyle.setCurrentText("Solid")
1✔
193

194
        # Disable the buttons initially
195
        self.pushButton_color.setEnabled(False)
1✔
196
        self.dsbx_linewidth.setEnabled(False)
1✔
197
        self.hsTransparencyControl.setEnabled(False)
1✔
198
        self.cbLineStyle.setEnabled(False)
1✔
199
        self.labelStatus.setText("Status: Select a flighttrack/operation")
1✔
200

201
        # Connect Signals and Slots
202
        self.listFlightTracks.model().rowsInserted.connect(self.wait)
1✔
203
        self.listFlightTracks.model().rowsRemoved.connect(self.flighttrackRemoved)
1✔
204
        self.ui.signal_activate_flighttrack1.connect(self.get_active)
1✔
205
        self.list_flighttrack.itemChanged.connect(self.flagop)
1✔
206

207
        self.pushButton_color.clicked.connect(self.select_color)
1✔
208
        self.ui.signal_ft_vertices_color_change.connect(self.ft_vertices_color)
1✔
209
        self.dsbx_linewidth.valueChanged.connect(self.set_linewidth)
1✔
210
        self.hsTransparencyControl.valueChanged.connect(self.set_transparency)
1✔
211
        self.cbLineStyle.currentTextChanged.connect(self.set_linestyle)
1✔
212
        self.cbSlectAll1.stateChanged.connect(self.selectAll)
1✔
213
        self.ui.signal_login_mscolab.connect(self.login)
1✔
214

215
        self.colorPixmap.setPixmap(self.show_color_pixmap(self.color))
1✔
216

217
        self.list_flighttrack.itemClicked.connect(self.listFlighttrack_itemClicked)
1✔
218

219
        if self.mscolab_server_url is not None:
1✔
220
            self.connect_mscolab_server()
1✔
221

222
        if parent is not None:
1✔
223
            parent.viewCloses.connect(lambda: self.signal_parent_closes.emit())
1✔
224

225
        # Load flighttracks
226
        for index in range(self.listFlightTracks.count()):
1✔
227
            wp_model = self.listFlightTracks.item(index).flighttrack_model
1✔
228
            self.create_list_item(wp_model)
1✔
229

230
        self.activate_flighttrack()
1✔
231
        self.multipleflightrack_worker = Worker(None)
1✔
232

233
    @QtCore.pyqtSlot()
1✔
234
    def logout(self):
1✔
235
        if self.operations is not None:
1✔
236
            self.operations.logout_mscolab()
1✔
237
            self.ui.signal_listFlighttrack_doubleClicked.disconnect()
1✔
238
            self.ui.signal_permission_revoked.disconnect()
1✔
239
            self.ui.signal_render_new_permission.disconnect()
1✔
240
            self.operations = None
1✔
241
            self.flighttrack_list = True
1✔
242
            self.operation_list = False
1✔
243
            for idx in range(len(self.obb)):
1✔
244
                del self.obb[idx]
1✔
245

246
    @QtCore.pyqtSlot(str, str)
1✔
247
    def login(self, url, token):
1✔
248
        self.mscolab_server_url = url
1✔
249
        self.token = token
1✔
250
        self.connect_mscolab_server()
1✔
251

252
    def connect_mscolab_server(self):
1✔
253
        if self.active_op_id is not None:
1✔
254
            self.deactivate_all_flighttracks()
1✔
255
        self.operations = MultipleFlightpathOperations(self, self.mscolab_server_url, self.token,
1✔
256
                                                       self.list_operation_track,
257
                                                       self.active_op_id,
258
                                                       self.listOperationsMSC, self.view)
259
        self.obb.append(self.operations)
1✔
260

261
        self.ui.signal_permission_revoked.connect(lambda op_id: self.operations.permission_revoked(op_id))
1✔
262
        self.ui.signal_render_new_permission.connect(lambda op_id, path: self.operations.render_permission(op_id, path))
1✔
263
        # Signal emitted, on activation of operation from MSUI
264
        self.ui.signal_activate_operation.connect(self.update_op_id)
1✔
265
        self.ui.signal_operation_added.connect(self.add_operation_slot)
1✔
266
        self.ui.signal_operation_removed.connect(self.remove_operation_slot)
1✔
267

268
        # deactivate vice versa selection of Operation or Flight Track
269
        self.list_operation_track.itemClicked.connect(self.operations.listOperations_itemClicked)
1✔
270

271
        # deactivate operation or flighttrack
272
        self.listOperationsMSC.itemDoubleClicked.connect(self.deactivate_all_flighttracks)
1✔
273
        self.ui.signal_listFlighttrack_doubleClicked.connect(self.operations.deactivate_all_operations)
1✔
274

275
        # Mscolab Server logout
276
        self.ui.signal_logout_mscolab.connect(self.logout)
1✔
277

278
    def update(self):
1✔
279
        for entry in self.dict_flighttrack.values():
×
280
            entry["patch"].update()
×
281

282
    def remove(self):
1✔
283
        for entry in self.dict_flighttrack.values():
×
284
            entry["patch"].remove()
×
285

286
    def wait(self, parent, start, end):
1✔
287
        """
288
        Adding of flighttrack takes time we use a worker new thread(it avoid freezing of UI).
289
        """
290
        self.multipleflightrack_worker.function = lambda: self.flighttrackAdded(parent, start, end)
×
291
        self.multipleflightrack_worker.start()
×
292
        self.flighttrack_added = True
×
293

294
    def flagop(self):
1✔
295
        if self.flighttrack_added:
1✔
296
            self.flighttrack_added = False
1✔
297
        elif self.flighttrack_activated:
1✔
298
            self.flighttrack_activated = False
1✔
299
        elif self.color_change:
1✔
300
            self.color_change = False
1✔
301
        else:
302
            self.drawInactiveFlighttracks(self.list_flighttrack)
1✔
303

304
    def flighttrackAdded(self, parent, start, end):
1✔
305
        """
306
        Slot to add flighttrack.
307
        """
308
        wp_model = self.listFlightTracks.item(start).flighttrack_model
×
309
        self.create_list_item(wp_model)
×
310
        if self.mscolab_server_url is not None:
×
311
            self.operations.deactivate_all_operations()
×
312
        self.activate_flighttrack()
×
313

314
    @QtCore.pyqtSlot(tuple)
1✔
315
    def ft_vertices_color(self, color):
1✔
316
        self.color = color
×
317
        self.colorPixmap.setPixmap(self.show_color_pixmap(color))
×
318

319
        if self.flighttrack_list:
×
320
            self.dict_flighttrack[self.active_flight_track]["color"] = color
×
321
            for index in range(self.list_flighttrack.count()):
×
322
                if self.list_flighttrack.item(index).flighttrack_model == self.active_flight_track:
×
323
                    self.list_flighttrack.item(index).setIcon(
×
324
                        self.show_color_icon(self.get_color(self.active_flight_track)))
325
                    break
×
326
        elif self.operation_list:
×
327
            self.operations.ft_color_update(color)
×
328

329
    @QtCore.pyqtSlot(int, str)
1✔
330
    def add_operation_slot(self, op_id, path):
1✔
331
        self.operations.operationsAdded(op_id, path)
×
332

333
    @QtCore.pyqtSlot(int)
1✔
334
    def remove_operation_slot(self, op_id):
1✔
335
        self.operations.operationRemoved(op_id)
×
336

337
    @QtCore.pyqtSlot(int)
1✔
338
    def update_op_id(self, op_id):
1✔
339
        self.operations.get_op_id(op_id)
1✔
340

341
    @QtCore.pyqtSlot(ft.WaypointsTableModel)
1✔
342
    def get_active(self, active_flighttrack):
1✔
343
        self.update_last_flighttrack()
1✔
344
        self.active_flight_track = active_flighttrack
1✔
345
        self.activate_flighttrack()
1✔
346

347
    def save_waypoint_model_data(self, wp_model, listWidget):
1✔
348
        wp_data = [(wp.lat, wp.lon, wp.flightlevel, wp.location, wp.comments) for wp in wp_model.all_waypoint_data()]
1✔
349
        if self.dict_flighttrack[wp_model] is None:
1✔
350
            self.create_list_item(wp_model)
×
351
        self.dict_flighttrack[wp_model]["wp_data"] = wp_data
1✔
352

353
    def normalize_rgb(self, rgb):
1✔
354
        return tuple(channel / 255 for channel in rgb)
1✔
355

356
    def get_random_color(self):
1✔
357
        """
358
        Get a random color from custom colors ensuring no repeats.
359
        """
360
        available_colors = [color for color in self.custom_colors if color not in self.used_colors]
1✔
361
        if not available_colors:
1✔
362
            # Reset the used colors if all colors have been used
363
            self.used_colors = []
×
364
            available_colors = self.custom_colors.copy()
×
365

366
        selected_color = random.choice(available_colors)
1✔
367
        self.used_colors.append(selected_color)
1✔
368
        return self.normalize_rgb(selected_color)
1✔
369

370
    def create_list_item(self, wp_model):
1✔
371
        """
372
        PyQt5 method : Add items in list and add checkbox functionality
373
        """
374
        # Create new key in dict
375
        self.dict_flighttrack[wp_model] = {}
1✔
376
        self.dict_flighttrack[wp_model]["patch"] = None
1✔
377
        self.dict_flighttrack[wp_model]["color"] = self.get_random_color()
1✔
378
        self.dict_flighttrack[wp_model]["linewidth"] = 2.0
1✔
379
        self.dict_flighttrack[wp_model]["line_transparency"] = 1.0
1✔
380
        self.dict_flighttrack[wp_model]["line_style"] = "solid"
1✔
381
        self.dict_flighttrack[wp_model]["wp_data"] = []
1✔
382
        self.dict_flighttrack[wp_model]["checkState"] = False
1✔
383

384
        self.save_waypoint_model_data(wp_model, self.list_flighttrack)
1✔
385

386
        listItem = msui_mainwindow.QFlightTrackListWidgetItem(wp_model, self.list_flighttrack)
1✔
387
        listItem.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable)
1✔
388
        if not self.flighttrack_added:
1✔
389
            self.flighttrack_added = True
1✔
390
        listItem.setCheckState(QtCore.Qt.Unchecked)
1✔
391
        if not self.flighttrack_added:
1✔
392
            self.flighttrack_added = True
1✔
393

394
        # Show flighttrack color icon
395
        listItem.setIcon(self.show_color_icon(self.get_color(wp_model)))
1✔
396
        self.update_flightpath_legend()
1✔
397

398
        return listItem
1✔
399

400
    def selectAll(self, state):
1✔
401
        """
402
        Select/deselect local operations
403
        """
404
        for i in range(self.list_flighttrack.count()):
1✔
405
            item = self.list_flighttrack.item(i)
1✔
406
            if self.active_flight_track is not None and item.flighttrack_model == self.active_flight_track:
1✔
407
                item.setCheckState(QtCore.Qt.Checked)  # Ensure the active flight track remains checked
1✔
408
            else:
409
                item.setCheckState(state)
1✔
410

411
    def select_color(self):
1✔
412
        """
413
        Sets the color of selected flighttrack when Change Color is clicked.
414
        """
415
        # ToDO: Use color defined in options for initial color of active flight path.
416
        #  afterwards deactivate the color change button in options and it needs also
417
        #  the check mark for enabled, but can't be changed (disabled). At the moment
418
        #  the dockingwidget is closed the button and checkmark has to become activated again.
419

420
        if self.list_flighttrack.currentItem() is not None:
1✔
421
            if (hasattr(self.list_flighttrack.currentItem(), "checkState")) and (
1✔
422
                    self.list_flighttrack.currentItem().checkState() == QtCore.Qt.Checked):
423
                wp_model = self.list_flighttrack.currentItem().flighttrack_model
1✔
424
                if wp_model == self.active_flight_track:
1✔
425
                    self.error_dialog = QtWidgets.QErrorMessage()
×
426
                    self.error_dialog.showMessage('Use "options" to change color of an activated flighttrack.')
×
427
                else:
428
                    color_dialog = CustomColorDialog(self)
1✔
429
                    color_dialog.color_selected.connect(lambda color: self.apply_color(wp_model, color))
1✔
430
                    color_dialog.show()
1✔
431
            else:
432
                self.labelStatus.setText("Status: Check Mark the flighttrack to change its color.")
×
433
        elif self.list_operation_track.currentItem() is not None:
×
434
            self.operations.select_color()
×
435
        else:
436
            self.labelStatus.setText("Status: No flighttrack selected")
×
437

438
    def apply_color(self, wp_model, color):
1✔
439
        if color.isValid():
1✔
440
            self.dict_flighttrack[wp_model]["color"] = color.getRgbF()
1✔
441
            self.color_change = True
1✔
442
            self.list_flighttrack.currentItem().setIcon(self.show_color_icon(self.get_color(wp_model)))
1✔
443
            self.dict_flighttrack[wp_model]["patch"].update(
1✔
444
                color=self.dict_flighttrack[wp_model]["color"])
445
            self.update_flightpath_legend()
1✔
446

447
    def get_color(self, wp_model):
1✔
448
        """
449
        Returns color of respective flighttrack.
450
        """
451
        return self.dict_flighttrack[wp_model]["color"]
1✔
452

453
    def show_color_pixmap(self, clr):
1✔
454
        pixmap = QtGui.QPixmap(20, 10)
1✔
455
        pixmap.fill(QtGui.QColor(int(clr[0] * 255), int(clr[1] * 255), int(clr[2] * 255)))
1✔
456
        return pixmap
1✔
457

458
    def show_color_icon(self, clr):
1✔
459
        """
460
        Creating object of QPixmap for displaying icon inside the listWidget.
461
        """
462
        pixmap = self.show_color_pixmap(clr)
1✔
463
        return QtGui.QIcon(pixmap)
1✔
464

465
    def set_linewidth(self):
1✔
466
        """
467
        Change the line width of selected flighttrack.
468
        """
469
        if self.list_flighttrack.currentItem() is not None:
1✔
470
            if (hasattr(self.list_flighttrack.currentItem(), "checkState")) and (
1✔
471
                    self.list_flighttrack.currentItem().checkState() == QtCore.Qt.Checked):
472
                wp_model = self.list_flighttrack.currentItem().flighttrack_model
1✔
473
                if wp_model != self.active_flight_track:
1✔
474
                    if self.dict_flighttrack[wp_model]["linewidth"] != self.dsbx_linewidth.value():
1✔
475
                        self.dict_flighttrack[wp_model]["linewidth"] = self.dsbx_linewidth.value()
1✔
476
                        self.update_flighttrack_patch(wp_model)
1✔
477
                        self.change_linewidth = True
1✔
478
                        self.dsbx_linewidth.setValue(self.dict_flighttrack[wp_model]["linewidth"])
1✔
479
            else:
480
                item = self.list_flighttrack.currentItem()
×
481
                self.dsbx_linewidth.setEnabled(item is not None and item.checkState() == QtCore.Qt.Checked)
×
482
                self.labelStatus.setText("Status: Check Mark the flighttrack to change its line width.")
×
483
        elif self.list_operation_track.currentItem() is not None:
×
484
            self.operations.set_linewidth()
×
485
        else:
486
            self.labelStatus.setText("Status: No flighttrack selected")
×
487

488
    def set_transparency(self):
1✔
489
        """
490
        Change the line transparency of the selected flight track.
491
        """
492
        if self.list_flighttrack.currentItem() is not None:
1✔
493
            if (hasattr(self.list_flighttrack.currentItem(), "checkState")) and (
1✔
494
                    self.list_flighttrack.currentItem().checkState() == QtCore.Qt.Checked):
495
                wp_model = self.list_flighttrack.currentItem().flighttrack_model
1✔
496
                if wp_model != self.active_flight_track:
1✔
497
                    new_transparency = self.hsTransparencyControl.value() / 100.0
1✔
498
                    if self.dict_flighttrack[wp_model]["line_transparency"] != new_transparency:
1✔
499
                        self.dict_flighttrack[wp_model]["line_transparency"] = new_transparency
1✔
500
                        self.update_flighttrack_patch(wp_model)
1✔
501
                        self.change_line_transparency = True
1✔
502
                        self.hsTransparencyControl.setValue(
1✔
503
                            int(self.dict_flighttrack[wp_model]["line_transparency"] * 100))
504
            else:
505
                item = self.list_flighttrack.currentItem()
×
506
                self.hsTransparencyControl.setEnabled(item is not None and item.checkState() == QtCore.Qt.Checked)
×
507
                self.labelStatus.setText("Status: Check Mark the flighttrack to change its transparency.")
×
508
        elif self.list_operation_track.currentItem() is not None:
×
509
            self.operations.set_transparency()
×
510
        else:
511
            self.labelStatus.setText("Status: No flighttrack selected")
×
512

513
    def set_linestyle(self):
1✔
514
        """
515
        Change the line style of the selected flight track.
516
        """
517

518
        if self.list_flighttrack.currentItem() is not None:
1✔
519
            if (hasattr(self.list_flighttrack.currentItem(), "checkState")) and (
1✔
520
                    self.list_flighttrack.currentItem().checkState() == QtCore.Qt.Checked):
521
                wp_model = self.list_flighttrack.currentItem().flighttrack_model
1✔
522
                if wp_model != self.active_flight_track:
1✔
523
                    selected_style = self.cbLineStyle.currentText()
1✔
524
                    new_linestyle = self.line_styles.get(selected_style, '-')  # Default to 'solid'
1✔
525

526
                    if self.dict_flighttrack[wp_model]["line_style"] != new_linestyle:
1✔
527
                        self.dict_flighttrack[wp_model]["line_style"] = new_linestyle
1✔
528
                        self.update_flighttrack_patch(wp_model)
1✔
529
                        self.change_line_style = True
1✔
530
                        self.cbLineStyle.setCurrentText(self.dict_flighttrack[wp_model]["line_style"])
1✔
531
            else:
532
                item = self.list_flighttrack.currentItem()
×
533
                self.cbLineStyle.setEnabled(item is not None and item.checkState() == QtCore.Qt.Checked)
×
534
                self.labelStatus.setText("Status: Check Mark the flighttrack to change its line style.")
×
535
        elif self.list_operation_track.currentItem() is not None:
×
536
            self.operations.set_linestyle()
×
537
        else:
538
            self.labelStatus.setText("Status: No flighttrack selected")
×
539

540
    def update_flighttrack_patch(self, wp_model):
1✔
541
        """
542
        Update the flighttrack patch with the latest attributes.
543
        """
544
        if self.dict_flighttrack[wp_model]["patch"] is not None:
×
545
            self.dict_flighttrack[wp_model]["patch"].remove()
×
546
        self.dict_flighttrack[wp_model]["patch"] = MultipleFlightpath(
×
547
            self.view.map,
548
            self.dict_flighttrack[wp_model]["wp_data"],
549
            color=self.dict_flighttrack[wp_model]["color"],
550
            linewidth=self.dict_flighttrack[wp_model]["linewidth"],
551
            line_transparency=self.dict_flighttrack[wp_model]["line_transparency"],
552
            line_style=self.dict_flighttrack[wp_model]["line_style"]
553
        )
554
        self.update_flightpath_legend()
×
555

556
    def update_flightpath_legend(self):
1✔
557
        """
558
        Collects flight path data and updates the legend in the TopView.
559
        Only checked flight tracks will be included in the legend.
560
        Unchecked flight tracks will be removed from the flightpath_dict.
561
        """
562
        # Iterate over all items in the list_flighttrack
563
        for i in range(self.list_flighttrack.count()):
1✔
564
            listItem = self.list_flighttrack.item(i)
1✔
565
            wp_model = listItem.flighttrack_model
1✔
566

567
            # If the flight track is checked, add/update it in the dictionary
568
            if listItem.checkState() == QtCore.Qt.Checked:
1✔
569
                name = wp_model.name if hasattr(wp_model, 'name') else 'Unnamed flighttrack'
1✔
570
                color = self.dict_flighttrack[wp_model].get('color', '#000000')  # Default to black
1✔
571
                linestyle = self.dict_flighttrack[wp_model].get('line_style', '-')  # Default to solid line
1✔
572
                self.flightpath_dict[name] = (color, linestyle)
1✔
573
            # If the flight track is unchecked, ensure it is removed from the dictionary
574
            else:
575
                name = wp_model.name if hasattr(wp_model, 'name') else 'Unnamed flighttrack'
1✔
576
                if name in self.flightpath_dict:
1✔
577
                    del self.flightpath_dict[name]
1✔
578

579
        # Update the legend in the view with the filtered flightpath_dict
580
        self.view.update_flightpath_legend(self.flightpath_dict)
1✔
581

582
    def flighttrackRemoved(self, parent, start, end):
1✔
583
        """
584
        Slot to remove flighttrack.
585
        """
586
        # ToDo: Add try..catch block
587
        if self.dict_flighttrack[self.list_flighttrack.item(start).flighttrack_model]["patch"] is None:
×
588
            del self.dict_flighttrack[self.list_flighttrack.item(start).flighttrack_model]
×
589
        else:
590
            self.dict_flighttrack[self.list_flighttrack.item(start).flighttrack_model]["patch"].remove()
×
591
        self.list_flighttrack.takeItem(start)
×
592
        self.update_flightpath_legend()
×
593

594
    def update_last_flighttrack(self):
1✔
595
        """
596
        Update waypoint model for most recently activated flighttrack in dict_flighttrack.
597
        """
598
        if self.active_flight_track is not None:
1✔
599
            self.save_waypoint_model_data(
1✔
600
                self.active_flight_track,
601
                self.list_flighttrack)  # Before activating new flighttrack, update waypoints of previous flighttrack
602

603
    def activate_flighttrack(self):
1✔
604
        """
605
        Activate the selected flighttrack and ensure its visual properties are correctly set.
606
        """
607
        font = QtGui.QFont()
1✔
608
        for i in range(self.list_flighttrack.count()):
1✔
609
            listItem = self.list_flighttrack.item(i)
1✔
610
            wp_model = listItem.flighttrack_model
1✔
611

612
            if self.active_flight_track == wp_model:  # active flighttrack
1✔
613
                listItem.setIcon(self.show_color_icon(self.color))
1✔
614
                font.setBold(True)
1✔
615

616
                # Ensure the patch is updated with the correct attributes
617
                if self.dict_flighttrack[wp_model]["patch"] is not None:
1✔
618
                    self.dict_flighttrack[wp_model]["patch"].remove()
×
619

620
                if listItem.checkState() == QtCore.Qt.Unchecked:
1✔
621
                    listItem.setCheckState(QtCore.Qt.Checked)
1✔
622
                    self.set_activate_flag()
1✔
623
                listItem.setFlags(listItem.flags() & ~QtCore.Qt.ItemIsUserCheckable)  # make activated track uncheckable
1✔
624
            else:
625
                listItem.setIcon(self.show_color_icon(self.get_color(wp_model)))
1✔
626
                font.setBold(False)
1✔
627
                listItem.setFlags(listItem.flags() | QtCore.Qt.ItemIsUserCheckable)
1✔
628
            self.set_activate_flag()
1✔
629
            listItem.setFont(font)
1✔
630
        self.update_line_properties_state()
1✔
631
        self.flagop()
1✔
632
        if self.operations is not None:
1✔
633
            self.operations.set_flag()
1✔
634

635
    def update_line_properties_state(self):
1✔
636
        """
637
        Check if there is an active flight track selected. If there is an active flight track selected in the
638
        list widget, disable these UI elements else enable
639
        """
640
        if self.list_flighttrack.currentItem() is not None:
1✔
641
            wp_model = self.list_flighttrack.currentItem().flighttrack_model
×
642
            self.enable_disable_line_style_buttons(wp_model != self.active_flight_track)
×
643

644
    def enable_disable_line_style_buttons(self, enable):
1✔
645
        self.pushButton_color.setEnabled(enable)
1✔
646
        self.dsbx_linewidth.setEnabled(enable)
1✔
647
        self.hsTransparencyControl.setEnabled(enable)
1✔
648
        self.cbLineStyle.setEnabled(enable)
1✔
649
        if enable:
1✔
650
            self.labelStatus.setText("Status: ✔ flight track selected")
1✔
651
        else:
652
            self.labelStatus.setText(
×
653
                "Status: You can change line attributes of the active flighttrack through options only.")
654

655
    def drawInactiveFlighttracks(self, list_widget):
1✔
656
        """
657
        Draw inactive flighttracks
658
        """
659
        for entry in self.dict_flighttrack.values():
1✔
660
            if entry["patch"] is not None:
1✔
661
                entry["patch"].remove()
1✔
662

663
        for index in range(list_widget.count()):
1✔
664
            listItem = list_widget.item(index)
1✔
665
            if hasattr(list_widget.item(index), "checkState") and (
1✔
666
                    list_widget.item(index).checkState() == QtCore.Qt.Checked):
667
                if listItem.flighttrack_model != self.active_flight_track:
1✔
668
                    patch = MultipleFlightpath(self.view.map,
1✔
669
                                               self.dict_flighttrack[listItem.flighttrack_model][
670
                                                   "wp_data"],
671
                                               color=self.dict_flighttrack[listItem.flighttrack_model]["color"],
672
                                               linewidth=self.dict_flighttrack[listItem.flighttrack_model]["linewidth"],
673
                                               line_transparency=self.dict_flighttrack[listItem.flighttrack_model][
674
                                                   "line_transparency"],
675
                                               line_style=self.dict_flighttrack[listItem.flighttrack_model][
676
                                                   "line_style"])
677

678
                    self.dict_flighttrack[listItem.flighttrack_model]["patch"] = patch
1✔
679
                    self.dict_flighttrack[listItem.flighttrack_model]["checkState"] = True
1✔
680
            else:
681
                # pass
682
                self.dict_flighttrack[listItem.flighttrack_model]["checkState"] = False
1✔
683

684
        # Update the legend after drawing the flight tracks
685
        self.update_flightpath_legend()
1✔
686

687
    def set_activate_flag(self):
1✔
688
        if not self.flighttrack_added:
1✔
689
            self.flighttrack_activated = True
1✔
690

691
    def deactivate_all_flighttracks(self):
1✔
692
        """
693
        Remove all flighttrack patches from topview canvas and make flighttracks userCheckable.
694
        """
695
        for index in range(self.list_flighttrack.count()):
1✔
696
            listItem = self.list_flighttrack.item(index)
1✔
697

698
            self.set_listControl(True, False)
1✔
699

700
            self.set_activate_flag()
1✔
701
            listItem.setFlags(listItem.flags() | QtCore.Qt.ItemIsUserCheckable)
1✔
702
            listItem.setIcon(self.show_color_icon(self.get_color(listItem.flighttrack_model)))
1✔
703

704
            if listItem.flighttrack_model == self.active_flight_track:
1✔
705
                font = QtGui.QFont()
1✔
706
                font.setBold(False)
1✔
707
                listItem.setFont(font)
1✔
708

709
        self.active_flight_track = None
1✔
710

711
    def set_listControl(self, operation, flighttrack):
1✔
712
        self.operation_list = operation
1✔
713
        self.flighttrack_list = flighttrack
1✔
714

715
    def get_ft_vertices_color(self):
1✔
716
        return self.get_random_color()
1✔
717

718
    def listFlighttrack_itemClicked(self):
1✔
719
        """
720
        reflect the linewidth, transparency, line_style of the selected flight track
721
        and toggles the visibility of the groupBox.
722
        """
723
        if self.list_operation_track.currentItem() is not None:
1✔
724
            self.list_operation_track.setCurrentItem(None)
×
725

726
        if self.list_flighttrack.currentItem() is not None:
1✔
727
            wp_model = self.list_flighttrack.currentItem().flighttrack_model
1✔
728

729
            linewidth = self.dict_flighttrack[wp_model]["linewidth"]
1✔
730
            line_transparency = self.dict_flighttrack[wp_model]["line_transparency"]
1✔
731
            line_style = self.dict_flighttrack[wp_model]["line_style"]
1✔
732

733
            self.dsbx_linewidth.setValue(linewidth)
1✔
734
            self.hsTransparencyControl.setValue(int(line_transparency * 100))
1✔
735
            # Use the reverse dictionary to set the current text of the combo box
736
            if line_style in self.line_styles_reverse:
1✔
737
                self.cbLineStyle.setCurrentText(self.line_styles_reverse[line_style])
×
738
            else:
739
                self.cbLineStyle.setCurrentText("Solid")
1✔
740

741
            print(wp_model != self.active_flight_track,
1✔
742
                  self.list_flighttrack.currentItem().checkState() != QtCore.Qt.Checked)
743
            self.enable_disable_line_style_buttons(
1✔
744
                wp_model != self.active_flight_track and self.list_flighttrack.currentItem().
745
                checkState() == QtCore.Qt.Checked)
746
            # Update the legend
747
            self.update_flightpath_legend()
1✔
748

749

750
class MultipleFlightpathOperations:
1✔
751
    """
752
    This class provides the functions for plotting Multiple Flighttracks from mscolab server
753
    on the TopView canvas.
754
    """
755

756
    def __init__(self, parent, mscolab_server_url, token, list_operation_track, active_op_id, listOperationsMSC, view):
1✔
757
        # Variables related to Mscolab Operations
758
        self.parent = parent
1✔
759
        self.active_op_id = active_op_id
1✔
760
        self.mscolab_server_url = mscolab_server_url
1✔
761
        self.token = token
1✔
762
        self.view = view
1✔
763
        self.dict_operations = {}
1✔
764
        self.list_operation_track = list_operation_track
1✔
765
        self.listOperationsMSC = listOperationsMSC
1✔
766

767
        self.operation_added = False
1✔
768
        self.operation_removed = False
1✔
769
        self.operation_activated = False
1✔
770
        self.color_change = False
1✔
771

772
        # Load operations from wps server
773
        server_operations = self.get_wps_from_server()
1✔
774
        sorted_server_operations = sorted(server_operations, key=lambda d: d["path"])
1✔
775

776
        for operations in sorted_server_operations:
1✔
777
            op_id = operations["op_id"]
1✔
778
            xml_content = self.request_wps_from_server(op_id)
1✔
779
            wp_model = ft.WaypointsTableModel(xml_content=xml_content)
1✔
780
            wp_model.name = operations["path"]
1✔
781
            self.create_operation(op_id, wp_model)
1✔
782

783
        # This needs to be done after operations are loaded
784
        # Connect signals and slots
785
        self.list_operation_track.itemChanged.connect(self.set_flag)
1✔
786
        self.parent.cbSlectAll2.stateChanged.connect(self.selectAll)
1✔
787

788
    def set_flag(self):
1✔
789
        if self.operation_added:
1✔
790
            self.operation_added = False
1✔
791
        elif self.operation_removed:
1✔
792
            self.operation_removed = False
×
793
        elif self.color_change:
1✔
794
            self.color_change = False
×
795
        else:
796
            self.draw_inactive_operations()
1✔
797

798
    def get_wps_from_server(self):
1✔
799
        operations = {}
1✔
800
        skip_archived = config_loader(dataset="MSCOLAB_skip_archived_operations")
1✔
801
        data = {
1✔
802
            "token": self.token,
803
            "skip_archived": skip_archived
804
        }
805
        url = urljoin(self.mscolab_server_url, "operations")
1✔
806
        r = requests.get(url, data=data, timeout=tuple(config_loader(dataset="MSCOLAB_timeout")))
1✔
807
        if r.text != "False":
1✔
808
            _json = json.loads(r.text)
1✔
809
            operations = _json["operations"]
1✔
810
        selected_category = self.parent.msc_category.currentText()
1✔
811
        if selected_category != "*ANY*":
1✔
812
            operations = [op for op in operations if op['category'] == selected_category]
1✔
813
        return operations
1✔
814

815
    def request_wps_from_server(self, op_id):
1✔
816
        if verify_user_token(self.mscolab_server_url, self.token):
1✔
817
            data = {
1✔
818
                "token": self.token,
819
                "op_id": op_id
820
            }
821
            url = urljoin(self.mscolab_server_url, "get_operation_by_id")
1✔
822
            r = requests.get(url, data=data, timeout=tuple(config_loader(dataset="MSCOLAB_timeout")))
1✔
823
            if r.text != "False":
1✔
824
                xml_content = json.loads(r.text)["content"]
1✔
825
                return xml_content
1✔
826

827
    def load_wps_from_server(self, op_id):
1✔
828
        xml_content = self.request_wps_from_server(op_id)
1✔
829
        if xml_content is not None:
1✔
830
            waypoints_model = ft.WaypointsTableModel(xml_content=xml_content)
1✔
831
            return waypoints_model
1✔
832

833
    def save_operation_data(self, op_id, wp_model):
1✔
834
        wp_data = [(wp.lat, wp.lon, wp.flightlevel, wp.location, wp.comments) for wp in wp_model.all_waypoint_data()]
1✔
835
        if self.dict_operations[op_id] is None:
1✔
836
            self.create_operation(op_id, wp_model)
×
837
        self.dict_operations[op_id]["wp_data"] = wp_data
1✔
838

839
    def create_operation(self, op_id, wp_model):
1✔
840
        """
841
        """
842
        self.dict_operations[op_id] = {}
1✔
843
        self.dict_operations[op_id]["patch"] = None
1✔
844
        self.dict_operations[op_id]["wp_data"] = None
1✔
845
        self.dict_operations[op_id]["linewidth"] = 2.0
1✔
846
        self.dict_operations[op_id]["line_transparency"] = 1.0
1✔
847
        self.dict_operations[op_id]["line_style"] = 'solid'
1✔
848
        self.dict_operations[op_id]["color"] = self.parent.get_ft_vertices_color()
1✔
849

850
        self.save_operation_data(op_id, wp_model)
1✔
851

852
        listItem = QMscolabOperationsListWidgetItem(wp_model, op_id, self.list_operation_track)
1✔
853
        listItem.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable)
1✔
854

855
        if not self.operation_added:
1✔
856
            self.operation_added = True
1✔
857
        listItem.setCheckState(QtCore.Qt.Unchecked)
1✔
858
        if not self.operation_added:
1✔
859
            self.operation_added = True
×
860

861
        # Show operations color icon
862
        listItem.setIcon(self.show_color_icon(self.get_color(op_id)))
1✔
863
        self.update_operation_legend()
1✔
864

865
        return listItem
1✔
866

867
    def activate_operation(self):
1✔
868
        """
869
        Activate Mscolab Operation
870
        """
871
        # disconnect itemChanged during activation loop
872
        self.list_operation_track.itemChanged.disconnect(self.set_flag)
1✔
873
        font = QtGui.QFont()
1✔
874
        for i in range(self.list_operation_track.count()):
1✔
875
            listItem = self.list_operation_track.item(i)
1✔
876
            if self.active_op_id == listItem.op_id:  # active operation
1✔
877
                listItem.setIcon(self.show_color_icon(self.parent.color))
1✔
878
                font.setBold(True)
1✔
879
                if self.dict_operations[listItem.op_id]["patch"] is not None:
1✔
880
                    self.dict_operations[listItem.op_id]["patch"].remove()
1✔
881
                if listItem.checkState() == QtCore.Qt.Unchecked:
1✔
882
                    listItem.setCheckState(QtCore.Qt.Checked)
1✔
883
                    self.set_activate_flag()
1✔
884
                listItem.setFlags(listItem.flags() & ~QtCore.Qt.ItemIsUserCheckable)  # make activated track uncheckable
1✔
885
            else:
886
                listItem.setIcon(self.show_color_icon(self.get_color(listItem.op_id)))
1✔
887
                font.setBold(False)
1✔
888
                listItem.setFlags(listItem.flags() | QtCore.Qt.ItemIsUserCheckable)
1✔
889
            self.set_activate_flag()
1✔
890
            listItem.setFont(font)
1✔
891
        # connect itemChanged after everything setup, otherwise it will be triggered on each entry
892
        self.list_operation_track.itemChanged.connect(self.set_flag)
1✔
893
        self.update_line_properties_state()
1✔
894
        self.set_flag()
1✔
895
        self.parent.flagop()
1✔
896

897
    def update_line_properties_state(self):
1✔
898
        """
899
        Check if there is an active flight track selected. If there is an active flight track selected in the
900
        list widget, disable these UI elements else enable
901
        """
902
        if self.list_operation_track.currentItem() is not None:
1✔
903
            op_id = self.list_operation_track.currentItem().op_id
×
904
            self.enable_disable_line_style_buttons(op_id != self.active_op_id)
×
905

906
    def enable_disable_line_style_buttons(self, enable):
1✔
907
        self.parent.pushButton_color.setEnabled(enable)
×
908
        self.parent.dsbx_linewidth.setEnabled(enable)
×
909
        self.parent.hsTransparencyControl.setEnabled(enable)
×
910
        self.parent.cbLineStyle.setEnabled(enable)
×
911
        if enable:
×
912
            self.parent.labelStatus.setText("Status: ✔ flight track selected")
×
913
        else:
914
            self.parent.labelStatus.setText(
×
915
                "Status: You can change line attributes of the active flighttrack through options only.")
916

917
    def save_last_used_operation(self, op_id):
1✔
918
        if self.active_op_id is not None:
1✔
919
            self.save_operation_data(op_id, self.load_wps_from_server(self.active_op_id))
1✔
920

921
    def draw_inactive_operations(self):
1✔
922
        """
923
        Draw flighttracks of inactive operations.
924
        """
925
        for entry in self.dict_operations.values():
1✔
926
            if entry is not None:
1✔
927
                if entry["patch"] is not None:
1✔
928
                    entry["patch"].remove()
1✔
929

930
        for index in range(self.list_operation_track.count()):
1✔
931
            listItem = self.list_operation_track.item(index)
1✔
932
            if hasattr(listItem, "checkState") and (
1✔
933
                    listItem.checkState() == QtCore.Qt.Checked):
934
                if listItem.op_id != self.active_op_id:
1✔
935
                    patch = MultipleFlightpath(self.view.map,
1✔
936
                                               self.dict_operations[listItem.op_id][
937
                                                   "wp_data"],
938
                                               color=self.dict_operations[listItem.op_id]["color"],
939
                                               linewidth=self.dict_operations[listItem.op_id]["linewidth"],
940
                                               line_transparency=self.dict_operations[listItem.op_id][
941
                                                   "line_transparency"],
942
                                               line_style=self.dict_operations[listItem.op_id][
943
                                                   "line_style"])
944

945
                    self.dict_operations[listItem.op_id]["patch"] = patch
1✔
946
        self.update_operation_legend()
1✔
947

948
    def get_op_id(self, op_id):
1✔
949
        if self.active_op_id is not None:
1✔
950
            tmp = self.active_op_id
1✔
951
            self.save_last_used_operation(tmp)
1✔
952
        self.active_op_id = op_id
1✔
953
        self.activate_operation()
1✔
954

955
    def operationsAdded(self, op_id, path):
1✔
956
        """
957
        Slot to add operation.
958
        """
959
        wp_model = self.load_wps_from_server(op_id)
×
960
        wp_model.name = path
×
961
        self.create_operation(op_id, wp_model)
×
962

963
    def operationRemoved(self, op_id):
1✔
964
        """
965
        Slot to remove operation.
966
        """
967
        self.list_operation_track.itemChanged.disconnect(self.set_flag)
×
968
        self.operation_removed = True
×
969
        for index in range(self.list_operation_track.count()):
×
970
            if self.list_operation_track.item(index).op_id == op_id:
×
971
                if self.dict_operations[self.list_operation_track.item(index).op_id]["patch"] is None:
×
972
                    del self.dict_operations[self.list_operation_track.item(index).op_id]
×
973
                else:
974
                    self.dict_operations[self.list_operation_track.item(index).op_id]["patch"].remove()
×
975
                self.list_operation_track.takeItem(index)
×
976
                self.active_op_id = None
×
977
                break
×
978
        self.list_operation_track.itemChanged.connect(self.set_flag)
×
979

980
    def set_activate_flag(self):
1✔
981
        if not self.operation_activated:
1✔
982
            self.operation_activated = True
1✔
983

984
    def deactivate_all_operations(self):
1✔
985
        """
986
        Removes all operations patches from topview canvas and make flighttracks userCheckable
987
        """
988
        for index in range(self.listOperationsMSC.count()):
1✔
989
            listItem = self.list_operation_track.item(index)
1✔
990

991
            self.parent.set_listControl(False, True)
1✔
992

993
            self.set_activate_flag()
1✔
994
            listItem.setFlags(listItem.flags() | QtCore.Qt.ItemIsUserCheckable)
1✔
995
            listItem.setIcon(self.show_color_icon(self.get_color(listItem.op_id)))
1✔
996

997
            # if listItem.op_id == self.active_op_id:
998
            self.set_activate_flag()
1✔
999
            font = QtGui.QFont()
1✔
1000
            font.setBold(False)
1✔
1001
            listItem.setFont(font)
1✔
1002

1003
        self.active_op_id = None
1✔
1004

1005
    def selectAll(self, state):
1✔
1006
        """
1007
        select/deselect mscolab operations
1008
        """
1009
        for i in range(self.list_operation_track.count()):
×
1010
            item = self.list_operation_track.item(i)
×
1011
            if self.active_op_id is not None and item.op_id == self.active_op_id:
×
1012
                item.setCheckState(QtCore.Qt.Checked)  # Ensure the active flight track remains checked
×
1013
            else:
1014
                item.setCheckState(state)
×
1015

1016
    def select_color(self):
1✔
1017
        """
1018
        Sets the color of selected operation when change Color is clicked.
1019
        """
1020
        if self.list_operation_track.currentItem() is not None:
×
1021
            if (hasattr(self.list_operation_track.currentItem(), "checkState")) and (
×
1022
                    self.list_operation_track.currentItem().checkState() == QtCore.Qt.Checked):
1023
                op_id = self.list_operation_track.currentItem().op_id
×
1024
                if self.list_operation_track.currentItem().op_id == self.active_op_id:
×
1025
                    self.error_dialog = QtWidgets.QErrorMessage()
×
1026
                    self.error_dialog.showMessage('Use "options" to change color of an activated operation.')
×
1027
                else:
1028
                    color_dialog = CustomColorDialog(self.parent)
×
1029
                    color_dialog.color_selected.connect(lambda color: self.apply_color(op_id, color))
×
1030
                    color_dialog.show()
×
1031
            else:
1032
                self.parent.labelStatus.setText("Status: Check Mark the flighttrack to change its color.")
×
1033

1034
    def apply_color(self, op_id, color):
1✔
1035
        if color.isValid():
×
1036
            self.dict_operations[op_id]["color"] = color.getRgbF()
×
1037
            self.color_change = True
×
1038
            self.list_operation_track.currentItem().setIcon(self.show_color_icon(self.get_color(op_id)))
×
1039
            self.dict_operations[op_id]["patch"].update(
×
1040
                color=self.dict_operations[op_id]["color"],
1041
                linewidth=self.dict_operations[op_id]["linewidth"])
1042
            self.update_operation_legend()
×
1043

1044
    def get_color(self, op_id):
1✔
1045
        """
1046
        Returns color of respective operation.
1047
        """
1048
        return self.dict_operations[op_id]["color"]
1✔
1049

1050
    def show_color_icon(self, clr):
1✔
1051
        """
1052
        """
1053
        pixmap = self.parent.show_color_pixmap(clr)
1✔
1054
        return QtGui.QIcon(pixmap)
1✔
1055

1056
    def ft_color_update(self, color):
1✔
1057
        self.color = color
×
1058
        self.dict_operations[self.active_op_id]["color"] = color
×
1059

1060
        for index in range(self.list_operation_track.count()):
×
1061
            if self.list_operation_track.item(index).op_id == self.active_op_id:
×
1062
                self.list_operation_track.item(index).setIcon(
×
1063
                    self.show_color_icon(self.get_color(self.active_op_id)))
1064
                break
×
1065

1066
    def logout_mscolab(self):
1✔
1067
        a = self.list_operation_track.count() - 1
1✔
1068
        while a >= 0:
1✔
1069
            if self.dict_operations[self.list_operation_track.item(0).op_id]['patch'] is None:
1✔
1070
                del self.dict_operations[self.list_operation_track.item(0).op_id]
1✔
1071
            else:
1072
                self.dict_operations[self.list_operation_track.item(0).op_id]['patch'].remove()
1✔
1073
            self.list_operation_track.takeItem(0)
1✔
1074
            a -= 1
1✔
1075

1076
        # Remove only the operations from flightpath_dict without affecting flight tracks
1077
        self.parent.flightpath_dict.clear()
1✔
1078

1079
        # Uncheck the "Select All" checkbox
1080
        self.parent.cbSlectAll2.setChecked(False)
1✔
1081
        self.parent.labelStatus.setText("Status: Select a flighttrack/operation")
1✔
1082

1083
        self.list_operation_track.itemChanged.disconnect()
1✔
1084
        self.mscolab_server_url = None
1✔
1085
        self.token = None
1✔
1086
        self.dict_operations = {}
1✔
1087

1088
    @QtCore.pyqtSlot(int)
1✔
1089
    def permission_revoked(self, op_id):
1✔
1090
        self.operationRemoved(op_id)
×
1091

1092
    @QtCore.pyqtSlot(int, str)
1✔
1093
    def render_permission(self, op_id, path):
1✔
1094
        self.operationsAdded(op_id, path)
×
1095

1096
    def set_linewidth(self):
1✔
1097
        """
1098
        Change the line width of the selected operation track.
1099
        """
1100
        item = self.list_operation_track.currentItem()
×
1101
        if hasattr(item, "checkState") and item.checkState() == QtCore.Qt.Checked:
×
1102
            op_id = item.op_id
×
1103
            if op_id != self.active_op_id:
×
1104
                self.parent.groupBox.show()
×
1105
                if self.dict_operations[op_id]["linewidth"] != self.parent.dsbx_linewidth.value():
×
1106
                    self.dict_operations[op_id]["linewidth"] = self.parent.dsbx_linewidth.value()
×
1107
                    self.update_flighttrack_patch(op_id)
×
1108
                    self.parent.change_linewidth = True
×
1109
                    self.parent.dsbx_linewidth.setValue(self.dict_operations[op_id]["linewidth"])
×
1110
        else:
1111
            self.parent.dsbx_linewidth.setEnabled(item is not None and item.checkState() == QtCore.Qt.Checked)
×
1112
            self.parent.labelStatus.setText("Status: Check Mark the flighttrack to change its line width.")
×
1113

1114
    def set_transparency(self):
1✔
1115
        """
1116
        Change the line transparency of the selected operation track.
1117
        """
1118
        item = self.list_operation_track.currentItem()
×
1119
        if hasattr(item, "checkState") and item.checkState() == QtCore.Qt.Checked:
×
1120
            op_id = item.op_id
×
1121
            if op_id != self.active_op_id:
×
1122
                self.parent.groupBox.show()
×
1123
                new_transparency = self.parent.hsTransparencyControl.value() / 100.0
×
1124
                if self.dict_operations[op_id]["line_transparency"] != new_transparency:
×
1125
                    self.dict_operations[op_id]["line_transparency"] = new_transparency
×
1126
                    self.update_flighttrack_patch(op_id)
×
1127
                    self.parent.change_line_transparency = True
×
1128
                    self.parent.hsTransparencyControl.setValue(
×
1129
                        int(self.dict_operations[op_id]["line_transparency"] * 100))
1130
        else:
1131
            self.parent.hsTransparencyControl.setEnabled(item is not None and item.checkState() == QtCore.Qt.Checked)
×
1132
            self.parent.labelStatus.setText("Status: Check Mark the flighttrack to change its transparency.")
×
1133

1134
    def set_linestyle(self):
1✔
1135
        """
1136
        Change the line style of the selected operation track.
1137
        """
1138
        item = self.list_operation_track.currentItem()
×
1139
        if hasattr(item, "checkState") and item.checkState() == QtCore.Qt.Checked:
×
1140
            op_id = item.op_id
×
1141
            if op_id != self.active_op_id:
×
1142
                self.parent.groupBox.show()
×
1143
            selected_style = self.parent.cbLineStyle.currentText()
×
1144
            new_linestyle = self.parent.line_styles.get(selected_style, '-')  # Default to 'solid'
×
1145

1146
            if self.dict_operations[op_id]["line_style"] != new_linestyle:
×
1147
                self.dict_operations[op_id]["line_style"] = new_linestyle
×
1148
                self.update_flighttrack_patch(op_id)
×
1149
                self.parent.change_line_style = True
×
1150
                self.parent.cbLineStyle.setCurrentText(self.dict_operations[op_id]["line_style"])
×
1151
        else:
1152
            self.parent.cbLineStyle.setEnabled(item is not None and item.checkState() == QtCore.Qt.Checked)
×
1153
            self.parent.labelStatus.setText("Status: Check Mark the flighttrack to change its line style.")
×
1154

1155
    def update_flighttrack_patch(self, op_id):
1✔
1156
        """
1157
        Update the flighttrack patch with the latest attributes.
1158
        """
1159
        if self.dict_operations[op_id]["patch"] is not None:
×
1160
            self.dict_operations[op_id]["patch"].remove()
×
1161
        self.dict_operations[op_id]["patch"] = MultipleFlightpath(
×
1162
            self.view.map,
1163
            self.dict_operations[op_id]["wp_data"],
1164
            color=self.dict_operations[op_id]["color"],
1165
            linewidth=self.dict_operations[op_id]["linewidth"],
1166
            line_transparency=self.dict_operations[op_id]["line_transparency"],
1167
            line_style=self.dict_operations[op_id]["line_style"]
1168
        )
1169
        self.update_operation_legend()
×
1170

1171
    def update_operation_legend(self):
1✔
1172
        """
1173
        Collects operation data and updates the legend in the TopView.
1174
        Only checked operations will be included in the legend.
1175
        Unchecked operations will be removed from the flightpath_dict.
1176
        """
1177
        # Iterate over all items in the list_operation_track
1178
        for i in range(self.list_operation_track.count()):
1✔
1179
            listItem = self.list_operation_track.item(i)
1✔
1180

1181
            # If the operation is checked, add/update it in the dictionary
1182
            if listItem.checkState() == QtCore.Qt.Checked:
1✔
1183
                wp_model = listItem.flighttrack_model
1✔
1184
                name = wp_model.name if hasattr(wp_model, 'name') else 'Unnamed operation'
1✔
1185
                op_id = listItem.op_id
1✔
1186
                color = self.dict_operations[op_id].get('color', '#000000')  # Default to black
1✔
1187
                linestyle = self.dict_operations[op_id].get('line_style', '-')  # Default to solid line
1✔
1188
                self.parent.flightpath_dict[name] = (color, linestyle)
1✔
1189
            # If the flight track is unchecked, ensure it is removed from the dictionary
1190
            else:
1191
                wp_model = listItem.flighttrack_model
1✔
1192
                name = wp_model.name if hasattr(wp_model, 'name') else 'Unnamed flighttrack'
1✔
1193
                if name in self.parent.flightpath_dict:
1✔
1194
                    del self.parent.flightpath_dict[name]
×
1195

1196
        # Update the legend in the view with the filtered flightpath_dict
1197
        self.view.update_flightpath_legend(self.parent.flightpath_dict)
1✔
1198

1199
    def listOperations_itemClicked(self):
1✔
1200
        """
1201
        reflect the linewidth, transparency, line_style of the selected flight track
1202
        and toggles the visibility of the groupBox.
1203
        """
1204
        if self.parent.list_flighttrack.currentItem() is not None:
×
1205
            self.parent.list_flighttrack.setCurrentItem(None)
×
1206

1207
        if self.list_operation_track.currentItem() is not None:
×
1208
            op_id = self.list_operation_track.currentItem().op_id
×
1209

1210
            linewidth = self.dict_operations[op_id]["linewidth"]
×
1211
            line_transparency = self.dict_operations[op_id]["line_transparency"]
×
1212
            line_style = self.dict_operations[op_id]["line_style"]
×
1213

1214
            self.parent.dsbx_linewidth.setValue(linewidth)
×
1215
            self.parent.hsTransparencyControl.setValue(int(line_transparency * 100))
×
1216
            # Use the reverse dictionary to set the current text of the combo box
1217
            if line_style in self.parent.line_styles_reverse:
×
1218
                self.parent.cbLineStyle.setCurrentText(self.parent.line_styles_reverse[line_style])
×
1219
            else:
1220
                self.parent.cbLineStyle.setCurrentText("Solid")
×
1221

1222
            self.enable_disable_line_style_buttons(op_id != self.active_op_id and self.list_operation_track.
×
1223
                                                   currentItem().checkState() == QtCore.Qt.Checked)
1224
            self.update_operation_legend()
×
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