From f7e67b672734e9c88641a54322028e4b93d1a8ee Mon Sep 17 00:00:00 2001 From: Lan Dam <ldam@passcal.nmt.edu> Date: Fri, 25 Aug 2023 10:01:39 -0600 Subject: [PATCH] Change to use matplotlib's lim for zooming --- .../multi_threaded_plotting_widget.py | 26 -------------- .../view/plotting/plotting_widget/plotting.py | 16 ++++----- .../plotting/plotting_widget/plotting_axes.py | 30 +++++++--------- .../plotting_widget/plotting_widget.py | 35 +++++++++++-------- .../view/plotting/state_of_health_widget.py | 21 +++-------- .../view/plotting/waveform_dialog.py | 21 +++-------- 6 files changed, 51 insertions(+), 98 deletions(-) diff --git a/sohstationviewer/view/plotting/plotting_widget/multi_threaded_plotting_widget.py b/sohstationviewer/view/plotting/plotting_widget/multi_threaded_plotting_widget.py index 46fecb3a2..0e36e7ab4 100644 --- a/sohstationviewer/view/plotting/plotting_widget/multi_threaded_plotting_widget.py +++ b/sohstationviewer/view/plotting/plotting_widget/multi_threaded_plotting_widget.py @@ -334,29 +334,3 @@ class MultiThreadedPlottingWidget(PlottingWidget): f'{self.name} plot stopped', LogType.INFO) self.is_working = False self.stopped.emit() - - def set_lim(self, first_time=False, is_waveform=False): - """ - The set_lim method of the base class PlottingWidget was not designed - with multi-threading in mind, so it made some assumption that is - difficult to satisfy in a multi-threaded design. While these - assumptions do not affect the initial plotting of the data, they make - designing a system for zooming more difficult. - - Rather than trying to comply with the design of PlottingWidget.set_lim, - we decide to work around. This set_lim method still keeps the - functionality of processing the data based on the zoom range. However, - it delegates setting the new limit of the x and y axes to - PlottingWidget.set_lim. - - :param first_time: flag that indicate whether set_lim is called the - fist time for a data set. - """ - self.data_processors = [] - if not self.is_working: - self.is_working = True - start_msg = 'Zooming in...' - display_tracking_info(self.tracking_box, start_msg, LogType.INFO) - self.create_plotting_channel_processors(self.plotting_data1) - self.create_plotting_channel_processors(self.plotting_data2) - self.process_channel() diff --git a/sohstationviewer/view/plotting/plotting_widget/plotting.py b/sohstationviewer/view/plotting/plotting_widget/plotting.py index faa1f90b0..74ebf7f5d 100644 --- a/sohstationviewer/view/plotting/plotting_widget/plotting.py +++ b/sohstationviewer/view/plotting/plotting_widget/plotting.py @@ -108,7 +108,6 @@ class Plotting: total_samples = len(x) - x = sorted(x) if len(colors) != 1: sample_no_colors = [clr['W']] else: @@ -118,7 +117,7 @@ class Plotting: ax, [total_samples], sample_no_colors=sample_no_colors, chan_db_info=chan_db_info, linked_ax=linked_ax) if linked_ax is None: - ax.x = x + ax.x_list = c_data['times'] else: ax.linkedX = x ax.chan_db_info = chan_db_info @@ -174,18 +173,19 @@ class Plotting: ax.plot(points_list[1], len(points_list[1]) * [0.5], linestyle="", marker='s', markersize=2, zorder=constants.Z_ORDER['DOT'], color=clr[colors[1]], picker=True, pickradius=3) - x = points_list[0] + points_list[1] - x = sorted(x) + ax.set_ylim(-2, 2) self.plotting_axes.set_axes_info( ax, [len(points_list[0]), len(points_list[1])], sample_no_colors=[clr[colors[0]], clr[colors[1]]], sample_no_pos=[0.25, 0.75], chan_db_info=chan_db_info, linked_ax=linked_ax) - if linked_ax is None: - ax.x = x - else: - ax.linkedX = x + + # x_bottom, x_top are the times of data points to be displayed at + # bottom or top of the plot + ax.x_bottom = np.array(points_list[0]) + ax.x_top = np.array(points_list[1]) + ax.chan_db_info = chan_db_info return ax diff --git a/sohstationviewer/view/plotting/plotting_widget/plotting_axes.py b/sohstationviewer/view/plotting/plotting_widget/plotting_axes.py index 9becc2273..cbe97d144 100644 --- a/sohstationviewer/view/plotting/plotting_widget/plotting_axes.py +++ b/sohstationviewer/view/plotting/plotting_widget/plotting_axes.py @@ -184,13 +184,6 @@ class PlottingAxes: axes, label of channel will be displayed with sub title's format - under main title. """ - if linked_ax is None: - # clear all texts before recreated. - # But not clear when this is a linked_ax because texts are already - # cleared with ax, if clear with linked_ax all info of ax won't be - # displayed - ax.texts.clear() - if label is None: label = chan_db_info['label'] @@ -229,7 +222,11 @@ class PlottingAxes: # set samples' total on right side if len(sample_no_list) == 1: - ax.sampleLbl = ax.text( + # center_total_point_lbl: The label to display total number of data + # points for plots whose ax has attribute x_list. + # The plotTypes that use this label are linesDot, linesSRate, + # linesMassPos, dotForTime, multiColorDot + ax.center_total_point_lbl = ax.text( 1.005, 0.5, sample_no_list[0], horizontalalignment='left', @@ -240,14 +237,13 @@ class PlottingAxes: size=self.parent.font_size ) else: - # Each zoom this infor is created again. - # Plots that have data separated in two to have text in top and - # bottom, sample rate= 1. These numbers completely depends - # on data created in trim_downsample_chan_with_spr_less_or_equal_1 - # and won't be changed in set_lim, then don't need to assign a - # variable for it. - # bottom - ax.text( + # bottom_total_point_lbl, top_total_point_lbl are label to diplay + # total number of data points which are splitted into top + # and bottom. The ax needs to include attributes x_bottom and x_top + # The plotTypes that use these labels are upDownDots (and linesDot + # with channel='GPS Lk/Unlk' which will have another MR to add + # x_bottom and x_top for this) + ax.bottom_total_point_lbl = ax.text( 1.005, sample_no_pos[0], sample_no_list[0], horizontalalignment='left', @@ -258,7 +254,7 @@ class PlottingAxes: size=self.parent.font_size ) # top - ax.text( + ax.top_total_point_lbl = ax.text( 1.005, sample_no_pos[1], sample_no_list[1], horizontalalignment='left', diff --git a/sohstationviewer/view/plotting/plotting_widget/plotting_widget.py b/sohstationviewer/view/plotting/plotting_widget/plotting_widget.py index 9437746df..69956159b 100755 --- a/sohstationviewer/view/plotting/plotting_widget/plotting_widget.py +++ b/sohstationviewer/view/plotting/plotting_widget/plotting_widget.py @@ -2,6 +2,7 @@ Class of which object is used to plot data """ from typing import List, Optional, Union +import numpy as np import matplotlib.text from matplotlib import pyplot as pl from matplotlib.transforms import Bbox @@ -557,7 +558,7 @@ class PlottingWidget(QtWidgets.QScrollArea): self.max_x)] # reset total of samples on the right - self.gap_bar.sampleLbl.set_text(len(new_gaps)) + self.gap_bar.center_total_point_lbl.set_text(len(new_gaps)) for ax in self.axes: if hasattr(ax, 'x') and ax.x is None: @@ -568,10 +569,25 @@ class PlottingWidget(QtWidgets.QScrollArea): if not first_time: new_min_y = None new_max_y = None + if hasattr(ax, 'x_top'): + # plot_up_down_dots + new_x_bottom_indexes = np.where( + (ax.x_bottom >= self.min_x) & + (ax.x_bottom <= self.max_x))[0] + ax.bottom_total_point_lbl.set_text( + new_x_bottom_indexes.size) + new_x_top_indexes = np.where( + (ax.x_top >= self.min_x) & + (ax.x_top <= self.max_x))[0] + ax.top_total_point_lbl.set_text( + new_x_top_indexes.size) if hasattr(ax, 'x_list'): if not hasattr(ax, 'y_list'): - # dotForTime plots have attribute 'x_list' but not - # 'y_list' + # plot_time_dots and plot_multi_color_dots + x = ax.x_list[0] + new_x_indexes = np.where( + (x >= self.min_x) & (x <= self.max_x))[0] + ax.center_total_point_lbl.set_text(new_x_indexes.size) continue total_points = 0 tr_min_ys = [] @@ -590,18 +606,7 @@ class PlottingWidget(QtWidgets.QScrollArea): if tr_min_ys != []: new_min_y = min(tr_min_ys) new_max_y = max(tr_max_ys) - else: - total_points = len(ax.x) - if hasattr(ax, 'y') and len(ax.y) > 0: - new_min_y = min(ax.y) - new_max_y = max(ax.y) - try: - ax.sampleLbl.set_text(total_points) - except AttributeError: - # for case of having top and bottom total points - # which is for RT130's SOH only, trust in total point - # calculated in set_axes_info - pass + ax.center_total_point_lbl.set_text(total_points) if new_min_y is not None: self.plotting_axes.set_axes_ylim( diff --git a/sohstationviewer/view/plotting/state_of_health_widget.py b/sohstationviewer/view/plotting/state_of_health_widget.py index acb00711d..bc219840a 100644 --- a/sohstationviewer/view/plotting/state_of_health_widget.py +++ b/sohstationviewer/view/plotting/state_of_health_widget.py @@ -66,19 +66,8 @@ class SOHWidget(MultiThreadedPlottingWidget): linked_ax = None if chan_db_info['linkedChan'] not in [None, 'None', '']: linked_ax = self.plotting_data1[chan_db_info['linkedChan']]['ax'] - if 'ax' not in c_data: - ax = getattr(self.plotting, plot_functions[plot_type][1])( - c_data, chan_db_info, chan_id, None, linked_ax) - if ax is None: - return - c_data['ax'] = ax - ax.chan = chan_id - self.axes.append(ax) - else: - for artist in c_data['ax'].lines + c_data['ax'].collections: - artist.remove() - getattr(self.plotting, plot_functions[plot_type][1])( - c_data, chan_db_info, chan_id, c_data['ax'], linked_ax) - - def set_lim(self, first_time=False): - super().set_lim(first_time, is_waveform=False) + ax = getattr(self.plotting, plot_functions[plot_type][1])( + c_data, chan_db_info, chan_id, None, linked_ax) + c_data['ax'] = ax + ax.chan = chan_id + self.axes.append(ax) diff --git a/sohstationviewer/view/plotting/waveform_dialog.py b/sohstationviewer/view/plotting/waveform_dialog.py index ffcc0eac5..19ff27ad7 100755 --- a/sohstationviewer/view/plotting/waveform_dialog.py +++ b/sohstationviewer/view/plotting/waveform_dialog.py @@ -51,22 +51,11 @@ class WaveformWidget(MultiThreadedPlottingWidget): plot_type = chan_db_info['plotType'] # refer to doc string for mass_pos_data to know the reason for 'ax_wf' - if 'ax_wf' not in c_data: - ax = getattr(self.plotting, plot_functions[plot_type][1])( - c_data, chan_db_info, chan_id, None, None) - if ax is None: - return - c_data['ax_wf'] = ax - ax.chan = chan_id - self.axes.append(ax) - else: - for artist in c_data['ax_wf'].lines + c_data['ax_wf'].collections: - artist.remove() - getattr(self.plotting, plot_functions[plot_type][1])( - c_data, chan_db_info, chan_id, c_data['ax_wf'], None) - - def set_lim(self, first_time=False): - super().set_lim(first_time, is_waveform=True) + ax = getattr(self.plotting, plot_functions[plot_type][1])( + c_data, chan_db_info, chan_id, None, None) + c_data['ax_wf'] = ax + ax.chan = chan_id + self.axes.append(ax) class WaveformDialog(QtWidgets.QWidget): -- GitLab