Deprecate inheritance hierachy of plot formats in event_log_visualizer.

Instead add separate printing functions for each plot format in the base class.

Bug: webrtc:11566
Change-Id: I8adfc983b4e8a66c477de4022c2d97b6975d7e5c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/176563
Reviewed-by: Artem Titov <titovartem@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Andrey Logvin <landrey@webrtc.org>
Commit-Queue: Björn Terelius <terelius@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31496}
diff --git a/rtc_tools/BUILD.gn b/rtc_tools/BUILD.gn
index 7d7ae99..d833f0a 100644
--- a/rtc_tools/BUILD.gn
+++ b/rtc_tools/BUILD.gn
@@ -343,6 +343,7 @@
       deps = [
         ":chart_proto",
         "../api:function_view",
+        "../rtc_base:deprecation",
         "../rtc_base:ignore_wundef",
 
         # TODO(kwiberg): Remove this dependency.
diff --git a/rtc_tools/rtc_event_log_visualizer/main.cc b/rtc_tools/rtc_event_log_visualizer/main.cc
index 2aa1653..2563338 100644
--- a/rtc_tools/rtc_event_log_visualizer/main.cc
+++ b/rtc_tools/rtc_event_log_visualizer/main.cc
@@ -34,8 +34,6 @@
 #include "rtc_tools/rtc_event_log_visualizer/analyze_audio.h"
 #include "rtc_tools/rtc_event_log_visualizer/analyzer.h"
 #include "rtc_tools/rtc_event_log_visualizer/plot_base.h"
-#include "rtc_tools/rtc_event_log_visualizer/plot_protobuf.h"
-#include "rtc_tools/rtc_event_log_visualizer/plot_python.h"
 #include "system_wrappers/include/field_trial.h"
 #include "test/field_trial.h"
 #include "test/testsupport/file_utils.h"
@@ -277,13 +275,7 @@
   }
 
   webrtc::EventLogAnalyzer analyzer(parsed_log, config);
-  std::unique_ptr<webrtc::PlotCollection> collection;
-  if (absl::GetFlag(FLAGS_protobuf_output)) {
-    collection.reset(new webrtc::ProtobufPlotCollection());
-  } else {
-    collection.reset(
-        new webrtc::PythonPlotCollection(absl::GetFlag(FLAGS_shared_xaxis)));
-  }
+  webrtc::PlotCollection collection;
 
   PlotMap plots;
   plots.RegisterPlot("incoming_packet_sizes", [&](Plot* plot) {
@@ -601,7 +593,7 @@
 
   for (const auto& plot : plots) {
     if (plot.enabled) {
-      Plot* output = collection->AppendNewPlot();
+      Plot* output = collection.AppendNewPlot();
       plot.plot_func(output);
       output->SetId(plot.label);
     }
@@ -621,11 +613,17 @@
          it != neteq_stats->cend(); ++it) {
       webrtc::CreateAudioJitterBufferGraph(parsed_log, config, it->first,
                                            it->second.get(),
-                                           collection->AppendNewPlot());
+                                           collection.AppendNewPlot());
     }
   }
 
-  collection->Draw();
+  if (absl::GetFlag(FLAGS_protobuf_output)) {
+    webrtc::analytics::ChartCollection proto_charts;
+    collection.ExportProtobuf(&proto_charts);
+    std::cout << proto_charts.SerializeAsString();
+  } else {
+    collection.PrintPythonCode(absl::GetFlag(FLAGS_shared_xaxis));
+  }
 
   if (absl::GetFlag(FLAGS_print_triage_alerts)) {
     webrtc::TriageHelper triage_alerts(config);
diff --git a/rtc_tools/rtc_event_log_visualizer/plot_base.cc b/rtc_tools/rtc_event_log_visualizer/plot_base.cc
index dfcd26f..dce601a 100644
--- a/rtc_tools/rtc_event_log_visualizer/plot_base.cc
+++ b/rtc_tools/rtc_event_log_visualizer/plot_base.cc
@@ -11,6 +11,7 @@
 #include "rtc_tools/rtc_event_log_visualizer/plot_base.h"
 
 #include <algorithm>
+#include <memory>
 
 #include "rtc_base/checks.h"
 
@@ -93,4 +94,232 @@
   }
 }
 
+void Plot::PrintPythonCode() const {
+  // Write python commands to stdout. Intended program usage is
+  // ./event_log_visualizer event_log160330.dump | python
+
+  if (!series_list_.empty()) {
+    printf("color_count = %zu\n", series_list_.size());
+    printf(
+        "hls_colors = [(i*1.0/color_count, 0.25+i*0.5/color_count, 0.8) for i "
+        "in range(color_count)]\n");
+    printf("colors = [colorsys.hls_to_rgb(*hls) for hls in hls_colors]\n");
+
+    for (size_t i = 0; i < series_list_.size(); i++) {
+      printf("\n# === Series: %s ===\n", series_list_[i].label.c_str());
+      // List x coordinates
+      printf("x%zu = [", i);
+      if (!series_list_[i].points.empty())
+        printf("%.3f", series_list_[i].points[0].x);
+      for (size_t j = 1; j < series_list_[i].points.size(); j++)
+        printf(", %.3f", series_list_[i].points[j].x);
+      printf("]\n");
+
+      // List y coordinates
+      printf("y%zu = [", i);
+      if (!series_list_[i].points.empty())
+        printf("%G", series_list_[i].points[0].y);
+      for (size_t j = 1; j < series_list_[i].points.size(); j++)
+        printf(", %G", series_list_[i].points[j].y);
+      printf("]\n");
+
+      if (series_list_[i].line_style == LineStyle::kBar) {
+        // There is a plt.bar function that draws bar plots,
+        // but it is *way* too slow to be useful.
+        printf(
+            "plt.vlines(x%zu, map(lambda t: min(t,0), y%zu), map(lambda t: "
+            "max(t,0), y%zu), color=colors[%zu], "
+            "label=\'%s\')\n",
+            i, i, i, i, series_list_[i].label.c_str());
+        if (series_list_[i].point_style == PointStyle::kHighlight) {
+          printf(
+              "plt.plot(x%zu, y%zu, color=colors[%zu], "
+              "marker='.', ls=' ')\n",
+              i, i, i);
+        }
+      } else if (series_list_[i].line_style == LineStyle::kLine) {
+        if (series_list_[i].point_style == PointStyle::kHighlight) {
+          printf(
+              "plt.plot(x%zu, y%zu, color=colors[%zu], label=\'%s\', "
+              "marker='.')\n",
+              i, i, i, series_list_[i].label.c_str());
+        } else {
+          printf("plt.plot(x%zu, y%zu, color=colors[%zu], label=\'%s\')\n", i,
+                 i, i, series_list_[i].label.c_str());
+        }
+      } else if (series_list_[i].line_style == LineStyle::kStep) {
+        // Draw lines from (x[0],y[0]) to (x[1],y[0]) to (x[1],y[1]) and so on
+        // to illustrate the "steps". This can be expressed by duplicating all
+        // elements except the first in x and the last in y.
+        printf("xd%zu = [dup for v in x%zu for dup in [v, v]]\n", i, i);
+        printf("yd%zu = [dup for v in y%zu for dup in [v, v]]\n", i, i);
+        printf(
+            "plt.plot(xd%zu[1:], yd%zu[:-1], color=colors[%zu], "
+            "label=\'%s\')\n",
+            i, i, i, series_list_[i].label.c_str());
+        if (series_list_[i].point_style == PointStyle::kHighlight) {
+          printf(
+              "plt.plot(x%zu, y%zu, color=colors[%zu], "
+              "marker='.', ls=' ')\n",
+              i, i, i);
+        }
+      } else if (series_list_[i].line_style == LineStyle::kNone) {
+        printf(
+            "plt.plot(x%zu, y%zu, color=colors[%zu], label=\'%s\', "
+            "marker='o', ls=' ')\n",
+            i, i, i, series_list_[i].label.c_str());
+      } else {
+        printf("raise Exception(\"Unknown graph type\")\n");
+      }
+    }
+
+    // IntervalSeries
+    printf("interval_colors = ['#ff8e82','#5092fc','#c4ffc4','#aaaaaa']\n");
+    RTC_CHECK_LE(interval_list_.size(), 4);
+    // To get the intervals to show up in the legend we have to create patches
+    // for them.
+    printf("legend_patches = []\n");
+    for (size_t i = 0; i < interval_list_.size(); i++) {
+      // List intervals
+      printf("\n# === IntervalSeries: %s ===\n",
+             interval_list_[i].label.c_str());
+      printf("ival%zu = [", i);
+      if (!interval_list_[i].intervals.empty()) {
+        printf("(%G, %G)", interval_list_[i].intervals[0].begin,
+               interval_list_[i].intervals[0].end);
+      }
+      for (size_t j = 1; j < interval_list_[i].intervals.size(); j++) {
+        printf(", (%G, %G)", interval_list_[i].intervals[j].begin,
+               interval_list_[i].intervals[j].end);
+      }
+      printf("]\n");
+
+      printf("for i in range(0, %zu):\n", interval_list_[i].intervals.size());
+      if (interval_list_[i].orientation == IntervalSeries::kVertical) {
+        printf(
+            "  plt.axhspan(ival%zu[i][0], ival%zu[i][1], "
+            "facecolor=interval_colors[%zu], "
+            "alpha=0.3)\n",
+            i, i, i);
+      } else {
+        printf(
+            "  plt.axvspan(ival%zu[i][0], ival%zu[i][1], "
+            "facecolor=interval_colors[%zu], "
+            "alpha=0.3)\n",
+            i, i, i);
+      }
+      printf(
+          "legend_patches.append(mpatches.Patch(ec=\'black\', "
+          "fc=interval_colors[%zu], label='%s'))\n",
+          i, interval_list_[i].label.c_str());
+    }
+  }
+
+  printf("plt.xlim(%f, %f)\n", xaxis_min_, xaxis_max_);
+  printf("plt.ylim(%f, %f)\n", yaxis_min_, yaxis_max_);
+  printf("plt.xlabel(\'%s\')\n", xaxis_label_.c_str());
+  printf("plt.ylabel(\'%s\')\n", yaxis_label_.c_str());
+  printf("plt.title(\'%s\')\n", title_.c_str());
+  printf("fig = plt.gcf()\n");
+  printf("fig.canvas.set_window_title(\'%s\')\n", id_.c_str());
+  if (!yaxis_tick_labels_.empty()) {
+    printf("yaxis_tick_labels = [");
+    for (const auto& kv : yaxis_tick_labels_) {
+      printf("(%f,\"%s\"),", kv.first, kv.second.c_str());
+    }
+    printf("]\n");
+    printf("yaxis_tick_labels = list(zip(*yaxis_tick_labels))\n");
+    printf("plt.yticks(*yaxis_tick_labels)\n");
+  }
+  if (!series_list_.empty() || !interval_list_.empty()) {
+    printf("handles, labels = plt.gca().get_legend_handles_labels()\n");
+    printf("for lp in legend_patches:\n");
+    printf("   handles.append(lp)\n");
+    printf("   labels.append(lp.get_label())\n");
+    printf("plt.legend(handles, labels, loc=\'best\', fontsize=\'small\')\n");
+  }
+}
+
+void Plot::ExportProtobuf(webrtc::analytics::Chart* chart) const {
+  for (size_t i = 0; i < series_list_.size(); i++) {
+    webrtc::analytics::DataSet* data_set = chart->add_data_sets();
+    for (const auto& point : series_list_[i].points) {
+      data_set->add_x_values(point.x);
+    }
+    for (const auto& point : series_list_[i].points) {
+      data_set->add_y_values(point.y);
+    }
+
+    if (series_list_[i].line_style == LineStyle::kBar) {
+      data_set->set_style(webrtc::analytics::ChartStyle::BAR_CHART);
+    } else if (series_list_[i].line_style == LineStyle::kLine) {
+      data_set->set_style(webrtc::analytics::ChartStyle::LINE_CHART);
+    } else if (series_list_[i].line_style == LineStyle::kStep) {
+      data_set->set_style(webrtc::analytics::ChartStyle::LINE_STEP_CHART);
+    } else if (series_list_[i].line_style == LineStyle::kNone) {
+      data_set->set_style(webrtc::analytics::ChartStyle::SCATTER_CHART);
+    } else {
+      data_set->set_style(webrtc::analytics::ChartStyle::UNDEFINED);
+    }
+
+    if (series_list_[i].point_style == PointStyle::kHighlight)
+      data_set->set_highlight_points(true);
+
+    data_set->set_label(series_list_[i].label);
+  }
+
+  chart->set_xaxis_min(xaxis_min_);
+  chart->set_xaxis_max(xaxis_max_);
+  chart->set_yaxis_min(yaxis_min_);
+  chart->set_yaxis_max(yaxis_max_);
+  chart->set_xaxis_label(xaxis_label_);
+  chart->set_yaxis_label(yaxis_label_);
+  chart->set_title(title_);
+  chart->set_id(id_);
+
+  for (const auto& kv : yaxis_tick_labels_) {
+    webrtc::analytics::TickLabel* tick = chart->add_yaxis_tick_labels();
+    tick->set_value(kv.first);
+    tick->set_label(kv.second);
+  }
+}
+
+void PlotCollection::PrintPythonCode(bool shared_xaxis) const {
+  printf("import matplotlib.pyplot as plt\n");
+  printf("plt.rcParams.update({'figure.max_open_warning': 0})\n");
+  printf("import matplotlib.patches as mpatches\n");
+  printf("import matplotlib.patheffects as pe\n");
+  printf("import colorsys\n");
+  for (size_t i = 0; i < plots_.size(); i++) {
+    printf("plt.figure(%zu)\n", i);
+    if (shared_xaxis) {
+      // Link x-axes across all figures for synchronized zooming.
+      if (i == 0) {
+        printf("axis0 = plt.subplot(111)\n");
+      } else {
+        printf("plt.subplot(111, sharex=axis0)\n");
+      }
+    }
+    plots_[i]->PrintPythonCode();
+  }
+  printf("plt.show()\n");
+}
+
+void PlotCollection::ExportProtobuf(
+    webrtc::analytics::ChartCollection* collection) const {
+  for (const auto& plot : plots_) {
+    // TODO(terelius): Ensure that there is no way to insert plots other than
+    // ProtobufPlots in a ProtobufPlotCollection. Needed to safely static_cast
+    // here.
+    webrtc::analytics::Chart* protobuf_representation =
+        collection->add_charts();
+    plot->ExportProtobuf(protobuf_representation);
+  }
+}
+
+Plot* PlotCollection::AppendNewPlot() {
+  plots_.push_back(std::make_unique<Plot>());
+  return plots_.back().get();
+}
+
 }  // namespace webrtc
diff --git a/rtc_tools/rtc_event_log_visualizer/plot_base.h b/rtc_tools/rtc_event_log_visualizer/plot_base.h
index 5e4ebfa..06a206f 100644
--- a/rtc_tools/rtc_event_log_visualizer/plot_base.h
+++ b/rtc_tools/rtc_event_log_visualizer/plot_base.h
@@ -15,6 +15,13 @@
 #include <utility>
 #include <vector>
 
+#include "rtc_base/deprecation.h"
+#include "rtc_base/ignore_wundef.h"
+
+RTC_PUSH_IGNORING_WUNDEF()
+#include "rtc_tools/rtc_event_log_visualizer/proto/chart.pb.h"
+RTC_POP_IGNORING_WUNDEF()
+
 namespace webrtc {
 
 enum class LineStyle {
@@ -94,8 +101,8 @@
  public:
   virtual ~Plot() {}
 
-  // Overloaded to draw the plot.
-  virtual void Draw() = 0;
+  // Deprecated. Use PrintPythonCode() or ExportProtobuf() instead.
+  RTC_DEPRECATED virtual void Draw() {}
 
   // Sets the lower x-axis limit to min_value (if left_margin == 0).
   // Sets the upper x-axis limit to max_value (if right_margin == 0).
@@ -158,6 +165,12 @@
   // Otherwise, the call has no effect and the timeseries is destroyed.
   void AppendTimeSeriesIfNotEmpty(TimeSeries&& time_series);
 
+  // Replaces PythonPlot::Draw()
+  void PrintPythonCode() const;
+
+  // Replaces ProtobufPlot::Draw()
+  void ExportProtobuf(webrtc::analytics::Chart* chart) const;
+
  protected:
   float xaxis_min_;
   float xaxis_max_;
@@ -175,8 +188,17 @@
 class PlotCollection {
  public:
   virtual ~PlotCollection() {}
-  virtual void Draw() = 0;
-  virtual Plot* AppendNewPlot() = 0;
+
+  // Deprecated. Use PrintPythonCode() or ExportProtobuf() instead.
+  RTC_DEPRECATED virtual void Draw() {}
+
+  virtual Plot* AppendNewPlot();
+
+  // Replaces PythonPlotCollection::Draw()
+  void PrintPythonCode(bool shared_xaxis) const;
+
+  // Replaces ProtobufPlotCollections::Draw()
+  void ExportProtobuf(webrtc::analytics::ChartCollection* collection) const;
 
  protected:
   std::vector<std::unique_ptr<Plot>> plots_;
diff --git a/rtc_tools/rtc_event_log_visualizer/plot_protobuf.cc b/rtc_tools/rtc_event_log_visualizer/plot_protobuf.cc
index 9e82c01..0f43191 100644
--- a/rtc_tools/rtc_event_log_visualizer/plot_protobuf.cc
+++ b/rtc_tools/rtc_event_log_visualizer/plot_protobuf.cc
@@ -24,49 +24,7 @@
 
 void ProtobufPlot::Draw() {}
 
-void ProtobufPlot::ExportProtobuf(webrtc::analytics::Chart* chart) {
-  for (size_t i = 0; i < series_list_.size(); i++) {
-    webrtc::analytics::DataSet* data_set = chart->add_data_sets();
-    for (const auto& point : series_list_[i].points) {
-      data_set->add_x_values(point.x);
-    }
-    for (const auto& point : series_list_[i].points) {
-      data_set->add_y_values(point.y);
-    }
 
-    if (series_list_[i].line_style == LineStyle::kBar) {
-      data_set->set_style(webrtc::analytics::ChartStyle::BAR_CHART);
-    } else if (series_list_[i].line_style == LineStyle::kLine) {
-      data_set->set_style(webrtc::analytics::ChartStyle::LINE_CHART);
-    } else if (series_list_[i].line_style == LineStyle::kStep) {
-      data_set->set_style(webrtc::analytics::ChartStyle::LINE_STEP_CHART);
-    } else if (series_list_[i].line_style == LineStyle::kNone) {
-      data_set->set_style(webrtc::analytics::ChartStyle::SCATTER_CHART);
-    } else {
-      data_set->set_style(webrtc::analytics::ChartStyle::UNDEFINED);
-    }
-
-    if (series_list_[i].point_style == PointStyle::kHighlight)
-      data_set->set_highlight_points(true);
-
-    data_set->set_label(series_list_[i].label);
-  }
-
-  chart->set_xaxis_min(xaxis_min_);
-  chart->set_xaxis_max(xaxis_max_);
-  chart->set_yaxis_min(yaxis_min_);
-  chart->set_yaxis_max(yaxis_max_);
-  chart->set_xaxis_label(xaxis_label_);
-  chart->set_yaxis_label(yaxis_label_);
-  chart->set_title(title_);
-  chart->set_id(id_);
-
-  for (const auto& kv : yaxis_tick_labels_) {
-    webrtc::analytics::TickLabel* tick = chart->add_yaxis_tick_labels();
-    tick->set_value(kv.first);
-    tick->set_label(kv.second);
-  }
-}
 
 ProtobufPlotCollection::ProtobufPlotCollection() {}
 
@@ -78,19 +36,6 @@
   std::cout << collection.SerializeAsString();
 }
 
-void ProtobufPlotCollection::ExportProtobuf(
-    webrtc::analytics::ChartCollection* collection) {
-  for (const auto& plot : plots_) {
-    // TODO(terelius): Ensure that there is no way to insert plots other than
-    // ProtobufPlots in a ProtobufPlotCollection. Needed to safely static_cast
-    // here.
-    webrtc::analytics::Chart* protobuf_representation =
-        collection->add_charts();
-    static_cast<ProtobufPlot*>(plot.get())
-        ->ExportProtobuf(protobuf_representation);
-  }
-}
-
 Plot* ProtobufPlotCollection::AppendNewPlot() {
   Plot* plot = new ProtobufPlot();
   plots_.push_back(std::unique_ptr<Plot>(plot));
diff --git a/rtc_tools/rtc_event_log_visualizer/plot_protobuf.h b/rtc_tools/rtc_event_log_visualizer/plot_protobuf.h
index 738247a..0773b58 100644
--- a/rtc_tools/rtc_event_log_visualizer/plot_protobuf.h
+++ b/rtc_tools/rtc_event_log_visualizer/plot_protobuf.h
@@ -23,16 +23,15 @@
   ProtobufPlot();
   ~ProtobufPlot() override;
   void Draw() override;
-  void ExportProtobuf(webrtc::analytics::Chart* chart);
 };
 
 class ProtobufPlotCollection final : public PlotCollection {
  public:
-  ProtobufPlotCollection();
+  // This class is deprecated. Use PlotCollection and ExportProtobuf() instead.
+  RTC_DEPRECATED ProtobufPlotCollection();
   ~ProtobufPlotCollection() override;
   void Draw() override;
   Plot* AppendNewPlot() override;
-  void ExportProtobuf(webrtc::analytics::ChartCollection* collection);
 };
 
 }  // namespace webrtc
diff --git a/rtc_tools/rtc_event_log_visualizer/plot_python.cc b/rtc_tools/rtc_event_log_visualizer/plot_python.cc
index e7cde45..b370811 100644
--- a/rtc_tools/rtc_event_log_visualizer/plot_python.cc
+++ b/rtc_tools/rtc_event_log_visualizer/plot_python.cc
@@ -25,149 +25,7 @@
 PythonPlot::~PythonPlot() {}
 
 void PythonPlot::Draw() {
-  // Write python commands to stdout. Intended program usage is
-  // ./event_log_visualizer event_log160330.dump | python
-
-  if (!series_list_.empty()) {
-    printf("color_count = %zu\n", series_list_.size());
-    printf(
-        "hls_colors = [(i*1.0/color_count, 0.25+i*0.5/color_count, 0.8) for i "
-        "in range(color_count)]\n");
-    printf("colors = [colorsys.hls_to_rgb(*hls) for hls in hls_colors]\n");
-
-    for (size_t i = 0; i < series_list_.size(); i++) {
-      printf("\n# === Series: %s ===\n", series_list_[i].label.c_str());
-      // List x coordinates
-      printf("x%zu = [", i);
-      if (!series_list_[i].points.empty())
-        printf("%.3f", series_list_[i].points[0].x);
-      for (size_t j = 1; j < series_list_[i].points.size(); j++)
-        printf(", %.3f", series_list_[i].points[j].x);
-      printf("]\n");
-
-      // List y coordinates
-      printf("y%zu = [", i);
-      if (!series_list_[i].points.empty())
-        printf("%G", series_list_[i].points[0].y);
-      for (size_t j = 1; j < series_list_[i].points.size(); j++)
-        printf(", %G", series_list_[i].points[j].y);
-      printf("]\n");
-
-      if (series_list_[i].line_style == LineStyle::kBar) {
-        // There is a plt.bar function that draws bar plots,
-        // but it is *way* too slow to be useful.
-        printf(
-            "plt.vlines(x%zu, map(lambda t: min(t,0), y%zu), map(lambda t: "
-            "max(t,0), y%zu), color=colors[%zu], "
-            "label=\'%s\')\n",
-            i, i, i, i, series_list_[i].label.c_str());
-        if (series_list_[i].point_style == PointStyle::kHighlight) {
-          printf(
-              "plt.plot(x%zu, y%zu, color=colors[%zu], "
-              "marker='.', ls=' ')\n",
-              i, i, i);
-        }
-      } else if (series_list_[i].line_style == LineStyle::kLine) {
-        if (series_list_[i].point_style == PointStyle::kHighlight) {
-          printf(
-              "plt.plot(x%zu, y%zu, color=colors[%zu], label=\'%s\', "
-              "marker='.')\n",
-              i, i, i, series_list_[i].label.c_str());
-        } else {
-          printf("plt.plot(x%zu, y%zu, color=colors[%zu], label=\'%s\')\n", i,
-                 i, i, series_list_[i].label.c_str());
-        }
-      } else if (series_list_[i].line_style == LineStyle::kStep) {
-        // Draw lines from (x[0],y[0]) to (x[1],y[0]) to (x[1],y[1]) and so on
-        // to illustrate the "steps". This can be expressed by duplicating all
-        // elements except the first in x and the last in y.
-        printf("xd%zu = [dup for v in x%zu for dup in [v, v]]\n", i, i);
-        printf("yd%zu = [dup for v in y%zu for dup in [v, v]]\n", i, i);
-        printf(
-            "plt.plot(xd%zu[1:], yd%zu[:-1], color=colors[%zu], "
-            "label=\'%s\')\n",
-            i, i, i, series_list_[i].label.c_str());
-        if (series_list_[i].point_style == PointStyle::kHighlight) {
-          printf(
-              "plt.plot(x%zu, y%zu, color=colors[%zu], "
-              "marker='.', ls=' ')\n",
-              i, i, i);
-        }
-      } else if (series_list_[i].line_style == LineStyle::kNone) {
-        printf(
-            "plt.plot(x%zu, y%zu, color=colors[%zu], label=\'%s\', "
-            "marker='o', ls=' ')\n",
-            i, i, i, series_list_[i].label.c_str());
-      } else {
-        printf("raise Exception(\"Unknown graph type\")\n");
-      }
-    }
-
-    // IntervalSeries
-    printf("interval_colors = ['#ff8e82','#5092fc','#c4ffc4','#aaaaaa']\n");
-    RTC_CHECK_LE(interval_list_.size(), 4);
-    // To get the intervals to show up in the legend we have to create patches
-    // for them.
-    printf("legend_patches = []\n");
-    for (size_t i = 0; i < interval_list_.size(); i++) {
-      // List intervals
-      printf("\n# === IntervalSeries: %s ===\n",
-             interval_list_[i].label.c_str());
-      printf("ival%zu = [", i);
-      if (!interval_list_[i].intervals.empty()) {
-        printf("(%G, %G)", interval_list_[i].intervals[0].begin,
-               interval_list_[i].intervals[0].end);
-      }
-      for (size_t j = 1; j < interval_list_[i].intervals.size(); j++) {
-        printf(", (%G, %G)", interval_list_[i].intervals[j].begin,
-               interval_list_[i].intervals[j].end);
-      }
-      printf("]\n");
-
-      printf("for i in range(0, %zu):\n", interval_list_[i].intervals.size());
-      if (interval_list_[i].orientation == IntervalSeries::kVertical) {
-        printf(
-            "  plt.axhspan(ival%zu[i][0], ival%zu[i][1], "
-            "facecolor=interval_colors[%zu], "
-            "alpha=0.3)\n",
-            i, i, i);
-      } else {
-        printf(
-            "  plt.axvspan(ival%zu[i][0], ival%zu[i][1], "
-            "facecolor=interval_colors[%zu], "
-            "alpha=0.3)\n",
-            i, i, i);
-      }
-      printf(
-          "legend_patches.append(mpatches.Patch(ec=\'black\', "
-          "fc=interval_colors[%zu], label='%s'))\n",
-          i, interval_list_[i].label.c_str());
-    }
-  }
-
-  printf("plt.xlim(%f, %f)\n", xaxis_min_, xaxis_max_);
-  printf("plt.ylim(%f, %f)\n", yaxis_min_, yaxis_max_);
-  printf("plt.xlabel(\'%s\')\n", xaxis_label_.c_str());
-  printf("plt.ylabel(\'%s\')\n", yaxis_label_.c_str());
-  printf("plt.title(\'%s\')\n", title_.c_str());
-  printf("fig = plt.gcf()\n");
-  printf("fig.canvas.set_window_title(\'%s\')\n", id_.c_str());
-  if (!yaxis_tick_labels_.empty()) {
-    printf("yaxis_tick_labels = [");
-    for (const auto& kv : yaxis_tick_labels_) {
-      printf("(%f,\"%s\"),", kv.first, kv.second.c_str());
-    }
-    printf("]\n");
-    printf("yaxis_tick_labels = list(zip(*yaxis_tick_labels))\n");
-    printf("plt.yticks(*yaxis_tick_labels)\n");
-  }
-  if (!series_list_.empty() || !interval_list_.empty()) {
-    printf("handles, labels = plt.gca().get_legend_handles_labels()\n");
-    printf("for lp in legend_patches:\n");
-    printf("   handles.append(lp)\n");
-    printf("   labels.append(lp.get_label())\n");
-    printf("plt.legend(handles, labels, loc=\'best\', fontsize=\'small\')\n");
-  }
+  PrintPythonCode();
 }
 
 PythonPlotCollection::PythonPlotCollection(bool shared_xaxis)
@@ -176,24 +34,7 @@
 PythonPlotCollection::~PythonPlotCollection() {}
 
 void PythonPlotCollection::Draw() {
-  printf("import matplotlib.pyplot as plt\n");
-  printf("plt.rcParams.update({'figure.max_open_warning': 0})\n");
-  printf("import matplotlib.patches as mpatches\n");
-  printf("import matplotlib.patheffects as pe\n");
-  printf("import colorsys\n");
-  for (size_t i = 0; i < plots_.size(); i++) {
-    printf("plt.figure(%zu)\n", i);
-    if (shared_xaxis_) {
-      // Link x-axes across all figures for synchronized zooming.
-      if (i == 0) {
-        printf("axis0 = plt.subplot(111)\n");
-      } else {
-        printf("plt.subplot(111, sharex=axis0)\n");
-      }
-    }
-    plots_[i]->Draw();
-  }
-  printf("plt.show()\n");
+  PrintPythonCode(shared_xaxis_);
 }
 
 Plot* PythonPlotCollection::AppendNewPlot() {
diff --git a/rtc_tools/rtc_event_log_visualizer/plot_python.h b/rtc_tools/rtc_event_log_visualizer/plot_python.h
index dcdcf23..998ed7b 100644
--- a/rtc_tools/rtc_event_log_visualizer/plot_python.h
+++ b/rtc_tools/rtc_event_log_visualizer/plot_python.h
@@ -23,7 +23,8 @@
 
 class PythonPlotCollection final : public PlotCollection {
  public:
-  explicit PythonPlotCollection(bool shared_xaxis = false);
+  // This class is deprecated. Use PlotCollection and PrintPythonCode() instead.
+  RTC_DEPRECATED explicit PythonPlotCollection(bool shared_xaxis = false);
   ~PythonPlotCollection() override;
   void Draw() override;
   Plot* AppendNewPlot() override;