From 5c70f14a2ccbea7af605c84aa33cbf0e9709b9f2 Mon Sep 17 00:00:00 2001 From: Rohit prasad Date: Thu, 4 Jul 2024 18:04:32 +0530 Subject: [PATCH 1/9] feat: New UI for option in topview & sideview (#2407) * UI and line thickness * line thickness working * every new feature working in option * Ui of the sideview * topview completely done without the ok * both topview and sideview working * key error fixed * key error fixed in sideview * ATQ feedback change UI --- mslib/msui/mpl_pathinteractor.py | 67 ++- mslib/msui/mpl_qtwidget.py | 34 +- mslib/msui/qt5/ui_sideview_options.py | 145 +++-- mslib/msui/qt5/ui_topview_mapappearance.py | 110 ++-- mslib/msui/sideview.py | 48 ++ mslib/msui/topview.py | 49 +- mslib/msui/ui/ui_sideview_options.ui | 661 +++++++++++---------- mslib/msui/ui/ui_topview_mapappearance.ui | 470 ++++++++------- 8 files changed, 981 insertions(+), 603 deletions(-) diff --git a/mslib/msui/mpl_pathinteractor.py b/mslib/msui/mpl_pathinteractor.py index 5c6f5a6d3..9b5a23364 100644 --- a/mslib/msui/mpl_pathinteractor.py +++ b/mslib/msui/mpl_pathinteractor.py @@ -336,7 +336,7 @@ class PathPlotter: def __init__(self, ax, mplpath=None, facecolor='blue', edgecolor='yellow', linecolor='blue', markerfacecolor='red', - marker='o', label_waypoints=True): + marker='o', label_waypoints=True, line_thickness=2, line_style="Solid", line_transparency=1.0): """The constructor initializes the path patches, overlying line plot and connects matplotlib signals. @@ -367,11 +367,20 @@ def __init__(self, ax, mplpath=None, self.pathpatch = pathpatch self.pathpatch.set_animated(True) # ensure correct redrawing + # Initialize line style options + self.line_style_dict = { + "Solid": '-', + "Dashed": '--', + "Dotted": ':', + "Dash-dot": '-.' + } + # Draw the line representing flight track or profile (correct # vertices handling for the line needs to be ensured in subclasses). x, y = list(zip(*self.pathpatch.get_path().vertices)) self.line, = self.ax.plot(x, y, color=linecolor, - marker=marker, linewidth=2, + marker=marker, linewidth=line_thickness, linestyle=self.line_style_dict[line_style], + alpha=line_transparency, markerfacecolor=markerfacecolor, animated=True) @@ -384,6 +393,26 @@ def __init__(self, ax, mplpath=None, canvas.mpl_connect('draw_event', self.draw_callback) self.canvas = canvas + def get_line_style_dict(self): + """return the line style dict so other class can access it""" + return self.line_style_dict + + def set_line_thickness(self, thickness): + """Set the line thickness of the flight track.""" + self.line.set_linewidth(thickness) + self.canvas.draw() + + def set_line_style(self, style): + """Set the line style of the flight track.""" + if style in self.line_style_dict: + self.line.set_linestyle(self.line_style_dict[style]) + self.canvas.draw() + + def set_line_transparency(self, transparency): + """Set the line transparency of the flight track.""" + self.line.set_alpha(transparency) + self.canvas.draw() + def draw_callback(self, event): """Called when the figure is redrawn. Stores background data (for later restoration) and draws artists. @@ -932,6 +961,23 @@ def __init__(self, ax, waypoints, redraw_xaxis=None, clear_figure=None, numintpo self.clear_figure = clear_figure super().__init__(plotter=plotter, waypoints=waypoints) + def set_line_thickness(self, thickness): + """Set the thickness of the line representing the flight track.""" + self.plotter.line.set_linewidth(thickness) + self.redraw_figure() + + def set_line_style(self, style): + """Set the style of the line representing the flight track.""" + line_style_dict = self.plotter.get_line_style_dict() + if style in line_style_dict: + self.plotter.set_line_style(style) + self.redraw_figure() + + def set_line_transparency(self, transparency): + """Set the transparency of the line representing the flight track.""" + self.plotter.line.set_alpha(transparency) + self.redraw_figure() + def redraw_figure(self): """For the side view, changes in the horizontal position of a waypoint (including moved waypoints, new or deleted waypoints) make a complete @@ -1129,6 +1175,23 @@ def __init__(self, mplmap, waypoints, super().__init__(plotter=plotter, waypoints=waypoints) self.redraw_path() + def set_line_thickness(self, thickness): + """Set the thickness of the line representing the flight track.""" + self.plotter.line.set_linewidth(thickness) + self.redraw_path() + + def set_line_style(self, style): + """Set the style of the line representing the flight track.""" + line_style_dict = self.plotter.get_line_style_dict() + if style in line_style_dict: + self.plotter.set_line_style(style) + self.redraw_path() + + def set_line_transparency(self, transparency): + """Set the transparency of the line representing the flight track.""" + self.plotter.line.set_alpha(transparency) + self.redraw_path() + def appropriate_epsilon(self, px=5): """Determine an epsilon value appropriate for the current projection and figure size. diff --git a/mslib/msui/mpl_qtwidget.py b/mslib/msui/mpl_qtwidget.py index 7b37b186a..721a0878e 100644 --- a/mslib/msui/mpl_qtwidget.py +++ b/mslib/msui/mpl_qtwidget.py @@ -1280,6 +1280,17 @@ def update_ceiling(self, visible, color): def set_settings(self, settings, save=False): """Apply settings to view. """ + if settings is None: + settings = self.plotter.get_settings() + settings.setdefault("line_thickness", 2) + settings.setdefault("line_style", "Solid") + settings.setdefault("line_transparency", 1.0) + settings.setdefault("colour_ft_vertices", "blue") + settings.setdefault("colour_ft_waypoints", "red") + settings.setdefault("draw_marker", True) + settings.setdefault("draw_flighttrack", True) + settings.setdefault("label_flighttrack", True) + old_vertical_lines = self.plotter.settings["draw_verticals"] if settings is not None: self.plotter.set_settings(settings, save) @@ -1303,6 +1314,11 @@ def set_settings(self, settings, save=False): patch_facecolor=settings["colour_ft_fill"]) wpi_plotter.set_patch_visible(settings["fill_flighttrack"]) wpi_plotter.set_labels_visible(settings["label_flighttrack"]) + wpi_plotter.set_line_thickness(settings["line_thickness"]) + wpi_plotter.set_line_style(settings["line_style"]) + wpi_plotter.set_line_transparency( + settings["line_transparency"] / 100.0 if settings["line_transparency"] > 1 else settings[ + "line_transparency"]) # Normalize the (transparency) value if self.waypoints_model is not None \ and settings["draw_verticals"] != old_vertical_lines: @@ -1653,6 +1669,18 @@ def set_settings(self, settings, save=False): If settings is None, apply default settings. """ + if settings is None: + # Default value if not present + settings = self.plotter.get_settings() + settings.setdefault("line_thickness", 2) + settings.setdefault("line_style", "Solid") + settings.setdefault("line_transparency", 1.0) + settings.setdefault("colour_ft_vertices", "blue") + settings.setdefault("colour_ft_waypoints", "red") + settings.setdefault("draw_marker", True) + settings.setdefault("draw_flighttrack", True) + settings.setdefault("label_flighttrack", True) + self.plotter.set_settings(settings, save) settings = self.get_settings() if self.waypoints_interactor is not None: @@ -1662,7 +1690,11 @@ def set_settings(self, settings, save=False): wpi_plotter.show_marker = settings["draw_marker"] wpi_plotter.set_vertices_visible(settings["draw_flighttrack"]) wpi_plotter.set_labels_visible(settings["label_flighttrack"]) - + wpi_plotter.set_line_thickness(settings["line_thickness"]) + wpi_plotter.set_line_style(settings["line_style"]) + wpi_plotter.set_line_transparency( + settings["line_transparency"] / 100.0 if settings["line_transparency"] > 1 else settings[ + "line_transparency"]) # Normalize the (transparency) value self.draw() def set_remote_sensing_appearance(self, settings): diff --git a/mslib/msui/qt5/ui_sideview_options.py b/mslib/msui/qt5/ui_sideview_options.py index 42872c64a..1ffa0de7d 100644 --- a/mslib/msui/qt5/ui_sideview_options.py +++ b/mslib/msui/qt5/ui_sideview_options.py @@ -13,7 +13,7 @@ class Ui_SideViewOptionsDialog(object): def setupUi(self, SideViewOptionsDialog): SideViewOptionsDialog.setObjectName("SideViewOptionsDialog") - SideViewOptionsDialog.resize(538, 711) + SideViewOptionsDialog.resize(493, 900) self.verticalLayout_4 = QtWidgets.QVBoxLayout(SideViewOptionsDialog) self.verticalLayout_4.setObjectName("verticalLayout_4") self.groupBox = QtWidgets.QGroupBox(SideViewOptionsDialog) @@ -115,12 +115,37 @@ def setupUi(self, SideViewOptionsDialog): self.btDelete = QtWidgets.QPushButton(self.groupBox_2) self.btDelete.setObjectName("btDelete") self.verticalLayout.addWidget(self.btDelete) - spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.verticalLayout.addItem(spacerItem) self.horizontalLayout_3.addLayout(self.verticalLayout) self.verticalLayout_2.addLayout(self.horizontalLayout_3) self.verticalLayout_4.addWidget(self.groupBox_2) - self.groupBox_3 = QtWidgets.QGroupBox(SideViewOptionsDialog) + self.label_6 = QtWidgets.QLabel(SideViewOptionsDialog) + self.label_6.setObjectName("label_6") + self.verticalLayout_4.addWidget(self.label_6) + self.frame = QtWidgets.QFrame(SideViewOptionsDialog) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame.sizePolicy().hasHeightForWidth()) + self.frame.setSizePolicy(sizePolicy) + self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame.setFrameShadow(QtWidgets.QFrame.Raised) + self.frame.setObjectName("frame") + self.label_8 = QtWidgets.QLabel(self.frame) + self.label_8.setGeometry(QtCore.QRect(160, 210, 101, 16)) + self.label_8.setObjectName("label_8") + self.hsTransparencyControl = QtWidgets.QSlider(self.frame) + self.hsTransparencyControl.setGeometry(QtCore.QRect(20, 250, 131, 16)) + self.hsTransparencyControl.setProperty("value", 80) + self.hsTransparencyControl.setOrientation(QtCore.Qt.Horizontal) + self.hsTransparencyControl.setObjectName("hsTransparencyControl") + self.label_10 = QtWidgets.QLabel(self.frame) + self.label_10.setGeometry(QtCore.QRect(160, 240, 81, 20)) + self.label_10.setObjectName("label_10") + self.sbLineThickness = QtWidgets.QDoubleSpinBox(self.frame) + self.sbLineThickness.setGeometry(QtCore.QRect(20, 210, 131, 21)) + self.sbLineThickness.setObjectName("sbLineThickness") + self.groupBox_3 = QtWidgets.QGroupBox(self.frame) + self.groupBox_3.setGeometry(QtCore.QRect(10, 10, 451, 187)) self.groupBox_3.setObjectName("groupBox_3") self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.groupBox_3) self.verticalLayout_3.setObjectName("verticalLayout_3") @@ -161,8 +186,8 @@ def setupUi(self, SideViewOptionsDialog): self.btVerticesColour.setMinimumSize(QtCore.QSize(140, 0)) self.btVerticesColour.setObjectName("btVerticesColour") self.horizontalLayout_4.addWidget(self.btVerticesColour) - spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_4.addItem(spacerItem1) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_4.addItem(spacerItem) self.verticalLayout_3.addLayout(self.horizontalLayout_4) self.horizontalLayout_8 = QtWidgets.QHBoxLayout() self.horizontalLayout_8.setObjectName("horizontalLayout_8") @@ -174,8 +199,8 @@ def setupUi(self, SideViewOptionsDialog): self.btWaypointsColour.setMinimumSize(QtCore.QSize(140, 0)) self.btWaypointsColour.setObjectName("btWaypointsColour") self.horizontalLayout_8.addWidget(self.btWaypointsColour) - spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_8.addItem(spacerItem2) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_8.addItem(spacerItem1) self.verticalLayout_3.addLayout(self.horizontalLayout_8) self.horizontalLayout_5 = QtWidgets.QHBoxLayout() self.horizontalLayout_5.setObjectName("horizontalLayout_5") @@ -192,8 +217,8 @@ def setupUi(self, SideViewOptionsDialog): self.btFillColour.setMinimumSize(QtCore.QSize(140, 0)) self.btFillColour.setObjectName("btFillColour") self.horizontalLayout_5.addWidget(self.btFillColour) - spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_5.addItem(spacerItem3) + spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_5.addItem(spacerItem2) self.verticalLayout_3.addLayout(self.horizontalLayout_5) self.horizontalLayout_6 = QtWidgets.QHBoxLayout() self.horizontalLayout_6.setObjectName("horizontalLayout_6") @@ -207,7 +232,13 @@ def setupUi(self, SideViewOptionsDialog): self.cbVerticalLines.setObjectName("cbVerticalLines") self.horizontalLayout_7.addWidget(self.cbVerticalLines) self.verticalLayout_3.addLayout(self.horizontalLayout_7) - self.verticalLayout_4.addWidget(self.groupBox_3) + self.cbLineStyle = QtWidgets.QComboBox(self.frame) + self.cbLineStyle.setGeometry(QtCore.QRect(260, 210, 131, 21)) + self.cbLineStyle.setObjectName("cbLineStyle") + self.label_9 = QtWidgets.QLabel(self.frame) + self.label_9.setGeometry(QtCore.QRect(400, 210, 61, 16)) + self.label_9.setObjectName("label_9") + self.verticalLayout_4.addWidget(self.frame) self.groupBox_4 = QtWidgets.QGroupBox(SideViewOptionsDialog) self.groupBox_4.setObjectName("groupBox_4") self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.groupBox_4) @@ -221,38 +252,14 @@ def setupUi(self, SideViewOptionsDialog): self.btCeilingColour.setMinimumSize(QtCore.QSize(140, 0)) self.btCeilingColour.setObjectName("btCeilingColour") self.horizontalLayout_2.addWidget(self.btCeilingColour) - spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_2.addItem(spacerItem4) + spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_2.addItem(spacerItem3) self.verticalLayout_5.addLayout(self.horizontalLayout_2) self.verticalLayout_4.addWidget(self.groupBox_4) - spacerItem5 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.verticalLayout_4.addItem(spacerItem5) self.groupBox_5 = QtWidgets.QGroupBox(SideViewOptionsDialog) self.groupBox_5.setObjectName("groupBox_5") self.gridLayout_4 = QtWidgets.QGridLayout(self.groupBox_5) self.gridLayout_4.setObjectName("gridLayout_4") - self.cbtitlesize = QtWidgets.QComboBox(self.groupBox_5) - self.cbtitlesize.setObjectName("cbtitlesize") - self.cbtitlesize.addItem("") - self.cbtitlesize.addItem("") - self.cbtitlesize.addItem("") - self.cbtitlesize.addItem("") - self.cbtitlesize.addItem("") - self.cbtitlesize.addItem("") - self.cbtitlesize.addItem("") - self.cbtitlesize.addItem("") - self.cbtitlesize.addItem("") - self.cbtitlesize.addItem("") - self.cbtitlesize.addItem("") - self.cbtitlesize.addItem("") - self.cbtitlesize.addItem("") - self.cbtitlesize.addItem("") - self.cbtitlesize.addItem("") - self.cbtitlesize.addItem("") - self.gridLayout_4.addWidget(self.cbtitlesize, 3, 1, 1, 1) - self.label_7 = QtWidgets.QLabel(self.groupBox_5) - self.label_7.setObjectName("label_7") - self.gridLayout_4.addWidget(self.label_7, 3, 2, 1, 1) self.cbaxessize = QtWidgets.QComboBox(self.groupBox_5) self.cbaxessize.setObjectName("cbaxessize") self.cbaxessize.addItem("") @@ -275,9 +282,29 @@ def setupUi(self, SideViewOptionsDialog): self.label = QtWidgets.QLabel(self.groupBox_5) self.label.setObjectName("label") self.gridLayout_4.addWidget(self.label, 3, 0, 1, 1) + self.cbtitlesize = QtWidgets.QComboBox(self.groupBox_5) + self.cbtitlesize.setObjectName("cbtitlesize") + self.cbtitlesize.addItem("") + self.cbtitlesize.addItem("") + self.cbtitlesize.addItem("") + self.cbtitlesize.addItem("") + self.cbtitlesize.addItem("") + self.cbtitlesize.addItem("") + self.cbtitlesize.addItem("") + self.cbtitlesize.addItem("") + self.cbtitlesize.addItem("") + self.cbtitlesize.addItem("") + self.cbtitlesize.addItem("") + self.cbtitlesize.addItem("") + self.cbtitlesize.addItem("") + self.cbtitlesize.addItem("") + self.cbtitlesize.addItem("") + self.cbtitlesize.addItem("") + self.gridLayout_4.addWidget(self.cbtitlesize, 3, 1, 1, 1) + self.label_7 = QtWidgets.QLabel(self.groupBox_5) + self.label_7.setObjectName("label_7") + self.gridLayout_4.addWidget(self.label_7, 3, 2, 1, 1) self.verticalLayout_4.addWidget(self.groupBox_5) - spacerItem6 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.verticalLayout_4.addItem(spacerItem6) self.buttonBox = QtWidgets.QDialogButtonBox(SideViewOptionsDialog) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) @@ -326,9 +353,12 @@ def retranslateUi(self, SideViewOptionsDialog): item.setText(_translate("SideViewOptionsDialog", "flight levels")) self.btAdd.setText(_translate("SideViewOptionsDialog", "add")) self.btDelete.setText(_translate("SideViewOptionsDialog", "delete selected")) + self.label_6.setText(_translate("SideViewOptionsDialog", "Flight Track style options")) + self.label_8.setText(_translate("SideViewOptionsDialog", "Line Thickness")) + self.label_10.setText(_translate("SideViewOptionsDialog", "Transparency")) self.groupBox_3.setTitle(_translate("SideViewOptionsDialog", "Flight Track Colours")) self.cbDrawFlightTrack.setText(_translate("SideViewOptionsDialog", "draw flight track")) - self.btVerticesColour.setText(_translate("SideViewOptionsDialog", "colour of flight path")) + self.btVerticesColour.setText(_translate("SideViewOptionsDialog", "colour of flight track")) self.cbDrawMarker.setToolTip(_translate("SideViewOptionsDialog", "Draw a circle marker on every waypoint along the flight track")) self.cbDrawMarker.setText(_translate("SideViewOptionsDialog", "draw marker")) self.btWaypointsColour.setText(_translate("SideViewOptionsDialog", "colour of waypoints")) @@ -337,27 +367,11 @@ def retranslateUi(self, SideViewOptionsDialog): self.cbLabelFlightTrack.setText(_translate("SideViewOptionsDialog", "label flight track")) self.cbVerticalLines.setToolTip(_translate("SideViewOptionsDialog", "Draw a vertical line to visualise each point in the flight path")) self.cbVerticalLines.setText(_translate("SideViewOptionsDialog", "draw vertical lines")) + self.label_9.setText(_translate("SideViewOptionsDialog", "Line style")) self.groupBox_4.setTitle(_translate("SideViewOptionsDialog", "Performance")) self.cbDrawCeiling.setText(_translate("SideViewOptionsDialog", "draw ceiling altitude")) self.btCeilingColour.setText(_translate("SideViewOptionsDialog", "colour")) self.groupBox_5.setTitle(_translate("SideViewOptionsDialog", "Plot Options")) - self.cbtitlesize.setItemText(0, _translate("SideViewOptionsDialog", "default")) - self.cbtitlesize.setItemText(1, _translate("SideViewOptionsDialog", "4")) - self.cbtitlesize.setItemText(2, _translate("SideViewOptionsDialog", "6")) - self.cbtitlesize.setItemText(3, _translate("SideViewOptionsDialog", "8")) - self.cbtitlesize.setItemText(4, _translate("SideViewOptionsDialog", "10")) - self.cbtitlesize.setItemText(5, _translate("SideViewOptionsDialog", "12")) - self.cbtitlesize.setItemText(6, _translate("SideViewOptionsDialog", "14")) - self.cbtitlesize.setItemText(7, _translate("SideViewOptionsDialog", "16")) - self.cbtitlesize.setItemText(8, _translate("SideViewOptionsDialog", "18")) - self.cbtitlesize.setItemText(9, _translate("SideViewOptionsDialog", "20")) - self.cbtitlesize.setItemText(10, _translate("SideViewOptionsDialog", "22")) - self.cbtitlesize.setItemText(11, _translate("SideViewOptionsDialog", "24")) - self.cbtitlesize.setItemText(12, _translate("SideViewOptionsDialog", "26")) - self.cbtitlesize.setItemText(13, _translate("SideViewOptionsDialog", "28")) - self.cbtitlesize.setItemText(14, _translate("SideViewOptionsDialog", "30")) - self.cbtitlesize.setItemText(15, _translate("SideViewOptionsDialog", "32")) - self.label_7.setText(_translate("SideViewOptionsDialog", " Axes Label Size ")) self.cbaxessize.setItemText(0, _translate("SideViewOptionsDialog", "default")) self.cbaxessize.setItemText(1, _translate("SideViewOptionsDialog", "4")) self.cbaxessize.setItemText(2, _translate("SideViewOptionsDialog", "6")) @@ -375,6 +389,23 @@ def retranslateUi(self, SideViewOptionsDialog): self.cbaxessize.setItemText(14, _translate("SideViewOptionsDialog", "30")) self.cbaxessize.setItemText(15, _translate("SideViewOptionsDialog", "32")) self.label.setText(_translate("SideViewOptionsDialog", " Plot Title Size")) + self.cbtitlesize.setItemText(0, _translate("SideViewOptionsDialog", "default")) + self.cbtitlesize.setItemText(1, _translate("SideViewOptionsDialog", "4")) + self.cbtitlesize.setItemText(2, _translate("SideViewOptionsDialog", "6")) + self.cbtitlesize.setItemText(3, _translate("SideViewOptionsDialog", "8")) + self.cbtitlesize.setItemText(4, _translate("SideViewOptionsDialog", "10")) + self.cbtitlesize.setItemText(5, _translate("SideViewOptionsDialog", "12")) + self.cbtitlesize.setItemText(6, _translate("SideViewOptionsDialog", "14")) + self.cbtitlesize.setItemText(7, _translate("SideViewOptionsDialog", "16")) + self.cbtitlesize.setItemText(8, _translate("SideViewOptionsDialog", "18")) + self.cbtitlesize.setItemText(9, _translate("SideViewOptionsDialog", "20")) + self.cbtitlesize.setItemText(10, _translate("SideViewOptionsDialog", "22")) + self.cbtitlesize.setItemText(11, _translate("SideViewOptionsDialog", "24")) + self.cbtitlesize.setItemText(12, _translate("SideViewOptionsDialog", "26")) + self.cbtitlesize.setItemText(13, _translate("SideViewOptionsDialog", "28")) + self.cbtitlesize.setItemText(14, _translate("SideViewOptionsDialog", "30")) + self.cbtitlesize.setItemText(15, _translate("SideViewOptionsDialog", "32")) + self.label_7.setText(_translate("SideViewOptionsDialog", " Axes Label Size ")) if __name__ == "__main__": diff --git a/mslib/msui/qt5/ui_topview_mapappearance.py b/mslib/msui/qt5/ui_topview_mapappearance.py index 9882d5238..a9bffaa49 100644 --- a/mslib/msui/qt5/ui_topview_mapappearance.py +++ b/mslib/msui/qt5/ui_topview_mapappearance.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'mslib/msui/ui/ui_topview_mapappearance.ui' +# Form implementation generated from reading ui file 'ui_topview_mapappearance.ui' # # Created by: PyQt5 UI code generator 5.12.3 # @@ -13,7 +13,7 @@ class Ui_MapAppearanceDialog(object): def setupUi(self, MapAppearanceDialog): MapAppearanceDialog.setObjectName("MapAppearanceDialog") - MapAppearanceDialog.resize(394, 332) + MapAppearanceDialog.resize(394, 459) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -28,6 +28,9 @@ def setupUi(self, MapAppearanceDialog): self.cbDrawCoastlines.setChecked(True) self.cbDrawCoastlines.setObjectName("cbDrawCoastlines") self.verticalLayout.addWidget(self.cbDrawCoastlines) + self.cbLabelFlightTrack = QtWidgets.QCheckBox(MapAppearanceDialog) + self.cbLabelFlightTrack.setObjectName("cbLabelFlightTrack") + self.verticalLayout.addWidget(self.cbLabelFlightTrack) self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") self.cbFillWaterBodies = QtWidgets.QCheckBox(MapAppearanceDialog) @@ -100,10 +103,39 @@ def setupUi(self, MapAppearanceDialog): spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Minimum) self.horizontalLayout_2.addItem(spacerItem1) self.verticalLayout.addLayout(self.horizontalLayout_2) - self.horizontalLayout_3 = QtWidgets.QHBoxLayout() - self.horizontalLayout_3.setObjectName("horizontalLayout_3") - self.cbDrawFlightTrack = QtWidgets.QCheckBox(MapAppearanceDialog) + spacerItem2 = QtWidgets.QSpacerItem(20, 10, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.verticalLayout.addItem(spacerItem2) + self.groupBox_2 = QtWidgets.QGroupBox(MapAppearanceDialog) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(150) + sizePolicy.setHeightForWidth(self.groupBox_2.sizePolicy().hasHeightForWidth()) + self.groupBox_2.setSizePolicy(sizePolicy) + self.groupBox_2.setObjectName("groupBox_2") + self.cbLineStyle = QtWidgets.QComboBox(self.groupBox_2) + self.cbLineStyle.setGeometry(QtCore.QRect(170, 120, 131, 22)) + self.cbLineStyle.setObjectName("cbLineStyle") + self.label_4 = QtWidgets.QLabel(self.groupBox_2) + self.label_4.setGeometry(QtCore.QRect(10, 120, 61, 16)) + self.label_4.setObjectName("label_4") + self.label_2 = QtWidgets.QLabel(self.groupBox_2) + self.label_2.setGeometry(QtCore.QRect(10, 150, 91, 16)) + self.label_2.setAlignment(QtCore.Qt.AlignBottom|QtCore.Qt.AlignHCenter) + self.label_2.setObjectName("label_2") + self.sbLineThickness = QtWidgets.QDoubleSpinBox(self.groupBox_2) + self.sbLineThickness.setGeometry(QtCore.QRect(170, 150, 131, 21)) + self.sbLineThickness.setObjectName("sbLineThickness") + self.cbDrawMarker = QtWidgets.QCheckBox(self.groupBox_2) + self.cbDrawMarker.setGeometry(QtCore.QRect(10, 30, 145, 21)) + self.cbDrawMarker.setMinimumSize(QtCore.QSize(145, 0)) + self.cbDrawMarker.setObjectName("cbDrawMarker") + self.btWaypointsColour = QtWidgets.QPushButton(self.groupBox_2) + self.btWaypointsColour.setGeometry(QtCore.QRect(170, 30, 135, 23)) + self.btWaypointsColour.setMinimumSize(QtCore.QSize(135, 0)) + self.btWaypointsColour.setObjectName("btWaypointsColour") + self.cbDrawFlightTrack = QtWidgets.QCheckBox(self.groupBox_2) self.cbDrawFlightTrack.setEnabled(True) + self.cbDrawFlightTrack.setGeometry(QtCore.QRect(10, 60, 145, 21)) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -132,39 +164,26 @@ def setupUi(self, MapAppearanceDialog): self.cbDrawFlightTrack.setPalette(palette) self.cbDrawFlightTrack.setChecked(True) self.cbDrawFlightTrack.setObjectName("cbDrawFlightTrack") - self.horizontalLayout_3.addWidget(self.cbDrawFlightTrack) - self.btVerticesColour = QtWidgets.QPushButton(MapAppearanceDialog) + self.btVerticesColour = QtWidgets.QPushButton(self.groupBox_2) + self.btVerticesColour.setGeometry(QtCore.QRect(170, 60, 135, 23)) self.btVerticesColour.setMinimumSize(QtCore.QSize(135, 0)) self.btVerticesColour.setObjectName("btVerticesColour") - self.horizontalLayout_3.addWidget(self.btVerticesColour) - spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_3.addItem(spacerItem2) - self.verticalLayout.addLayout(self.horizontalLayout_3) - self.horizontalLayout_5 = QtWidgets.QHBoxLayout() - self.horizontalLayout_5.setObjectName("horizontalLayout_5") - self.cbDrawMarker = QtWidgets.QCheckBox(MapAppearanceDialog) - self.cbDrawMarker.setMinimumSize(QtCore.QSize(145, 0)) - self.cbDrawMarker.setObjectName("cbDrawMarker") - self.horizontalLayout_5.addWidget(self.cbDrawMarker) - self.btWaypointsColour = QtWidgets.QPushButton(MapAppearanceDialog) - self.btWaypointsColour.setMinimumSize(QtCore.QSize(135, 0)) - self.btWaypointsColour.setObjectName("btWaypointsColour") - self.horizontalLayout_5.addWidget(self.btWaypointsColour) - spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_5.addItem(spacerItem3) - self.verticalLayout.addLayout(self.horizontalLayout_5) - self.horizontalLayout_4 = QtWidgets.QHBoxLayout() - self.horizontalLayout_4.setObjectName("horizontalLayout_4") - self.cbLabelFlightTrack = QtWidgets.QCheckBox(MapAppearanceDialog) - self.cbLabelFlightTrack.setObjectName("cbLabelFlightTrack") - self.horizontalLayout_4.addWidget(self.cbLabelFlightTrack) - self.verticalLayout.addLayout(self.horizontalLayout_4) - spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.verticalLayout.addItem(spacerItem4) + self.label_3 = QtWidgets.QLabel(self.groupBox_2) + self.label_3.setGeometry(QtCore.QRect(10, 90, 131, 16)) + self.label_3.setObjectName("label_3") + self.hsTransparencyControl = QtWidgets.QSlider(self.groupBox_2) + self.hsTransparencyControl.setGeometry(QtCore.QRect(170, 90, 131, 22)) + self.hsTransparencyControl.setProperty("value", 20) + self.hsTransparencyControl.setOrientation(QtCore.Qt.Horizontal) + self.hsTransparencyControl.setObjectName("hsTransparencyControl") + self.verticalLayout.addWidget(self.groupBox_2) self.groupBox = QtWidgets.QGroupBox(MapAppearanceDialog) self.groupBox.setObjectName("groupBox") self.gridLayout_4 = QtWidgets.QGridLayout(self.groupBox) self.gridLayout_4.setObjectName("gridLayout_4") + self.label_7 = QtWidgets.QLabel(self.groupBox) + self.label_7.setObjectName("label_7") + self.gridLayout_4.addWidget(self.label_7, 3, 2, 1, 1) self.tov_cbtitlesize = QtWidgets.QComboBox(self.groupBox) self.tov_cbtitlesize.setObjectName("tov_cbtitlesize") self.tov_cbtitlesize.addItem("") @@ -184,9 +203,6 @@ def setupUi(self, MapAppearanceDialog): self.tov_cbtitlesize.addItem("") self.tov_cbtitlesize.addItem("") self.gridLayout_4.addWidget(self.tov_cbtitlesize, 3, 1, 1, 1) - self.label_7 = QtWidgets.QLabel(self.groupBox) - self.label_7.setObjectName("label_7") - self.gridLayout_4.addWidget(self.label_7, 3, 2, 1, 1) self.tov_cbaxessize = QtWidgets.QComboBox(self.groupBox) self.tov_cbaxessize.setObjectName("tov_cbaxessize") self.tov_cbaxessize.addItem("") @@ -210,8 +226,6 @@ def setupUi(self, MapAppearanceDialog): self.label.setObjectName("label") self.gridLayout_4.addWidget(self.label, 3, 0, 1, 1) self.verticalLayout.addWidget(self.groupBox) - spacerItem5 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.verticalLayout.addItem(spacerItem5) self.buttonBox = QtWidgets.QDialogButtonBox(MapAppearanceDialog) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) @@ -228,17 +242,22 @@ def retranslateUi(self, MapAppearanceDialog): MapAppearanceDialog.setWindowTitle(_translate("MapAppearanceDialog", "Top View Map Appearance")) self.cbDrawGraticule.setText(_translate("MapAppearanceDialog", "draw graticule")) self.cbDrawCoastlines.setText(_translate("MapAppearanceDialog", "draw coastlines")) + self.cbLabelFlightTrack.setText(_translate("MapAppearanceDialog", "label flight path")) self.cbFillWaterBodies.setText(_translate("MapAppearanceDialog", "fill map background")) self.btWaterColour.setText(_translate("MapAppearanceDialog", "colour")) self.cbFillContinents.setText(_translate("MapAppearanceDialog", "fill continents")) self.btLandColour.setText(_translate("MapAppearanceDialog", "colour")) - self.cbDrawFlightTrack.setText(_translate("MapAppearanceDialog", "draw flight track")) - self.btVerticesColour.setText(_translate("MapAppearanceDialog", "colour of flight path")) + self.groupBox_2.setTitle(_translate("MapAppearanceDialog", "Flight Track style options")) + self.label_4.setText(_translate("MapAppearanceDialog", "Line style")) + self.label_2.setText(_translate("MapAppearanceDialog", "Line Thickness")) self.cbDrawMarker.setToolTip(_translate("MapAppearanceDialog", "Draw a circle marker on every waypoint along the flight track")) self.cbDrawMarker.setText(_translate("MapAppearanceDialog", "draw marker")) self.btWaypointsColour.setText(_translate("MapAppearanceDialog", "colour of waypoints")) - self.cbLabelFlightTrack.setText(_translate("MapAppearanceDialog", "label flight track")) + self.cbDrawFlightTrack.setText(_translate("MapAppearanceDialog", "draw flight path")) + self.btVerticesColour.setText(_translate("MapAppearanceDialog", "colour of flight path")) + self.label_3.setText(_translate("MapAppearanceDialog", "Transparency control")) self.groupBox.setTitle(_translate("MapAppearanceDialog", "Plot Options")) + self.label_7.setText(_translate("MapAppearanceDialog", " Axes Label Size ")) self.tov_cbtitlesize.setItemText(0, _translate("MapAppearanceDialog", "default")) self.tov_cbtitlesize.setItemText(1, _translate("MapAppearanceDialog", "4")) self.tov_cbtitlesize.setItemText(2, _translate("MapAppearanceDialog", "6")) @@ -255,7 +274,6 @@ def retranslateUi(self, MapAppearanceDialog): self.tov_cbtitlesize.setItemText(13, _translate("MapAppearanceDialog", "28")) self.tov_cbtitlesize.setItemText(14, _translate("MapAppearanceDialog", "30")) self.tov_cbtitlesize.setItemText(15, _translate("MapAppearanceDialog", "32")) - self.label_7.setText(_translate("MapAppearanceDialog", " Axes Label Size ")) self.tov_cbaxessize.setItemText(0, _translate("MapAppearanceDialog", "default")) self.tov_cbaxessize.setItemText(1, _translate("MapAppearanceDialog", "4")) self.tov_cbaxessize.setItemText(2, _translate("MapAppearanceDialog", "6")) @@ -273,3 +291,13 @@ def retranslateUi(self, MapAppearanceDialog): self.tov_cbaxessize.setItemText(14, _translate("MapAppearanceDialog", "30")) self.tov_cbaxessize.setItemText(15, _translate("MapAppearanceDialog", "32")) self.label.setText(_translate("MapAppearanceDialog", " Plot Title Size")) + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + MapAppearanceDialog = QtWidgets.QDialog() + ui = Ui_MapAppearanceDialog() + ui.setupUi(MapAppearanceDialog) + MapAppearanceDialog.show() + sys.exit(app.exec_()) diff --git a/mslib/msui/sideview.py b/mslib/msui/sideview.py index f388ed597..64620fdd5 100644 --- a/mslib/msui/sideview.py +++ b/mslib/msui/sideview.py @@ -51,6 +51,9 @@ class MSUI_SV_OptionsDialog(QtWidgets.QDialog, ui_opt.Ui_SideViewOptionsDialog): Dialog to specify sideview options. User interface is specified in "ui_sideview_options.py". """ + signal_line_thickness_change = QtCore.pyqtSignal(float) + signal_line_style_change = QtCore.pyqtSignal(str) + signal_transparency_change = QtCore.pyqtSignal(float) def __init__(self, parent=None, settings=None): """ @@ -100,6 +103,11 @@ def __init__(self, parent=None, settings=None): self.cbVerticalLines.setChecked(settings["draw_verticals"]) self.cbDrawMarker.setChecked(settings["draw_marker"]) + self.sbLineThickness.setValue(settings.get("line_thickness", 2)) + self.cbLineStyle.addItems(["Solid", "Dashed", "Dotted", "Dash-dot"]) # Item added in the list + self.cbLineStyle.setCurrentText(settings.get("line_style", "Solid")) + self.hsTransparencyControl.setValue(int(settings.get("line_transparency", 1.0) * 100)) + for button, ids in [(self.btFillColour, "colour_ft_fill"), (self.btWaypointsColour, "colour_ft_waypoints"), (self.btVerticesColour, "colour_ft_vertices"), @@ -122,6 +130,24 @@ def __init__(self, parent=None, settings=None): self.tableWidget.itemChanged.connect(self.itemChanged) + # Store values instead of emitting signals immediately + self.line_thickness = settings.get("line_thickness", 2) + self.line_style = settings.get("line_style", "Solid") + self.line_transparency = settings.get("line_transparency", 1.0) + + self.sbLineThickness.valueChanged.connect(self.onLineThicknessChanged) + self.cbLineStyle.currentTextChanged.connect(self.onLineStyleChanged) + self.hsTransparencyControl.valueChanged.connect(self.onTransparencyChanged) + + def onLineThicknessChanged(self, value): + self.line_thickness = value + + def onLineStyleChanged(self, value): + self.line_style = value + + def onTransparencyChanged(self, value): + self.line_transparency = value / 100 + def setBotTopLimits(self, axis_type): bot, top = { "maximum": (0, 2132), @@ -217,6 +243,9 @@ def get_settings(self): "draw_flighttrack": self.cbDrawFlightTrack.isChecked(), "fill_flighttrack": self.cbFillFlightTrack.isChecked(), "label_flighttrack": self.cbLabelFlightTrack.isChecked(), + "line_thickness": self.line_thickness, + "line_style": self.line_style, + "line_transparency": self.line_transparency, "colour_ft_vertices": QtGui.QPalette(self.btVerticesColour.palette()).color(QtGui.QPalette.Button).getRgbF(), "colour_ft_waypoints": @@ -403,9 +432,28 @@ def open_settings_dialog(self): self.currlevel = settings["vertical_axis"] dlg = MSUI_SV_OptionsDialog(parent=self, settings=settings) dlg.setModal(True) + dlg.signal_line_thickness_change.connect(self.set_line_thickness) # Connect to signal + dlg.signal_line_style_change.connect(self.set_line_style) + dlg.signal_transparency_change.connect(self.set_line_transparency) if dlg.exec_() == QtWidgets.QDialog.Accepted: settings = dlg.get_settings() self.getView().set_settings(settings, save=True) + self.set_line_thickness(settings["line_thickness"]) + self.set_line_style(settings["line_style"]) + self.set_line_transparency(settings["line_transparency"]) + settings.update(settings) self.currvertical = ', '.join(map(str, settings["vertical_extent"])) self.currlevel = settings["vertical_axis"] dlg.destroy() + + def set_line_thickness(self, thickness): + """Set the line thickness of the flight track.""" + self.mpl.canvas.waypoints_interactor.set_line_thickness(thickness) + + def set_line_style(self, style): + """Set the line style of the flight track""" + self.mpl.canvas.waypoints_interactor.set_line_style(style) + + def set_line_transparency(self, transparency): + """Set the line transparency of the flight track""" + self.mpl.canvas.waypoints_interactor.set_line_transparency(transparency) diff --git a/mslib/msui/topview.py b/mslib/msui/topview.py index a162fe891..68e773e68 100644 --- a/mslib/msui/topview.py +++ b/mslib/msui/topview.py @@ -64,6 +64,9 @@ class MSUI_TV_MapAppearanceDialog(QtWidgets.QDialog, ui_ma.Ui_MapAppearanceDialo defined in "ui_topview_mapappearance.py". """ signal_ft_vertices_color_change = QtCore.pyqtSignal(str, tuple) + signal_line_thickness_change = QtCore.pyqtSignal(float) + signal_line_style_change = QtCore.pyqtSignal(str) + signal_transparency_change = QtCore.pyqtSignal(float) def __init__(self, parent=None, settings=None, wms_connected=False): """ @@ -97,6 +100,11 @@ def __init__(self, parent=None, settings=None, wms_connected=False): self.cbDrawMarker.setChecked(settings["draw_marker"]) self.cbLabelFlightTrack.setChecked(settings["label_flighttrack"]) + self.sbLineThickness.setValue(settings.get("line_thickness", 2)) + self.cbLineStyle.addItems(["Solid", "Dashed", "Dotted", "Dash-dot"]) # Item added in the list + self.cbLineStyle.setCurrentText(settings.get("line_style", "Solid")) + self.hsTransparencyControl.setValue(int(settings.get("line_transparency", 1.0) * 100)) + for button, ids in [(self.btWaterColour, "colour_water"), (self.btLandColour, "colour_land"), (self.btWaypointsColour, "colour_ft_waypoints"), @@ -113,6 +121,15 @@ def __init__(self, parent=None, settings=None, wms_connected=False): self.btWaypointsColour.clicked.connect(functools.partial(self.setColour, "ft_waypoints")) self.btVerticesColour.clicked.connect(functools.partial(self.setColour, "ft_vertices")) + # Store values instead of emitting signals immediately + self.line_thickness = settings.get("line_thickness", 2) + self.line_style = settings.get("line_style", "Solid") + self.line_transparency = settings.get("line_transparency", 1.0) + + self.sbLineThickness.valueChanged.connect(self.onLineThicknessChanged) + self.cbLineStyle.currentTextChanged.connect(self.onLineStyleChanged) + self.hsTransparencyControl.valueChanged.connect(self.onTransparencyChanged) + # Shows previously selected element in the fontsize comboboxes as the current index. for i in range(self.tov_cbtitlesize.count()): if self.tov_cbtitlesize.itemText(i) == settings["tov_plot_title_size"]: @@ -122,6 +139,15 @@ def __init__(self, parent=None, settings=None, wms_connected=False): if self.tov_cbaxessize.itemText(i) == settings["tov_axes_label_size"]: self.tov_cbaxessize.setCurrentIndex(i) + def onLineThicknessChanged(self, value): + self.line_thickness = value + + def onLineStyleChanged(self, value): + self.line_style = value + + def onTransparencyChanged(self, value): + self.line_transparency = value / 100.0 + def get_settings(self): """ """ @@ -135,7 +161,9 @@ def get_settings(self): "label_flighttrack": self.cbLabelFlightTrack.isChecked(), "tov_plot_title_size": self.tov_cbtitlesize.currentText(), "tov_axes_label_size": self.tov_cbaxessize.currentText(), - + "line_thickness": self.line_thickness, + "line_style": self.line_style, + "line_transparency": self.line_transparency, "colour_water": QtGui.QPalette(self.btWaterColour.palette()).color(QtGui.QPalette.Button).getRgbF(), "colour_land": @@ -508,17 +536,36 @@ def setIdentifier(self, identifier): def open_settings_dialog(self): """ + Open the map appearance settings dialog. """ settings = self.getView().get_settings() dlg = MSUI_TV_MapAppearanceDialog(parent=self, settings=settings, wms_connected=self.wms_connected) dlg.setModal(False) dlg.signal_ft_vertices_color_change.connect(self.set_ft_vertices_color) + dlg.signal_line_thickness_change.connect(self.set_line_thickness) # Connect to signal + dlg.signal_line_style_change.connect(self.set_line_style) + dlg.signal_transparency_change.connect(self.set_line_transparency) if dlg.exec_() == QtWidgets.QDialog.Accepted: settings = dlg.get_settings() + self.set_line_thickness(settings["line_thickness"]) + self.set_line_style(settings["line_style"]) + self.set_line_transparency(settings["line_transparency"]) self.getView().set_settings(settings, save=True) self.mpl.canvas.waypoints_interactor.redraw_path() dlg.destroy() + def set_line_thickness(self, thickness): + """Set the line thickness of the flight track.""" + self.mpl.canvas.waypoints_interactor.set_line_thickness(thickness) + + def set_line_style(self, style): + """Set the line style of the flight track""" + self.mpl.canvas.waypoints_interactor.set_line_style(style) + + def set_line_transparency(self, transparency): + """Set the line transparency of the flight track""" + self.mpl.canvas.waypoints_interactor.set_line_transparency(transparency) + @QtCore.pyqtSlot(str, tuple) def set_ft_vertices_color(self, which, color): if which == "ft_vertices": diff --git a/mslib/msui/ui/ui_sideview_options.ui b/mslib/msui/ui/ui_sideview_options.ui index 8ffa3b08b..c835b9413 100644 --- a/mslib/msui/ui/ui_sideview_options.ui +++ b/mslib/msui/ui/ui_sideview_options.ui @@ -6,8 +6,8 @@ 0 0 - 538 - 711 + 493 + 900 @@ -213,19 +213,6 @@ - - - - Qt::Vertical - - - - 20 - 40 - - - - @@ -234,254 +221,358 @@ - - - Flight Track Colours + + + Flight Track style options - - - - - - - true - - - - 0 - 0 - - - - - 140 - 0 - - - - - - - - - 20 - 19 - 18 - - - - - - - 20 - 19 - 18 - - - - - - - - - 20 - 19 - 18 - - - - - - - 20 - 19 - 18 - - - - - - - - - 0 - 0 - 0 - - - - - - - 173 - 173 - 173 - - - - - - - - draw flight track - - - true - - - - - - - - 140 - 0 - - - - colour of flight path - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - 140 - 0 - - - - Draw a circle marker on every waypoint along the flight track - - - draw marker - - - - - - - - 140 - 0 - - - - colour of waypoints - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - 0 - 0 - - - - - 140 - 0 - - - - fill flight track - - - - - - - - 140 - 0 - - - - colour - - - - - - - Qt::Horizontal - - - QSizePolicy::MinimumExpanding - - - - 40 - 20 - - - - - - - - - - - - label flight track - - - - - - - - - - - Draw a vertical line to visualise each point in the flight path - - - draw vertical lines - - - - - - + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 160 + 210 + 101 + 16 + + + + Line Thickness + + + + + + 20 + 250 + 131 + 16 + + + + 80 + + + Qt::Horizontal + + + + + + 160 + 240 + 81 + 20 + + + + Transparency + + + + + + 20 + 210 + 131 + 21 + + + + + + + 10 + 10 + 451 + 187 + + + + Flight Track Colours + + + + + + + + true + + + + 0 + 0 + + + + + 140 + 0 + + + + + + + + + 20 + 19 + 18 + + + + + + + 20 + 19 + 18 + + + + + + + + + 20 + 19 + 18 + + + + + + + 20 + 19 + 18 + + + + + + + + + 0 + 0 + 0 + + + + + + + 173 + 173 + 173 + + + + + + + + draw flight track + + + true + + + + + + + + 140 + 0 + + + + colour of flight track + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 140 + 0 + + + + Draw a circle marker on every waypoint along the flight track + + + draw marker + + + + + + + + 140 + 0 + + + + colour of waypoints + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 0 + 0 + + + + + 140 + 0 + + + + fill flight track + + + + + + + + 140 + 0 + + + + colour + + + + + + + Qt::Horizontal + + + QSizePolicy::MinimumExpanding + + + + 40 + 20 + + + + + + + + + + + + label flight track + + + + + + + + + + + Draw a vertical line to visualise each point in the flight path + + + draw vertical lines + + + + + + + + + + + 260 + 210 + 131 + 21 + + + + + + + 400 + 210 + 61 + 16 + + + + Line style + + @@ -530,27 +621,14 @@ - - - - Qt::Vertical - - - - 20 - 40 - - - - Plot Options - - + + default @@ -633,15 +711,15 @@ - - + + - Axes Label Size + Plot Title Size - - + + default @@ -724,29 +802,16 @@ - - + + - Plot Title Size + Axes Label Size - - - - Qt::Vertical - - - - 20 - 40 - - - - diff --git a/mslib/msui/ui/ui_topview_mapappearance.ui b/mslib/msui/ui/ui_topview_mapappearance.ui index bf6c93920..752c0910f 100644 --- a/mslib/msui/ui/ui_topview_mapappearance.ui +++ b/mslib/msui/ui/ui_topview_mapappearance.ui @@ -7,7 +7,7 @@ 0 0 394 - 332 + 459 @@ -37,6 +37,13 @@ + + + + label flight path + + + @@ -263,200 +270,277 @@ - - - - - true - - - - 0 - 0 - - - - - 145 - 0 - - - - - - - - - 20 - 19 - 18 - - - - - - - 20 - 19 - 18 - - - - - - - - - 20 - 19 - 18 - - - - - - - 20 - 19 - 18 - - - - - - - - - 0 - 0 - 0 - - - - - - - 173 - 173 - 173 - - - - - - - - draw flight track - - - true - - - - - - - - 135 - 0 - - - - colour of flight path - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - 145 - 0 - - - - Draw a circle marker on every waypoint along the flight track - - - draw marker - - - - - - - - 135 - 0 - - - - colour of waypoints - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - label flight track - - - - - - - + Qt::Vertical 20 - 40 + 10 + + + + + 0 + 150 + + + + Flight Track style options + + + + + 170 + 120 + 131 + 22 + + + + + + + 10 + 120 + 61 + 16 + + + + Line style + + + + + + 10 + 150 + 91 + 16 + + + + Line Thickness + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + 170 + 150 + 131 + 21 + + + + + + + 10 + 30 + 145 + 21 + + + + + 145 + 0 + + + + Draw a circle marker on every waypoint along the flight track + + + draw marker + + + + + + 170 + 30 + 135 + 23 + + + + + 135 + 0 + + + + colour of waypoints + + + + + true + + + + 10 + 60 + 145 + 21 + + + + + 0 + 0 + + + + + 145 + 0 + + + + + + + + + 20 + 19 + 18 + + + + + + + 20 + 19 + 18 + + + + + + + + + 20 + 19 + 18 + + + + + + + 20 + 19 + 18 + + + + + + + + + 0 + 0 + 0 + + + + + + + 173 + 173 + 173 + + + + + + + + draw flight path + + + true + + + + + + 170 + 60 + 135 + 23 + + + + + 135 + 0 + + + + colour of flight path + + + + + + 10 + 90 + 131 + 16 + + + + Transparency control + + + + + + 170 + 90 + 131 + 22 + + + + 20 + + + Qt::Horizontal + + + + Plot Options + + + + Axes Label Size + + + @@ -541,13 +625,6 @@ - - - - Axes Label Size - - - @@ -642,19 +719,6 @@ - - - - Qt::Vertical - - - - 20 - 40 - - - - @@ -676,8 +740,8 @@ accept() - 248 - 254 + 259 + 464 157 @@ -692,8 +756,8 @@ reject() - 316 - 260 + 327 + 464 286 From 9a1a1ce8a5e46d96fc62459b823fcc7df551adc9 Mon Sep 17 00:00:00 2001 From: Rohit prasad Date: Mon, 19 Aug 2024 21:08:56 +0530 Subject: [PATCH 2/9] feat: new features for docking widget and a new color pallet (#2428) * UI of MFDW * new features working (MScolab to be..) * new features working locally as well as mscolab * select feature and a ui * new color dialog implemented * new color dialog implemented in every widgets and automatic color assignment implemented * flake8 resolved * requested changes * requested changes * UI elements to reflect user change * UI elements to reflect user change * UI elements to reflect user change * UI elements to reflect user change done in both * UI button disable and enable * UI button disable and enable * more on ui button disable/enable * in mscolab with more efficiency * fix with the checkboxs * requested fix * requested chages * requested chages on issue no 2 * flake8 resolve * requested changes * path fix * changes by joernu * reuested change --- mslib/msui/multiple_flightpath_dockwidget.py | 433 +++++++++++++++--- .../qt5/ui_multiple_flightpath_dockwidget.py | 81 +++- mslib/msui/qt5/ui_topview_mapappearance.py | 12 +- mslib/msui/sideview.py | 18 +- mslib/msui/topview.py | 16 +- .../ui/ui_multiple_flightpath_dockwidget.ui | 244 +++++++--- mslib/msui/ui/ui_topview_mapappearance.ui | 2 +- mslib/utils/colordialog.py | 81 ++++ tests/_test_msui/test_sideview.py | 14 +- 9 files changed, 725 insertions(+), 176 deletions(-) create mode 100644 mslib/utils/colordialog.py diff --git a/mslib/msui/multiple_flightpath_dockwidget.py b/mslib/msui/multiple_flightpath_dockwidget.py index d80f10615..380fb6bc8 100644 --- a/mslib/msui/multiple_flightpath_dockwidget.py +++ b/mslib/msui/multiple_flightpath_dockwidget.py @@ -24,7 +24,7 @@ See the License for the specific language governing permissions and limitations under the License. """ - +import random import requests import json from PyQt5 import QtWidgets, QtGui, QtCore @@ -35,6 +35,7 @@ from mslib.utils.qt import Worker from mslib.utils.config import config_loader from urllib.parse import urljoin +from mslib.utils.colordialog import CustomColorDialog class QMscolabOperationsListWidgetItem(QtWidgets.QListWidgetItem): @@ -56,18 +57,21 @@ class MultipleFlightpath: Represent a Multiple FLightpath """ - def __init__(self, mapcanvas, wp, linewidth=2.0, color='blue'): + def __init__(self, mapcanvas, wp, linewidth=2.0, color='blue', line_transparency=1.0, line_style="solid"): self.map = mapcanvas self.flightlevel = None self.comments = '' self.patches = [] self.waypoints = wp self.linewidth = linewidth + self.line_transparency = line_transparency + self.line_style = line_style self.color = color self.draw() def draw_line(self, x, y): - self.patches.append(self.map.plot(x, y, color=self.color, linewidth=self.linewidth)) + self.patches.append(self.map.plot(x, y, color=self.color, linewidth=self.linewidth, + alpha=self.line_transparency, linestyle=self.line_style)) def compute_xy(self, lon, lat): x, y = self.map.gcpoints_path(lon, lat) @@ -81,13 +85,24 @@ def get_lonlat(self): lon.append(self.waypoints[i][1]) return lat, lon - def update(self, linewidth=None, color=None): + def update(self, linewidth=None, color=None, line_transparency=None, line_style=None): if linewidth is not None: self.linewidth = linewidth if color is not None: self.color = color - self.remove() - self.draw() + if line_transparency is not None: + self.line_transparency = line_transparency + if line_style is not None: + self.line_style = line_style + + for patch in self.patches: # allows dynamic updating of the flight path's appearance without needing to + # remove and redraw the entire path + for elem in patch: + elem.set_linewidth(self.linewidth) + elem.set_color(self.color) + elem.set_alpha(self.line_transparency) + elem.set_linestyle(self.line_style) + self.map.ax.figure.canvas.draw() def draw(self): lat, lon = self.get_lonlat() @@ -114,6 +129,25 @@ class MultipleFlightpathControlWidget(QtWidgets.QWidget, ui.Ui_MultipleViewWidge signal_parent_closes = QtCore.pyqtSignal() + custom_colors = [ + (128, 0, 0), (195, 31, 89), (245, 151, 87), (253, 228, 66), (0, 0, 255), + (96, 195, 110), (101, 216, 242), (164, 70, 190), (241, 90, 234), (185, 186, 187), + (230, 25, 75), (210, 207, 148), (53, 110, 51), (245, 130, 49), (44, 44, 44), + (0, 0, 117), (154, 99, 36), (128, 128, 0), (0, 0, 0) + # Add more colors as needed + ] + + # Define the mapping from combo box text to line style codes + line_styles = { + "Solid": '-', + "Dashed": '--', + "Dotted": ':', + "Dash-dot": '-.' + } + + # Reverse dictionary + line_styles_reverse = {v: k for k, v in line_styles.items()} + def __init__(self, parent=None, view=None, listFlightTracks=None, listOperationsMSC=None, category=None, activeFlightTrack=None, active_op_id=None, mscolab_server_url=None, token=None): @@ -124,6 +158,7 @@ def __init__(self, parent=None, view=None, listFlightTracks=None, self.view = view # canvas self.flight_path = None # flightpath object self.dict_flighttrack = {} # Dictionary of flighttrack data: patch,color,wp_model + self.used_colors = [] # Instance variable to track used colors self.active_flight_track = activeFlightTrack self.active_op_id = active_op_id self.msc_category = category # object of active category @@ -135,9 +170,10 @@ def __init__(self, parent=None, view=None, listFlightTracks=None, ft_settings_dict = self.ui.getView().get_settings() self.color = ft_settings_dict["colour_ft_vertices"] else: - self.color = (0, 0, 1, 1) + self.color = self.get_random_color() self.obb = [] + self.operations = None self.operation_list = False self.flighttrack_list = True @@ -147,7 +183,19 @@ def __init__(self, parent=None, view=None, listFlightTracks=None, self.flighttrack_activated = False self.color_change = False self.change_linewidth = False + self.change_line_transparency = False + self.change_line_style = False self.dsbx_linewidth.setValue(2.0) + self.hsTransparencyControl.setValue(100) + self.cbLineStyle.addItems(["Solid", "Dashed", "Dotted", "Dash-dot"]) # Item added in the list + self.cbLineStyle.setCurrentText("Solid") + + # Disable the buttons initially + self.pushButton_color.setEnabled(False) + self.dsbx_linewidth.setEnabled(False) + self.hsTransparencyControl.setEnabled(False) + self.cbLineStyle.setEnabled(False) + self.labelStatus.setText("Status: Select a flighttrack/operation") # Connect Signals and Slots self.listFlightTracks.model().rowsInserted.connect(self.wait) @@ -158,6 +206,9 @@ def __init__(self, parent=None, view=None, listFlightTracks=None, self.pushButton_color.clicked.connect(self.select_color) self.ui.signal_ft_vertices_color_change.connect(self.ft_vertices_color) self.dsbx_linewidth.valueChanged.connect(self.set_linewidth) + self.hsTransparencyControl.valueChanged.connect(self.set_transparency) + self.cbLineStyle.currentTextChanged.connect(self.set_linestyle) + self.cbSlectAll1.stateChanged.connect(self.selectAll) self.ui.signal_login_mscolab.connect(self.login) self.colorPixmap.setPixmap(self.show_color_pixmap(self.color)) @@ -298,6 +349,23 @@ def save_waypoint_model_data(self, wp_model, listWidget): self.create_list_item(wp_model) self.dict_flighttrack[wp_model]["wp_data"] = wp_data + def normalize_rgb(self, rgb): + return tuple(channel / 255 for channel in rgb) + + def get_random_color(self): + """ + Get a random color from custom colors ensuring no repeats. + """ + available_colors = [color for color in self.custom_colors if color not in self.used_colors] + if not available_colors: + # Reset the used colors if all colors have been used + self.used_colors = [] + available_colors = self.custom_colors.copy() + + selected_color = random.choice(available_colors) + self.used_colors.append(selected_color) + return self.normalize_rgb(selected_color) + def create_list_item(self, wp_model): """ PyQt5 method : Add items in list and add checkbox functionality @@ -305,8 +373,10 @@ def create_list_item(self, wp_model): # Create new key in dict self.dict_flighttrack[wp_model] = {} self.dict_flighttrack[wp_model]["patch"] = None - self.dict_flighttrack[wp_model]["color"] = self.color + self.dict_flighttrack[wp_model]["color"] = self.get_random_color() self.dict_flighttrack[wp_model]["linewidth"] = 2.0 + self.dict_flighttrack[wp_model]["line_transparency"] = 1.0 + self.dict_flighttrack[wp_model]["line_style"] = "solid" self.dict_flighttrack[wp_model]["wp_data"] = [] self.dict_flighttrack[wp_model]["checkState"] = False @@ -325,6 +395,14 @@ def create_list_item(self, wp_model): return listItem + def selectAll(self, state): + """ + select/deselect local operations + """ + for i in range(self.list_flighttrack.count()): + item = self.list_flighttrack.item(i) + item.setCheckState(state) + def select_color(self): """ Sets the color of selected flighttrack when Change Color is clicked. @@ -342,18 +420,23 @@ def select_color(self): self.error_dialog = QtWidgets.QErrorMessage() self.error_dialog.showMessage('Use "options" to change color of an activated flighttrack.') else: - color = QtWidgets.QColorDialog.getColor() - if color.isValid(): - self.dict_flighttrack[wp_model]["color"] = color.getRgbF() - self.color_change = True - self.list_flighttrack.currentItem().setIcon(self.show_color_icon(self.get_color(wp_model))) - self.dict_flighttrack[wp_model]["patch"].update(color=self.dict_flighttrack[wp_model]["color"]) + color_dialog = CustomColorDialog(self) + color_dialog.color_selected.connect(lambda color: self.apply_color(wp_model, color)) + color_dialog.exec_() else: - self.labelStatus.setText("Check Mark the flighttrack to change its color.") + self.labelStatus.setText("Status: Check Mark the flighttrack to change its color.") elif self.list_operation_track.currentItem() is not None: self.operations.select_color() else: - self.labelStatus.setText("Status: No flight track selected") + self.labelStatus.setText("Status: No flighttrack selected") + + def apply_color(self, wp_model, color): + if color.isValid(): + self.dict_flighttrack[wp_model]["color"] = color.getRgbF() + self.color_change = True + self.list_flighttrack.currentItem().setIcon(self.show_color_icon(self.get_color(wp_model))) + self.dict_flighttrack[wp_model]["patch"].update( + color=self.dict_flighttrack[wp_model]["color"]) def get_color(self, wp_model): """ @@ -384,19 +467,84 @@ def set_linewidth(self): if wp_model != self.active_flight_track: if self.dict_flighttrack[wp_model]["linewidth"] != self.dsbx_linewidth.value(): self.dict_flighttrack[wp_model]["linewidth"] = self.dsbx_linewidth.value() - - self.dict_flighttrack[wp_model]["patch"].remove() - self.dict_flighttrack[wp_model]["patch"].update( - self.dict_flighttrack[wp_model]["linewidth"], self.dict_flighttrack[wp_model]["color"] - ) + self.update_flighttrack_patch(wp_model) self.change_linewidth = True self.dsbx_linewidth.setValue(self.dict_flighttrack[wp_model]["linewidth"]) else: - self.labelStatus.setText("Status: No flight track selected") + item = self.list_flighttrack.currentItem() + self.dsbx_linewidth.setEnabled(item is not None and item.checkState() == QtCore.Qt.Checked) + self.labelStatus.setText("Status: Check Mark the flighttrack to change its line width.") elif self.list_operation_track.currentItem() is not None: self.operations.set_linewidth() else: - self.labelStatus.setText("Status: No flight track selected") + self.labelStatus.setText("Status: No flighttrack selected") + + def set_transparency(self): + """ + Change the line transparency of the selected flight track. + """ + if self.list_flighttrack.currentItem() is not None: + if (hasattr(self.list_flighttrack.currentItem(), "checkState")) and ( + self.list_flighttrack.currentItem().checkState() == QtCore.Qt.Checked): + wp_model = self.list_flighttrack.currentItem().flighttrack_model + if wp_model != self.active_flight_track: + new_transparency = self.hsTransparencyControl.value() / 100.0 + if self.dict_flighttrack[wp_model]["line_transparency"] != new_transparency: + self.dict_flighttrack[wp_model]["line_transparency"] = new_transparency + self.update_flighttrack_patch(wp_model) + self.change_line_transparency = True + self.hsTransparencyControl.setValue( + int(self.dict_flighttrack[wp_model]["line_transparency"] * 100)) + else: + item = self.list_flighttrack.currentItem() + self.hsTransparencyControl.setEnabled(item is not None and item.checkState() == QtCore.Qt.Checked) + self.labelStatus.setText("Status: Check Mark the flighttrack to change its transparency.") + elif self.list_operation_track.currentItem() is not None: + self.operations.set_transparency() + else: + self.labelStatus.setText("Status: No flighttrack selected") + + def set_linestyle(self): + """ + Change the line style of the selected flight track. + """ + + if self.list_flighttrack.currentItem() is not None: + if (hasattr(self.list_flighttrack.currentItem(), "checkState")) and ( + self.list_flighttrack.currentItem().checkState() == QtCore.Qt.Checked): + wp_model = self.list_flighttrack.currentItem().flighttrack_model + if wp_model != self.active_flight_track: + selected_style = self.cbLineStyle.currentText() + new_linestyle = self.line_styles.get(selected_style, '-') # Default to 'solid' + + if self.dict_flighttrack[wp_model]["line_style"] != new_linestyle: + self.dict_flighttrack[wp_model]["line_style"] = new_linestyle + self.update_flighttrack_patch(wp_model) + self.change_line_style = True + self.cbLineStyle.setCurrentText(self.dict_flighttrack[wp_model]["line_style"]) + else: + item = self.list_flighttrack.currentItem() + self.cbLineStyle.setEnabled(item is not None and item.checkState() == QtCore.Qt.Checked) + self.labelStatus.setText("Status: Check Mark the flighttrack to change its line style.") + elif self.list_operation_track.currentItem() is not None: + self.operations.set_linestyle() + else: + self.labelStatus.setText("Status: No flighttrack selected") + + def update_flighttrack_patch(self, wp_model): + """ + Update the flighttrack patch with the latest attributes. + """ + if self.dict_flighttrack[wp_model]["patch"] is not None: + self.dict_flighttrack[wp_model]["patch"].remove() + self.dict_flighttrack[wp_model]["patch"] = MultipleFlightpath( + self.view.map, + self.dict_flighttrack[wp_model]["wp_data"], + color=self.dict_flighttrack[wp_model]["color"], + linewidth=self.dict_flighttrack[wp_model]["linewidth"], + line_transparency=self.dict_flighttrack[wp_model]["line_transparency"], + line_style=self.dict_flighttrack[wp_model]["line_style"] + ) def flighttrackRemoved(self, parent, start, end): """ @@ -420,26 +568,55 @@ def update_last_flighttrack(self): def activate_flighttrack(self): """ - Activate flighttrack + Activate the selected flighttrack and ensure its visual properties are correctly set. """ font = QtGui.QFont() for i in range(self.list_flighttrack.count()): listItem = self.list_flighttrack.item(i) - if self.active_flight_track == listItem.flighttrack_model: # active flighttrack + wp_model = listItem.flighttrack_model + + if self.active_flight_track == wp_model: # active flighttrack listItem.setIcon(self.show_color_icon(self.color)) font.setBold(True) - if self.dict_flighttrack[listItem.flighttrack_model]["patch"] is not None: - self.dict_flighttrack[listItem.flighttrack_model]["patch"].remove() + + # Ensure the patch is updated with the correct attributes + if self.dict_flighttrack[wp_model]["patch"] is not None: + self.dict_flighttrack[wp_model]["patch"].remove() + if listItem.checkState() == QtCore.Qt.Unchecked: listItem.setCheckState(QtCore.Qt.Checked) self.set_activate_flag() listItem.setFlags(listItem.flags() & ~QtCore.Qt.ItemIsUserCheckable) # make activated track uncheckable else: - listItem.setIcon(self.show_color_icon(self.get_color(listItem.flighttrack_model))) + listItem.setIcon(self.show_color_icon(self.get_color(wp_model))) font.setBold(False) listItem.setFlags(listItem.flags() | QtCore.Qt.ItemIsUserCheckable) self.set_activate_flag() listItem.setFont(font) + self.update_line_properties_state() + self.flagop() + if self.operations is not None: + self.operations.set_flag() + + def update_line_properties_state(self): + """ + Check if there is an active flight track selected. If there is an active flight track selected in the + list widget, disable these UI elements else enable + """ + if self.list_flighttrack.currentItem() is not None: + wp_model = self.list_flighttrack.currentItem().flighttrack_model + self.enable_disable_line_style_buttons(wp_model != self.active_flight_track) + + def enable_disable_line_style_buttons(self, enable): + self.pushButton_color.setEnabled(enable) + self.dsbx_linewidth.setEnabled(enable) + self.hsTransparencyControl.setEnabled(enable) + self.cbLineStyle.setEnabled(enable) + if enable: + self.labelStatus.setText("Status: ✔ flight track selected") + else: + self.labelStatus.setText( + "Status: You can change line attributes of the active flighttrack through options only.") def drawInactiveFlighttracks(self, list_widget): """ @@ -457,7 +634,12 @@ def drawInactiveFlighttracks(self, list_widget): patch = MultipleFlightpath(self.view.map, self.dict_flighttrack[listItem.flighttrack_model][ "wp_data"], - color=self.dict_flighttrack[listItem.flighttrack_model]['color']) + color=self.dict_flighttrack[listItem.flighttrack_model]["color"], + linewidth=self.dict_flighttrack[listItem.flighttrack_model]["linewidth"], + line_transparency=self.dict_flighttrack[listItem.flighttrack_model][ + "line_transparency"], + line_style=self.dict_flighttrack[listItem.flighttrack_model][ + "line_style"]) self.dict_flighttrack[listItem.flighttrack_model]["patch"] = patch self.dict_flighttrack[listItem.flighttrack_model]["checkState"] = True @@ -480,6 +662,7 @@ def deactivate_all_flighttracks(self): self.set_activate_flag() listItem.setFlags(listItem.flags() | QtCore.Qt.ItemIsUserCheckable) + listItem.setIcon(self.show_color_icon(self.get_color(listItem.flighttrack_model))) if listItem.flighttrack_model == self.active_flight_track: font = QtGui.QFont() @@ -493,20 +676,36 @@ def set_listControl(self, operation, flighttrack): self.flighttrack_list = flighttrack def get_ft_vertices_color(self): - return self.color + return self.get_random_color() def listFlighttrack_itemClicked(self): + """ + reflect the linewidth, transparency, line_style of the selected flight track + and toggles the visibility of the groupBox. + """ if self.list_operation_track.currentItem() is not None: self.list_operation_track.setCurrentItem(None) if self.list_flighttrack.currentItem() is not None: wp_model = self.list_flighttrack.currentItem().flighttrack_model - self.dsbx_linewidth.setValue(self.dict_flighttrack[wp_model]["linewidth"]) - if self.list_flighttrack.currentItem().flighttrack_model == self.active_flight_track: - self.frame.hide() + linewidth = self.dict_flighttrack[wp_model]["linewidth"] + line_transparency = self.dict_flighttrack[wp_model]["line_transparency"] + line_style = self.dict_flighttrack[wp_model]["line_style"] + + self.dsbx_linewidth.setValue(linewidth) + self.hsTransparencyControl.setValue(int(line_transparency * 100)) + # Use the reverse dictionary to set the current text of the combo box + if line_style in self.line_styles_reverse: + self.cbLineStyle.setCurrentText(self.line_styles_reverse[line_style]) else: - self.frame.show() + self.cbLineStyle.setCurrentText("Solid") + + print(wp_model != self.active_flight_track, + self.list_flighttrack.currentItem().checkState() != QtCore.Qt.Checked) + self.enable_disable_line_style_buttons( + wp_model != self.active_flight_track and self.list_flighttrack.currentItem(). + checkState() == QtCore.Qt.Checked) class MultipleFlightpathOperations: @@ -545,6 +744,7 @@ def __init__(self, parent, mscolab_server_url, token, list_operation_track, acti # This needs to be done after operations are loaded # Connect signals and slots self.list_operation_track.itemChanged.connect(self.set_flag) + self.parent.cbSlectAll2.stateChanged.connect(self.selectAll) def set_flag(self): if self.operation_added: @@ -604,6 +804,8 @@ def create_operation(self, op_id, wp_model): self.dict_operations[op_id]["patch"] = None self.dict_operations[op_id]["wp_data"] = None self.dict_operations[op_id]["linewidth"] = 2.0 + self.dict_operations[op_id]["line_transparency"] = 1.0 + self.dict_operations[op_id]["line_style"] = 'solid' self.dict_operations[op_id]["color"] = self.parent.get_ft_vertices_color() self.save_operation_data(op_id, wp_model) @@ -648,6 +850,29 @@ def activate_operation(self): listItem.setFont(font) # connect itemChanged after everything setup, otherwise it will be triggered on each entry self.list_operation_track.itemChanged.connect(self.set_flag) + self.update_line_properties_state() + self.set_flag() + self.parent.flagop() + + def update_line_properties_state(self): + """ + Check if there is an active flight track selected. If there is an active flight track selected in the + list widget, disable these UI elements else enable + """ + if self.list_operation_track.currentItem() is not None: + op_id = self.list_operation_track.currentItem().op_id + self.enable_disable_line_style_buttons(op_id != self.active_op_id) + + def enable_disable_line_style_buttons(self, enable): + self.parent.pushButton_color.setEnabled(enable) + self.parent.dsbx_linewidth.setEnabled(enable) + self.parent.hsTransparencyControl.setEnabled(enable) + self.parent.cbLineStyle.setEnabled(enable) + if enable: + self.parent.labelStatus.setText("Status: ✔ flight track selected") + else: + self.parent.labelStatus.setText( + "Status: You can change line attributes of the active flighttrack through options only.") def save_last_used_operation(self, op_id): if self.active_op_id is not None: @@ -671,7 +896,11 @@ def draw_inactive_operations(self): self.dict_operations[listItem.op_id][ "wp_data"], color=self.dict_operations[listItem.op_id]["color"], - linewidth=self.dict_operations[listItem.op_id]["linewidth"]) + linewidth=self.dict_operations[listItem.op_id]["linewidth"], + line_transparency=self.dict_operations[listItem.op_id][ + "line_transparency"], + line_style=self.dict_operations[listItem.op_id][ + "line_style"]) self.dict_operations[listItem.op_id]["patch"] = patch @@ -722,6 +951,7 @@ def deactivate_all_operations(self): self.set_activate_flag() listItem.setFlags(listItem.flags() | QtCore.Qt.ItemIsUserCheckable) + listItem.setIcon(self.show_color_icon(self.get_color(listItem.op_id))) # if listItem.op_id == self.active_op_id: self.set_activate_flag() @@ -731,6 +961,14 @@ def deactivate_all_operations(self): self.active_op_id = None + def selectAll(self, state): + """ + select/deselect mscolab operations + """ + for i in range(self.list_operation_track.count()): + item = self.list_operation_track.item(i) + item.setCheckState(state) + def select_color(self): """ Sets the color of selected operation when change Color is clicked. @@ -743,16 +981,20 @@ def select_color(self): self.error_dialog = QtWidgets.QErrorMessage() self.error_dialog.showMessage('Use "options" to change color of an activated operation.') else: - color = QtWidgets.QColorDialog.getColor() - if color.isValid(): - self.dict_operations[op_id]["color"] = color.getRgbF() - self.color_change = True - self.list_operation_track.currentItem().setIcon(self.show_color_icon(self.get_color(op_id))) - self.dict_operations[op_id]["patch"].update( - color=self.dict_operations[op_id]["color"], - linewidth=self.dict_operations[op_id]["linewidth"]) + color_dialog = CustomColorDialog(self.parent) + color_dialog.color_selected.connect(lambda color: self.apply_color(op_id, color)) + color_dialog.exec_() else: - self.parent.labelStatus.setText("Check Mark the Operation to change color.") + self.parent.labelStatus.setText("Status: Check Mark the flighttrack to change its color.") + + def apply_color(self, op_id, color): + if color.isValid(): + self.dict_operations[op_id]["color"] = color.getRgbF() + self.color_change = True + self.list_operation_track.currentItem().setIcon(self.show_color_icon(self.get_color(op_id))) + self.dict_operations[op_id]["patch"].update( + color=self.dict_operations[op_id]["color"], + linewidth=self.dict_operations[op_id]["linewidth"]) def get_color(self, op_id): """ @@ -786,6 +1028,10 @@ def logout_mscolab(self): self.list_operation_track.takeItem(0) a -= 1 + # Uncheck the "Select All" checkbox + self.parent.cbSlectAll2.setChecked(False) + self.parent.labelStatus.setText("Status: Select a flighttrack/operation") + self.list_operation_track.itemChanged.disconnect() self.mscolab_server_url = None self.token = None @@ -800,30 +1046,101 @@ def render_permission(self, op_id, path): self.operationsAdded(op_id, path) def set_linewidth(self): - if (hasattr(self.list_operation_track.currentItem(), "checkState")) and ( - self.list_operation_track.currentItem().checkState() == QtCore.Qt.Checked): - op_id = self.list_operation_track.currentItem().op_id + """ + Change the line width of the selected operation track. + """ + item = self.list_operation_track.currentItem() + if hasattr(item, "checkState") and item.checkState() == QtCore.Qt.Checked: + op_id = item.op_id if op_id != self.active_op_id: - self.parent.frame.show() + self.parent.groupBox.show() if self.dict_operations[op_id]["linewidth"] != self.parent.dsbx_linewidth.value(): self.dict_operations[op_id]["linewidth"] = self.parent.dsbx_linewidth.value() - - self.dict_operations[op_id]["patch"].remove() - self.dict_operations[op_id]["patch"].update( - self.dict_operations[op_id]["linewidth"], self.dict_operations[op_id]["color"] - ) - self.change_linewidth = True + self.update_flighttrack_patch(op_id) + self.parent.change_linewidth = True self.parent.dsbx_linewidth.setValue(self.dict_operations[op_id]["linewidth"]) + else: + self.parent.dsbx_linewidth.setEnabled(item is not None and item.checkState() == QtCore.Qt.Checked) + self.parent.labelStatus.setText("Status: Check Mark the flighttrack to change its line width.") + + def set_transparency(self): + """ + Change the line transparency of the selected operation track. + """ + item = self.list_operation_track.currentItem() + if hasattr(item, "checkState") and item.checkState() == QtCore.Qt.Checked: + op_id = item.op_id + if op_id != self.active_op_id: + self.parent.groupBox.show() + new_transparency = self.parent.hsTransparencyControl.value() / 100.0 + if self.dict_operations[op_id]["line_transparency"] != new_transparency: + self.dict_operations[op_id]["line_transparency"] = new_transparency + self.update_flighttrack_patch(op_id) + self.parent.change_line_transparency = True + self.parent.hsTransparencyControl.setValue( + int(self.dict_operations[op_id]["line_transparency"] * 100)) + else: + self.parent.hsTransparencyControl.setEnabled(item is not None and item.checkState() == QtCore.Qt.Checked) + self.parent.labelStatus.setText("Status: Check Mark the flighttrack to change its transparency.") + + def set_linestyle(self): + """ + Change the line style of the selected operation track. + """ + item = self.list_operation_track.currentItem() + if hasattr(item, "checkState") and item.checkState() == QtCore.Qt.Checked: + op_id = item.op_id + if op_id != self.active_op_id: + self.parent.groupBox.show() + selected_style = self.parent.cbLineStyle.currentText() + new_linestyle = self.parent.line_styles.get(selected_style, '-') # Default to 'solid' + + if self.dict_operations[op_id]["line_style"] != new_linestyle: + self.dict_operations[op_id]["line_style"] = new_linestyle + self.update_flighttrack_patch(op_id) + self.parent.change_line_style = True + self.parent.cbLineStyle.setCurrentText(self.dict_operations[op_id]["line_style"]) + else: + self.parent.cbLineStyle.setEnabled(item is not None and item.checkState() == QtCore.Qt.Checked) + self.parent.labelStatus.setText("Status: Check Mark the flighttrack to change its line style.") + + def update_flighttrack_patch(self, op_id): + """ + Update the flighttrack patch with the latest attributes. + """ + if self.dict_operations[op_id]["patch"] is not None: + self.dict_operations[op_id]["patch"].remove() + self.dict_operations[op_id]["patch"] = MultipleFlightpath( + self.view.map, + self.dict_operations[op_id]["wp_data"], + color=self.dict_operations[op_id]["color"], + linewidth=self.dict_operations[op_id]["linewidth"], + line_transparency=self.dict_operations[op_id]["line_transparency"], + line_style=self.dict_operations[op_id]["line_style"] + ) def listOperations_itemClicked(self): + """ + reflect the linewidth, transparency, line_style of the selected flight track + and toggles the visibility of the groupBox. + """ if self.parent.list_flighttrack.currentItem() is not None: self.parent.list_flighttrack.setCurrentItem(None) if self.list_operation_track.currentItem() is not None: op_id = self.list_operation_track.currentItem().op_id - self.parent.dsbx_linewidth.setValue(self.dict_operations[op_id]["linewidth"]) - if self.list_operation_track.currentItem().op_id == self.active_op_id: - self.parent.frame.hide() + linewidth = self.dict_operations[op_id]["linewidth"] + line_transparency = self.dict_operations[op_id]["line_transparency"] + line_style = self.dict_operations[op_id]["line_style"] + + self.parent.dsbx_linewidth.setValue(linewidth) + self.parent.hsTransparencyControl.setValue(int(line_transparency * 100)) + # Use the reverse dictionary to set the current text of the combo box + if line_style in self.parent.line_styles_reverse: + self.parent.cbLineStyle.setCurrentText(self.parent.line_styles_reverse[line_style]) else: - self.parent.frame.show() + self.parent.cbLineStyle.setCurrentText("Solid") + + self.enable_disable_line_style_buttons(op_id != self.active_op_id and self.list_operation_track. + currentItem().checkState() == QtCore.Qt.Checked) diff --git a/mslib/msui/qt5/ui_multiple_flightpath_dockwidget.py b/mslib/msui/qt5/ui_multiple_flightpath_dockwidget.py index ec9fda2e4..11257f53d 100644 --- a/mslib/msui/qt5/ui_multiple_flightpath_dockwidget.py +++ b/mslib/msui/qt5/ui_multiple_flightpath_dockwidget.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'mslib/msui/ui/ui_multiple_flightpath_dockwidget.ui' +# Form implementation generated from reading ui file 'ui_multiple_flightpath_dockwidget.ui' # # Created by: PyQt5 UI code generator 5.12.3 # @@ -13,7 +13,7 @@ class Ui_MultipleViewWidget(object): def setupUi(self, MultipleViewWidget): MultipleViewWidget.setObjectName("MultipleViewWidget") - MultipleViewWidget.resize(778, 235) + MultipleViewWidget.resize(798, 282) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -22,11 +22,12 @@ def setupUi(self, MultipleViewWidget): self.verticalLayout_2 = QtWidgets.QVBoxLayout(MultipleViewWidget) self.verticalLayout_2.setObjectName("verticalLayout_2") self.frame_2 = QtWidgets.QFrame(MultipleViewWidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) + sizePolicy.setVerticalStretch(10) sizePolicy.setHeightForWidth(self.frame_2.sizePolicy().hasHeightForWidth()) self.frame_2.setSizePolicy(sizePolicy) + self.frame_2.setMinimumSize(QtCore.QSize(0, 20)) self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel) self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised) self.frame_2.setObjectName("frame_2") @@ -51,9 +52,16 @@ def setupUi(self, MultipleViewWidget): self.horizontalLayout_3.addWidget(self.colorPixmap) self.verticalLayout_2.addWidget(self.frame_2) self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize) + self.horizontalLayout.setSpacing(7) self.horizontalLayout.setObjectName("horizontalLayout") self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setObjectName("verticalLayout") + self.cbSlectAll1 = QtWidgets.QCheckBox(MultipleViewWidget) + self.cbSlectAll1.setObjectName("cbSlectAll1") + self.verticalLayout.addWidget(self.cbSlectAll1) self.list_flighttrack = QtWidgets.QListWidget(MultipleViewWidget) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy.setHorizontalStretch(0) @@ -62,45 +70,66 @@ def setupUi(self, MultipleViewWidget): self.list_flighttrack.setSizePolicy(sizePolicy) self.list_flighttrack.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents) self.list_flighttrack.setObjectName("list_flighttrack") - self.horizontalLayout_2.addWidget(self.list_flighttrack) + self.verticalLayout.addWidget(self.list_flighttrack) + self.horizontalLayout_2.addLayout(self.verticalLayout) + self.verticalLayout_3 = QtWidgets.QVBoxLayout() + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.cbSlectAll2 = QtWidgets.QCheckBox(MultipleViewWidget) + self.cbSlectAll2.setObjectName("cbSlectAll2") + self.verticalLayout_3.addWidget(self.cbSlectAll2) self.list_operation_track = QtWidgets.QListWidget(MultipleViewWidget) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) + sizePolicy.setVerticalStretch(250) sizePolicy.setHeightForWidth(self.list_operation_track.sizePolicy().hasHeightForWidth()) self.list_operation_track.setSizePolicy(sizePolicy) self.list_operation_track.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents) self.list_operation_track.setObjectName("list_operation_track") - self.horizontalLayout_2.addWidget(self.list_operation_track) - self.frame = QtWidgets.QFrame(MultipleViewWidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + self.verticalLayout_3.addWidget(self.list_operation_track) + self.horizontalLayout_2.addLayout(self.verticalLayout_3) + self.groupBox = QtWidgets.QGroupBox(MultipleViewWidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.frame.sizePolicy().hasHeightForWidth()) - self.frame.setSizePolicy(sizePolicy) - self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.frame.setFrameShadow(QtWidgets.QFrame.Raised) - self.frame.setObjectName("frame") - self.verticalLayout = QtWidgets.QVBoxLayout(self.frame) - self.verticalLayout.setObjectName("verticalLayout") - self.pushButton_color = QtWidgets.QPushButton(self.frame) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHeightForWidth(self.groupBox.sizePolicy().hasHeightForWidth()) + self.groupBox.setSizePolicy(sizePolicy) + self.groupBox.setMinimumSize(QtCore.QSize(220, 160)) + self.groupBox.setObjectName("groupBox") + self.pushButton_color = QtWidgets.QPushButton(self.groupBox) + self.pushButton_color.setGeometry(QtCore.QRect(10, 30, 174, 23)) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.pushButton_color.sizePolicy().hasHeightForWidth()) self.pushButton_color.setSizePolicy(sizePolicy) self.pushButton_color.setObjectName("pushButton_color") - self.verticalLayout.addWidget(self.pushButton_color) - self.dsbx_linewidth = QtWidgets.QDoubleSpinBox(self.frame) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + self.label = QtWidgets.QLabel(self.groupBox) + self.label.setGeometry(QtCore.QRect(120, 130, 101, 20)) + self.label.setObjectName("label") + self.dsbx_linewidth = QtWidgets.QDoubleSpinBox(self.groupBox) + self.dsbx_linewidth.setGeometry(QtCore.QRect(10, 120, 101, 31)) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.dsbx_linewidth.sizePolicy().hasHeightForWidth()) self.dsbx_linewidth.setSizePolicy(sizePolicy) self.dsbx_linewidth.setAlignment(QtCore.Qt.AlignCenter) self.dsbx_linewidth.setObjectName("dsbx_linewidth") - self.verticalLayout.addWidget(self.dsbx_linewidth) - self.horizontalLayout_2.addWidget(self.frame) + self.label_3 = QtWidgets.QLabel(self.groupBox) + self.label_3.setGeometry(QtCore.QRect(120, 100, 61, 16)) + self.label_3.setObjectName("label_3") + self.cbLineStyle = QtWidgets.QComboBox(self.groupBox) + self.cbLineStyle.setGeometry(QtCore.QRect(10, 90, 101, 22)) + self.cbLineStyle.setObjectName("cbLineStyle") + self.label_2 = QtWidgets.QLabel(self.groupBox) + self.label_2.setGeometry(QtCore.QRect(120, 60, 81, 20)) + self.label_2.setObjectName("label_2") + self.hsTransparencyControl = QtWidgets.QSlider(self.groupBox) + self.hsTransparencyControl.setGeometry(QtCore.QRect(10, 60, 101, 21)) + self.hsTransparencyControl.setProperty("value", 20) + self.hsTransparencyControl.setOrientation(QtCore.Qt.Horizontal) + self.hsTransparencyControl.setObjectName("hsTransparencyControl") + self.horizontalLayout_2.addWidget(self.groupBox) self.horizontalLayout.addLayout(self.horizontalLayout_2) self.verticalLayout_2.addLayout(self.horizontalLayout) self.labelStatus = QtWidgets.QLabel(MultipleViewWidget) @@ -114,9 +143,15 @@ def retranslateUi(self, MultipleViewWidget): _translate = QtCore.QCoreApplication.translate MultipleViewWidget.setWindowTitle(_translate("MultipleViewWidget", "Form")) self.ft_color_label.setText(_translate("MultipleViewWidget", "Activated Flighttrack/Operation Vertices Color: ")) + self.cbSlectAll1.setText(_translate("MultipleViewWidget", "Select all Flighttracks")) self.list_flighttrack.setToolTip(_translate("MultipleViewWidget", "List of Open Flighttracks.\n" "Check box to activate and display track on topview.")) + self.cbSlectAll2.setText(_translate("MultipleViewWidget", "Select all Operations")) self.list_operation_track.setToolTip(_translate("MultipleViewWidget", "List of Mscolab Operations.\n" "Check box to activate and display track on topview.")) + self.groupBox.setTitle(_translate("MultipleViewWidget", "Flight Track style options")) self.pushButton_color.setText(_translate("MultipleViewWidget", "Change Color")) + self.label.setText(_translate("MultipleViewWidget", "Thickness")) + self.label_3.setText(_translate("MultipleViewWidget", "Style")) + self.label_2.setText(_translate("MultipleViewWidget", "Transparency")) self.labelStatus.setText(_translate("MultipleViewWidget", "Status: ")) diff --git a/mslib/msui/qt5/ui_topview_mapappearance.py b/mslib/msui/qt5/ui_topview_mapappearance.py index a9bffaa49..300750055 100644 --- a/mslib/msui/qt5/ui_topview_mapappearance.py +++ b/mslib/msui/qt5/ui_topview_mapappearance.py @@ -13,7 +13,7 @@ class Ui_MapAppearanceDialog(object): def setupUi(self, MapAppearanceDialog): MapAppearanceDialog.setObjectName("MapAppearanceDialog") - MapAppearanceDialog.resize(394, 459) + MapAppearanceDialog.resize(394, 465) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -291,13 +291,3 @@ def retranslateUi(self, MapAppearanceDialog): self.tov_cbaxessize.setItemText(14, _translate("MapAppearanceDialog", "30")) self.tov_cbaxessize.setItemText(15, _translate("MapAppearanceDialog", "32")) self.label.setText(_translate("MapAppearanceDialog", " Plot Title Size")) - - -if __name__ == "__main__": - import sys - app = QtWidgets.QApplication(sys.argv) - MapAppearanceDialog = QtWidgets.QDialog() - ui = Ui_MapAppearanceDialog() - ui.setupUi(MapAppearanceDialog) - MapAppearanceDialog.show() - sys.exit(app.exec_()) diff --git a/mslib/msui/sideview.py b/mslib/msui/sideview.py index 64620fdd5..6324cf394 100644 --- a/mslib/msui/sideview.py +++ b/mslib/msui/sideview.py @@ -28,9 +28,7 @@ import logging import functools - from PyQt5 import QtGui, QtWidgets, QtCore - from mslib.msui.qt5 import ui_sideview_window as ui from mslib.msui.qt5 import ui_sideview_options as ui_opt from mslib.msui.viewwindows import MSUIMplViewWindow @@ -40,6 +38,7 @@ from mslib.utils.config import config_loader from mslib.utils.units import units, convert_to from mslib.msui import autoplot_dockwidget as apd +from mslib.utils.colordialog import CustomColorDialog # Dock window indices. WMS = 0 @@ -173,15 +172,18 @@ def setColour(self, which): elif which == "ceiling": button = self.btCeilingColour - palette = QtGui.QPalette(button.palette()) - colour = palette.color(QtGui.QPalette.Button) - colour = QtWidgets.QColorDialog.getColor(colour) - if colour.isValid(): + dialog = CustomColorDialog(self) + dialog.color_selected.connect(lambda color: self.on_color_selected(which, color, button)) + dialog.exec_() + + def on_color_selected(self, which, color, button): + if color.isValid(): if which == "ft_fill": # Fill colour is transparent with an alpha value of 0.15. If # you like to change this, modify the PathInteractor class. - colour.setAlphaF(0.15) - palette.setColor(QtGui.QPalette.Button, colour) + color.setAlphaF(0.15) + palette = QtGui.QPalette(button.palette()) + palette.setColor(QtGui.QPalette.Button, color) button.setPalette(palette) def addItem(self): diff --git a/mslib/msui/topview.py b/mslib/msui/topview.py index 68e773e68..311b977ba 100644 --- a/mslib/msui/topview.py +++ b/mslib/msui/topview.py @@ -47,6 +47,7 @@ from mslib.msui import autoplot_dockwidget as apd from mslib.msui.icons import icons from mslib.msui.flighttrack import Waypoint +from mslib.utils.colordialog import CustomColorDialog # Dock window indices. WMS = 0 @@ -189,12 +190,15 @@ def setColour(self, which): elif which == "ft_waypoints": button = self.btWaypointsColour - palette = QtGui.QPalette(button.palette()) - colour = palette.color(QtGui.QPalette.Button) - colour = QtWidgets.QColorDialog.getColor(colour) - if colour.isValid(): - self.signal_ft_vertices_color_change.emit(which, colour.getRgbF()) - palette.setColor(QtGui.QPalette.Button, colour) + dialog = CustomColorDialog(self) + dialog.color_selected.connect(lambda color: self.on_color_selected(which, color, button)) + dialog.exec_() + + def on_color_selected(self, which, color, button): + if color.isValid(): + self.signal_ft_vertices_color_change.emit(which, color.getRgbF()) + palette = QtGui.QPalette(button.palette()) + palette.setColor(QtGui.QPalette.Button, color) button.setPalette(palette) diff --git a/mslib/msui/ui/ui_multiple_flightpath_dockwidget.ui b/mslib/msui/ui/ui_multiple_flightpath_dockwidget.ui index 037a5b136..01154e3a1 100644 --- a/mslib/msui/ui/ui_multiple_flightpath_dockwidget.ui +++ b/mslib/msui/ui/ui_multiple_flightpath_dockwidget.ui @@ -6,8 +6,8 @@ 0 0 - 778 - 235 + 798 + 282 @@ -23,11 +23,17 @@ - + 0 - 0 + 10 + + + 0 + 20 + + QFrame::StyledPanel @@ -66,84 +72,190 @@ + + 7 + + + QLayout::SetMinimumSize + - - - - 0 - 0 - - - - List of Open Flighttracks. + + + + + Select all Flighttracks + + + + + + + + 0 + 0 + + + + List of Open Flighttracks. Check box to activate and display track on topview. - - - QAbstractScrollArea::AdjustToContents - - + + + QAbstractScrollArea::AdjustToContents + + + + - - - - 0 - 0 - - - - List of Mscolab Operations. + + + + + Select all Operations + + + + + + + + 0 + 250 + + + + List of Mscolab Operations. Check box to activate and display track on topview. - - - QAbstractScrollArea::AdjustToContents - - + + + QAbstractScrollArea::AdjustToContents + + + + - + - + 0 0 - - QFrame::StyledPanel + + + 220 + 160 + - - QFrame::Raised + + Flight Track style options - - - - - - 0 - 0 - - - - Change Color - - - - - - - - 0 - 0 - - - - Qt::AlignCenter - - - - + + + + 10 + 30 + 174 + 23 + + + + + 0 + 0 + + + + Change Color + + + + + + 120 + 130 + 101 + 20 + + + + Thickness + + + + + + 10 + 120 + 101 + 31 + + + + + 0 + 0 + + + + Qt::AlignCenter + + + + + + 120 + 100 + 61 + 16 + + + + Style + + + + + + 10 + 90 + 101 + 22 + + + + + + + 120 + 60 + 81 + 20 + + + + Transparency + + + + + + 10 + 60 + 101 + 21 + + + + 20 + + + Qt::Horizontal + + diff --git a/mslib/msui/ui/ui_topview_mapappearance.ui b/mslib/msui/ui/ui_topview_mapappearance.ui index 752c0910f..7d824edf5 100644 --- a/mslib/msui/ui/ui_topview_mapappearance.ui +++ b/mslib/msui/ui/ui_topview_mapappearance.ui @@ -7,7 +7,7 @@ 0 0 394 - 459 + 465 diff --git a/mslib/utils/colordialog.py b/mslib/utils/colordialog.py new file mode 100644 index 000000000..53ac19f27 --- /dev/null +++ b/mslib/utils/colordialog.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- +""" + + mslib.utils.colordialog.py + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Custom Color Dialog to select color from predefined swatches. + + This file is part of MSS. + + :copyright: Copyright 2024 Rohit Prasad + :copyright: Copyright 2024 by the MSS team, see AUTHORS. + :license: APACHE-2.0, see LICENSE for details. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +import functools +from PyQt5.QtWidgets import QVBoxLayout, QGridLayout +from PyQt5 import QtGui, QtWidgets, QtCore + + +class CustomColorDialog(QtWidgets.QDialog): + """ + Custom Color Dialog to select color from predefined swatches. + """ + color_selected = QtCore.pyqtSignal(QtGui.QColor) + + def __init__(self, parent=None): + super().__init__(parent) + self.setWindowTitle("Select Color") + self.setFixedSize(350, 300) + + self.colors = [ + "#800000", "#c31f59", "#f59757", "#fde442", "#0000ff", + "#60c36e", "#65d8f2", "#a446be", "#f15aea", "#b9babb", + "#e6194B", "#d2cf94", "#356e33", "#f58231", "#2c2c2c", + "#000075", "#9A6324", "#808000", "#000000", "#f8cbdc" + ] + + layout = QVBoxLayout() + # Color swatches layout + swatch_layout = QGridLayout() + self.color_buttons = [] + + for i, color in enumerate(self.colors): + button = QtWidgets.QPushButton() + button.setFixedSize(50, 50) + button.setStyleSheet(f"background-color: {color}") + button.clicked.connect(functools.partial(self.on_color_clicked, color)) + row = i // 5 + col = i % 5 + swatch_layout.addWidget(button, row, col) + + # Add "Pick Custom Color" button + self.custom_color_button = QtWidgets.QPushButton("Pick Custom Color") + self.custom_color_button.clicked.connect(self.on_custom_color_clicked) + self.custom_color_button.setFixedSize(325, 30) + + layout.addLayout(swatch_layout) + layout.addWidget(self.custom_color_button) + self.setLayout(layout) + + def on_color_clicked(self, color): + self.color_selected.emit(QtGui.QColor(color)) + self.accept() + + def on_custom_color_clicked(self): + color = QtWidgets.QColorDialog.getColor() + if color.isValid(): + self.color_selected.emit(color) + self.accept() diff --git a/tests/_test_msui/test_sideview.py b/tests/_test_msui/test_sideview.py index bd5ae74ff..4028f47be 100644 --- a/tests/_test_msui/test_sideview.py +++ b/tests/_test_msui/test_sideview.py @@ -31,7 +31,7 @@ import pytest import shutil import tempfile -from PyQt5 import QtTest, QtCore, QtGui +from PyQt5 import QtTest, QtCore, QtGui, QtWidgets from mslib.msui import flighttrack as ft import mslib.msui.sideview as tv from mslib.msui.msui import MSUIMainWindow @@ -66,11 +66,19 @@ def test_getFlightLevels(self): levels = self.window.get_flight_levels() assert all(x == y for x, y in zip(levels, [0, 300, 320, 340])) - @mock.patch("PyQt5.QtWidgets.QColorDialog.getColor", return_value=QtGui.QColor()) - def test_setColour(self, mockdlg): + @mock.patch("mslib.utils.colordialog.CustomColorDialog.exec_", return_value=QtWidgets.QDialog.Accepted) + @mock.patch("mslib.utils.colordialog.CustomColorDialog.color_selected", new_callable=mock.Mock) + def test_setColour(self, mock_color_selected, mockdlg): QtTest.QTest.mouseClick(self.window.btFillColour, QtCore.Qt.LeftButton) assert mockdlg.call_count == 1 + # Simulate a color being selected + color = QtGui.QColor("#0000ff") # Example color + mock_color_selected.emit(color) + + # Ensure the color_selected signal was emitted + mock_color_selected.emit.assert_called_with(color) + class Test_MSSSideViewWindow: @pytest.fixture(autouse=True) From 2abf221d9925d8e637f879c3929c44d64d8242d1 Mon Sep 17 00:00:00 2001 From: Rohit prasad Date: Thu, 29 Aug 2024 17:34:28 +0530 Subject: [PATCH 3/9] test for the new features in the MFDW, topview & sideview (#2471) * MFDW test setup * set_colour test * requested way of implementation * all line style method test * get_random_colour method test * requested changes * CI fix * top_view tests * requested change in color test * test on sideview and topview complete * requested change --- mslib/msui/multiple_flightpath_dockwidget.py | 4 +- mslib/msui/sideview.py | 2 +- mslib/msui/topview.py | 2 +- mslib/utils/colordialog.py | 1 + .../test_multiple_flightpath_dockwidget.py | 240 ++++++++++++++++-- tests/_test_msui/test_sideview.py | 73 +++++- tests/_test_msui/test_topview.py | 68 ++++- 7 files changed, 349 insertions(+), 41 deletions(-) diff --git a/mslib/msui/multiple_flightpath_dockwidget.py b/mslib/msui/multiple_flightpath_dockwidget.py index 380fb6bc8..bdaf9cfad 100644 --- a/mslib/msui/multiple_flightpath_dockwidget.py +++ b/mslib/msui/multiple_flightpath_dockwidget.py @@ -422,7 +422,7 @@ def select_color(self): else: color_dialog = CustomColorDialog(self) color_dialog.color_selected.connect(lambda color: self.apply_color(wp_model, color)) - color_dialog.exec_() + color_dialog.show() else: self.labelStatus.setText("Status: Check Mark the flighttrack to change its color.") elif self.list_operation_track.currentItem() is not None: @@ -983,7 +983,7 @@ def select_color(self): else: color_dialog = CustomColorDialog(self.parent) color_dialog.color_selected.connect(lambda color: self.apply_color(op_id, color)) - color_dialog.exec_() + color_dialog.show() else: self.parent.labelStatus.setText("Status: Check Mark the flighttrack to change its color.") diff --git a/mslib/msui/sideview.py b/mslib/msui/sideview.py index 6324cf394..54778c70d 100644 --- a/mslib/msui/sideview.py +++ b/mslib/msui/sideview.py @@ -174,7 +174,7 @@ def setColour(self, which): dialog = CustomColorDialog(self) dialog.color_selected.connect(lambda color: self.on_color_selected(which, color, button)) - dialog.exec_() + dialog.show() def on_color_selected(self, which, color, button): if color.isValid(): diff --git a/mslib/msui/topview.py b/mslib/msui/topview.py index 311b977ba..d1db60818 100644 --- a/mslib/msui/topview.py +++ b/mslib/msui/topview.py @@ -192,7 +192,7 @@ def setColour(self, which): dialog = CustomColorDialog(self) dialog.color_selected.connect(lambda color: self.on_color_selected(which, color, button)) - dialog.exec_() + dialog.show() def on_color_selected(self, which, color, button): if color.isValid(): diff --git a/mslib/utils/colordialog.py b/mslib/utils/colordialog.py index 53ac19f27..6a2a0f0f2 100644 --- a/mslib/utils/colordialog.py +++ b/mslib/utils/colordialog.py @@ -60,6 +60,7 @@ def __init__(self, parent=None): row = i // 5 col = i % 5 swatch_layout.addWidget(button, row, col) + self.color_buttons.append(button) # Add "Pick Custom Color" button self.custom_color_button = QtWidgets.QPushButton("Pick Custom Color") diff --git a/tests/_test_msui/test_multiple_flightpath_dockwidget.py b/tests/_test_msui/test_multiple_flightpath_dockwidget.py index c53ba3092..bf1404912 100644 --- a/tests/_test_msui/test_multiple_flightpath_dockwidget.py +++ b/tests/_test_msui/test_multiple_flightpath_dockwidget.py @@ -25,33 +25,221 @@ limitations under the License. """ import pytest -from PyQt5 import QtTest +from PyQt5 import QtWidgets, QtCore, QtGui, QtTest +from unittest import mock + from mslib.msui import msui -from mslib.msui.multiple_flightpath_dockwidget import MultipleFlightpathControlWidget -from mslib.msui import flighttrack as ft import mslib.msui.topview as tv -class Test_MultipleFlightpathControlWidget: - @pytest.fixture(autouse=True) - def setup(self, qtbot): - self.window = msui.MSUIMainWindow() - self.window.create_new_flight_track() - - self.window.actionNewFlightTrack.trigger() - self.window.actionTopView.trigger() - initial_waypoints = [ft.Waypoint(40., 25., 0), ft.Waypoint(60., -10., 0), ft.Waypoint(40., 10, 0)] - self.waypoints_model = ft.WaypointsTableModel("myname") - self.waypoints_model.insertRows( - 0, rows=len(initial_waypoints), waypoints=initial_waypoints) - - self.widget = tv.MSUITopViewWindow(model=self.waypoints_model, mainwindow=self.window, parent=self.window) - self.window.show() - QtTest.QTest.qWaitForWindowExposed(self.window) - yield - self.window.hide() - - def test_initialization(self): - widget = MultipleFlightpathControlWidget(parent=self.widget, - listFlightTracks=self.window.listFlightTracks) - assert widget.color == (0, 0, 1, 1) +@pytest.fixture +def main_window(qtbot): + """ + Set-up for the docking widget + """ + # Start a MSUI window + window = msui.MSUIMainWindow() + window.show() + qtbot.wait_exposed(window) + + # Create two flight tracks + window.actionNewFlightTrack.trigger() + window.actionNewFlightTrack.trigger() + + # Open a Top View window + window.actionTopView.trigger() + topview_window = window.listViews.currentItem().window + + # Switch to the Multiple Flightpath Widget + topview_window.cbTools.setCurrentIndex(6) + + # Get a reference to the created MultipleFlightpathControlWidget + multiple_flightpath_widget = topview_window.docks[tv.MULTIPLEFLIGHTPATH].widget() + + yield window, multiple_flightpath_widget + + window.hide() + + +def test_initialization(main_window): + """ + test for conforming docking widget has initialized + """ + _, multiple_flightpath_widget = main_window + + # Ensure the MultipleFlightpathControlWidget is correctly initialized + assert multiple_flightpath_widget is not None + assert multiple_flightpath_widget.color == (0, 0, 1, 1) + + +def test_setColour(main_window): + """ + test for the filghttrack colour + """ + main_window, multiple_flightpath_widget = main_window + color_button = multiple_flightpath_widget.pushButton_color + + # Activate the first flight track + activate_flight_track_at_index(main_window, 0) + + # Click on the second flight track in the docking widget + click_on_flight_track_in_docking_widget_at_index(multiple_flightpath_widget, 1) + + # Simulate clicking the button to open the color dialog + color_button.click() + + # Get a reference to the custom color dialog + color_dialog = multiple_flightpath_widget.findChild(QtWidgets.QDialog) + assert color_dialog is not None + + # Select the first color + color = QtGui.QColor(color_dialog.colors[0]) + color_dialog.color_buttons[0].click() + + # Verify that the flight track data structure was updated with the new color + wp_model = multiple_flightpath_widget.list_flighttrack.currentItem().flighttrack_model + applied_color_rgba = multiple_flightpath_widget.get_color(wp_model) + + # Convert the applied_color_rgba to a QColor object + applied_color = QtGui.QColor.fromRgbF(*applied_color_rgba) + + assert applied_color == color + + +def test_set_linewidth(main_window): + """ + test for the filghttrack line width + """ + main_window, multiple_flightpath_widget = main_window + + activate_flight_track_at_index(main_window, 0) + click_on_flight_track_in_docking_widget_at_index(multiple_flightpath_widget, 1) + + # Ensure the current item is checked + item = multiple_flightpath_widget.list_flighttrack.currentItem() + item.setCheckState(QtCore.Qt.Checked) + + # mock update_flighttrack_patch method to check if it gets called + with mock.patch.object(multiple_flightpath_widget, "update_flighttrack_patch") as mock_update_patch: + # s.et the line width + multiple_flightpath_widget.dsbx_linewidth.setValue(3.0) + mock_update_patch.assert_called_once_with(item.flighttrack_model) + + # Verify the line width has been updated in dict_flighttrack + wp_model = item.flighttrack_model + assert multiple_flightpath_widget.dict_flighttrack[wp_model]["linewidth"] == 3.0 + assert multiple_flightpath_widget.change_linewidth is True + + +def test_set_transparency(main_window): + """ + test for the filghttrack line transparency + """ + main_window, multiple_flightpath_widget = main_window + + activate_flight_track_at_index(main_window, 0) + click_on_flight_track_in_docking_widget_at_index(multiple_flightpath_widget, 1) + + item = multiple_flightpath_widget.list_flighttrack.currentItem() + item.setCheckState(QtCore.Qt.Checked) + + # Mock the update_flighttrack_patch method + with mock.patch.object(multiple_flightpath_widget, "update_flighttrack_patch") as mock_update_patch: + + multiple_flightpath_widget.hsTransparencyControl.setValue(50) + mock_update_patch.assert_called_once_with(item.flighttrack_model) + + # Verify the transparency has been updated in dict_flighttrack + wp_model = item.flighttrack_model + assert multiple_flightpath_widget.dict_flighttrack[wp_model]["line_transparency"] == (50 / 100) + assert multiple_flightpath_widget.change_line_transparency is True + + +def test_set_linestyle(main_window): + """ + test for the filghttrack line style + """ + main_window, multiple_flightpath_widget = main_window + + activate_flight_track_at_index(main_window, 0) + click_on_flight_track_in_docking_widget_at_index(multiple_flightpath_widget, 1) + + item = multiple_flightpath_widget.list_flighttrack.currentItem() + item.setCheckState(QtCore.Qt.Checked) + + # Mock the cbLineStyle setCurrentText and the update_flighttrack_patch method + with mock.patch.object(multiple_flightpath_widget, "update_flighttrack_patch") as mock_update_patch: + + multiple_flightpath_widget.cbLineStyle.setCurrentText('Dashed') + mock_update_patch.assert_called_once_with(item.flighttrack_model) + + # Verify the line style has been updated in dict_flighttrack + wp_model = item.flighttrack_model + expected_style = '--' + assert multiple_flightpath_widget.dict_flighttrack[wp_model]["line_style"] == expected_style + assert multiple_flightpath_widget.change_line_style is True + mock_update_patch.assert_called_once_with(wp_model) + + +def test_selectAll(main_window): + """ + Test the selectAll method by interacting with the UI directly. + """ + main_window, multiple_flightpath_widget = main_window + + # Check the "Select All" checkbox + select_all_checkbox = multiple_flightpath_widget.cbSlectAll1 + select_all_checkbox.setCheckState(QtCore.Qt.Checked) + + # Verify that all items in the list are checked + for i in range(multiple_flightpath_widget.list_flighttrack.count()): + item = multiple_flightpath_widget.list_flighttrack.item(i) + assert item.checkState() == QtCore.Qt.Checked + + select_all_checkbox.setCheckState(QtCore.Qt.Unchecked) + + # Verify that all items in the list are unchecked + for i in range(multiple_flightpath_widget.list_flighttrack.count()): + item = multiple_flightpath_widget.list_flighttrack.item(i) + assert item.checkState() == QtCore.Qt.Unchecked + + +def test_random_custom_color_selection(main_window): + """ + Test that a random custom color is selected automatically each time + and ensure that it is different from the previous selections. + """ + _, multiple_flightpath_widget = main_window + + # Select random colors multiple times + selected_colors = set() + for _ in range(10): # Test with 10 random selections + color = multiple_flightpath_widget.get_random_color() + normalized_color = tuple(int(c * 255) for c in color) + assert normalized_color not in selected_colors, "Duplicate color selected!" + selected_colors.add(normalized_color) + + # Check that all selected colors are from the custom_colors list + for color in selected_colors: + assert color in multiple_flightpath_widget.custom_colors + + +def activate_flight_track_at_index(main_window, index): + # The main window must be on top + main_window.activateWindow() + # get the item by its index + item = main_window.listFlightTracks.item(index) + point = main_window.listFlightTracks.visualItemRect(item).center() + QtTest.QTest.mouseClick(main_window.listFlightTracks.viewport(), QtCore.Qt.LeftButton, pos=point) + QtTest.QTest.mouseDClick(main_window.listFlightTracks.viewport(), QtCore.Qt.LeftButton, pos=point) + + +def click_on_flight_track_in_docking_widget_at_index(multiple_flightpath_widget, index): + # Activating the dock_widget window + multiple_flightpath_widget.activateWindow() + # Get the item by its index + item = multiple_flightpath_widget.list_flighttrack.item(index) + multiple_flightpath_widget.list_flighttrack.setCurrentItem(item) + # Simulate selection of the flight track by single clicking the item + point = multiple_flightpath_widget.list_flighttrack.visualItemRect(item).center() + QtTest.QTest.mouseClick(multiple_flightpath_widget.list_flighttrack.viewport(), QtCore.Qt.LeftButton, pos=point) diff --git a/tests/_test_msui/test_sideview.py b/tests/_test_msui/test_sideview.py index 4028f47be..1a803fa23 100644 --- a/tests/_test_msui/test_sideview.py +++ b/tests/_test_msui/test_sideview.py @@ -66,18 +66,71 @@ def test_getFlightLevels(self): levels = self.window.get_flight_levels() assert all(x == y for x, y in zip(levels, [0, 300, 320, 340])) - @mock.patch("mslib.utils.colordialog.CustomColorDialog.exec_", return_value=QtWidgets.QDialog.Accepted) - @mock.patch("mslib.utils.colordialog.CustomColorDialog.color_selected", new_callable=mock.Mock) - def test_setColour(self, mock_color_selected, mockdlg): - QtTest.QTest.mouseClick(self.window.btFillColour, QtCore.Qt.LeftButton) - assert mockdlg.call_count == 1 + def test_setColour(self): + """ + Test the setColour function to ensure the color dialog opens and the correct color is set. + """ + # Simulate clicking the "Water" color button to open the color dialog + self.window.setColour("ft_vertices") + + # Get a reference to the custom color dialog + color_dialog = self.window.findChild(QtWidgets.QDialog) + assert color_dialog is not None + + # Select the first color in the color dialog (assuming color_buttons is a list of buttons) + color_dialog.color_buttons[0].click() + + # Get the selected color + selected_color = QtGui.QColor(color_dialog.colors[0]) + + # Verify that the button's color has been set correctly + button_palette = self.window.btVerticesColour.palette() + button_color = button_palette.button().color() + assert button_color.getRgbF() == selected_color.getRgbF() + + # Verify that the correct color was set in the settings + settings = self.window.get_settings() + assert settings['colour_ft_vertices'] == selected_color.getRgbF() + + def test_line_thickness_change(self): + """ + Test the thickness of the flighttrack + """ + # Verify initial value + assert self.window.line_thickness == _DEFAULT_SETTINGS_SIDEVIEW.get("line_thickness", 2) + + # Simulate changing line thickness + new_thickness = 5.00 + self.window.sbLineThickness.setValue(new_thickness) + + settings = self.window.get_settings() + assert settings['line_thickness'] == new_thickness + + def test_line_style_change(self): + """ + Test the style of the flighttrack + """ + assert self.window.line_style == _DEFAULT_SETTINGS_SIDEVIEW.get("line_style", "Solid") + + # Simulate changing line style + new_style = "Dashed" + self.window.cbLineStyle.setCurrentText(new_style) + + settings = self.window.get_settings() + assert settings['line_style'] == new_style + + def test_line_transparency_change(self): + """ + Test the transparency of the flighttrack + """ + assert self.window.line_transparency == _DEFAULT_SETTINGS_SIDEVIEW.get("line_transparency", 1.0) - # Simulate a color being selected - color = QtGui.QColor("#0000ff") # Example color - mock_color_selected.emit(color) + # Simulate changing transparency + new_transparency = 50 # == 0.5 + self.window.hsTransparencyControl.setValue(new_transparency) - # Ensure the color_selected signal was emitted - mock_color_selected.emit.assert_called_with(color) + settings = self.window.get_settings() + assert settings['line_transparency'] == new_transparency / 100 class Test_MSSSideViewWindow: diff --git a/tests/_test_msui/test_topview.py b/tests/_test_msui/test_topview.py index b83fc4997..9e268d8ca 100644 --- a/tests/_test_msui/test_topview.py +++ b/tests/_test_msui/test_topview.py @@ -31,7 +31,7 @@ import shutil import tempfile import mslib.msui.topview as tv -from PyQt5 import QtWidgets, QtCore, QtTest +from PyQt5 import QtWidgets, QtCore, QtTest, QtGui from mslib.msui import flighttrack as ft from mslib.msui.msui import MSUIMainWindow from mslib.msui.mpl_qtwidget import _DEFAULT_SETTINGS_TOPVIEW @@ -52,6 +52,72 @@ def test_show(self): def test_get(self): self.window.get_settings() + def test_setColour(self): + """ + Test the setColour function to ensure the color dialog opens and the correct color is set. + """ + # Simulate clicking the "Water" color button to open the color dialog + self.window.setColour("ft_vertices") + + # Get a reference to the custom color dialog + color_dialog = self.window.findChild(QtWidgets.QDialog) + assert color_dialog is not None + + # Select the first color in the color dialog (assuming color_buttons is a list of buttons) + color_dialog.color_buttons[0].click() + + # Get the selected color + selected_color = QtGui.QColor(color_dialog.colors[0]) + + # Verify that the button's color has been set correctly + button_palette = self.window.btVerticesColour.palette() + button_color = button_palette.button().color() + assert button_color.getRgbF() == selected_color.getRgbF() + + # Verify that the correct color was set in the settings + settings = self.window.get_settings() + assert settings['colour_ft_vertices'] == selected_color.getRgbF() + + def test_line_thickness_change(self): + """ + Test the thickness of flighttrack + """ + # Verify initial value + assert self.window.line_thickness == _DEFAULT_SETTINGS_TOPVIEW.get("line_thickness", 2) + + # Simulate changing line thickness + new_thickness = 5.00 + self.window.sbLineThickness.setValue(new_thickness) + + settings = self.window.get_settings() + assert settings['line_thickness'] == new_thickness + + def test_line_style_change(self): + """ + Test the style of flighttrack + """ + assert self.window.line_style == _DEFAULT_SETTINGS_TOPVIEW.get("line_style", "Solid") + + # Simulate changing line style + new_style = "Dashed" + self.window.cbLineStyle.setCurrentText(new_style) + + settings = self.window.get_settings() + assert settings['line_style'] == new_style + + def test_line_transparency_change(self): + """ + Test the transparency of flighttrack + """ + assert self.window.line_transparency == _DEFAULT_SETTINGS_TOPVIEW.get("line_transparency", 1.0) + + # Simulate changing transparency + new_transparency = 50 # == 0.5 + self.window.hsTransparencyControl.setValue(new_transparency) + + settings = self.window.get_settings() + assert settings['line_transparency'] == new_transparency / 100 + class Test_MSSTopViewWindow: @pytest.fixture(autouse=True) From af5d26d23011a94abaf8946679b9f0007c128fa8 Mon Sep 17 00:00:00 2001 From: rohit Date: Mon, 26 Aug 2024 23:07:55 +0530 Subject: [PATCH 4/9] legend ui and some code --- mslib/msui/qt5/ui_topview_window.py | 10 +++++++ mslib/msui/topview.py | 44 +++++++++++++++++++++++++++++ mslib/msui/ui/ui_topview_window.ui | 22 +++++++++++++++ 3 files changed, 76 insertions(+) diff --git a/mslib/msui/qt5/ui_topview_window.py b/mslib/msui/qt5/ui_topview_window.py index fe421804a..0196449bc 100644 --- a/mslib/msui/qt5/ui_topview_window.py +++ b/mslib/msui/qt5/ui_topview_window.py @@ -28,6 +28,16 @@ def setupUi(self, TopViewWindow): self.mpl.setSizePolicy(sizePolicy) self.mpl.setMinimumSize(QtCore.QSize(100, 100)) self.mpl.setObjectName("mpl") + self.gridLayoutWidget = QtWidgets.QWidget(self.mpl) + self.gridLayoutWidget.setGeometry(QtCore.QRect(10, 60, 192, 81)) + self.gridLayoutWidget.setObjectName("gridLayoutWidget") + self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget) + self.gridLayout.setContentsMargins(0, 0, 0, 0) + self.gridLayout.setObjectName("gridLayout") + self.widget = QtWidgets.QWidget(self.gridLayoutWidget) + self.widget.setMinimumSize(QtCore.QSize(190, 0)) + self.widget.setObjectName("widget") + self.gridLayout.addWidget(self.widget, 0, 0, 1, 1) self.horizontalLayout_2.addWidget(self.mpl) self.verticalLayout.addLayout(self.horizontalLayout_2) self.horizontalLayout = QtWidgets.QHBoxLayout() diff --git a/mslib/msui/topview.py b/mslib/msui/topview.py index d1db60818..9cfbde639 100644 --- a/mslib/msui/topview.py +++ b/mslib/msui/topview.py @@ -310,9 +310,53 @@ def __init__(self, parent=None, mainwindow=None, model=None, _id=None, self.mainwindow_signal_login_mscolab.connect(self.login) + # Initialize flight tracks and legend + self.load_flight_tracks() + self.init_legend() + def __del__(self): del self.mpl.canvas.waypoints_interactor + def load_flight_tracks(self): + """ + Load flight track data from the main window's listFlightTracks widget. + """ + self.flight_tracks = [] + for index in range(self.mainwindow_listFlightTracks.count()): + item = self.mainwindow_listFlightTracks.item(index) + flighttrack_model = item.flighttrack_model + track_name = flighttrack_model.name + self.flight_tracks.append({"name": track_name}) + + def init_legend(self): + """ + Initialize the legend by creating label and line pairs for each flight track. + """ + # Clear existing legend items + while self.gridLayout.count(): + child = self.gridLayout.takeAt(0) + if child.widget(): + child.widget().deleteLater() + + # Add new legend items + for row, flight_track in enumerate(self.flight_tracks): + # Create label for the flight track name + label = QtWidgets.QLabel(flight_track["name"]) + + # Create a small colored line to represent the flight track + line = QtWidgets.QFrame() + line.setFrameShape(QtWidgets.QFrame.HLine) + line.setFrameShadow(QtWidgets.QFrame.Plain) + line.setFixedWidth(50) + # line_color = flight_track["color"] + # line.setStyleSheet( + # f"background-color: rgb({line_color[0] * 255}, {line_color[1] * 255}, {line_color[2] * 255});") + # to do : color implementation for the line + + # Add label and line to the grid layout in the same row, different columns + self.gridLayout.addWidget(label, row, 0, alignment=QtCore.Qt.AlignLeft) + self.gridLayout.addWidget(line, row, 1, alignment=QtCore.Qt.AlignRight) + @QtCore.pyqtSlot(ft.WaypointsTableModel) def update_active_flighttrack(self, active_flighttrack): """ diff --git a/mslib/msui/ui/ui_topview_window.ui b/mslib/msui/ui/ui_topview_window.ui index f21134e81..13ea76129 100644 --- a/mslib/msui/ui/ui_topview_window.ui +++ b/mslib/msui/ui/ui_topview_window.ui @@ -31,6 +31,28 @@ 100 + + + + 10 + 60 + 192 + 81 + + + + + + + + 190 + 0 + + + + + + From a5261eae9ef14639d9b15c4727dc65e3e825bf20 Mon Sep 17 00:00:00 2001 From: rohit Date: Thu, 29 Aug 2024 20:13:27 +0530 Subject: [PATCH 5/9] working legend with some issues --- mslib/msui/mpl_qtwidget.py | 40 ++++++++++++++++++ mslib/msui/multiple_flightpath_dockwidget.py | 16 ++++++++ mslib/msui/qt5/ui_topview_window.py | 10 ----- mslib/msui/topview.py | 43 -------------------- mslib/msui/ui/ui_topview_window.ui | 22 ---------- 5 files changed, 56 insertions(+), 75 deletions(-) diff --git a/mslib/msui/mpl_qtwidget.py b/mslib/msui/mpl_qtwidget.py index 721a0878e..5b95ba6df 100644 --- a/mslib/msui/mpl_qtwidget.py +++ b/mslib/msui/mpl_qtwidget.py @@ -42,6 +42,7 @@ from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT, FigureCanvasQTAgg import matplotlib.backend_bases from PyQt5 import QtCore, QtWidgets, QtGui +from matplotlib.lines import Line2D from mslib.utils.thermolib import convert_pressure_to_vertical_axis_measure from mslib.utils import thermolib, FatalUserError @@ -348,6 +349,38 @@ def draw_legend(self, img): self.legimg = self.legax.imshow(img, origin=PIL_IMAGE_ORIGIN, aspect="equal", interpolation="nearest") self.ax.figure.canvas.draw() + def draw_flightpath_legend(self, flightpath_dict): + """ + Draw the flight path legend on the plot. + """ + # Clear any existing legend + if self.legax is not None: + self.legax.remove() + self.legax = None + + if not flightpath_dict: + self.ax.figure.canvas.draw() + return + + # Create a new axis for the legend + self.legax = self.fig.add_axes([0.85, 0.7, 0.13, 0.2], frameon=False) + self.legax.axis('off') # Hide the axis + + # Create legend handles + legend_handles = [] + for name, (color, linestyle) in flightpath_dict.items(): + line = Line2D([0], [0], color=color, linestyle=linestyle, linewidth=2) + legend_handles.append((line, name)) + + # Add legend to the legend axis + self.legax.legend( + [handle for handle, _ in legend_handles], + [name for _, name in legend_handles], + frameon=False + ) + + self.ax.figure.canvas.draw_idle() + class SideViewPlotter(ViewPlotter): _pres_maj = np.concatenate([np.arange(top * 10, top, -top) for top in (10000, 1000, 100, 10)] + [[10]]) @@ -1639,6 +1672,13 @@ def draw_legend(self, img): # required so that it is actually drawn... QtWidgets.QApplication.processEvents() + def update_flightpath_legend(self, flightpath_dict): + """ + Update the flight path legend. + flightpath_dict: Dictionary where keys are flighttrack names, and values are tuples with (color, linestyle). + """ + self.plotter.draw_flightpath_legend(flightpath_dict) + def plot_satellite_overpass(self, segments): """Plots a satellite track on top of the map. """ diff --git a/mslib/msui/multiple_flightpath_dockwidget.py b/mslib/msui/multiple_flightpath_dockwidget.py index bdaf9cfad..eaa980497 100644 --- a/mslib/msui/multiple_flightpath_dockwidget.py +++ b/mslib/msui/multiple_flightpath_dockwidget.py @@ -392,6 +392,7 @@ def create_list_item(self, wp_model): # Show flighttrack color icon listItem.setIcon(self.show_color_icon(self.get_color(wp_model))) + self.update_flightpath_legend() return listItem @@ -545,6 +546,20 @@ def update_flighttrack_patch(self, wp_model): line_transparency=self.dict_flighttrack[wp_model]["line_transparency"], line_style=self.dict_flighttrack[wp_model]["line_style"] ) + self.update_flightpath_legend() + + def update_flightpath_legend(self): + """ + Collects flight path data and updates the legend in the TopView. + """ + flightpath_dict = {} + for wp_model, flighttrack_data in self.dict_flighttrack.items(): + name = wp_model.name if hasattr(wp_model, 'name') else 'Unnamed Flighttrack' + color = flighttrack_data.get('color', '#000000') # Default to black + linestyle = flighttrack_data.get('line_style', '-') # Default to solid line + flightpath_dict[name] = (color, linestyle) + + self.view.update_flightpath_legend(flightpath_dict) def flighttrackRemoved(self, parent, start, end): """ @@ -556,6 +571,7 @@ def flighttrackRemoved(self, parent, start, end): else: self.dict_flighttrack[self.list_flighttrack.item(start).flighttrack_model]["patch"].remove() self.list_flighttrack.takeItem(start) + self.update_flightpath_legend() def update_last_flighttrack(self): """ diff --git a/mslib/msui/qt5/ui_topview_window.py b/mslib/msui/qt5/ui_topview_window.py index 0196449bc..fe421804a 100644 --- a/mslib/msui/qt5/ui_topview_window.py +++ b/mslib/msui/qt5/ui_topview_window.py @@ -28,16 +28,6 @@ def setupUi(self, TopViewWindow): self.mpl.setSizePolicy(sizePolicy) self.mpl.setMinimumSize(QtCore.QSize(100, 100)) self.mpl.setObjectName("mpl") - self.gridLayoutWidget = QtWidgets.QWidget(self.mpl) - self.gridLayoutWidget.setGeometry(QtCore.QRect(10, 60, 192, 81)) - self.gridLayoutWidget.setObjectName("gridLayoutWidget") - self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget) - self.gridLayout.setContentsMargins(0, 0, 0, 0) - self.gridLayout.setObjectName("gridLayout") - self.widget = QtWidgets.QWidget(self.gridLayoutWidget) - self.widget.setMinimumSize(QtCore.QSize(190, 0)) - self.widget.setObjectName("widget") - self.gridLayout.addWidget(self.widget, 0, 0, 1, 1) self.horizontalLayout_2.addWidget(self.mpl) self.verticalLayout.addLayout(self.horizontalLayout_2) self.horizontalLayout = QtWidgets.QHBoxLayout() diff --git a/mslib/msui/topview.py b/mslib/msui/topview.py index 9cfbde639..c93134320 100644 --- a/mslib/msui/topview.py +++ b/mslib/msui/topview.py @@ -310,53 +310,10 @@ def __init__(self, parent=None, mainwindow=None, model=None, _id=None, self.mainwindow_signal_login_mscolab.connect(self.login) - # Initialize flight tracks and legend - self.load_flight_tracks() - self.init_legend() def __del__(self): del self.mpl.canvas.waypoints_interactor - def load_flight_tracks(self): - """ - Load flight track data from the main window's listFlightTracks widget. - """ - self.flight_tracks = [] - for index in range(self.mainwindow_listFlightTracks.count()): - item = self.mainwindow_listFlightTracks.item(index) - flighttrack_model = item.flighttrack_model - track_name = flighttrack_model.name - self.flight_tracks.append({"name": track_name}) - - def init_legend(self): - """ - Initialize the legend by creating label and line pairs for each flight track. - """ - # Clear existing legend items - while self.gridLayout.count(): - child = self.gridLayout.takeAt(0) - if child.widget(): - child.widget().deleteLater() - - # Add new legend items - for row, flight_track in enumerate(self.flight_tracks): - # Create label for the flight track name - label = QtWidgets.QLabel(flight_track["name"]) - - # Create a small colored line to represent the flight track - line = QtWidgets.QFrame() - line.setFrameShape(QtWidgets.QFrame.HLine) - line.setFrameShadow(QtWidgets.QFrame.Plain) - line.setFixedWidth(50) - # line_color = flight_track["color"] - # line.setStyleSheet( - # f"background-color: rgb({line_color[0] * 255}, {line_color[1] * 255}, {line_color[2] * 255});") - # to do : color implementation for the line - - # Add label and line to the grid layout in the same row, different columns - self.gridLayout.addWidget(label, row, 0, alignment=QtCore.Qt.AlignLeft) - self.gridLayout.addWidget(line, row, 1, alignment=QtCore.Qt.AlignRight) - @QtCore.pyqtSlot(ft.WaypointsTableModel) def update_active_flighttrack(self, active_flighttrack): """ diff --git a/mslib/msui/ui/ui_topview_window.ui b/mslib/msui/ui/ui_topview_window.ui index 13ea76129..f21134e81 100644 --- a/mslib/msui/ui/ui_topview_window.ui +++ b/mslib/msui/ui/ui_topview_window.ui @@ -31,28 +31,6 @@ 100 - - - - 10 - 60 - 192 - 81 - - - - - - - - 190 - 0 - - - - - - From fcf37e54e12763baf21daf41bf550ab39caa8090 Mon Sep 17 00:00:00 2001 From: rohit Date: Thu, 29 Aug 2024 20:14:53 +0530 Subject: [PATCH 6/9] flake8 resolve --- mslib/msui/topview.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mslib/msui/topview.py b/mslib/msui/topview.py index c93134320..d1db60818 100644 --- a/mslib/msui/topview.py +++ b/mslib/msui/topview.py @@ -310,7 +310,6 @@ def __init__(self, parent=None, mainwindow=None, model=None, _id=None, self.mainwindow_signal_login_mscolab.connect(self.login) - def __del__(self): del self.mpl.canvas.waypoints_interactor From 9d00deb11aa2b808efcff8bf427086516e132986 Mon Sep 17 00:00:00 2001 From: rohit Date: Sat, 31 Aug 2024 21:42:53 +0530 Subject: [PATCH 7/9] legend in mscolab & locally with 1 know issue --- mslib/msui/mpl_qtwidget.py | 18 ++--- mslib/msui/multiple_flightpath_dockwidget.py | 82 +++++++++++++++++--- 2 files changed, 80 insertions(+), 20 deletions(-) diff --git a/mslib/msui/mpl_qtwidget.py b/mslib/msui/mpl_qtwidget.py index 5b95ba6df..cd09d5a6f 100644 --- a/mslib/msui/mpl_qtwidget.py +++ b/mslib/msui/mpl_qtwidget.py @@ -351,31 +351,29 @@ def draw_legend(self, img): def draw_flightpath_legend(self, flightpath_dict): """ - Draw the flight path legend on the plot. + Draw the flight path legend on the plot, attached to the upper-left corner. """ # Clear any existing legend - if self.legax is not None: - self.legax.remove() - self.legax = None + if self.ax.get_legend() is not None: + self.ax.get_legend().remove() if not flightpath_dict: self.ax.figure.canvas.draw() return - # Create a new axis for the legend - self.legax = self.fig.add_axes([0.85, 0.7, 0.13, 0.2], frameon=False) - self.legax.axis('off') # Hide the axis - # Create legend handles legend_handles = [] for name, (color, linestyle) in flightpath_dict.items(): line = Line2D([0], [0], color=color, linestyle=linestyle, linewidth=2) legend_handles.append((line, name)) - # Add legend to the legend axis - self.legax.legend( + # Add legend directly to the main axis, attached to the upper-left corner + self.ax.legend( [handle for handle, _ in legend_handles], [name for _, name in legend_handles], + loc='upper left', + bbox_to_anchor=(0, 1), # (x, y) coordinates relative to the figure + bbox_transform=self.fig.transFigure, # Use figure coordinates frameon=False ) diff --git a/mslib/msui/multiple_flightpath_dockwidget.py b/mslib/msui/multiple_flightpath_dockwidget.py index eaa980497..13741f5f9 100644 --- a/mslib/msui/multiple_flightpath_dockwidget.py +++ b/mslib/msui/multiple_flightpath_dockwidget.py @@ -166,6 +166,7 @@ def __init__(self, parent=None, view=None, listFlightTracks=None, self.listFlightTracks = listFlightTracks self.mscolab_server_url = mscolab_server_url self.token = token + self.flightpath_dict = {} if self.ui is not None: ft_settings_dict = self.ui.getView().get_settings() self.color = ft_settings_dict["colour_ft_vertices"] @@ -398,11 +399,14 @@ def create_list_item(self, wp_model): def selectAll(self, state): """ - select/deselect local operations + Select/deselect local operations """ for i in range(self.list_flighttrack.count()): item = self.list_flighttrack.item(i) - item.setCheckState(state) + if self.active_flight_track is not None and item.flighttrack_model == self.active_flight_track: + item.setCheckState(QtCore.Qt.Checked) # Ensure the active flight track remains checked + else: + item.setCheckState(state) def select_color(self): """ @@ -438,6 +442,7 @@ def apply_color(self, wp_model, color): self.list_flighttrack.currentItem().setIcon(self.show_color_icon(self.get_color(wp_model))) self.dict_flighttrack[wp_model]["patch"].update( color=self.dict_flighttrack[wp_model]["color"]) + self.update_flightpath_legend() def get_color(self, wp_model): """ @@ -551,15 +556,28 @@ def update_flighttrack_patch(self, wp_model): def update_flightpath_legend(self): """ Collects flight path data and updates the legend in the TopView. + Only checked flight tracks will be included in the legend. + Unchecked flight tracks will be removed from the flightpath_dict. """ - flightpath_dict = {} - for wp_model, flighttrack_data in self.dict_flighttrack.items(): - name = wp_model.name if hasattr(wp_model, 'name') else 'Unnamed Flighttrack' - color = flighttrack_data.get('color', '#000000') # Default to black - linestyle = flighttrack_data.get('line_style', '-') # Default to solid line - flightpath_dict[name] = (color, linestyle) + # Iterate over all items in the list_flighttrack + for i in range(self.list_flighttrack.count()): + listItem = self.list_flighttrack.item(i) + wp_model = listItem.flighttrack_model - self.view.update_flightpath_legend(flightpath_dict) + # If the flight track is checked, add/update it in the dictionary + if listItem.checkState() == QtCore.Qt.Checked: + name = wp_model.name if hasattr(wp_model, 'name') else 'Unnamed flighttrack' + color = self.dict_flighttrack[wp_model].get('color', '#000000') # Default to black + linestyle = self.dict_flighttrack[wp_model].get('line_style', '-') # Default to solid line + self.flightpath_dict[name] = (color, linestyle) + # If the flight track is unchecked, ensure it is removed from the dictionary + else: + name = wp_model.name if hasattr(wp_model, 'name') else 'Unnamed flighttrack' + if name in self.flightpath_dict: + del self.flightpath_dict[name] + + # Update the legend in the view with the filtered flightpath_dict + self.view.update_flightpath_legend(self.flightpath_dict) def flighttrackRemoved(self, parent, start, end): """ @@ -663,6 +681,9 @@ def drawInactiveFlighttracks(self, list_widget): # pass self.dict_flighttrack[listItem.flighttrack_model]["checkState"] = False + # Update the legend after drawing the flight tracks + self.update_flightpath_legend() + def set_activate_flag(self): if not self.flighttrack_added: self.flighttrack_activated = True @@ -722,6 +743,8 @@ def listFlighttrack_itemClicked(self): self.enable_disable_line_style_buttons( wp_model != self.active_flight_track and self.list_flighttrack.currentItem(). checkState() == QtCore.Qt.Checked) + # Update the legend + self.update_flightpath_legend() class MultipleFlightpathOperations: @@ -837,6 +860,7 @@ def create_operation(self, op_id, wp_model): # Show operations color icon listItem.setIcon(self.show_color_icon(self.get_color(op_id))) + self.update_operation_legend() return listItem @@ -919,6 +943,7 @@ def draw_inactive_operations(self): "line_style"]) self.dict_operations[listItem.op_id]["patch"] = patch + self.update_operation_legend() def get_op_id(self, op_id): if self.active_op_id is not None: @@ -983,7 +1008,10 @@ def selectAll(self, state): """ for i in range(self.list_operation_track.count()): item = self.list_operation_track.item(i) - item.setCheckState(state) + if self.active_op_id is not None and item.op_id == self.active_op_id: + item.setCheckState(QtCore.Qt.Checked) # Ensure the active flight track remains checked + else: + item.setCheckState(state) def select_color(self): """ @@ -1011,6 +1039,7 @@ def apply_color(self, op_id, color): self.dict_operations[op_id]["patch"].update( color=self.dict_operations[op_id]["color"], linewidth=self.dict_operations[op_id]["linewidth"]) + self.update_operation_legend() def get_color(self, op_id): """ @@ -1044,6 +1073,9 @@ def logout_mscolab(self): self.list_operation_track.takeItem(0) a -= 1 + # Remove only the operations from flightpath_dict without affecting flight tracks + self.parent.flightpath_dict.clear() + # Uncheck the "Select All" checkbox self.parent.cbSlectAll2.setChecked(False) self.parent.labelStatus.setText("Status: Select a flighttrack/operation") @@ -1134,6 +1166,35 @@ def update_flighttrack_patch(self, op_id): line_transparency=self.dict_operations[op_id]["line_transparency"], line_style=self.dict_operations[op_id]["line_style"] ) + self.update_operation_legend() + + def update_operation_legend(self): + """ + Collects operation data and updates the legend in the TopView. + Only checked operations will be included in the legend. + Unchecked operations will be removed from the flightpath_dict. + """ + # Iterate over all items in the list_operation_track + for i in range(self.list_operation_track.count()): + listItem = self.list_operation_track.item(i) + + # If the operation is checked, add/update it in the dictionary + if listItem.checkState() == QtCore.Qt.Checked: + wp_model = listItem.flighttrack_model + name = wp_model.name if hasattr(wp_model, 'name') else 'Unnamed operation' + op_id = listItem.op_id + color = self.dict_operations[op_id].get('color', '#000000') # Default to black + linestyle = self.dict_operations[op_id].get('line_style', '-') # Default to solid line + self.parent.flightpath_dict[name] = (color, linestyle) + # If the flight track is unchecked, ensure it is removed from the dictionary + else: + wp_model = listItem.flighttrack_model + name = wp_model.name if hasattr(wp_model, 'name') else 'Unnamed flighttrack' + if name in self.parent.flightpath_dict: + del self.parent.flightpath_dict[name] + + # Update the legend in the view with the filtered flightpath_dict + self.view.update_flightpath_legend(self.parent.flightpath_dict) def listOperations_itemClicked(self): """ @@ -1160,3 +1221,4 @@ def listOperations_itemClicked(self): self.enable_disable_line_style_buttons(op_id != self.active_op_id and self.list_operation_track. currentItem().checkState() == QtCore.Qt.Checked) + self.update_operation_legend() From aa55ad657b43bee229cdafd54bdcd1bfdc405a6b Mon Sep 17 00:00:00 2001 From: rohit Date: Tue, 3 Sep 2024 20:33:28 +0530 Subject: [PATCH 8/9] select all test fix --- .../test_multiple_flightpath_dockwidget.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/_test_msui/test_multiple_flightpath_dockwidget.py b/tests/_test_msui/test_multiple_flightpath_dockwidget.py index bf1404912..d25f84c17 100644 --- a/tests/_test_msui/test_multiple_flightpath_dockwidget.py +++ b/tests/_test_msui/test_multiple_flightpath_dockwidget.py @@ -187,6 +187,13 @@ def test_selectAll(main_window): """ main_window, multiple_flightpath_widget = main_window + # Activate the first flight track + activate_flight_track_at_index(main_window, 0) + + # Retrieve the index of the active flight track + active_item = main_window.listFlightTracks.currentItem() + active_index = main_window.listFlightTracks.row(active_item) + # Check the "Select All" checkbox select_all_checkbox = multiple_flightpath_widget.cbSlectAll1 select_all_checkbox.setCheckState(QtCore.Qt.Checked) @@ -196,12 +203,16 @@ def test_selectAll(main_window): item = multiple_flightpath_widget.list_flighttrack.item(i) assert item.checkState() == QtCore.Qt.Checked + # Uncheck the "Select All" checkbox select_all_checkbox.setCheckState(QtCore.Qt.Unchecked) - # Verify that all items in the list are unchecked + # Verify that all items except the active one are unchecked for i in range(multiple_flightpath_widget.list_flighttrack.count()): item = multiple_flightpath_widget.list_flighttrack.item(i) - assert item.checkState() == QtCore.Qt.Unchecked + if i == active_index: + assert item.checkState() == QtCore.Qt.Checked # Active item should remain checked + else: + assert item.checkState() == QtCore.Qt.Unchecked def test_random_custom_color_selection(main_window): From 56de3bd88faed2fdca3f4d66cf46e3f44d1b47ee Mon Sep 17 00:00:00 2001 From: rohit Date: Tue, 3 Sep 2024 21:02:33 +0530 Subject: [PATCH 9/9] test on the legend method --- .../test_multiple_flightpath_dockwidget.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/_test_msui/test_multiple_flightpath_dockwidget.py b/tests/_test_msui/test_multiple_flightpath_dockwidget.py index d25f84c17..5a28cffdb 100644 --- a/tests/_test_msui/test_multiple_flightpath_dockwidget.py +++ b/tests/_test_msui/test_multiple_flightpath_dockwidget.py @@ -235,6 +235,41 @@ def test_random_custom_color_selection(main_window): assert color in multiple_flightpath_widget.custom_colors +def test_update_flightpath_legend(main_window): + """ + Test update_flightpath_legend to ensure only checked flight tracks + are included in the legend with correct name, color, and style. + """ + main_window, multiple_flightpath_widget = main_window + + # Activate the first flight track + activate_flight_track_at_index(main_window, 0) + + # Set the first flight track as checked and the second as unchecked + first_item = multiple_flightpath_widget.list_flighttrack.item(0) + second_item = multiple_flightpath_widget.list_flighttrack.item(1) + first_item.setCheckState(QtCore.Qt.Checked) + second_item.setCheckState(QtCore.Qt.Unchecked) + + # Define color and style for the first flight track + multiple_flightpath_widget.dict_flighttrack[first_item.flighttrack_model] = { + "color": "#FF0000", + "line_style": "--" + } + + # Calling the method + multiple_flightpath_widget.update_flightpath_legend() + + # Verify that only the checked flight track is included in the legend + assert first_item.flighttrack_model.name in multiple_flightpath_widget.flightpath_dict + assert second_item.flighttrack_model.name not in multiple_flightpath_widget.flightpath_dict + + # Verify that the color and style in the legend match the first flight track + legend_color, legend_style = multiple_flightpath_widget.flightpath_dict[first_item.flighttrack_model.name] + assert legend_color == "#FF0000" + assert legend_style == "--" + + def activate_flight_track_at_index(main_window, index): # The main window must be on top main_window.activateWindow()