Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to implement a gauge/meter/dial in Java?

485 views
Skip to first unread message

Dave Stallard

unread,
Jun 13, 2002, 1:32:21 PM6/13/02
to
I need to implement a meter/gauge in an application I'm building. The
quantity is a real number between 0 and 1 (it's a volume actually).
Ideally, I'd like a vertical line gauge which would display a red
portion of the column when the quantity got too high, but that's a
detail.

I can't find anything in Swing that does this, except maybe the
JProgress bar. I could try to build my own component with a specialized
paint method, etc, but I bet that would take a lot of time. Does
anybody know of an existing class or bean (preferably with source code)
that does this?

thanks,
Dave

Chris Smith

unread,
Jun 13, 2002, 2:04:14 PM6/13/02
to
Dave Stallard wrote ...

I don't know of anything... but I don't know why it would take much time
at all to build it. It seems pretty straight-forward.

Chris Smith

Steve Horsley

unread,
Jun 13, 2002, 2:48:48 PM6/13/02
to
On Thu, 13 Jun 2002 17:32:21 GMT, Dave Stallard <stal...@bbn.com>
wrote:

Here is one of my early stumblings. It doesn't do vertical bar, and
only does integer scale values (0-100 by default). It should be a good
start for you, though. You should be able to copy and tweak the
horizontal bar to go vertically fairly easily.

It has a main() method that will run a little demo.
Have fun.

Steve.

package steve.horsley.util;

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;

/** The Meter class displays a representation of a meter. It can
display a horizontal bar (like a progress bar),
or a classical VU style meter. To use it, create the Meter Object,
set the scale (full scale by default is 100).
*/
public class Meter extends JComponent implements java.io.Serializable
{
int scale, current, min, max;
boolean showMin, showMax;
boolean gradientChange = true;
GradientPaint gp;
boolean gradient = false;
Color lowColour = Color.red;
Color highColour = Color.green;
int vuScaleExtent = 120;// full scale size
int vuScaleFullAngle = 30;// the angle of needle at full scale
public static final int TYPE_VU = 1;
public static final int TYPE_HBAR = 2;
int type = 1;
Arc2D.Double arc = new Arc2D.Double(); // arc for drawing the
vu meter
Rectangle2D.Double rect = new Rectangle2D.Double();
Line2D.Double line = new Line2D.Double();
int lastWidth = -1;
int lastHeight = -1;

/** Calling main() makes a test widow and excercises a Meter
object. */
public static void main(String[] args) {
System.out.println("Testing Meter...");
JFrame jf = new JFrame("Meter Test");
final Meter m = new Meter();
m.setPreferredSize(new Dimension(200, 100));
//vc.setDoubleBuffered(true);
jf.getContentPane().add(m);
jf.pack();
jf.setVisible(true);
jf.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}});
m.setScale(100);
m.setCurrent(50);
m.resetMinMax();
m.setShowMin(true);
m.setShowMax(true);
while(true) {
m.setLowColour(Color.red);
m.setHighColour(Color.green);
m.setGradient(false);
m.setType(TYPE_VU);
testMeter(m);
try { Thread.sleep(2000); } catch(Exception e) { }
m.setType(TYPE_HBAR);
testMeter(m);
try { Thread.sleep(2000); } catch(Exception e) { }
m.setLowColour(Color.green);
m.setHighColour(Color.red);
m.setGradient(true);
m.setType(TYPE_VU);
testMeter(m);
try { Thread.sleep(2000); } catch(Exception e) { }
m.setType(TYPE_HBAR);
testMeter(m);
try { Thread.sleep(2000); } catch(Exception e) { }
}
}

static void testMeter(Meter m) {
m.setCurrent(50);
m.resetMinMax();
int time = 25;
for(int x = 50 ; x <= 75 ; x+= 1) {
m.setCurrent(x);
try { Thread.sleep(time); } catch(Exception e) { }
}
for(int x = 75 ; x >= 25 ; x -= 1) {
m.setCurrent(x);
try { Thread.sleep(time); } catch(Exception e) { }
}
for(int x = 25 ; x <= 100 ; x+= 1) {
m.setCurrent(x);
try { Thread.sleep(time); } catch(Exception e) { }
}
for(int x = 100 ; x >= 0 ; x -= 1) {
m.setCurrent(x);
try { Thread.sleep(time); } catch(Exception e) { }
}
for(int x = 0 ; x <= 50 ; x += 1) {
m.setCurrent(x);
try { Thread.sleep(time); } catch(Exception e) { }
}
}

/** Construct a meter with default settings.
Default settings are: Scale=100, type=TYPE_VU, current=0,
showMin=ShowMax=false,
lowColour=Color.red, highColour=Color.green
*/
public Meter() {
scale = 100;
}

/** Construct a meter with default settings, but of the given
type.
Type can be Meter.TYPE_HBAR or Meter.TYPE_VU. */
public Meter(int type) {
scale = 100;
this.type = type;
}

/** Set the full scale reading for the Meter. The default value is
100, so that setCurrent(100) shows full scale,
and setCurrent(50) shows half-scale. */
public void setScale(int s) {
if(s > 0)
scale = s;
min = max = 0;
repaint();
}

public int getScale() {return scale;}

/** Set the current meter reading. If c is less than 0, the Meter
will show 0.
If c is greater than the value given by setScale(), the Meter will
show full-scale. */
public void setCurrent(int c) {
int newc = c;
if(newc < 0)
newc = 0;
if(newc > scale)
newc = scale;
if(newc == current)
return;
current = newc;
if(min > newc)
min = newc;
if(max < newc)
max = newc;
repaint();
}

public int getCurrent() {return current;}

/** Reset the minimum indicator to the current value. */
public void resetMin() {
min = current;
repaint();
}

/** Reset the maximum indicator to the current value. */
public void resetMax() {
max = current;
repaint();
}

/** Reset both the maximum and maximum indicators to the current
value. */
public void resetMinMax() {
max = min = current;
repaint();
}


/** Set b to true if you want a minimum (low water mark) indicator
to be displayed. */
public void setShowMin(boolean b) {
showMin = b;
repaint();
}

public boolean getShowMin() { return showMin; }

/** Set b to true if you want a maximum (high water mark)
indicator to be displayed. */
public void setShowMax(boolean b) {
showMax = b;
repaint();
}

public boolean getShowMax() { return showMax; }

public String toString() {
return new String("Meter: current = " + current + ", min = " +
min + ", max = " + max + ", scale = " + scale);
}

/** Set the colour of the low half of the meter (from zero to the
current reading). */
public void setLowColour(Color c) {
lowColour = c;
gradientChange = true;
repaint();
}

public Color getLowColour() { return lowColour; }

/** Set the colour of the high half of the meter (from the current
reading to full scale). */
public void setHighColour(Color c) {
highColour = c;
gradientChange = true;
repaint();
}

public Color getHighColour() { return highColour; }

/** Sets the type of display. type can be Meter.TYPE_VU or
Meter.TYPE_HBAR. */
public void setType(int type) {
this.type = type;
repaint();
}

public int getType() { return type; }

/** Set whether the meter should show a colour gradient.
If true, the meter will show a fixed colour gradient over
which the needle moves.
If false, the meter will show two colours, the division
between them moving with the needle. */
public void setGradient(boolean b) {
if(b == gradient)
return;
gradient = b;
gradientChange = true;
repaint();
}

public boolean getGradient() { return gradient; }

public void paint(Graphics g) {
Dimension d = getSize();
if((d.width != lastWidth) || (d.height != lastHeight))
gradientChange = true;
lastWidth = d.width;
lastHeight = d.height;
if(gradient && gradientChange) {
gp = new GradientPaint(0, 0, lowColour, d.width, 0,
highColour);
gradientChange = false;
}
if(type == TYPE_HBAR)
paintHBar(g);
else
paintVU(g);
}

void paintHBar(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Dimension d = getSize();
int wrange = d.width - 1;
int curx = current * wrange / scale;
int minx = min * wrange / scale ;
int maxx = max * wrange / scale;
if(gradient) {
rect.setRect(0, 0, d.width, d.height);
g2.setPaint(gp);
g2.fill(rect);
}
else {
rect.setRect(0, 0, curx, d.height);
g2.setPaint(lowColour);
g2.fill(rect);
rect.setRect(curx, 0, d.width - curx, d.height);
g2.setPaint(highColour);
g2.fill(rect);
}
// draw min and max lines
if(showMin) {
line.setLine(minx, 0, minx, d.height);
g2.setPaint(Color.black);
g2.draw(line);
}
if(showMax) {
line.setLine(maxx, 0, maxx, d.height);
g2.setPaint(Color.black);
g2.draw(line);
}
// draw current line in black
g2.setPaint(Color.black);
line.setLine(curx, 0, curx, d.height);
g2.draw(line);
}

void paintVU(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Dimension d = getSize();
//g2.clearRect(0, 0, d.width, d.height);
double fullExtent = vuScaleExtent;
double lowExtent = current * fullExtent / scale;// extent of
low arc
double hiExtent = fullExtent - lowExtent; // size of high
arc
double fullAngle = vuScaleFullAngle;
double lowStart = hiExtent + fullAngle;
// draw the coloured areas
if(gradient) {
arc.setArc(0, 0, d.width, d.height * 2, fullAngle,
fullExtent, Arc2D.PIE);
g2.setPaint(gp);
g2.fill(arc);
}
else {
arc.setArc(0, 0, d.width, d.height * 2, lowStart,
lowExtent, Arc2D.PIE);
g2.setPaint(lowColour);
g2.fill(arc);
arc.setArc(0, 0, d.width, d.height * 2, fullAngle,
hiExtent, Arc2D.PIE);
g2.setPaint(highColour);
g2.fill(arc);
}
// draw the needle
arc.setArc(0, 0, d.width, d.height * 2, lowStart, 0,
Arc2D.PIE);
g2.setPaint(Color.black);
g2.draw(arc);
double mina = min * fullExtent / scale ;
double maxa = max * fullExtent / scale;
if(showMin) {
//minArc.setArc(0, 0, d.width, d.height * 2, fullAngle +
fullExtent - mina, 1, Arc2D.PIE);
arc.setArc(0, 0, d.width, d.height * 2, fullAngle +
fullExtent - mina, 0, Arc2D.PIE);
g2.setPaint(Color.black);
g2.draw(arc);
}
if(showMax) {
//maxArc.setArc(0, 0, d.width, d.height * 2, fullAngle +
fullExtent - maxa -1, 1, Arc2D.PIE);
arc.setArc(0, 0, d.width, d.height * 2, fullAngle +
fullExtent - maxa, 0, Arc2D.PIE);
g2.setPaint(Color.black);
g2.draw(arc);
}
// draw the needle cut-out
//arc.setArc(d.width * 9 / 20, d.height * 9 / 10, d.width/10,
d.height /5, 0, 360, Arc2D.PIE);
//g2.setPaint(g2.getBackground());
//g2.fill(arc);
}
}

Paul Keeble

unread,
Jun 13, 2002, 6:01:07 PM6/13/02
to

"Dave Stallard" <stal...@bbn.com> wrote in message
news:3D08D724...@bbn.com...

You right in thinking that JProgress bar will do exactly as you require. It
can be set to a vertical mode, all you have to do is then set the current
progress and it'll update. Since JProgressBar uses int's for minimum and
maximum I'd recommend you use 0 - 100 and just times your value by 100 and
cast it as an int. JProgressBar will suit your needs unless you were looking
for a sort of car speedo dial.

As for displaying red when it got to high I suspect that's a look and feel
issue, and should be easy enough to do, by drawing past a certain value red
instead of blue.


David Gilbert

unread,
Jun 13, 2002, 6:36:40 PM6/13/02
to
Dave Stallard wrote:

> I need to implement a meter/gauge in an application I'm building. The
> quantity is a real number between 0 and 1 (it's a volume actually).
> Ideally, I'd like a vertical line gauge which would display a red
> portion of the column when the quantity got too high, but that's a
> detail.

Hi Dave,

A couple of developers have contributed code to the JFreeChart project for
meter plots. At present there is a gauge plot and a thermometer plot. You
might want to look through those...both define ranges (low, normal, high)
where the color changes depending on which range the value falls into.

I would guess that these classes are not going to meet your requirement
exactly, but if you end up coding something yourself and want to fit it
into a larger framework (and get feedback from others) then maybe you
should consider the JFreeChart project:

http://sourceforge.net/projects/jfreechart

Regards,

Dave Gilbert
JFreeChart Project Leader

0 new messages