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

Last change on this file since 14511 was 14511, checked in by pfleck, 3 years ago

#2709

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