Fixed Issue 209.
3 Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 The contents of this file are subject to the Mozilla Public License Version
6 1.1 (the "License"); you may not use this file except in compliance with
7 the License. You may obtain a copy of the License at
9 http://www.mozilla.org/MPL/
11 Software distributed under the License is distributed on an "AS IS" basis,
12 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 for the specific language governing rights and limitations under the License.
15 The Original Code is the Open Hardware Monitor code.
17 The Initial Developer of the Original Code is
18 Michael Möller <m.moeller@gmx.ch>.
19 Portions created by the Initial Developer are Copyright (C) 2009-2011
20 the Initial Developer. All Rights Reserved.
24 Alternatively, the contents of this file may be used under the terms of
25 either the GNU General Public License Version 2 or later (the "GPL"), or
26 the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 in which case the provisions of the GPL or the LGPL are applicable instead
28 of those above. If you wish to allow use of your version of this file only
29 under the terms of either the GPL or the LGPL, and not to allow others to
30 use your version of this file under the terms of the MPL, indicate your
31 decision by deleting the provisions above and replace them with the notice
32 and other provisions required by the GPL or the LGPL. If you do not delete
33 the provisions above, a recipient may use your version of this file under
34 the terms of any one of the MPL, the GPL or the LGPL.
39 using System.Collections.Generic;
40 using System.ComponentModel;
42 using System.Drawing.Drawing2D;
43 using System.Windows.Forms;
44 using OpenHardwareMonitor.Hardware;
46 namespace OpenHardwareMonitor.GUI {
47 public class PlotPanel : UserControl {
49 private PersistentSettings settings;
52 private List<ISensor> clocks = new List<ISensor>();
53 private List<ISensor> temperatures = new List<ISensor>();
54 private List<ISensor> fans = new List<ISensor>();
55 private IDictionary<ISensor, Color> colors;
57 private StringFormat centerlower;
58 private StringFormat centerleft;
59 private StringFormat lowerleft;
60 private Brush lightBrush;
63 private UserRadioGroup timeWindowRadioGroup;
65 public PlotPanel(PersistentSettings settings) {
66 this.settings = settings;
68 this.SetStyle(ControlStyles.DoubleBuffer |
69 ControlStyles.UserPaint |
70 ControlStyles.AllPaintingInWmPaint |
71 ControlStyles.ResizeRedraw, true);
76 centerlower = new StringFormat();
77 centerlower.Alignment = StringAlignment.Center;
78 centerlower.LineAlignment = StringAlignment.Near;
80 centerleft = new StringFormat();
81 centerleft.Alignment = StringAlignment.Far;
82 centerleft.LineAlignment = StringAlignment.Center;
84 lowerleft = new StringFormat();
85 lowerleft.Alignment = StringAlignment.Far;
86 lowerleft.LineAlignment = StringAlignment.Near;
88 lightBrush = new SolidBrush(Color.FromArgb(245, 245, 245));
89 lightPen = new Pen(Color.FromArgb(200, 200, 200));
92 private void CreateContextMenu() {
93 MenuItem timeWindow = new MenuItem("Time Scale");
95 MenuItem[] timeWindowMenuItems =
96 { new MenuItem("Auto"),
97 new MenuItem("5 min"),
98 new MenuItem("10 min"),
99 new MenuItem("20 min"),
100 new MenuItem("30 min"),
101 new MenuItem("45 min"),
103 new MenuItem("1.5 h"),
107 new MenuItem("12 h"),
108 new MenuItem("24 h") };
110 foreach (MenuItem mi in timeWindowMenuItems)
111 timeWindow.MenuItems.Add(mi);
113 timeWindowRadioGroup = new UserRadioGroup("timeWindow", 0,
114 timeWindowMenuItems, settings);
116 this.ContextMenu = new ContextMenu();
117 this.ContextMenu.MenuItems.Add(timeWindow);
120 private List<float> GetTemperatureGrid() {
122 float? minTempNullable = null;
123 float? maxTempNullable = null;
124 foreach (ISensor sensor in temperatures) {
125 IEnumerable<SensorValue> values = sensor.Values;
126 foreach (SensorValue value in values) {
127 if (!float.IsNaN(value.Value)) {
128 if (!minTempNullable.HasValue || minTempNullable > value.Value)
129 minTempNullable = value.Value;
130 if (!maxTempNullable.HasValue || maxTempNullable < value.Value)
131 maxTempNullable = value.Value;
135 if (!minTempNullable.HasValue) {
136 minTempNullable = 20;
137 maxTempNullable = 30;
140 float maxTemp = (float)Math.Ceiling(maxTempNullable.Value / 10) * 10;
141 float minTemp = (float)Math.Floor(minTempNullable.Value / 10) * 10;
142 if (maxTemp == minTemp)
145 int countTempMax = 4;
146 float deltaTemp = maxTemp - minTemp;
147 int countTemp = (int)Math.Round(deltaTemp / 2);
148 if (countTemp > countTempMax)
149 countTemp = (int)Math.Round(deltaTemp / 5);
150 if (countTemp > countTempMax)
151 countTemp = (int)Math.Round(deltaTemp / 10);
152 if (countTemp > countTempMax)
153 countTemp = (int)Math.Round(deltaTemp / 20);
155 List<float> grid = new List<float>(countTemp + 1);
156 for (int i = 0; i <= countTemp; i++) {
157 grid.Add(minTemp + i * deltaTemp / countTemp);
162 private List<float> GetTimeGrid() {
165 if (timeWindowRadioGroup.Value == 0) { // Auto
167 if (temperatures.Count > 0) {
168 IEnumerator<SensorValue> enumerator =
169 temperatures[0].Values.GetEnumerator();
170 if (enumerator.MoveNext()) {
171 maxTime = (float)(now - enumerator.Current.Time).TotalMinutes;
176 { 5, 10, 20, 30, 45, 60, 90, 120, 180, 360, 720, 1440 };
178 maxTime = maxTimes[timeWindowRadioGroup.Value - 1];
183 while (deltaTime + 1 <= maxTime && deltaTime < 10)
185 while (deltaTime + 2 <= maxTime && deltaTime < 30)
187 while (deltaTime + 5 <= maxTime && deltaTime < 100)
189 while (deltaTime + 50 <= maxTime && deltaTime < 1000)
191 while (deltaTime + 100 <= maxTime && deltaTime < 10000)
194 List<float> grid = new List<float>(countTime + 1);
195 for (int i = 0; i <= countTime; i++) {
196 grid.Add(i * deltaTime / countTime);
201 protected override void OnPaint(PaintEventArgs e) {
202 now = DateTime.UtcNow - new TimeSpan(0, 0, 4);
204 List<float> timeGrid = GetTimeGrid();
205 List<float> tempGrid = GetTemperatureGrid();
207 Graphics g = e.Graphics;
210 new RectangleF(0, 0, Bounds.Width, Bounds.Height);
215 float w = r.Width - ml - mr;
220 float h = r.Height - mt - mb;
222 float leftScaleSpace = 5;
223 float bottomScaleSpace = 5;
225 g.Clear(Color.White);
227 if (w > 0 && h > 0) {
228 g.FillRectangle(lightBrush, x0, y0, w, h);
230 g.SmoothingMode = SmoothingMode.HighQuality;
231 for (int i = 0; i < timeGrid.Count; i++) {
232 float x = x0 + i * w / (timeGrid.Count - 1);
233 g.DrawLine(lightPen, x, y0, x, y0 + h);
236 for (int i = 0; i < tempGrid.Count; i++) {
237 float y = y0 + i * h / (tempGrid.Count - 1);
238 g.DrawLine(lightPen, x0, y, x0 + w, y);
241 float deltaTemp = tempGrid[tempGrid.Count - 1] - tempGrid[0];
242 float deltaTime = timeGrid[timeGrid.Count - 1];
243 foreach (ISensor sensor in temperatures) {
244 using (Pen pen = new Pen(colors[sensor])) {
245 IEnumerable<SensorValue> values = sensor.Values;
246 PointF last = new PointF();
248 foreach (SensorValue v in values) {
249 if (!float.IsNaN(v.Value)) {
250 PointF point = new PointF(
251 x0 + w - w * (float)(now - v.Time).TotalMinutes / deltaTime,
252 y0 + h - h * (v.Value - tempGrid[0]) / deltaTemp);
254 g.DrawLine(pen, last, point);
264 g.SmoothingMode = SmoothingMode.None;
265 g.FillRectangle(Brushes.White, 0, 0, x0, r.Height);
266 g.FillRectangle(Brushes.White, x0 + w + 1, 0, r.Width - x0 - w,
269 for (int i = 1; i < timeGrid.Count; i++) {
270 float x = x0 + (timeGrid.Count - 1 - i) * w / (timeGrid.Count - 1);
271 g.DrawString(timeGrid[i].ToString(), Font, Brushes.Black, x,
272 y0 + h + bottomScaleSpace, centerlower);
275 for (int i = 0; i < tempGrid.Count - 1; i++) {
276 float y = y0 + (tempGrid.Count - 1 - i) * h / (tempGrid.Count - 1);
277 g.DrawString(tempGrid[i].ToString(), Font, Brushes.Black,
278 x0 - leftScaleSpace, y, centerleft);
281 g.SmoothingMode = SmoothingMode.HighQuality;
282 g.DrawString("[°C]", Font, Brushes.Black, x0 - leftScaleSpace, y0,
284 g.DrawString("[min]", Font, Brushes.Black, x0 + w,
285 y0 + h + bottomScaleSpace, lowerleft);
289 public void SetSensors(List<ISensor> sensors,
290 IDictionary<ISensor, Color> colors) {
291 this.colors = colors;
292 List<ISensor> clocks = new List<ISensor>();
293 List<ISensor> temperatures = new List<ISensor>();
294 List<ISensor> fans = new List<ISensor>();
295 foreach (ISensor sensor in sensors)
296 switch (sensor.SensorType) {
297 case SensorType.Clock: clocks.Add(sensor); break;
298 case SensorType.Temperature: temperatures.Add(sensor); break;
299 case SensorType.Fan: fans.Add(sensor); break;
301 this.clocks = clocks;
302 this.temperatures = temperatures;