Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • software_public/passoft/sohstationviewer
1 result
Show changes
......@@ -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:
"""
......@@ -720,6 +732,10 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow):
return
self.clear_plots()
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
......@@ -842,6 +858,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()
......@@ -1060,10 +1077,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):
......
......@@ -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)
......@@ -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 = []
......
......@@ -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')
......@@ -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
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_())
......@@ -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')
......
......@@ -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 = ""
......