diff --git a/documentation/01 _ Table of Contents.help.md b/documentation/01 _ Table of Contents.help.md index f7e158e1eb463f7b419d7f220879acbe094331ae..60e076d08b46d31af4e52b8bcd509a2c73aaefde 100644 --- a/documentation/01 _ Table of Contents.help.md +++ b/documentation/01 _ Table of Contents.help.md @@ -4,6 +4,8 @@ Welcome to the SOH Station Viewer documentation. Here you will find usage guides On the left-hand side you will find a list of currently available help topics. +If the links of the Table of Contents are broken, click on Recreate Table of Content <img src='recreate_table_contents.png' height=30 style='margin: 3px 0px 0px 0px;'/> to rebuild it. + The home button can be used to return to this page at any time. # Table of Contents @@ -14,19 +16,23 @@ The home button can be used to return to this page at any time. + [How to Use Help](03%20_%20How%20to%20Use%20Help.help.md) -+ [Search SOH n LOG](04%20_%20Search%20SOH%20n%20LOG.help.md) ++ [Search List of Directories](04%20_%20Search%20List%20of%20Directories.help.md) + ++ [Read from Data Card](05%20_%20Read%20from%20Data%20Card.help.md) + ++ [Select SOH](06%20_%20Select%20SOH.help.md) -+ [Search List of Directories](05%20_%20Search%20List%20of%20Directories.help.md) ++ [Select Mass Position](07%20_%20Select%20Mass%20Position.help.md) -+ [Read from Data Card](06%20_%20Read%20from%20Data%20Card.help.md) ++ [Select Waveforms](08%20_%20Select%20Waveforms.help.md) -+ [Select SOH](07%20_%20Select%20SOH.help.md) ++ [Gap Display](09%20_%20Gap%20Display.help.md) -+ [Select Mass Position](08%20_%20Select%20Mass%20Position.help.md) ++ [Change TPS Color Range](10%20_%20Change%20TPS%20Color%20Range.help.md) -+ [Select Waveforms](09%20_%20Select%20Waveforms.help.md) ++ [Save Plots](11%20_%20Save%20Plots.help.md) -+ [Gap Display](10%20_%20Gap%20Display.help.md) ++ [Search SOH n LOG](12%20_%20Search%20SOH%20n%20LOG.help.md) + [GPS Dialog](20%20_%20GPS%20Dialog.help.md) diff --git a/documentation/05 _ Search List of Directories.help.md b/documentation/04 _ Search List of Directories.help.md similarity index 100% rename from documentation/05 _ Search List of Directories.help.md rename to documentation/04 _ Search List of Directories.help.md diff --git a/documentation/06 _ Read from Data Card.help.md b/documentation/05 _ Read from Data Card.help.md similarity index 100% rename from documentation/06 _ Read from Data Card.help.md rename to documentation/05 _ Read from Data Card.help.md diff --git a/documentation/07 _ Select SOH.help.md b/documentation/06 _ Select SOH.help.md similarity index 100% rename from documentation/07 _ Select SOH.help.md rename to documentation/06 _ Select SOH.help.md diff --git a/documentation/08 _ Select Mass Position.help.md b/documentation/07 _ Select Mass Position.help.md similarity index 100% rename from documentation/08 _ Select Mass Position.help.md rename to documentation/07 _ Select Mass Position.help.md diff --git a/documentation/09 _ Select Waveforms.help.md b/documentation/08 _ Select Waveforms.help.md similarity index 100% rename from documentation/09 _ Select Waveforms.help.md rename to documentation/08 _ Select Waveforms.help.md diff --git a/documentation/11 _ Gap Display.help.md b/documentation/09 _ Gap Display.help.md similarity index 100% rename from documentation/11 _ Gap Display.help.md rename to documentation/09 _ Gap Display.help.md diff --git a/documentation/11 _ Save Plots.help.md b/documentation/11 _ Save Plots.help.md new file mode 100644 index 0000000000000000000000000000000000000000..0027b76db29eeb97aa0adf7cbe68dc7fa5126b09 --- /dev/null +++ b/documentation/11 _ Save Plots.help.md @@ -0,0 +1,60 @@ +# Save Plots + +--------------------------- +--------------------------- + +## Step 1: click 'Save Plot' +In Main Window, Raw Data Plot and TPS Plot there are buttons labeled 'Save Plot'. + +User need to click those button to save plots in each window. + +* Saving State-of-Health plots +<br /> +<img alt="Save SOH" src="images/save_plots/save_button_soh.png" height="30" /> +<br /> +* Saving Raw data plots +<br /> +<img alt="Save Waveform" src="images/save_plots/save_button_wf.png" height="60" /> +<br /> +* Saving Time-power-square plots +<br /> +<img alt="Save TPS" src="images/save_plots/save_button_tps.png" height="80" /> +<br /> +<br /> +<br /> + +If the current color mode is black, user will be asked to continue or cancel +to change mode before saving the image. + +<br /> +<br /> +<img alt="Want to change color mode?" src="images/save_plots/question_on_changing_black_mode.png" height="150" /> +<br /> + +* If user click 'Cancel'. The process of saving plots will be canceled for user +to change mode before restarting saving plots again. +* If user click 'Continue'. The process of saving plots will be continue and the +image will be saved in black mode. +<br /> + +--------------------------- +## Step 2: Edit file path and select image's format +Once clicking on 'Save Plot' button, the 'Save Plot' dialog will pop up. + +<br /> +<br /> +<img alt="Select Image Format dialog" src="images/save_plots/save_file_dialog.png" height="200" /> +<br /> + ++ The default path to save the image file is preset in (1) text box. If user +wants to change the path, click on 'Save Directory button' to open file dialog +for changing path. ++ The default filename to save the image is preset in (2) text box. User can +change the name in this box. ++ In side oval (3) are the radio buttons to select image format to save +file. ++ For 'PNG' format, user can change DPI which is the resolution of the +image. Other formats are vector formats which don't require resolution. + +Then user can click 'CANCEL' to cancel saving plot or click 'SAVE PLOT' to save +the current plots to file. \ No newline at end of file diff --git a/documentation/04 _ Search SOH n LOG.help.md b/documentation/12 _ Search SOH n LOG.help.md similarity index 100% rename from documentation/04 _ Search SOH n LOG.help.md rename to documentation/12 _ Search SOH n LOG.help.md diff --git a/documentation/99 _ test.md b/documentation/99 _ test.md index 7ef0655b760ac6880ab28c7b87f54ad34c2bb4ae..84fbede232f89c3fc5c6e9c03a105021552adb20 100644 --- a/documentation/99 _ test.md +++ b/documentation/99 _ test.md @@ -39,7 +39,7 @@ printf("%s\n", syntaxHighlighting.doesItWork ? "Success!" : "Oof."); ^ This is a horizontal line v This is an image - + --- Another horizontal line diff --git a/documentation/images/save_plots/question_on_changing_black_mode.png b/documentation/images/save_plots/question_on_changing_black_mode.png new file mode 100644 index 0000000000000000000000000000000000000000..7424afda3387e8cbcad71a7fba63903072d2f23d Binary files /dev/null and b/documentation/images/save_plots/question_on_changing_black_mode.png differ diff --git a/documentation/images/save_plots/save_button_soh.png b/documentation/images/save_plots/save_button_soh.png new file mode 100644 index 0000000000000000000000000000000000000000..588e20ca07de4e9dfde974de414107bb855ac1c8 Binary files /dev/null and b/documentation/images/save_plots/save_button_soh.png differ diff --git a/documentation/images/save_plots/save_button_tps.png b/documentation/images/save_plots/save_button_tps.png new file mode 100644 index 0000000000000000000000000000000000000000..1bfe4977370d6b904ff3d63a79bb6a4fbfe67266 Binary files /dev/null and b/documentation/images/save_plots/save_button_tps.png differ diff --git a/documentation/images/save_plots/save_button_wf.png b/documentation/images/save_plots/save_button_wf.png new file mode 100644 index 0000000000000000000000000000000000000000..f65ac57c793dd9b43cfd4814e56604eb3f3f3c80 Binary files /dev/null and b/documentation/images/save_plots/save_button_wf.png differ diff --git a/documentation/images/save_plots/save_file_dialog.png b/documentation/images/save_plots/save_file_dialog.png new file mode 100644 index 0000000000000000000000000000000000000000..ddb40fe65456a44943792bd94933a88a64556111 Binary files /dev/null and b/documentation/images/save_plots/save_file_dialog.png differ diff --git a/documentation/img.png b/documentation/img.png deleted file mode 100644 index 5d8c5a2165cf11862b70318e57343665de6e1a77..0000000000000000000000000000000000000000 Binary files a/documentation/img.png and /dev/null differ diff --git a/documentation/recreate_table_contents.png b/documentation/recreate_table_contents.png new file mode 100644 index 0000000000000000000000000000000000000000..34ab02a858eb4da3d62325cff47e1bd56dc90186 Binary files /dev/null and b/documentation/recreate_table_contents.png differ diff --git a/sohstationviewer/conf/constants.py b/sohstationviewer/conf/constants.py index 8bd00e091e0c436c87c64027c626cfa716dab02f..d060a1f8a3ac0865a719cddd898f39a6d55dd97e 100644 --- a/sohstationviewer/conf/constants.py +++ b/sohstationviewer/conf/constants.py @@ -50,8 +50,11 @@ TABLE_CONTENTS = "01 _ Table of Contents.help.md" SEARCH_RESULTS = "Search Results.md" # the list of all color modes -ALL_COLOR_MODES = {'B', 'W'} +ALL_COLOR_MODES = {'B': 'black', 'W': 'white'} +# List of image formats. Have to put PNG at the beginning to go with +# dpi in dialog +IMG_FORMAT = ['PNG', 'PDF', 'EPS', 'SVG'] # ================================================================= # # PLOTTING CONSTANT # ================================================================= # diff --git a/sohstationviewer/controller/processing.py b/sohstationviewer/controller/processing.py index 28f3c17269fb45098332e490e9328ce1971051ba..4c34572b75880464d1cbb933b417165f388058e8 100644 --- a/sohstationviewer/controller/processing.py +++ b/sohstationviewer/controller/processing.py @@ -139,7 +139,7 @@ def read_mseed_channels(tracking_box: QTextBrowser, list_of_dir: List[str], spr_gr_1_chan_ids.update(ret[3]) if not on_unittest: QApplication.restoreOverrideCursor() - return sorted(list(soh_chan_ids)), sorted(list(mass_pos_chan_ids)),\ + return sorted(list(soh_chan_ids)), sorted(list(mass_pos_chan_ids)), \ sorted(list(wf_chan_ids)), sorted(list(spr_gr_1_chan_ids)) diff --git a/sohstationviewer/view/db_config/param_dialog.py b/sohstationviewer/view/db_config/param_dialog.py index 2fc8c8ad99d312e01857c2d4514062aeb49b4e10..21ecf7bcca7316e30a6b5e7253d7f1ce19ef400b 100755 --- a/sohstationviewer/view/db_config/param_dialog.py +++ b/sohstationviewer/view/db_config/param_dialog.py @@ -47,7 +47,7 @@ class ParamDialog(UiDBInfoDialog): color_mode_label = QtWidgets.QLabel('Color mode:') color_selector = QComboBox() color_selector.insertItem(0, initial_color_mode) - other_color_modes = ALL_COLOR_MODES - {initial_color_mode} + other_color_modes = set(ALL_COLOR_MODES.keys()) - {initial_color_mode} color_selector.insertItems(1, other_color_modes) color_selector.setFixedWidth(100) color_selector.currentTextChanged.connect(self.on_color_mode_changed) diff --git a/sohstationviewer/view/main_window.py b/sohstationviewer/view/main_window.py index 55229391942838e45a3383ae4c3cf458eaab4cc8..0f5fdcd435719e19840e5a2ac133ed55bfb4186f 100755 --- a/sohstationviewer/view/main_window.py +++ b/sohstationviewer/view/main_window.py @@ -63,9 +63,17 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): """ self.dir_names: List[Path] = [] """ - current_dir: str - the current main data directory + current_dir: the current main data directory """ - self.current_dir = '' + self.current_dir: str = '' + """ + save_plot_dir: directory to save plot + """ + self.save_plot_dir: str = '' + """ + save_plot_format: format to save plot + """ + self.save_plot_format: str = 'SVG' """ rt130_das_dict: dict by rt130 for data paths, so user can choose dasses to assign list of data paths to selected_rt130_paths @@ -185,6 +193,10 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): self.validate_config() self.apply_config() + @QtCore.Slot() + def save_plot(self): + self.plotting_widget.save_plot('SOH-Plot') + @QtCore.Slot() def open_data_type(self) -> None: """ @@ -732,6 +744,10 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): if self.has_problem: return self.is_plotting_soh = True + self.plotting_widget.set_colors(self.color_mode) + self.waveform_dlg.plotting_widget.set_colors(self.color_mode) + self.tps_dlg.plotting_widget.set_colors(self.color_mode) + self.gps_dialog.set_colors(self.color_mode) d_obj = self.data_object @@ -854,6 +870,7 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): # current directory self.current_directory_changed.emit(path) self.current_dir = path + self.save_plot_dir = path execute_db(f'UPDATE PersistentData SET FieldValue="{path}" WHERE ' 'FieldName="currentDirectory"') self.set_open_files_list_texts() @@ -1072,10 +1089,6 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): if not checked: return self.color_mode = color_mode - self.plotting_widget.set_colors(color_mode) - self.waveform_dlg.plotting_widget.set_colors(color_mode) - self.tps_dlg.plotting_widget.set_colors(color_mode) - self.gps_dialog.set_colors(color_mode) @QtCore.Slot() def clear_file_search(self): diff --git a/sohstationviewer/view/plotting/plotting_widget/plotting_axes.py b/sohstationviewer/view/plotting/plotting_widget/plotting_axes.py index 508f96cd256e6277d51c23807a657c1c6a5f3da8..002e0dd6fc613ba135b3df1a919a5894401e2583 100644 --- a/sohstationviewer/view/plotting/plotting_widget/plotting_axes.py +++ b/sohstationviewer/view/plotting/plotting_widget/plotting_axes.py @@ -78,6 +78,7 @@ class PlottingAxes: labelbottom = False else: labelbottom = True + self.parent.plotting_bot -= 0.007 # space for ticks timestamp_bar.tick_params(which='major', length=7, width=2, direction='inout', colors=self.parent.display_color['basic'], @@ -90,7 +91,8 @@ class PlottingAxes: fontweight='bold', fontsize=self.parent.font_size, rotation=0, - labelpad=constants.HOUR_TO_TMBAR_D, + labelpad=constants.HOUR_TO_TMBAR_D * + self.parent.ratio_w, ha='left', color=self.parent.display_color['basic']) # not show any y ticks @@ -112,7 +114,8 @@ class PlottingAxes: timestamp_bar.set_xticks(times, minor=True) timestamp_bar.set_xticks(major_times) timestamp_bar.set_xticklabels(major_time_labels, - fontsize=self.parent.font_size + 2) + fontsize=self.parent.font_size + + 2 * self.parent.ratio_w) timestamp_bar.set_xlim(self.parent.min_x, self.parent.max_x) def create_axes(self, plot_b, plot_h, has_min_max_lines=True): @@ -221,7 +224,7 @@ class PlottingAxes: rotation='horizontal', transform=ax.transAxes, color=color, - size=self.parent.font_size + 2 + size=self.parent.font_size + 2 * self.parent.ratio_w ) # set samples' total on right side @@ -352,17 +355,23 @@ class PlottingAxes: ) ) - def get_height(self, ratio, bw_plots_distance=0.0015): + def get_height(self, ratio: float, bw_plots_distance: float = 0.0015, + pixel_height: float = 19) -> float: """ Calculate new plot's bottom position and return plot's height. - :param ratio: float - ratio of the plot height on the BASIC_HEIGHT - :param bw_plots_distance: float - distance between plots - :return plot_h: float - height of the plot + :param ratio: ratio of the plot height on the BASIC_HEIGHT + :param bw_plots_distance: distance between plots + :param pixel_height: height of plot in pixel ( + for TPS/TPS legend, height of each day row) + + :return plot_h: height of the plot """ plot_h = constants.BASIC_HEIGHT * ratio # ratio with figure height self.parent.plotting_bot -= plot_h + bw_plots_distance - self.parent.plotting_bot_pixel += 19 * ratio + bw_plots_distance_pixel = 3000 * bw_plots_distance + self.parent.plotting_bot_pixel += (pixel_height * ratio + + bw_plots_distance_pixel) return plot_h def add_ruler(self, color): @@ -403,4 +412,4 @@ class PlottingAxes: horizontalalignment='left', transform=self.parent.timestamp_bar_top.transAxes, color=self.parent.display_color['basic'], - size=self.parent.font_size) + size=self.parent.font_size + 2 * self.parent.ratio_w) diff --git a/sohstationviewer/view/plotting/plotting_widget/plotting_widget.py b/sohstationviewer/view/plotting/plotting_widget/plotting_widget.py index 9cc7a78fbcbd701deedda58b3b3f1b1d912900aa..20a8d99105e4b0c73d33577f34776d8d96b93db9 100755 --- a/sohstationviewer/view/plotting/plotting_widget/plotting_widget.py +++ b/sohstationviewer/view/plotting/plotting_widget/plotting_widget.py @@ -2,10 +2,10 @@ Class of which object is used to plot data """ from typing import List, Optional, Union - import matplotlib.text -from PySide2.QtCore import QTimer, Qt from matplotlib import pyplot as pl +from matplotlib.transforms import Bbox +from PySide2.QtCore import QTimer, Qt from PySide2 import QtCore, QtWidgets from PySide2.QtWidgets import QWidget, QApplication, QTextBrowser @@ -18,6 +18,7 @@ from sohstationviewer.view.plotting.plotting_widget.plotting_axes import ( PlottingAxes ) from sohstationviewer.view.plotting.plotting_widget.plotting import Plotting +from sohstationviewer.view.save_plot_dialog import SavePlotDialog from sohstationviewer.controller.plotting_data import format_time from sohstationviewer.controller.util import display_tracking_info @@ -110,6 +111,7 @@ class PlottingWidget(QtWidgets.QScrollArea): font_size: float - font size on plot. With some require bigger font, +2 to the font_size """ + self.base_font_size = 7 self.font_size = 7 """ bottom: float - y position of the bottom edge of all plots in self.axes @@ -243,6 +245,7 @@ class PlottingWidget(QtWidgets.QScrollArea): # set view size fit with the scroll's view port size self.main_widget.setFixedWidth(geo.width()) self.ratio_w = geo.width() / self.width_base_px + self.font_size = self.ratio_w * self.base_font_size self.plotting_w = self.ratio_w * self.width_base self.plotting_l = self.ratio_w * self.plotting_l_base if self.plot_total == 0: @@ -652,6 +655,57 @@ class PlottingWidget(QtWidgets.QScrollArea): """ self.peer_plotting_widgets = widgets + def save_plot(self, default_name='plot'): + if self.c_mode != self.main_window.color_mode: + main_color = constants.ALL_COLOR_MODES[self.main_window.color_mode] + curr_color = constants.ALL_COLOR_MODES[self.c_mode] + msg = (f"Main window's color mode is {main_color}" + f" but the mode haven't been applied to plotting.\n\n" + f"Do you want to cancel to apply {main_color} mode " + f"by clicking RePlot?\n" + f"Or continue with {curr_color}?") + msgbox = QtWidgets.QMessageBox() + msgbox.setWindowTitle("Color Mode Conflict") + msgbox.setText(msg) + msgbox.addButton(QtWidgets.QMessageBox.Cancel) + msgbox.addButton('Continue', QtWidgets.QMessageBox.YesRole) + result = msgbox.exec_() + if result == QtWidgets.QMessageBox.Cancel: + return + self.main_window.color_mode = self.c_mode + if self.c_mode == 'B': + self.main_window.background_black_radio_button.setChecked(True) + else: + self.main_window.background_white_radio_button.setChecked(True) + if self.c_mode == 'B': + msg = ("The current background mode is black.\n" + "Do you want to cancel to change the background mode " + "before saving the plots to file?") + msgbox = QtWidgets.QMessageBox() + msgbox.setWindowTitle("Background Mode Confirmation") + msgbox.setText(msg) + msgbox.addButton(QtWidgets.QMessageBox.Cancel) + msgbox.addButton('Continue', QtWidgets.QMessageBox.YesRole) + result = msgbox.exec_() + if result == QtWidgets.QMessageBox.Cancel: + return + save_plot_dlg = SavePlotDialog( + self.parent, self.main_window, default_name) + save_plot_dlg.exec_() + save_file_path = save_plot_dlg.save_file_path + if save_file_path is None: + return + dpi = save_plot_dlg.dpi + + self.plotting_axes.fig.savefig( + save_file_path, + bbox_inches=Bbox([[0, self.plotting_bot*100], + [self.ratio_w*15.5, 100]]), + dpi=dpi + ) + msg = f"Graph is saved at {save_file_path}" + display_tracking_info(self.tracking_box, msg) + def clear(self): self.plotting_axes.fig.clear() self.axes = [] diff --git a/sohstationviewer/view/plotting/time_power_squared_dialog.py b/sohstationviewer/view/plotting/time_power_squared_dialog.py index 1c5acdb972716024206009c925052032f57c0538..dbdaa82f57f03ef74dc7b04e361ba540174ffd1b 100755 --- a/sohstationviewer/view/plotting/time_power_squared_dialog.py +++ b/sohstationviewer/view/plotting/time_power_squared_dialog.py @@ -89,8 +89,10 @@ class TimePowerSquaredWidget(plotting_widget.PlottingWidget): self.is_working = True self.set_key = key self.plotting_data1 = d_obj.waveform_data[key] + self.plot_total = len(self.plotting_data1) self.plotting_bot = const.BOTTOM + self.plotting_bot_pixel = const.BOTTOM_PX self.processed_channels = [] self.channels = [] self.tps_processors = [] @@ -111,7 +113,7 @@ class TimePowerSquaredWidget(plotting_widget.PlottingWidget): title = get_title(key, self.min_x, self.max_x, self.date_mode) self.timestamp_bar_top = self.plotting_axes.add_timestamp_bar(0.) - self.plotting_axes.set_title(title, y=0, v_align='bottom') + self.plotting_axes.set_title(title, y=5, v_align='bottom') if self.plotting_data1 == {}: self.is_working = False @@ -220,11 +222,12 @@ class TimePowerSquaredWidget(plotting_widget.PlottingWidget): total_days = c_data['tps_data'].shape[0] plot_h = self.plotting_axes.get_height( - 1.5 * total_days, bw_plots_distance=0.003) + total_days/1.5, bw_plots_distance=0.003, pixel_height=12.1) ax = self.create_axes(self.plotting_bot, plot_h) + ax.spines[['right', 'left', 'top', 'bottom']].set_visible(False) ax.text( - -0.1, 1.2, - f"{get_chan_label(chan_id)} {c_data['samplerate']}", + -0.12, 1, + f"{get_chan_label(chan_id)} {c_data['samplerate']}sps", horizontalalignment='left', verticalalignment='top', rotation='horizontal', @@ -234,17 +237,17 @@ class TimePowerSquaredWidget(plotting_widget.PlottingWidget): ) zoom_marker1 = ax.plot( - [], [], marker='|', markersize=10, + [], [], marker='|', markersize=5, markeredgecolor=self.display_color['zoom_marker'])[0] self.zoom_marker1s.append(zoom_marker1) zoom_marker2 = ax.plot( - [], [], marker='|', markersize=10, + [], [], marker='|', markersize=5, markeredgecolor=self.display_color['zoom_marker'])[0] self.zoom_marker2s.append(zoom_marker2) ruler = ax.plot( - [], [], marker='s', markersize=5, + [], [], marker='s', markersize=4, markeredgecolor=self.display_color['time_ruler'], markerfacecolor='None')[0] self.rulers.append(ruler) @@ -258,8 +261,8 @@ class TimePowerSquaredWidget(plotting_widget.PlottingWidget): # not draw data out of day range color_set = self.get_color_set(y, square_counts, color_codes) # (- dayIdx): each day is a line, increase from top to bottom - ax.scatter(x, [- dayIdx] * len(x), marker='|', - c=color_set, s=7, alpha=0.8) + ax.scatter(x, [- dayIdx] * len(x), marker='s', + c=color_set, s=3) # extra to show highlight square ax.set_ylim(-(c_data['tps_data'].shape[0] + 1), 1) @@ -274,11 +277,13 @@ class TimePowerSquaredWidget(plotting_widget.PlottingWidget): ax.legend will create one label for each dot. """ # set height of legend and distance bw legend and upper ax - plot_h = self.plotting_axes.get_height(7, bw_plots_distance=0.003) + plot_h = self.plotting_axes.get_height( + 21, bw_plots_distance=0.004, pixel_height=12) ax = self.plotting_axes.canvas.figure.add_axes( [self.plotting_l, self.plotting_bot, self.plotting_w, plot_h], picker=True ) + ax.axis('off') ax.patch.set_alpha(0) c_labels = self.parent.sel_col_labels clrs = self.parent.color_def # colordef @@ -466,6 +471,7 @@ class TimePowerSquaredWidget(plotting_widget.PlottingWidget): with new color range selected. """ self.clear() + self.set_colors(self.main_window.color_mode) self.plotting_bot = const.BOTTOM title = get_title(self.set_key, self.min_x, self.max_x, self.date_mode) self.timestamp_bar_top = self.plotting_axes.add_timestamp_bar(0.) @@ -554,6 +560,7 @@ class TimePowerSquaredDialog(QtWidgets.QWidget): """ self.color_range_choice = QtWidgets.QComboBox(self) self.color_range_choice.addItems(self.color_ranges) + self.color_range_choice.setCurrentText('High') color_layout.addWidget(self.color_range_choice) # ##################### Replot button ######################## @@ -561,8 +568,8 @@ class TimePowerSquaredDialog(QtWidgets.QWidget): buttons_layout.addWidget(self.replot_button) # ##################### Save button ########################## - self.save_button = QtWidgets.QPushButton('Save', self) - buttons_layout.addWidget(self.save_button) + self.save_plot_button = QtWidgets.QPushButton('Save Plot', self) + buttons_layout.addWidget(self.save_plot_button) self.info_text_browser.setFixedHeight(60) bottom_layout.addWidget(self.info_text_browser) @@ -595,7 +602,7 @@ class TimePowerSquaredDialog(QtWidgets.QWidget): """ Connect functions to widgets """ - self.save_button.clicked.connect(self.save) + self.save_plot_button.clicked.connect(self.save_plot) self.replot_button.clicked.connect(self.plotting_widget.replot) self.color_range_choice.currentTextChanged.connect( self.color_range_changed) @@ -612,8 +619,8 @@ class TimePowerSquaredDialog(QtWidgets.QWidget): self.sel_col_labels = self.color_label[cr_index] @QtCore.Slot() - def save(self): + def save_plot(self): """ Save the plotting to a file """ - print("save") + self.plotting_widget.save_plot('TPS-Plot') diff --git a/sohstationviewer/view/plotting/waveform_dialog.py b/sohstationviewer/view/plotting/waveform_dialog.py index ba9a2a2cd66f18d3658bacd72751b834901ff404..8a161ab29dfac4a906fe6c3109e3f0338c9a5980 100755 --- a/sohstationviewer/view/plotting/waveform_dialog.py +++ b/sohstationviewer/view/plotting/waveform_dialog.py @@ -118,11 +118,11 @@ class WaveformDialog(QtWidgets.QWidget): bottom_layout = QtWidgets.QHBoxLayout() main_layout.addLayout(bottom_layout) """ - save_button: save plot in plotting_widget to file + save_plot_button: save plot in plotting_widget to file """ - self.save_button = QtWidgets.QPushButton('Save', self) - self.save_button.clicked.connect(self.save) - bottom_layout.addWidget(self.save_button) + self.save_plot_button = QtWidgets.QPushButton('Save Plot', self) + self.save_plot_button.clicked.connect(self.save_plot) + bottom_layout.addWidget(self.save_plot_button) self.info_text_browser.setFixedHeight(60) bottom_layout.addWidget(self.info_text_browser) @@ -148,11 +148,11 @@ class WaveformDialog(QtWidgets.QWidget): self.plotting_widget.init_size() @QtCore.Slot() - def save(self): + def save_plot(self): """ Save the plotting to a file """ - print("save") + self.plotting_widget.save_plot('Waveform-Plot') def plot_finished(self): self.parent.is_plotting_waveform = False diff --git a/sohstationviewer/view/save_plot_dialog.py b/sohstationviewer/view/save_plot_dialog.py new file mode 100644 index 0000000000000000000000000000000000000000..77a988f25a6679ac7ecd3bd4f916ca625d6a97d1 --- /dev/null +++ b/sohstationviewer/view/save_plot_dialog.py @@ -0,0 +1,139 @@ +import sys +import platform +import os +from pathlib import Path +from typing import Union, Optional + +from PySide2 import QtWidgets, QtCore, QtGui +from PySide2.QtWidgets import QApplication, QWidget, QDialog + +from sohstationviewer.conf import constants + + +class SavePlotDialog(QDialog): + def __init__(self, parent: Union[QWidget, QApplication], + main_window: QApplication, + default_name: str): + """ + Dialog allow choosing file format and open file dialog to + save file as + + :param parent: the parent widget + :param main_window: to keep path to save file + :param default_name: default name for graph file to be saved as + """ + super(SavePlotDialog, self).__init__(parent) + self.main_window = main_window + """ + save_file_path: path to save file + """ + self.save_file_path: Optional[Path] = None + """ + save_dir_path: path to save dir + """ + self.save_dir_path: Path = main_window.save_plot_dir + """ + dpi: resolution for png format + """ + self.dpi: int = 100 + + self.save_dir_btn = QtWidgets.QPushButton("Save Directory", self) + self.save_dir_textbox = QtWidgets.QLineEdit(self.save_dir_path) + self.save_filename_textbox = QtWidgets.QLineEdit(default_name) + + self.dpi_line_edit = QtWidgets.QSpinBox(self) + self.format_radio_btns = {} + for fmt in constants.IMG_FORMAT: + self.format_radio_btns[fmt] = QtWidgets.QRadioButton(fmt, self) + if fmt == self.main_window.save_plot_format: + self.format_radio_btns[fmt].setChecked(True) + self.cancel_btn = QtWidgets.QPushButton('CANCEL', self) + self.continue_btn = QtWidgets.QPushButton('SAVE PLOT', self) + + self.setup_ui() + self.connect_signals() + + def setup_ui(self) -> None: + self.setWindowTitle("Save Plot") + + main_layout = QtWidgets.QGridLayout() + self.setLayout(main_layout) + + main_layout.addWidget(self.save_dir_btn, 0, 0, 1, 1) + self.save_dir_textbox.setFixedWidth(500) + main_layout.addWidget(self.save_dir_textbox, 0, 1, 1, 5) + main_layout.addWidget(QtWidgets.QLabel('Save Filename'), + 1, 0, 1, 1) + main_layout.addWidget(self.save_filename_textbox, 1, 1, 1, 5) + + main_layout.addWidget(QtWidgets.QLabel('DPI'), + 2, 2, 1, 1, QtGui.Qt.AlignRight) + self.dpi_line_edit.setRange(50, 300) + self.dpi_line_edit.setValue(100) + main_layout.addWidget(self.dpi_line_edit, 2, 3, 1, 1) + rowidx = 2 + for fmt in self.format_radio_btns: + main_layout.addWidget(self.format_radio_btns[fmt], rowidx, 1, 1, 1) + rowidx += 1 + + main_layout.addWidget(self.cancel_btn, rowidx, 1, 1, 1) + main_layout.addWidget(self.continue_btn, rowidx, 3, 1, 1) + + def connect_signals(self) -> None: + self.save_dir_btn.clicked.connect(self.change_save_directory) + self.cancel_btn.clicked.connect(self.close) + self.continue_btn.clicked.connect(self.on_continue) + + @QtCore.Slot() + def change_save_directory(self) -> None: + """ + Show a file selection window and change the GPS data save directory + based on the folder selected by the user. + """ + fd = QtWidgets.QFileDialog(self) + fd.setFileMode(QtWidgets.QFileDialog.Directory) + fd.setDirectory(self.save_dir_textbox.text()) + fd.exec_() + new_path = fd.selectedFiles()[0] + self.save_dir_textbox.setText(new_path) + self.save_dir_path = new_path + self.main_window.save_plot_dir = new_path + + @QtCore.Slot() + def on_continue(self): + if self.save_dir_textbox.text().strip() == '': + QtWidgets.QMessageBox.warning( + self, "Add Directory", + "A directory need to be given before continue.") + return + + if self.save_filename_textbox.text().strip() == '': + QtWidgets.QMessageBox.warning( + self, "Add Filename", + "A file name need to be given before continue.") + return + + for img_format in self.format_radio_btns: + if self.format_radio_btns[img_format].isChecked(): + save_format = img_format + self.main_window.save_plot_format = img_format + break + + self.save_file_path = Path(self.save_dir_path).joinpath( + f"{self.save_filename_textbox.text()}.{save_format}") + self.dpi = self.dpi_line_edit.value() + self.close() + + +if __name__ == '__main__': + os_name, version, *_ = platform.platform().split('-') + if os_name == 'macOS': + os.environ['QT_MAC_WANTS_LAYER'] = '1' + app = QtWidgets.QApplication(sys.argv) + save_path = '/Users/ldam/Documents/GIT/sohstationviewer/tests/test_data/Q330-sample' # noqa: E501 + test = SavePlotDialog(None, 'test_plot') + test.set_save_directory(save_path) + test.exec_() + print("dpi:", test.dpi) + print("save file path:", test.save_file_path) + sys.exit(app.exec_()) diff --git a/sohstationviewer/view/ui/main_ui.py b/sohstationviewer/view/ui/main_ui.py index 005029668262238706fc02f0bf176aa25995df5e..194b23483bc13cbd916c0d77a737d556eee6a313 100755 --- a/sohstationviewer/view/ui/main_ui.py +++ b/sohstationviewer/view/ui/main_ui.py @@ -793,6 +793,8 @@ class UIMainWindow(object): self.stop_button.clicked.connect(main_window.stop) + self.save_plot_button.clicked.connect(main_window.save_plot) + def read_config(self): self.config = configparser.ConfigParser() config_path = Path('sohstationviewer/conf/read_settings.ini') diff --git a/sohstationviewer/view/util/functions.py b/sohstationviewer/view/util/functions.py index 2927cae8c88a35dbe38423b4448c5c615c469da9..254f32030c796164cd0399d3e7d938174df27c8d 100644 --- a/sohstationviewer/view/util/functions.py +++ b/sohstationviewer/view/util/functions.py @@ -96,6 +96,9 @@ def create_table_of_content_file(base_path: Path) -> None: "this software.\n\n" "On the left-hand side you will find a list of currently available" " help topics.\n\n" + "If the links of the Table of Contents are broken, click on Recreate " + "Table of Content <img src='recreate_table_contents.png' height=30 /> " + "to rebuild it.\n\n" "The home button can be used to return to this page at any time.\n\n" "# Table of Contents\n\n") links = ""