Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 14495 was 14495, checked in by pfleck, 7 years ago

#2709

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