Code for Custom Widget Example
/****************************************************************************
**
** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
**
** This file is part of Qt Jambi.
**
** ** This file may be used under the terms of the GNU General Public
** License version 2.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file. Please review the following information to ensure GNU
** General Public Licensing requirements will be met:
** http://www.trolltech.com/products/qt/opensource.html
**
** If you are unsure which license is appropriate for your use, please
** review the following information:
** http://www.trolltech.com/products/qt/licensing.html or contact the
** sales department at sales@trolltech.com.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/
package com.trolltech.examples;
import com.trolltech.qt.*;
import com.trolltech.qt.gui.*;
import com.trolltech.qt.core.*;
public class CustomWidget extends QWidget {
public enum SpeedUnit {
MilesPerHour("mph"),
KilometersPerHour("km/h");
private String title;
private SpeedUnit(String title) {
this.title = title;
}
public String title() { return title; }
}
//
// Constructors
//
public CustomWidget() { this(null); }
public CustomWidget(QWidget parent) { super(parent); }
//
// Caches for commonly used stuff
//
private int x = rect().x();
private int y = rect().y();
private int w = rect().width();
private int h = rect().height();
private QRect rect = rect();
private QBrush gradient = null;
private QRectF innerCircle = null;
private QRectF outerCircle = null;
private QPainterPath innerCirclePath = null;
private QPainterPath needlePath = null;
private QBrush radialGradient = null;
private QPainterPath framePath = null;
private QPen speedBarPen = new QPen(QColor.black, 4);
private QTimeLine timeLine = new QTimeLine(1, this); {
timeLine.valueChanged.connect(this, "animateSpeed(double)");
timeLine.finished.connect(this, "noLongerAnimating()");
}
//
// Signals
//
public Signal0 propertyChanged = new Signal0(); {
propertyChanged.connect(this, "update()");
}
public Signal1<Integer> speedChanged = new Signal1<Integer>();
/**
* Calculates the size of the frame.
* @return The width of the frame.
*/
private static final double FRAMING_SIZE = 0.01f;
private double framingSize() {
return FRAMING_SIZE * (double)outerCircle().width();
}
//
// topSpeed property
//
private static final int MAX_SPEED = 260;
private int maxSpeed = MAX_SPEED;
@QtPropertyReader(name="topSpeed")
@QtBlockedSlot public final int maxSpeed() {
return maxSpeed;
}
@QtPropertyWriter(name="topSpeed")
public final void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed; propertyChanged.emit();
}
@QtPropertyResetter(name="topSpeed")
public final void resetMaxSpeed() {
setMaxSpeed(MAX_SPEED);
}
//
// animationSpeed property
//
private static final double ANIMATION_SPEED = 10.0f;
private double animationSpeed = ANIMATION_SPEED;
public final double animationSpeed() { return animationSpeed; }
public final void setAnimationSpeed(double animationSpeed) {
this.animationSpeed = animationSpeed;
propertyChanged.emit();
}
@QtPropertyResetter(name="animationSpeed")
public final void resetAnimationSpeed() {
setAnimationSpeed(ANIMATION_SPEED);
}
private int tail[] = new int[TAIL_LENGTH];
private void resetTail() {
int newTail[] = new int[tailLength()];
for (int i=0; i<(tail.length > newTail.length ? newTail.length : tail.length); ++i)
newTail[i] = tail[i];
tail = newTail;
}
//
// tailLength property
//
private static final int TAIL_LENGTH = 10;
private int tailLength = TAIL_LENGTH;
@QtBlockedSlot public final int tailLength() { return tailLength; }
public final void setTailLength(int tailLength) {
if (tailLength < 0)
return ;
this.tailLength = tailLength;
resetTail();
propertyChanged.emit();
}
@QtPropertyResetter(name="tailLength")
public final void resetTailLength() { setTailLength(TAIL_LENGTH); }
//
// currentSpeed property
//
private boolean isAnimating = false;
private int startTail;
private int endTail;
private int oldSpeed;
private int diff = 0;
private int requestedSpeed = 0;
private int currentSpeed = 0;
@QtPropertyDesignable(value="false")
@QtBlockedSlot public int currentSpeed() { return currentSpeed; }
public void setCurrentSpeed(int currentSpeed) {
oldSpeed = this.currentSpeed;
requestedSpeed = currentSpeed;
diff = requestedSpeed - oldSpeed;
startTail = 0;
endTail = 0;
isAnimating = true;
if (timeLine.state() == QTimeLine.State.Running)
timeLine.stop();
timeLine.setDuration((int)(Math.abs(diff) * animationSpeed()) + 1);
timeLine.start();
}
//
// skip property
//
private static final int SKIP = 20;
private int skip = SKIP;
@QtBlockedSlot public final int skip() {
return skip;
}
public final void setSkip(int skip) { this.skip = skip; propertyChanged.emit(); }
@QtPropertyResetter(name="skip")
public final void resetSkip() { setSkip(SKIP); }
//
// unit property
//
private SpeedUnit unit = SpeedUnit.KilometersPerHour;
@QtBlockedSlot public SpeedUnit unit() { return unit; }
public void setUnit(SpeedUnit unit) { this.unit = unit; propertyChanged.emit(); }
//
// startAngle property
//
private static final double START_ANGLE = -30.0f;
private double startAngle = START_ANGLE;
@QtBlockedSlot public final double startAngle() { return startAngle; }
public final void setStartAngle(double startAngle) { this.startAngle = startAngle; propertyChanged.emit(); }
@QtPropertyResetter(name="startAngle")
public final void resetStartAngle() { setStartAngle(START_ANGLE); }
//
// endAngle property
//
private static final double END_ANGLE = 210.0f;
private double endAngle = END_ANGLE;
@QtBlockedSlot public final double endAngle() { return endAngle; }
public final void setEndAngle(double endAngle) { this.endAngle = endAngle; propertyChanged.emit(); }
@QtPropertyResetter(name="endAngle")
public final void resetEndAngle() { setEndAngle(endAngle); }
//
// font property
//
private QFont font = font();
@QtBlockedSlot public final QFont speedFont() { return font; }
public final void setSpeedFont(QFont font) { this.font = font; propertyChanged.emit(); }
@QtPropertyResetter(name="speedFont")
public final void resetSpeedFont() { setSpeedFont(font()); }
//
// highlightLight property
//
private static final QColor HIGHLIGHT_LIGHT = QColor.fromRgba(0xffffffff);
private QColor highlightLight = HIGHLIGHT_LIGHT;
@QtBlockedSlot public final QColor highlightLight() { return highlightLight; }
public final void setHighlightLight(QColor color) { highlightLight = color; propertyChanged.emit(); }
@QtPropertyResetter(name="highlightLight")
public final void resetHighlightLight() { setHighlightLight(HIGHLIGHT_LIGHT); }
//
// highlightDark property
//
private static final QColor HIGHLIGHT_DARK = QColor.fromRgba(0x00ffffff);
private QColor highlightDark = HIGHLIGHT_DARK;
@QtBlockedSlot public final QColor highlightDark() { return highlightDark; }
public final void setHighlightDark(QColor color) { highlightDark = color; propertyChanged.emit(); }
@QtPropertyResetter(name="highlightDark")
public final void resetHighlightDark() { setHighlightDark(HIGHLIGHT_DARK); }
//
// needleColor property
//
private static final QColor NEEDLE_COLOR = QColor.red;
private QColor needleColor = NEEDLE_COLOR;
@QtBlockedSlot public final QColor needleColor() { return needleColor; }
public final void setNeedleColor(QColor needleColor) { this.needleColor = needleColor; propertyChanged.emit(); }
@QtPropertyResetter(name="needleColor")
public final void resetNeedleColor() { setNeedleColor(NEEDLE_COLOR); }
//
// backgroundColor property
//
private static final QColor BACKGROUND_COLOR = QColor.white;
private QColor backgroundColor = BACKGROUND_COLOR;
@QtBlockedSlot public final QColor backgroundColor() { return backgroundColor; }
public final void setBackgroundColor(QColor backgroundColor) {
this.backgroundColor = backgroundColor;
propertyChanged.emit();
}
@QtPropertyResetter(name="backgroundColor")
public final void resetBackgroundColor() { setBackgroundColor(BACKGROUND_COLOR); }
//
// needleFrameColor property
//
private static final QColor NEEDLE_FRAME_COLOR = QColor.black;
private QColor needleFrameColor = NEEDLE_FRAME_COLOR;
@QtBlockedSlot public final QColor needleFrameColor() { return needleFrameColor; }
public final void setNeedleFrameColor(QColor needleFrameColor) { this.needleFrameColor = needleFrameColor; propertyChanged.emit(); }
@QtPropertyResetter(name="needleFrameColor")
public final void resetNeedleFrameColor() { setNeedleFrameColor(NEEDLE_FRAME_COLOR); }
//
// frameColorDark property
//
private static final QColor FRAME_COLOR_DARK = QColor.fromRgb(0x00514f48);
private QColor frameColorDark = FRAME_COLOR_DARK;
@QtBlockedSlot public final QColor frameColorDark() { return frameColorDark; }
public final void setFrameColorDark(QColor frameColorDark) { this.frameColorDark = frameColorDark; propertyChanged.emit(); }
@QtPropertyResetter(name="frameColorDark")
public final void resetFrameColorDark() { setFrameColorDark(FRAME_COLOR_DARK); }
//
// frameColorLight property
//
private static final QColor FRAME_COLOR_LIGHT = QColor.fromRgb(0x00a49a92);
private QColor frameColorLight = FRAME_COLOR_LIGHT;
@QtBlockedSlot public final QColor frameColorLight() { return frameColorLight; }
public final void setFrameColorLight(QColor frameColorLight) { this.frameColorLight = frameColorLight; propertyChanged.emit(); }
@QtPropertyResetter(name="frameColorLight")
public final void resetFrameColorLight() { setFrameColorLight(FRAME_COLOR_LIGHT); }
@Override
protected void resizeEvent(QResizeEvent e) {
this.rect = rect();
x = rect.x();
y = rect.y();
w = rect.width();
h = rect.height();
resetAll();
}
private void resetAll() {
resetLinearGradient();
resetInnerCircle();
resetOuterCircle();
resetFramePath();
resetRadialGradient();
resetNeedlePath();
}
private QSize sizeHint = new QSize(200, 200);
@Override
public QSize sizeHint() {
return sizeHint;
}
@Override
protected void paintEvent(QPaintEvent e) {
QPainter p = new QPainter();
p.begin(this); {
p.setRenderHint(QPainter.RenderHint.Antialiasing, true);
drawFrame(p);
drawPlate(p);
p.end();
}
}
private void resetLinearGradient() {
gradient = null;
}
private QBrush linearGradient() {
if (gradient == null) {
QRectF rect = outerCircle();
QLinearGradient g = new QLinearGradient(new QPointF(rect.x() + rect.width() / 2.0, rect.top()),
new QPointF(rect.x() + rect.width() / 2.0, rect.top() + framingSize() / 2.0));
g.setColorAt(0.0, highlightLight());
g.setColorAt(1.0, highlightDark());
gradient = new QBrush(g);
}
return gradient;
}
private QRectF outerCircle() {
if (outerCircle == null) {
if (w > h)
outerCircle = new QRectF(x + w/2 - h/2, y, h, h);
else
outerCircle = new QRectF(x, y + h/2 - w/2, w, w);
// Compensate for the antialiased edges of the drawing by shrinking the rect by 1 pixel
outerCircle = outerCircle.adjusted(1, 1, -1, -1);
}
return outerCircle;
}
private void resetOuterCircle() {
outerCircle = null;
}
private QRectF innerCircle() {
if (innerCircle == null) {
QRectF outerCircle = outerCircle();
innerCircle = new QRectF(outerCircle.x() + framingSize(), outerCircle.y() + framingSize(),
outerCircle.width() - framingSize()*2,
outerCircle.height() - framingSize()*2);
}
return innerCircle;
}
private QPainterPath innerCirclePath() {
if (innerCirclePath == null) {
innerCirclePath = new QPainterPath();
innerCirclePath.addEllipse(innerCircle());
}
return innerCirclePath;
}
private void resetInnerCircle() {
innerCircle = null;
innerCirclePath = null;
}
private QPainterPath needlePath() {
if (needlePath == null) {
QRectF innerCircle = innerCircle();
needlePath = new QPainterPath();
needlePath.moveTo(0.0, 0.0);
double tipLength = (double)innerCircle.width() / 50.0f;
needlePath.lineTo(-innerCircle.width() / 2.0 + tipLength, -tipLength);
needlePath.lineTo(-innerCircle.width() / 2.0, 0.0);
needlePath.lineTo(-innerCircle.width() / 2.0 + tipLength, tipLength);
needlePath.lineTo(0.0, 0.0);
}
return needlePath;
}
private void resetNeedlePath() {
needlePath = null;
}
private QRectF cachedTextRect = new QRectF();
private void drawPlate(QPainter p) {
p.save();
QPainterPath path = innerCirclePath();
p.fillPath(path, new QBrush(backgroundColor()));
p.setClipPath(path);
QRectF innerCircle = innerCircle();
double x = (double)innerCircle.x() + (double)innerCircle.width() / 2.0f;
double y = (double)innerCircle.y() + (double)innerCircle.height() / 2.0f;
// Draw the speed bars
int skip = skip();
double step = ((double)skip / (double)maxSpeed()) * (endAngle() - startAngle());
QFont font = speedFont();
p.setFont(font);
QFontMetrics fm = new QFontMetrics(font);
{
if (step > 0.0) {
p.save();
p.translate(x,y);
speedBarPen.setWidthF(innerCircle.width() / 100.0f);
p.setPen(speedBarPen);
double angle;
int speed = 0;
p.rotate(startAngle());
for (angle=startAngle(); angle<=endAngle(); angle += step) {
p.drawLine((int)(-innerCircle.width() / 2.0f) + (int)(innerCircle.width() / 30.0f), 0, (int)(-innerCircle.width() / 2.0f) + (int)(innerCircle.width() / 14.0f), 0);
String speedString = "" + speed;
QSize size = fm.size(0, speedString);
int x1 = (int)(-innerCircle.width() / 2.0f) + (int)(innerCircle.width() / 14.0f);
cachedTextRect.setCoords(x1, 0, x1 + size.width(), size.height());
p.drawText(cachedTextRect, "" + speed);
speed += skip;
p.rotate(step);
}
p.restore();
}
}
p.translate(x, y);
p.setFont(font);
QSize size = fm.size(0, unit().title());
cachedTextRect.setCoords(-size.width() / 2.0, 0, size.width() / 2.0, size.height());
p.drawText(cachedTextRect, unit().title());
p.restore();
p.setPen(QPen.NoPen);
p.setBrush(new QBrush(needleColor()));
int i=startTail;
while (i != endTail) {
p.save();
matrix.reset();
matrix.translate(x, y);
matrix.rotate(angleOfSpeed(tail[i]));
int len = i >= startTail ? i - startTail : i + tail.length - startTail;
p.setOpacity((needleColor().alphaF() / (tail.length+1)) * len);
QPainterPath temporaryPath = matrix.map(needlePath());
p.drawPath(temporaryPath);
if ((++i) >= tail.length)
i = 0;
p.restore();
}
if (!isAnimating && startTail != endTail) {
if ((++startTail) >= tail.length) {
startTail = 0;
}
update();
}
matrix.reset();
matrix.translate(x, y);
matrix.rotate(angleOfSpeed(currentSpeed));
p.setPen(needleFrameColor());
QPainterPath temporaryPath = matrix.map(needlePath());
p.drawPath(temporaryPath);
}
private void drawHighlights(QPainter p) {
p.setPen(QPen.NoPen);
p.setBrush(linearGradient());
p.setClipPath(framePath());
p.drawEllipse(outerCircle());
}
private QPainterPath framePath() {
if (framePath == null) {
framePath = new QPainterPath();
framePath.addEllipse(outerCircle());
{
QPainterPath innerEllipse = new QPainterPath();
innerEllipse.addEllipse(innerCircle());
framePath = framePath.subtracted(innerEllipse);
}
}
return framePath;
}
private void resetFramePath() {
framePath = null;
}
private QBrush radialGradient() {
if (radialGradient == null) {
QRadialGradient g = new QRadialGradient(new QPointF(rect.center()), rect.width() / 2.0);
g.setColorAt(0.0, frameColorDark());
g.setColorAt(1.0, frameColorLight());
g.setCoordinateMode(QGradient.CoordinateMode.ObjectBoundingMode);
radialGradient = new QBrush(g);
}
return radialGradient;
}
private void resetRadialGradient() {
radialGradient = null;
}
private void drawFrame(QPainter p) {
p.save();
p.setPen(new QPen(QColor.black, 2));
p.setBrush(radialGradient());
p.drawPath(framePath());
drawHighlights(p);
p.restore();
}
private double angleOfSpeed(int speed) {
int skip = skip();
double step = ((double)skip / (double)maxSpeed()) * (endAngle() - startAngle());
return startAngle() + step * ((double)speed / (double)skip);
}
private void noLongerAnimating() {
isAnimating = false;
}
private QMatrix matrix = new QMatrix();
private void animateSpeed(double value) {
if (tail.length > 0) {
tail[endTail++] = currentSpeed;
if (endTail >= tail.length)
endTail = 0;
if (endTail <= startTail) {
startTail = endTail + 1;
if (startTail >= tail.length)
startTail = 0;
}
}
currentSpeed = (int)(oldSpeed + diff * value);
propertyChanged.emit();
speedChanged.emit(currentSpeed);
}
}