[29] | 1 | /* |
---|
| 2 | * This program is free software; you can redistribute it and/or modify |
---|
| 3 | * it under the terms of the GNU General Public License as published by |
---|
| 4 | * the Free Software Foundation; either version 2 of the License, or |
---|
| 5 | * (at your option) any later version. |
---|
| 6 | * |
---|
| 7 | * This program is distributed in the hope that it will be useful, |
---|
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
| 10 | * GNU General Public License for more details. |
---|
| 11 | * |
---|
| 12 | * You should have received a copy of the GNU General Public License |
---|
| 13 | * along with this program; if not, write to the Free Software |
---|
| 14 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
---|
| 15 | */ |
---|
| 16 | |
---|
| 17 | /* |
---|
| 18 | * MemoryUsagePanel.java |
---|
| 19 | * Copyright (C) 2008 University of Waikato, Hamilton, New Zealand |
---|
| 20 | */ |
---|
| 21 | |
---|
| 22 | package weka.gui; |
---|
| 23 | |
---|
| 24 | import weka.core.Memory; |
---|
| 25 | import weka.core.Utils; |
---|
| 26 | import weka.gui.visualize.VisualizeUtils; |
---|
| 27 | |
---|
| 28 | import java.awt.BorderLayout; |
---|
| 29 | import java.awt.Color; |
---|
| 30 | import java.awt.Dimension; |
---|
| 31 | import java.awt.Graphics; |
---|
| 32 | import java.awt.Point; |
---|
| 33 | import java.awt.event.ActionEvent; |
---|
| 34 | import java.awt.event.ActionListener; |
---|
| 35 | import java.util.Collections; |
---|
| 36 | import java.util.Enumeration; |
---|
| 37 | import java.util.Hashtable; |
---|
| 38 | import java.util.Properties; |
---|
| 39 | import java.util.Vector; |
---|
| 40 | |
---|
| 41 | import javax.swing.JButton; |
---|
| 42 | import javax.swing.JOptionPane; |
---|
| 43 | import javax.swing.JPanel; |
---|
| 44 | import javax.swing.SwingUtilities; |
---|
| 45 | |
---|
| 46 | /** |
---|
| 47 | * A panel for displaying the memory usage. |
---|
| 48 | * |
---|
| 49 | * @author fracpete (fracpete at waikato dot ac dot nz) |
---|
| 50 | * @version $Revision: 1.1 $ |
---|
| 51 | */ |
---|
| 52 | public class MemoryUsagePanel |
---|
| 53 | extends JPanel { |
---|
| 54 | |
---|
| 55 | /** for serialization. */ |
---|
| 56 | private static final long serialVersionUID = -4812319791687471721L; |
---|
| 57 | |
---|
| 58 | /** |
---|
| 59 | * Specialized thread for monitoring the memory usage. |
---|
| 60 | * |
---|
| 61 | * @author fracpete (fracpete at waikato dot ac dot nz) |
---|
| 62 | * @version $Revision: 1.1 $ |
---|
| 63 | */ |
---|
| 64 | protected class MemoryMonitor |
---|
| 65 | extends Thread { |
---|
| 66 | |
---|
| 67 | /** the refresh interval in msecs. */ |
---|
| 68 | protected int m_Interval; |
---|
| 69 | |
---|
| 70 | /** whether the thread is still running. */ |
---|
| 71 | protected boolean m_Monitoring; |
---|
| 72 | |
---|
| 73 | /** |
---|
| 74 | * default constructor. |
---|
| 75 | */ |
---|
| 76 | public MemoryMonitor() { |
---|
| 77 | super(); |
---|
| 78 | |
---|
| 79 | setInterval(1000); // TODO: via props file |
---|
| 80 | } |
---|
| 81 | |
---|
| 82 | /** |
---|
| 83 | * Returns the refresh interval in msecs. |
---|
| 84 | * |
---|
| 85 | * @return returns the refresh interval |
---|
| 86 | */ |
---|
| 87 | public int getInterval() { |
---|
| 88 | return m_Interval; |
---|
| 89 | } |
---|
| 90 | |
---|
| 91 | /** |
---|
| 92 | * Sets the refresh interval in msecs. |
---|
| 93 | * |
---|
| 94 | * @param value the refresh interval |
---|
| 95 | */ |
---|
| 96 | public void setInterval(int value) { |
---|
| 97 | m_Interval = value; |
---|
| 98 | } |
---|
| 99 | |
---|
| 100 | /** |
---|
| 101 | * Returns whether the thread is still running. |
---|
| 102 | * |
---|
| 103 | * @return true if the thread is still running |
---|
| 104 | */ |
---|
| 105 | public boolean isMonitoring() { |
---|
| 106 | return m_Monitoring; |
---|
| 107 | } |
---|
| 108 | |
---|
| 109 | /** |
---|
| 110 | * stops the monitoring thread. |
---|
| 111 | */ |
---|
| 112 | public void stopMonitoring() { |
---|
| 113 | m_Monitoring = false; |
---|
| 114 | } |
---|
| 115 | |
---|
| 116 | /** |
---|
| 117 | * The run method. |
---|
| 118 | */ |
---|
| 119 | public void run() { |
---|
| 120 | m_Monitoring = true; |
---|
| 121 | |
---|
| 122 | while (m_Monitoring) { |
---|
| 123 | try { |
---|
| 124 | Thread.sleep(m_Interval); |
---|
| 125 | |
---|
| 126 | // update GUI |
---|
| 127 | if (m_Monitoring) { |
---|
| 128 | Runnable doUpdate = new Runnable() { |
---|
| 129 | public void run() { |
---|
| 130 | update(); |
---|
| 131 | } |
---|
| 132 | }; |
---|
| 133 | SwingUtilities.invokeLater(doUpdate); |
---|
| 134 | } |
---|
| 135 | } |
---|
| 136 | catch(InterruptedException ex) { |
---|
| 137 | ex.printStackTrace(); |
---|
| 138 | } |
---|
| 139 | } |
---|
| 140 | } |
---|
| 141 | |
---|
| 142 | /** |
---|
| 143 | * Updates the GUI. |
---|
| 144 | */ |
---|
| 145 | protected void update() { |
---|
| 146 | double perc; |
---|
| 147 | Dimension size; |
---|
| 148 | |
---|
| 149 | // current usage |
---|
| 150 | perc = (double) m_Memory.getCurrent() / (double) m_Memory.getMax(); |
---|
| 151 | perc = Math.round(perc * 1000) / 10; |
---|
| 152 | |
---|
| 153 | // tool tip |
---|
| 154 | setToolTipText("" + perc + "% used"); |
---|
| 155 | |
---|
| 156 | // update history |
---|
| 157 | m_History.insertElementAt(perc, 0); |
---|
| 158 | size = getSize(); |
---|
| 159 | while (m_History.size() > size.getWidth()) |
---|
| 160 | m_History.remove(m_History.size() - 1); |
---|
| 161 | |
---|
| 162 | // display history |
---|
| 163 | repaint(); |
---|
| 164 | } |
---|
| 165 | } |
---|
| 166 | |
---|
| 167 | /** The name of the properties file. */ |
---|
| 168 | protected static String PROPERTY_FILE = "weka/gui/MemoryUsage.props"; |
---|
| 169 | |
---|
| 170 | /** Contains the properties. */ |
---|
| 171 | protected static Properties PROPERTIES; |
---|
| 172 | |
---|
| 173 | /** the memory usage over time. */ |
---|
| 174 | protected Vector<Double> m_History; |
---|
| 175 | |
---|
| 176 | /** for monitoring the memory usage. */ |
---|
| 177 | protected Memory m_Memory; |
---|
| 178 | |
---|
| 179 | /** the thread for monitoring the memory usage. */ |
---|
| 180 | protected MemoryMonitor m_Monitor; |
---|
| 181 | |
---|
| 182 | /** the button for running the garbage collector. */ |
---|
| 183 | protected JButton m_ButtonGC; |
---|
| 184 | |
---|
| 185 | /** the threshold percentages to change color. */ |
---|
| 186 | protected Vector<Double> m_Percentages; |
---|
| 187 | |
---|
| 188 | /** the corresponding colors for the thresholds. */ |
---|
| 189 | protected Hashtable<Double,Color> m_Colors; |
---|
| 190 | |
---|
| 191 | /** the default color. */ |
---|
| 192 | protected Color m_DefaultColor; |
---|
| 193 | |
---|
| 194 | /** the background color. */ |
---|
| 195 | protected Color m_BackgroundColor; |
---|
| 196 | |
---|
| 197 | /** the position for the dialog. */ |
---|
| 198 | protected Point m_FrameLocation; |
---|
| 199 | |
---|
| 200 | /** |
---|
| 201 | * Loads the configuration property file (USE_DYNAMIC is FALSE) or determines |
---|
| 202 | * the classes dynamically (USE_DYNAMIC is TRUE) |
---|
| 203 | * @see #USE_DYNAMIC |
---|
| 204 | * @see GenericPropertiesCreator |
---|
| 205 | */ |
---|
| 206 | static { |
---|
| 207 | // Allow a properties file in the current directory to override |
---|
| 208 | try { |
---|
| 209 | PROPERTIES = Utils.readProperties(PROPERTY_FILE); |
---|
| 210 | Enumeration keys = PROPERTIES.propertyNames(); |
---|
| 211 | if (!keys.hasMoreElements()) |
---|
| 212 | throw new Exception("Failed to read a property file for the " |
---|
| 213 | +"memory usage panel"); |
---|
| 214 | } |
---|
| 215 | catch (Exception ex) { |
---|
| 216 | JOptionPane.showMessageDialog( |
---|
| 217 | null, |
---|
| 218 | "Could not read a configuration file for the memory usage\n" |
---|
| 219 | +"panel. An example file is included with the Weka distribution.\n" |
---|
| 220 | +"This file should be named \"" + PROPERTY_FILE + "\" and\n" |
---|
| 221 | +"should be placed either in your user home (which is set\n" |
---|
| 222 | + "to \"" + System.getProperties().getProperty("user.home") + "\")\n" |
---|
| 223 | + "or the directory that java was started from\n", |
---|
| 224 | "MemoryUsagePanel", |
---|
| 225 | JOptionPane.ERROR_MESSAGE); |
---|
| 226 | } |
---|
| 227 | } |
---|
| 228 | |
---|
| 229 | /** |
---|
| 230 | * default constructor. |
---|
| 231 | */ |
---|
| 232 | public MemoryUsagePanel() { |
---|
| 233 | super(); |
---|
| 234 | |
---|
| 235 | // initializes members |
---|
| 236 | m_Memory = new Memory(); |
---|
| 237 | m_History = new Vector<Double>(); |
---|
| 238 | m_Percentages = new Vector<Double>(); |
---|
| 239 | m_Colors = new Hashtable<Double,Color>(); |
---|
| 240 | |
---|
| 241 | // colors and percentages |
---|
| 242 | m_BackgroundColor = parseColor("BackgroundColor", Color.WHITE); |
---|
| 243 | m_DefaultColor = parseColor("DefaultColor", Color.GREEN); |
---|
| 244 | String[] percs = PROPERTIES.getProperty("Percentages", "70,80,90").split(","); |
---|
| 245 | for (int i = 0; i < percs.length; i++) { |
---|
| 246 | // do we have a color associated with percentage? |
---|
| 247 | if (PROPERTIES.getProperty(percs[i]) != null) { |
---|
| 248 | double perc; |
---|
| 249 | Color color; |
---|
| 250 | |
---|
| 251 | // try parsing the number |
---|
| 252 | try { |
---|
| 253 | perc = Double.parseDouble(percs[i]); |
---|
| 254 | } |
---|
| 255 | catch (Exception e) { |
---|
| 256 | System.err.println( |
---|
| 257 | "MemoryUsagePanel: cannot parse percentage '" |
---|
| 258 | + percs[i] + "' - ignored!"); |
---|
| 259 | continue; |
---|
| 260 | } |
---|
| 261 | |
---|
| 262 | // try parsing the color |
---|
| 263 | color = parseColor(percs[i], null); |
---|
| 264 | if (color == null) |
---|
| 265 | continue; |
---|
| 266 | |
---|
| 267 | // store color and percentage |
---|
| 268 | m_Percentages.add(perc); |
---|
| 269 | m_Colors.put(perc, color); |
---|
| 270 | } |
---|
| 271 | else { |
---|
| 272 | System.err.println( |
---|
| 273 | "MemoryUsagePanel: cannot find color for percentage '" |
---|
| 274 | + percs[i] + "' - ignored!"); |
---|
| 275 | } |
---|
| 276 | } |
---|
| 277 | Collections.sort(m_Percentages); |
---|
| 278 | |
---|
| 279 | // layout |
---|
| 280 | setLayout(new BorderLayout()); |
---|
| 281 | |
---|
| 282 | JPanel panel = new JPanel(new BorderLayout()); |
---|
| 283 | add(panel, BorderLayout.EAST); |
---|
| 284 | |
---|
| 285 | m_ButtonGC = new JButton("GC"); |
---|
| 286 | m_ButtonGC.setToolTipText("Runs the garbage collector."); |
---|
| 287 | m_ButtonGC.addActionListener(new ActionListener() { |
---|
| 288 | public void actionPerformed(ActionEvent evt) { |
---|
| 289 | System.gc(); |
---|
| 290 | } |
---|
| 291 | }); |
---|
| 292 | panel.add(m_ButtonGC, BorderLayout.NORTH); |
---|
| 293 | |
---|
| 294 | // dimensions |
---|
| 295 | int height; |
---|
| 296 | int width; |
---|
| 297 | try { |
---|
| 298 | height = Integer.parseInt(PROPERTIES.getProperty("Height", "" + (int) m_ButtonGC.getPreferredSize().getHeight())); |
---|
| 299 | width = Integer.parseInt(PROPERTIES.getProperty("Width", "400")); |
---|
| 300 | } |
---|
| 301 | catch (Exception e) { |
---|
| 302 | System.err.println("MemoryUsagePanel: Problem parsing the dimensions - " + e); |
---|
| 303 | height = (int) m_ButtonGC.getPreferredSize().getHeight(); |
---|
| 304 | width = 400; |
---|
| 305 | } |
---|
| 306 | setPreferredSize(new Dimension(width, height)); |
---|
| 307 | |
---|
| 308 | // position |
---|
| 309 | int top; |
---|
| 310 | int left; |
---|
| 311 | try { |
---|
| 312 | top = Integer.parseInt(PROPERTIES.getProperty("Top", "0")); |
---|
| 313 | left = Integer.parseInt(PROPERTIES.getProperty("Left", "0")); |
---|
| 314 | } |
---|
| 315 | catch (Exception e) { |
---|
| 316 | System.err.println("MemoryUsagePanel: Problem parsing the position - " + e); |
---|
| 317 | top = 0; |
---|
| 318 | left = 0; |
---|
| 319 | } |
---|
| 320 | m_FrameLocation = new Point(left, top); |
---|
| 321 | |
---|
| 322 | // monitoring thread |
---|
| 323 | int interval; |
---|
| 324 | try { |
---|
| 325 | interval = Integer.parseInt(PROPERTIES.getProperty("Interval", "1000")); |
---|
| 326 | } |
---|
| 327 | catch (Exception e) { |
---|
| 328 | System.err.println("MemoryUsagePanel: Problem parsing the refresh interval - " + e); |
---|
| 329 | interval = 1000; |
---|
| 330 | } |
---|
| 331 | m_Monitor = new MemoryMonitor(); |
---|
| 332 | m_Monitor.setInterval(interval); |
---|
| 333 | m_Monitor.setPriority(Thread.MAX_PRIORITY); |
---|
| 334 | m_Monitor.start(); |
---|
| 335 | } |
---|
| 336 | |
---|
| 337 | /** |
---|
| 338 | * parses the color and returns the corresponding Color object. |
---|
| 339 | * |
---|
| 340 | * @param prop the color property to read and parse |
---|
| 341 | * @param defValue the default color |
---|
| 342 | * @return the parsed color or the default color of the |
---|
| 343 | */ |
---|
| 344 | protected Color parseColor(String prop, Color defValue) { |
---|
| 345 | Color result; |
---|
| 346 | Color color; |
---|
| 347 | String colorStr; |
---|
| 348 | |
---|
| 349 | result = defValue; |
---|
| 350 | |
---|
| 351 | try { |
---|
| 352 | colorStr = PROPERTIES.getProperty(prop); |
---|
| 353 | color = VisualizeUtils.processColour(colorStr, result); |
---|
| 354 | if (color == null) |
---|
| 355 | throw new Exception(colorStr); |
---|
| 356 | result = color; |
---|
| 357 | } |
---|
| 358 | catch (Exception e) { |
---|
| 359 | System.err.println( |
---|
| 360 | "MemoryUsagePanel: cannot parse color '" |
---|
| 361 | + e.getMessage() + "' - ignored!"); |
---|
| 362 | } |
---|
| 363 | |
---|
| 364 | return result; |
---|
| 365 | } |
---|
| 366 | |
---|
| 367 | /** |
---|
| 368 | * Returns whether the thread is still running. |
---|
| 369 | * |
---|
| 370 | * @return true if the thread is still running |
---|
| 371 | */ |
---|
| 372 | public boolean isMonitoring() { |
---|
| 373 | return m_Monitor.isMonitoring(); |
---|
| 374 | } |
---|
| 375 | |
---|
| 376 | /** |
---|
| 377 | * stops the monitoring thread. |
---|
| 378 | */ |
---|
| 379 | public void stopMonitoring() { |
---|
| 380 | m_Monitor.stopMonitoring(); |
---|
| 381 | } |
---|
| 382 | |
---|
| 383 | /** |
---|
| 384 | * Returns the default position for the dialog. |
---|
| 385 | * |
---|
| 386 | * @return the default position |
---|
| 387 | */ |
---|
| 388 | public Point getFrameLocation() { |
---|
| 389 | return m_FrameLocation; |
---|
| 390 | } |
---|
| 391 | |
---|
| 392 | /** |
---|
| 393 | * draws the background image. |
---|
| 394 | * |
---|
| 395 | * @param g the graphics context |
---|
| 396 | */ |
---|
| 397 | public void paintComponent(Graphics g) { |
---|
| 398 | int i; |
---|
| 399 | int n; |
---|
| 400 | int len; |
---|
| 401 | double scale; |
---|
| 402 | double perc; |
---|
| 403 | Color color; |
---|
| 404 | |
---|
| 405 | super.paintComponent(g); |
---|
| 406 | |
---|
| 407 | g.setColor(m_BackgroundColor); |
---|
| 408 | g.fillRect(0, 0, getWidth(), getHeight()); |
---|
| 409 | scale = (double) getHeight() / 100.0; |
---|
| 410 | for (i = 0; i < m_History.size(); i++) { |
---|
| 411 | perc = m_History.get(i); |
---|
| 412 | |
---|
| 413 | // determine color |
---|
| 414 | color = m_DefaultColor; |
---|
| 415 | for (n = m_Percentages.size() - 1; n >= 0; n--) { |
---|
| 416 | if (perc >= m_Percentages.get(n)) { |
---|
| 417 | color = m_Colors.get(m_Percentages.get(n)); |
---|
| 418 | break; |
---|
| 419 | } |
---|
| 420 | } |
---|
| 421 | |
---|
| 422 | // paint line |
---|
| 423 | g.setColor(color); |
---|
| 424 | len = (int) Math.round(perc * scale); |
---|
| 425 | g.drawLine(i, getHeight() - 1, i, getHeight() - len); |
---|
| 426 | } |
---|
| 427 | } |
---|
| 428 | } |
---|