Qt仿Android带特效的数字时钟源码分析(滑动,翻页,旋转效果)
这个数字时钟的源码可以在Qt Demo中找到,风格是仿Android的,不过该Demo中含有三种动画效果(鉴于本人未曾用过Android的系统,因此不知道Android的数字时钟是否也含有这三种效果),其分别为滑动、翻页和旋转。
由于本人的Qt Creator输入中文后显示的都是乱码,因而在此只能使用英文进行注释,后期如果有时间再进行中文的相关整理。可能有些地方理解并不是很正确。希望大家多多指正!
以下为源码:
- #include <QtCore>
- #include <QtGui>
- class Digits: public QWidget
- {
- Q_OBJECT
- public:
- /*Define three transition modes of the digital clock*/
- enum {
- Slide,
- Flip,
- Rotate
- };
- Digits(QWidget *parent)
- : QWidget(parent)
- , m_number(0)
- , m_transition(Slide)
- {
- setAttribute(Qt::WA_OpaquePaintEvent, true);
- //Widget paints all its pixels when it receives a paint event
- setAttribute(Qt::WA_NoSystemBackground, true);
- //Indicates that the widget has no background, i.e. when the widget receives paint events, the background is not automatically repainted.
- connect(&m_animator, SIGNAL(frameChanged(int)), SLOT(update()));
- //start animation
- m_animator.setFrameRange(0, 100);
- m_animator.setDuration(600);
- //Construct a 0.6-second timeline with a frame range of 0 - 100
- m_animator.setCurveShape(QTimeLine::EaseInOutCurve);
- //starts growing slowly, then runs steadily, then grows slowly again
- }
- /*Set transition when time changed*/
- void setTransition(int tr) {
- m_transition = tr;
- }
- /*Get transition mode*/
- int transition() const {
- return m_transition;
- }
- /*Set hours and minutes*/
- void setNumber(int n) {
- if (m_number != n) {
- m_number = qBound(0, n, 99);
- preparePixmap();
- update();
- }
- }
- /*Flip to next state*/
- void flipTo(int n) {
- if (m_number != n) {
- m_number = qBound(0, n, 99);
- m_lastPixmap = m_pixmap;
- preparePixmap();
- m_animator.stop();
- m_animator.start();
- }
- }
- protected:
- /*Draw the main frame of the digits area*/
- void drawFrame(QPainter *p, const QRect &rect) {
- p->setPen(Qt::NoPen);
- QLinearGradient gradient(rect.topLeft(), rect.bottomLeft());
- //Set linear gradient area
- gradient.setColorAt(0.00, QColor(245, 245, 245));
- gradient.setColorAt(0.49, QColor(192, 192, 192));
- gradient.setColorAt(0.51, QColor(245, 245, 245));
- gradient.setColorAt(1.00, QColor(192, 192, 192));
- //Creates stop points at the given position with the given color
- p->setBrush(gradient);
- QRect r = rect;
- p->drawRoundedRect(r, 15, 15, Qt::RelativeSize);
- /*
- Draws outer rectangle rect with rounded corners.
- Qt::RelativeSize specifies the size relative to the bounding rectangle,
- typically using percentage measurements.
- */
- r.adjust(1, 4, -1, -4);
- //Adds 1, 4, -1 and -4 respectively to the existing coordinates of the rectangle
- p->setPen(QColor(181, 181, 181));
- p->setBrush(Qt::NoBrush);
- p->drawRoundedRect(r, 15, 15, Qt::RelativeSize);
- //Draws inner rectangle rect with rounded corners.
- p->setPen(QColor(159, 159, 159));
- int y = rect.top() + rect.height() / 2 - 1;
- p->drawLine(rect.left(), y, rect.right(), y);
- //Draws the mid-line from (rect.left(), y) to (rect.right(), y) and sets the current pen position to (rect.right(), y)
- }
- /*Draw the digits*/
- QPixmap drawDigits(int n, const QRect &rect) {
- int scaleFactor = 2;
- #if defined(Q_OS_SYMBIAN) || defined(Q_OS_WINCE_WM)
- if (rect.height() > 240)
- scaleFactor = 1;
- #endif
- QString str = QString::number(n);
- if (str.length() == 1)
- str.prepend("0");
- //Ensure it is double-digit
- QFont font;
- font.setFamily("Helvetica");
- int fontHeight = scaleFactor * 0.55 * rect.height();
- font.setPixelSize(fontHeight);
- //Sets the font size to pixelSize pixels
- font.setBold(true);
- QPixmap pixmap(rect.size() * scaleFactor);
- pixmap.fill(Qt::transparent);
- QLinearGradient gradient(QPoint(0, 0), QPoint(0, pixmap.height()));
- //Constructs a linear gradient with interpolation area between (0,0) and (0,pixmap.height())
- gradient.setColorAt(0.00, QColor(128, 128, 128));
- gradient.setColorAt(0.49, QColor(64, 64, 64));
- gradient.setColorAt(0.51, QColor(128, 128, 128));
- gradient.setColorAt(1.00, QColor(16, 16, 16));
- //Creates stop points at the given position with the given color
- QPainter p;
- p.begin(&pixmap);
- p.setFont(font);
- QPen pen;
- pen.setBrush(QBrush(gradient));
- //Set penbrush with linergrident
- p.setPen(pen);
- p.drawText(pixmap.rect(), Qt::AlignCenter, str);
- //Draws the digit number(str here) within the provided rectangle
- p.end();
- return pixmap.scaledToWidth(width(), Qt::SmoothTransformation);
- //Returns a scaled copy of the image which is transformed using bilinear filtering
- }
- /*prepare the pixmap */
- void preparePixmap() {
- m_pixmap = QPixmap(size());
- m_pixmap.fill(Qt::transparent);
- //Fills the pixmap with the given transparent black value (i.e., QColor(0, 0, 0, 0))
- QPainter p;
- p.begin(&m_pixmap);
- p.drawPixmap(0, 0, drawDigits(m_number, rect()));
- //Draws the given digits-pixmap at position (0, 0)
- p.end();
- }
- /*define a resize event*/
- void resizeEvent(QResizeEvent*) {
- preparePixmap();
- update();//Causes a paintEvent() call
- }
- /*Paint the static state*/
- void paintStatic() {
- QPainter p(this);
- p.fillRect(rect(), Qt::black);
- //Fill the widget rec with black color
- int pad = width() / 10;
- drawFrame(&p, rect().adjusted(pad, pad, -pad, -pad));
- p.drawPixmap(0, 0, m_pixmap);
- }
- /*Paint the slide state*/
- void paintSlide() {
- QPainter p(this);
- p.fillRect(rect(), Qt::black);
- int pad = width() / 10;
- QRect fr = rect().adjusted(pad, pad, -pad, -pad);
- drawFrame(&p, fr);
- p.setClipRect(fr);
- //sets the clip region to the given rectangle using the given clip operation
- int y = height() * m_animator.currentFrame() / 100;
- p.drawPixmap(0, y, m_lastPixmap);
- //Draw last-time state pixmap from 0 to height()(Y Coordinate) in 0.6 second
- p.drawPixmap(0, y - height(), m_pixmap);
- //Draw current-time state pixmap from -height() to 0 (Y Coordinate) in 0.6 second
- }
- /*Paint the flip state*/
- void paintFlip() {
- QPainter p(this);
- #if !defined(Q_OS_SYMBIAN) && !defined(Q_OS_WINCE_WM)
- p.setRenderHint(QPainter::SmoothPixmapTransform, true);
- p.setRenderHint(QPainter::Antialiasing, true);
- #endif
- p.fillRect(rect(), Qt::black);
- int hw = width() / 2;
- int hh = height() / 2;
- // behind is the new pixmap
- int pad = width() / 10;
- QRect fr = rect().adjusted(pad, pad, -pad, -pad);
- drawFrame(&p, fr);
- p.drawPixmap(0, 0, m_pixmap);
- int index = m_animator.currentFrame();
- if (index <= 50) {
- // the top part of the old pixmap is flipping
- int angle = -180 * index / 100;
- QTransform transform;
- transform.translate(hw, hh);
- //Moves the coordinate system to the center of widget
- transform.rotate(angle, Qt::XAxis);
- //Rotates the coordinate system counterclockwise by angle about the X axis
- p.setTransform(transform);
- drawFrame(&p, fr.adjusted(-hw, -hh, -hw, -hh));
- p.drawPixmap(-hw, -hh, m_lastPixmap);
- // the bottom part is still the old pixmap
- p.resetTransform();
- p.setClipRect(0, hh, width(), hh);
- //Enables clipping, and sets the clip region to the rectangle beginning at (0, hh) with the given width and height
- drawFrame(&p, fr);
- p.drawPixmap(0, 0, m_lastPixmap);
- } else {
- p.setClipRect(0, hh, width(), hh);
- // the bottom part is still the old pixmap
- drawFrame(&p, fr);
- p.drawPixmap(0, 0, m_lastPixmap);
- // the bottom part of the new pixmap is flipping
- int angle = 180 - 180 * m_animator.currentFrame() / 100;
- QTransform transform;
- transform.translate(hw, hh);
- transform.rotate(angle, Qt::XAxis);
- p.setTransform(transform);
- drawFrame(&p, fr.adjusted(-hw, -hh, -hw, -hh));
- p.drawPixmap(-hw, -hh, m_pixmap);
- }
- }
- /*Paint the rotate state*/
- void paintRotate() {
- QPainter p(this);
- int pad = width() / 10;
- QRect fr = rect().adjusted(pad, pad, -pad, -pad);
- drawFrame(&p, fr);
- p.setClipRect(fr);
- int angle1 = -180 * m_animator.currentFrame() / 100;
- int angle2 = 180 - 180 * m_animator.currentFrame() / 100;
- int angle = (m_animator.currentFrame() <= 50) ? angle1 : angle2;
- QPixmap pix = (m_animator.currentFrame() <= 50) ? m_lastPixmap : m_pixmap;
- QTransform transform;
- transform.translate(width() / 2, height() / 2);
- transform.rotate(angle, Qt::XAxis);
- p.setTransform(transform);
- p.setRenderHint(QPainter::SmoothPixmapTransform, true);
- p.drawPixmap(-width() / 2, -height() / 2, pix);
- }
- void paintEvent(QPaintEvent *event) {
- Q_UNUSED(event);
- if (m_animator.state() == QTimeLine::Running) {
- if (m_transition == Slide)
- paintSlide();
- if (m_transition == Flip)
- paintFlip();
- if (m_transition == Rotate)
- paintRotate();
- } else {
- paintStatic();
- }
- }
- private:
- int m_number;//number to set to digits
- int m_transition;//transition mode(change effect)
- QPixmap m_pixmap;//current time pixmap
- QPixmap m_lastPixmap;//next state time pixmap
- QTimeLine m_animator;
- //used to animate a GUI control by calling a slot periodically
- //The timeline's duration describes for how long the animation will run
- //connect the frameChanged() signal to a suitable slot in the widget you wish to animate
- };
- class DigiFlip : public QMainWindow
- {
- Q_OBJECT
- public:
- DigiFlip(QWidget *parent = 0)
- : QMainWindow(parent)
- {
- m_hour = new Digits(this);
- m_hour->show();
- m_minute = new Digits(this);
- m_minute->show();
- QPalette pal = palette();
- pal.setColor(QPalette::Window, Qt::black);
- //Sets the color used for the given color role, in all color groups, to the specified solid color.
- setPalette(pal);
- m_ticker.start(1000, this);
- //Send a timer event every second
- QTime t = QTime::currentTime();
- m_hour->setNumber(t.hour());
- m_minute->setNumber(t.minute());
- updateTime();
- QAction *slideAction = new QAction("&Slide", this);
- QAction *flipAction = new QAction("&Flip", this);
- QAction *rotateAction = new QAction("&Rotate", this);
- connect(slideAction, SIGNAL(triggered()), SLOT(chooseSlide()));
- connect(flipAction, SIGNAL(triggered()), SLOT(chooseFlip()));
- connect(rotateAction, SIGNAL(triggered()), SLOT(chooseRotate()));
- #if defined(Q_OS_SYMBIAN) || defined(Q_OS_WINCE_WM)
- menuBar()->addAction(slideAction);
- menuBar()->addAction(flipAction);
- menuBar()->addAction(rotateAction);
- #else
- addAction(slideAction);
- addAction(flipAction);
- addAction(rotateAction);
- setContextMenuPolicy(Qt::ActionsContextMenu);
- //Shows a context menu(right click)
- #endif
- }
- /*Real-time updates*/
- void updateTime() {
- QTime t = QTime::currentTime();
- m_hour->flipTo(t.hour());
- m_minute->flipTo(t.minute());
- QString str = t.toString("hh:mm:ss");
- str.prepend(": ");
- if (m_hour->transition() == Digits::Slide)
- str.prepend("Slide");
- if (m_hour->transition() == Digits::Flip)
- str.prepend("Flip");
- if (m_hour->transition() == Digits::Rotate)
- str.prepend("Rotate");
- setWindowTitle(str);
- }
- /*Switch transition mode*/
- void switchTransition(int delta) {
- int i = (m_hour->transition() + delta + 3) % 3;
- m_hour->setTransition(i);
- m_minute->setTransition(i);
- updateTime();
- }
- protected:
- void resizeEvent(QResizeEvent*) {
- int digitsWidth = width() / 2;
- int digitsHeight = digitsWidth * 1.2;
- int y = (height() - digitsHeight) / 3;
- m_hour->resize(digitsWidth, digitsHeight);
- m_hour->move(0, y);
- m_minute->resize(digitsWidth, digitsHeight);
- m_minute->move(width() / 2, y);
- }
- /*Timer event,receive timer events */
- void timerEvent(QTimerEvent*) {
- updateTime();
- }
- /* Get key press event */
- void keyPressEvent(QKeyEvent *event) {
- if (event->key() == Qt::Key_Right) {
- switchTransition(1);
- event->accept();
- }
- if (event->key() == Qt::Key_Left) {
- switchTransition(-1);
- event->accept();
- }
- }
- private slots:
- void chooseSlide() {
- m_hour->setTransition(0);
- m_minute->setTransition(0);
- updateTime();
- }
- void chooseFlip() {
- m_hour->setTransition(1);
- m_minute->setTransition(1);
- updateTime();
- }
- void chooseRotate() {
- m_hour->setTransition(2);
- m_minute->setTransition(2);
- updateTime();
- }
- private:
- QBasicTimer m_ticker;
- Digits *m_hour;
- Digits *m_minute;
- };
- #include "digiflip.moc"
- int main(int argc, char *argv[])
- {
- QApplication app(argc, argv);
- DigiFlip time;
- //#if defined(Q_OS_SYMBIAN) || defined(Q_OS_WINCE_WM)
- // time.showFullScreen();
- //#else
- time.resize(320, 240);
- time.show();
- //#endif
- return app.exec();
- }
以下为程序截图:
http://blog.csdn.net/huihui1988/article/details/5853728#comments