Free cookie consent management tool by TermsFeed Policy Generator

source: branches/DataPreprocessing Enhancements/HeuristicLab.DataPreprocessing.Views/3.4/ScatterPlotMultiView.cs @ 14580

Last change on this file since 14580 was 14580, checked in by mkommend, 7 years ago

#2709: Changed initialization of caches to avoid NullReferenceExceptions.

File size: 19.8 KB
RevLine 
[10882]1using System;
2using System.Collections.Generic;
3using System.Drawing;
4using System.Linq;
5using System.Windows.Forms;
6using HeuristicLab.Analysis;
[14446]7using HeuristicLab.Analysis.Views;
[14381]8using HeuristicLab.Collections;
9using HeuristicLab.Data;
[10882]10using HeuristicLab.MainForm;
[14381]11using HeuristicLab.MainForm.WindowsForms;
[14514]12using RegressionType = HeuristicLab.Analysis.ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType;
[10882]13
14namespace HeuristicLab.DataPreprocessing.Views {
[10987]15  [View("Scatter Plot Multi View")]
[14462]16  [Content(typeof(MultiScatterPlotContent), true)]
[14381]17  public partial class ScatterPlotMultiView : PreprocessingCheckedVariablesView {
[14580]18    private readonly IDictionary<string, Label> columnHeaderCache = new Dictionary<string, Label>();
19    private readonly IDictionary<string, VerticalLabel> rowHeaderCache = new Dictionary<string, VerticalLabel>();
20    private readonly IDictionary<Tuple<string/*col*/, string/*row*/>, Control> bodyCache = new Dictionary<Tuple<string, string>, Control>();
[14384]21
[10987]22    public ScatterPlotMultiView() {
[10882]23      InitializeComponent();
[14381]24
[14514]25      regressionTypeComboBox.DataSource = Enum.GetValues(typeof(RegressionType));
26      regressionTypeComboBox.SelectedItem = RegressionType.None;
27
[14388]28      #region Initialize Scrollbars
[14384]29      columnHeaderScrollPanel.HorizontalScroll.Enabled = true;
30      columnHeaderScrollPanel.VerticalScroll.Enabled = false;
31      columnHeaderScrollPanel.HorizontalScroll.Visible = false;
32      columnHeaderScrollPanel.VerticalScroll.Visible = false;
[14381]33
[14384]34      rowHeaderScrollPanel.HorizontalScroll.Enabled = false;
35      rowHeaderScrollPanel.VerticalScroll.Enabled = true;
36      rowHeaderScrollPanel.HorizontalScroll.Visible = false;
37      rowHeaderScrollPanel.VerticalScroll.Visible = false;
[14381]38
[14384]39      bodyScrollPanel.HorizontalScroll.Enabled = true;
40      bodyScrollPanel.VerticalScroll.Enabled = true;
41      bodyScrollPanel.HorizontalScroll.Visible = true;
42      bodyScrollPanel.VerticalScroll.Visible = true;
43      bodyScrollPanel.AutoScroll = true;
[14388]44      #endregion
[14381]45
[14384]46      bodyScrollPanel.MouseWheel += bodyScrollPanel_MouseWheel;
[10882]47    }
48
[14462]49    public new MultiScatterPlotContent Content {
50      get { return (MultiScatterPlotContent)base.Content; }
[10882]51      set { base.Content = value; }
52    }
53
54    protected override void OnContentChanged() {
55      base.OnContentChanged();
56      if (Content != null) {
[14381]57        GenerateCharts();
[10882]58      }
59    }
60
[14514]61    protected override void SetEnabledStateOfControls() {
62      base.SetEnabledStateOfControls();
63      var regressionType = (RegressionType)regressionTypeComboBox.SelectedValue;
64      polynomialRegressionOrderNumericUpDown.Enabled = regressionType == RegressionType.Polynomial;
65    }
66
[14381]67    protected override void CheckedItemsChanged(object sender, CollectionItemsChangedEventArgs<IndexedItem<StringValue>> checkedItems) {
68      base.CheckedItemsChanged(sender, checkedItems);
[14511]69      if (suppressCheckedChangedUpdate)
70        return;
[14384]71      foreach (var variable in checkedItems.Items.Select(i => i.Value.Value)) {
72        if (IsVariableChecked(variable))
73          AddChartToTable(variable);
74        else
75          RemoveChartFromTable(variable);
76      }
77    }
[10952]78
[14388]79    #region Add and remove charts
[14384]80    private void AddChartToTable(string variable) {
[14388]81      frameTableLayoutPanel.SuspendLayout();
[14384]82
[14388]83      // find index to insert
[14511]84      var variables = Content.VariableItemList.Select(v => v.Value).ToList();
[14388]85      int idx = variables              // all variables
86        .TakeWhile(t => t != variable) // ... until the variable that was checked
87        .Count(IsVariableChecked);     // ... how many checked variables
88
89      // add column header
90      var colH = columnHeaderTableLayoutPanel;
91      AddColumnHelper(colH, idx, _ => GetColumnHeader(variable));
92
93      // add row header
94      var rowH = rowHeaderTableLayoutPanel;
95      AddRowHelper(rowH, idx, _ => GetRowHeader(variable));
96
97      // add body
98      var body = bodyTableLayoutPanel;
99      var vars = GetCheckedVariables();
100      var varsMinus = vars.Except(new[] { variable }).ToList();
101      AddColumnHelper(body, idx, r => GetBody(variable, varsMinus[r])); // exclude "variable" because the row for it does not exist yet
102      AddRowHelper(body, idx, c => GetBody(vars[c], variable));
103
104      frameTableLayoutPanel.ResumeLayout(true);
[14381]105    }
[14388]106    private void AddColumnHelper(TableLayoutPanel tlp, int idx, Func<int, Control> creatorFunc) {
107      // add column
108      tlp.ColumnCount++;
109      tlp.ColumnStyles.Insert(idx, new ColumnStyle(SizeType.Absolute, GetColumnWidth()));
110      // shift right
[14580]111      for (int c = tlp.ColumnCount; c > idx - 1; c--) {
[14388]112        for (int r = 0; r < tlp.RowCount; r++) {
113          var control = tlp.GetControlFromPosition(c, r);
114          if (control != null) {
115            tlp.SetColumn(control, c + 1);
116          }
117        }
118      }
119      // add controls
120      for (int r = 0; r < tlp.RowCount; r++) {
121        if (tlp.GetControlFromPosition(idx, r) == null)
122          tlp.Controls.Add(creatorFunc(r), idx, r);
123      }
124
125    }
126    private void AddRowHelper(TableLayoutPanel tlp, int idx, Func<int, Control> creatorFunc) {
127      // add row
128      tlp.RowCount++;
129      tlp.RowStyles.Insert(idx, new RowStyle(SizeType.Absolute, GetRowHeight()));
130      // shift right
131      for (int r = tlp.RowCount; r > idx - 1; r--) {
132        for (int c = 0; c < tlp.ColumnCount; c++) {
133          var control = tlp.GetControlFromPosition(c, r);
134          if (control != null) {
135            tlp.SetRow(control, r + 1);
136          }
137        }
138      }
139      // add controls
140      for (int c = 0; c < tlp.ColumnCount; c++)
141        if (tlp.GetControlFromPosition(c, idx) == null)
142          tlp.Controls.Add(creatorFunc(c), c, idx);
143    }
144
[14384]145    private void RemoveChartFromTable(string variable) {
146      frameTableLayoutPanel.SuspendLayout();
[10987]147
[14384]148      // remove column header
149      var colH = columnHeaderTableLayoutPanel;
150      int colIdx = colH.GetColumn(colH.Controls[variable]);
151      RemoveColumnHelper(colH, colIdx);
152
153      // remove row header
154      var rowH = rowHeaderTableLayoutPanel;
155      int rowIdx = rowH.GetRow(rowH.Controls[variable]);
156      RemoveRowHelper(rowH, rowIdx);
157
158      // remove from body
159      var body = bodyTableLayoutPanel;
160      RemoveColumnHelper(body, colIdx);
161      RemoveRowHelper(body, rowIdx);
162
163      frameTableLayoutPanel.ResumeLayout(true);
164    }
165    private void RemoveColumnHelper(TableLayoutPanel tlp, int idx) {
[14388]166      // remove controls
[14384]167      for (int r = 0; r < tlp.RowCount; r++)
168        tlp.Controls.Remove(tlp.GetControlFromPosition(idx, r));
[14388]169      // shift left
[14384]170      for (int c = idx + 1; c < tlp.ColumnCount; c++) {
171        for (int r = 0; r < tlp.RowCount; r++) {
172          var control = tlp.GetControlFromPosition(c, r);
173          if (control != null) {
174            tlp.SetColumn(control, c - 1);
175          }
176        }
177      }
[14388]178      // delete column
[14384]179      tlp.ColumnStyles.RemoveAt(tlp.ColumnCount - 1);
180      tlp.ColumnCount--;
181    }
182    private void RemoveRowHelper(TableLayoutPanel tlp, int idx) {
[14388]183      // remove controls
[14384]184      for (int c = 0; c < tlp.ColumnCount; c++)
185        tlp.Controls.Remove(tlp.GetControlFromPosition(c, idx));
[14388]186      // shift left
[14384]187      for (int r = idx + 1; r < tlp.RowCount; r++) {
188        for (int c = 0; c < tlp.ColumnCount; c++) {
189          var control = tlp.GetControlFromPosition(c, r);
190          if (control != null) {
191            tlp.SetRow(control, r - 1);
192          }
193        }
194      }
[14388]195      // delete rows
[14384]196      tlp.RowStyles.RemoveAt(tlp.RowCount - 1);
197      tlp.RowCount--;
198    }
[14388]199    #endregion
[14384]200
[14388]201    #region Add/Remove/Update Variable
[14381]202    protected override void AddVariable(string name) {
203      base.AddVariable(name);
[14388]204      if (IsVariableChecked(name))
205        AddChartToTable(name);
[10952]206    }
[14381]207    protected override void RemoveVariable(string name) {
208      base.RemoveVariable(name);
[10952]209
[14384]210      // clear caches
211      columnHeaderCache.Remove(name);
212      rowHeaderCache.Remove(name);
213      var keys = bodyCache.Keys.Where(t => t.Item1 == name || t.Item2 == name).ToList();
214      foreach (var key in keys)
215        bodyCache.Remove(key);
216
217      if (IsVariableChecked(name)) {
218        RemoveChartFromTable(name);
219      }
[14381]220    }
221    protected override void UpdateVariable(string name) {
222      base.UpdateVariable(name);
[14388]223      RemoveVariable(name);
224      AddVariable(name);
[14381]225    }
226    protected override void ResetAllVariables() {
227      GenerateCharts();
228    }
229    #endregion
230
[14580]231    #region Creating Headers and Body
[14384]232    private Label GetColumnHeader(string variable) {
233      if (!columnHeaderCache.ContainsKey(variable)) {
234        columnHeaderCache.Add(variable, new Label() {
235          Text = variable,
236          TextAlign = ContentAlignment.MiddleCenter,
237          Name = variable,
238          Height = columnHeaderTableLayoutPanel.Height,
239          Dock = DockStyle.Fill,
240          Margin = new Padding(3)
241        });
242      }
243      return columnHeaderCache[variable];
244    }
245    private Label GetRowHeader(string variable) {
246      if (!rowHeaderCache.ContainsKey(variable)) {
[14514]247        rowHeaderCache.Add(variable, new VerticalLabel() {
[14384]248          Text = variable,
249          TextAlign = ContentAlignment.MiddleCenter,
250          Name = variable,
251          Width = rowHeaderTableLayoutPanel.Width,
[14514]252          Height = columnHeaderScrollPanel.Width,
[14384]253          Dock = DockStyle.Fill,
254          Margin = new Padding(3)
255        });
256      }
257      return rowHeaderCache[variable];
258    }
[14446]259    private Control GetBody(string colVariable, string rowVariable) {
[14384]260      var key = Tuple.Create(colVariable, rowVariable);
261      if (!bodyCache.ContainsKey(key)) {
262        if (rowVariable == colVariable) { // use historgram if x and y variable are equal
[14459]263          var dataTable = new DataTable();
[14384]264          DataRow dataRow = Content.CreateDataRow(rowVariable, DataRowVisualProperties.DataRowChartType.Histogram);
[14459]265          dataRow.VisualProperties.IsVisibleInLegend = false;
[14384]266          dataTable.Rows.Add(dataRow);
[14459]267          var pcv = new DataTableControl {
[14384]268            Name = key.ToString(),
269            Content = dataTable,
270            Dock = DockStyle.Fill,
[14459]271            //ShowLegend = false,
272            //XAxisFormat = "G3"
[14384]273          };
[14459]274          //pcv.ChartDoubleClick += HistogramDoubleClick;
[14384]275          bodyCache.Add(key, pcv);
276        } else { //scatter plot
[14446]277          var scatterPlot = Content.CreateScatterPlot(colVariable, rowVariable);
[14514]278          var regressionType = (RegressionType)regressionTypeComboBox.SelectedValue;
279          int order = (int)polynomialRegressionOrderNumericUpDown.Value;
280          foreach (var row in scatterPlot.Rows) {
[14495]281            row.VisualProperties.PointSize = 3;
[14514]282            row.VisualProperties.IsRegressionVisibleInLegend = false;
283            row.VisualProperties.RegressionType = regressionType;
284            row.VisualProperties.PolynomialRegressionOrder = order;
285          }
[14446]286          scatterPlot.VisualProperties.Title = string.Empty;
[14514]287          var scatterPlotControl = new ScatterPlotControl {
[14384]288            Name = key.ToString(),
289            Content = scatterPlot,
290            Dock = DockStyle.Fill,
[14446]291            //ShowLegend = false,
292            //XAxisFormat = "G3"
[14384]293          };
[14514]294          //scatterPlotControl.DoubleClick += ScatterPlotDoubleClick; // ToDo: not working; double click is already handled by the chart
[14446]295          bodyCache.Add(key, scatterPlotControl);
[14384]296        }
297      }
298      return bodyCache[key];
299    }
300    #endregion
301
[14511]302    protected override void CheckedChangedUpdate() {
303      GenerateCharts();
304    }
305
[14381]306    #region Generate Charts
307    private void GenerateCharts() {
[14511]308      if (suppressCheckedChangedUpdate) return;
309
[14384]310      var variables = GetCheckedVariables();
[10987]311
[14384]312      // Clear old layouts and cache
313      foreach (var tableLayoutPanel in new[] { columnHeaderTableLayoutPanel, rowHeaderTableLayoutPanel, bodyTableLayoutPanel }) {
314        tableLayoutPanel.Controls.Clear();
315        tableLayoutPanel.ColumnStyles.Clear();
316        tableLayoutPanel.RowStyles.Clear();
[14381]317      }
[14384]318      columnHeaderCache.Clear();
319      rowHeaderCache.Clear();
320      bodyCache.Clear();
[10882]321
[14381]322      // Set row and column count
[14384]323      columnHeaderTableLayoutPanel.ColumnCount = variables.Count;
324      rowHeaderTableLayoutPanel.RowCount = variables.Count;
[14381]325      bodyTableLayoutPanel.ColumnCount = variables.Count;
326      bodyTableLayoutPanel.RowCount = variables.Count;
[10915]327
[14381]328      // Set column and row layout
329      for (int i = 0; i < variables.Count; i++) {
[14388]330        columnHeaderTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, GetColumnWidth()));
331        rowHeaderTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Absolute, GetRowHeight()));
332        bodyTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, GetColumnWidth()));
333        bodyTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Absolute, GetRowHeight()));
[10882]334      }
[10915]335
[14384]336      frameTableLayoutPanel.SuspendLayout();
[14381]337      AddHeaderToTableLayoutPanels();
338      AddChartsToTableLayoutPanel();
[14384]339      UpdateHeaderMargin();
340      frameTableLayoutPanel.ResumeLayout(true);
[10952]341    }
[10915]342
[14381]343    private void AddHeaderToTableLayoutPanels() {
[14384]344      int i = 0;
345      foreach (var variable in GetCheckedVariables()) {
346        columnHeaderTableLayoutPanel.Controls.Add(GetColumnHeader(variable), i, 0);
347        rowHeaderTableLayoutPanel.Controls.Add(GetRowHeader(variable), 0, i);
348        i++;
[14381]349      }
350    }
351    private void AddChartsToTableLayoutPanel() {
[14384]352      int c = 0;
353      foreach (var colVar in GetCheckedVariables()) {
354        if (!IsVariableChecked(colVar)) continue;
355        int r = 0;
356        foreach (var rowVar in GetCheckedVariables()) {
357          if (!IsVariableChecked(rowVar)) continue;
358          bodyTableLayoutPanel.Controls.Add(GetBody(colVar, rowVar), c, r);
359          r++;
[10915]360        }
[14384]361        c++;
[10915]362      }
[14514]363      UpdateRegressionLine();
[10882]364    }
365
[14381]366    #endregion
367
368    #region DoubleClick Events
[10952]369    //Open scatter plot in new tab with new content when double clicked
370    private void ScatterPlotDoubleClick(object sender, EventArgs e) {
[14446]371      var scatterPlotControl = (ScatterPlotControl)sender;
[14467]372      var scatterContent = new SingleScatterPlotContent(Content.PreprocessingData);
[14446]373      ScatterPlot scatterPlot = scatterPlotControl.Content;
[10915]374
[14381]375      //Extract variable names from scatter plot and set them in content
[10952]376      if (scatterPlot.Rows.Count == 1) {
[10992]377        string[] variables = scatterPlot.Rows.ElementAt(0).Name.Split(new string[] { " - " }, StringSplitOptions.None); // extract variable names from string
[10952]378        scatterContent.SelectedXVariable = variables[0];
379        scatterContent.SelectedYVariable = variables[1];
380      }
[14381]381
382      MainFormManager.MainForm.ShowContent(scatterContent, typeof(ScatterPlotSingleView));  // open in new tab
[10915]383    }
[10952]384
[14381]385    //open histogram in new tab with new content when double clicked
386    private void HistogramDoubleClick(object sender, EventArgs e) {
[14459]387      DataTableControl pcv = (DataTableControl)sender;
[14381]388      HistogramContent histoContent = new HistogramContent(Content.PreprocessingData);  // create new content     
[14511]389      //ToDo: histoContent.VariableItemList = Content.CreateVariableItemList();
[14459]390      var dataTable = pcv.Content;
[10952]391
[14381]392      //Set variable item list from with variable from data table
393      if (dataTable.Rows.Count == 1) { // only one data row should be in data table
[10952]394        string variableName = dataTable.Rows.ElementAt(0).Name;
395
396        // set only variable name checked
[10992]397        foreach (var checkedItem in histoContent.VariableItemList) {
[14381]398          histoContent.VariableItemList.SetItemCheckedState(checkedItem, checkedItem.Value == variableName);
[10952]399        }
400      }
[14381]401      MainFormManager.MainForm.ShowContent(histoContent, typeof(HistogramView));  // open in new tab
[10952]402    }
[14381]403    #endregion
[10952]404
[14388]405    #region Scrolling
[14384]406    private void bodyScrollPanel_Scroll(object sender, ScrollEventArgs e) {
[14381]407      SyncScroll();
[10952]408
[14384]409      UpdateHeaderMargin();
[10952]410    }
[14384]411    private void bodyScrollPanel_MouseWheel(object sender, MouseEventArgs e) {
[14381]412      // Scrolling with the mouse wheel is not captured in the Scoll event
413      SyncScroll();
414    }
415    private void SyncScroll() {
416      frameTableLayoutPanel.SuspendRepaint();
[10992]417
[14384]418      columnHeaderScrollPanel.HorizontalScroll.Minimum = bodyScrollPanel.HorizontalScroll.Minimum;
419      columnHeaderScrollPanel.HorizontalScroll.Maximum = bodyScrollPanel.HorizontalScroll.Maximum;
420      rowHeaderScrollPanel.VerticalScroll.Minimum = bodyScrollPanel.VerticalScroll.Minimum;
421      rowHeaderScrollPanel.VerticalScroll.Maximum = bodyScrollPanel.VerticalScroll.Maximum;
[10882]422
[14384]423      columnHeaderScrollPanel.HorizontalScroll.Value = Math.Max(bodyScrollPanel.HorizontalScroll.Value, 1);
424      rowHeaderScrollPanel.VerticalScroll.Value = Math.Max(bodyScrollPanel.VerticalScroll.Value, 1);
[14381]425      // minimum 1 is nececary  because of two factors:
426      // - setting the Value-property of Horizontal/VerticalScroll updates the internal state but the Value-property stays 0
427      // - setting the same number of the Value-property has no effect
428      // since the Value-property is always 0, setting it to 0 would have no effect; so it is set to 1 instead
[10992]429
[14381]430      frameTableLayoutPanel.ResumeRepaint(true);
431    }
[14384]432    // add a margin to the header table layouts if the scollbar is visible to account for the width/height of the scrollbar
433    private void UpdateHeaderMargin() {
434      columnHeaderScrollPanel.Margin = new Padding(0, 0, bodyScrollPanel.VerticalScroll.Visible ? SystemInformation.VerticalScrollBarWidth : 0, 0);
435      rowHeaderScrollPanel.Margin = new Padding(0, 0, 0, bodyScrollPanel.HorizontalScroll.Visible ? SystemInformation.HorizontalScrollBarHeight : 0);
436    }
[14388]437    #endregion
438
439    #region Sizing of Charts
440    private int GetColumnWidth() { return (int)(bodyScrollPanel.Width * ((float)widthTrackBar.Value / 100)); }
441    private int GetRowHeight() { return (int)(bodyScrollPanel.Height * ((float)heightTrackBar.Value / 100)); }
442    private void widthTrackBar_ValueChanged(object sender, EventArgs e) {
443      frameTableLayoutPanel.SuspendRepaint();
444      for (int i = 0; i < columnHeaderTableLayoutPanel.ColumnCount; i++) {
445        columnHeaderTableLayoutPanel.ColumnStyles[i].Width = GetColumnWidth();
446        bodyTableLayoutPanel.ColumnStyles[i].Width = GetColumnWidth();
447      }
448      frameTableLayoutPanel.ResumeRepaint(true);
449    }
450    private void heightTrackBar_ValueChanged(object sender, EventArgs e) {
451      frameTableLayoutPanel.SuspendRepaint();
452
453      for (int i = 0; i < rowHeaderTableLayoutPanel.RowCount; i++) {
454        rowHeaderTableLayoutPanel.RowStyles[i].Height = GetRowHeight();
455        bodyTableLayoutPanel.RowStyles[i].Height = GetRowHeight();
456      }
457      frameTableLayoutPanel.ResumeRepaint(true);
458    }
459    #endregion
[14514]460
461    #region Regression Line
462    private void regressionTypeComboBox_SelectedValueChanged(object sender, EventArgs e) {
463      var regressionType = (RegressionType)regressionTypeComboBox.SelectedValue;
464      polynomialRegressionOrderNumericUpDown.Enabled = regressionType == RegressionType.Polynomial;
465      UpdateRegressionLine();
466    }
467
468    private void polynomialRegressionOrderNumericUpDown_ValueChanged(object sender, EventArgs e) {
469      UpdateRegressionLine();
470    }
471
472    private void UpdateRegressionLine() {
473      var regressionType = (RegressionType)regressionTypeComboBox.SelectedValue;
474      int order = (int)polynomialRegressionOrderNumericUpDown.Value;
475
476      foreach (var control in bodyCache.Values) {
477        var scatterPlotControl = control as ScatterPlotControl;
478        if (scatterPlotControl != null) {
479          foreach (var row in scatterPlotControl.Content.Rows) {
480            row.VisualProperties.IsRegressionVisibleInLegend = false;
481            row.VisualProperties.RegressionType = regressionType;
482            row.VisualProperties.PolynomialRegressionOrder = order;
483          }
484        }
485      }
486    }
487    #endregion
[14381]488  }
[10882]489}
[14514]490
Note: See TracBrowser for help on using the repository browser.