Qt Reference Documentation

main.cpp Example File

demos/glhypnotizer/main.cpp
 /****************************************************************************
 **
 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
 ** Contact: http://www.qt-project.org/legal
 **
 ** This file is part of the demonstration applications of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:LGPL$
 ** Commercial License Usage
 ** Licensees holding valid commercial Qt licenses may use this file in
 ** accordance with the commercial license agreement provided with the
 ** Software or, alternatively, in accordance with the terms contained in
 ** a written agreement between you and Digia.  For licensing terms and
 ** conditions see http://qt.digia.com/licensing.  For further information
 ** use the contact form at http://qt.digia.com/contact-us.
 **
 ** GNU Lesser General Public License Usage
 ** Alternatively, this file may be used under the terms of the GNU Lesser
 ** General Public License version 2.1 as published by the Free Software
 ** Foundation and appearing in the file LICENSE.LGPL included in the
 ** packaging of this file.  Please review the following information to
 ** ensure the GNU Lesser General Public License version 2.1 requirements
 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 **
 ** In addition, as a special exception, Digia gives you certain additional
 ** rights.  These rights are described in the Digia Qt LGPL Exception
 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 **
 ** GNU General Public License Usage
 ** Alternatively, this file may be used under the terms of the GNU
 ** General Public License version 3.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 the GNU General Public License version 3.0 requirements will be
 ** met: http://www.gnu.org/copyleft/gpl.html.
 **
 **
 ** $QT_END_LICENSE$
 **
 ****************************************************************************/

 #include <QtGui>
 #include <QtOpenGL>
 #include <QtSvg>

 #define NUM_SWIRLY_ITEMS 10

 class GLPainter : public QObject
 {
     Q_OBJECT
 public:
     GLPainter(QGLWidget *widget);
     void stop();
     void resizeViewport(const QSize &size);

 public slots:
     void start();

 protected:
     void timerEvent(QTimerEvent *event);
     void paint();
     void initSwirlyItems();
     void updateSwirlyItems();
     void drawSuggestiveMessages(QPainter *p);

 private:
     QMutex mutex;
     QGLWidget *glWidget;
     int viewportWidth;
     int viewportHeight;
     bool doRendering;
     qreal rotationAngle;
     bool swirlClockwise;
     QSvgRenderer svgRenderer;
     QPixmap logo;
     QPainter::PixmapFragment swirlyItems[NUM_SWIRLY_ITEMS];
     int swirlyCounter;
     int textCounter;
     int messageYPos;
     qreal scaleFactor;
 };

 GLPainter::GLPainter(QGLWidget *widget)
     : glWidget(widget)
     , doRendering(true)
     , rotationAngle(rand() % 360)
     , swirlClockwise((rand() % 2) == 1)
     , svgRenderer(QLatin1String(":/spiral.svg"), this)
     , logo(QLatin1String(":/qt-logo.png"))
 {
 }

 void GLPainter::start()
 {
     glWidget->makeCurrent();
     startTimer(20);
 }

 void GLPainter::stop()
 {
     QMutexLocker locker(&mutex);
     doRendering = false;
 }

 void GLPainter::resizeViewport(const QSize &size)
 {
     QMutexLocker locker(&mutex);
     viewportWidth = size.width();
     viewportHeight = size.height();
     initSwirlyItems();
     textCounter = 0;
     messageYPos = -1;
 }

 void GLPainter::timerEvent(QTimerEvent *event)
 {
     QMutexLocker locker(&mutex);
     if (!doRendering) {
         killTimer(event->timerId());
         QThread::currentThread()->quit();
         return;
     }
     updateSwirlyItems();
     paint();
 }

 void GLPainter::paint()
 {
     QPainter p(glWidget);
     p.fillRect(QRect(0, 0, viewportWidth, viewportHeight), Qt::white);
     p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
     p.translate(viewportWidth / 2, viewportHeight / 2);
     p.rotate(rotationAngle * (swirlClockwise ? 1 : -1));
     p.scale(svgRenderer.viewBox().width() / 200.0 * 0.65,
             svgRenderer.viewBox().height() / 200.0 * 0.65);
     p.translate(-viewportWidth / 2, -viewportHeight / 2);
     svgRenderer.render(&p);
     p.resetTransform();
     p.drawPixmapFragments(swirlyItems, NUM_SWIRLY_ITEMS, logo);
     drawSuggestiveMessages(&p);
     p.end();
     rotationAngle += 1.2;
 }

 void GLPainter::drawSuggestiveMessages(QPainter *p)
 {
     const int numSuggestiveMessages = 7;
     const char *texts[numSuggestiveMessages] = {" You feel relaxed.. ",
                                                 " Let your mind wander.. ",
                                                 " Look deep into the swirls.. ",
                                                 " Even deeper.. ",
                                                 " Qt is good! ",
                                                 " Qt is good for you! ",
                                                 " You MUST use Qt! "};
     QFont font(p->font());
     font.setPointSizeF(font.pointSizeF() * viewportWidth/200.0);
     p->setFont(font);
     QFontMetrics fm(font);
     int messageNo = textCounter/314;
     if (messageNo > 6 || messageNo < 0) {
         qFatal("This should not happen: %d - %d", messageNo, textCounter);
     }
     QLatin1String text(texts[textCounter / 314]);
     qreal textWidth = fm.width(text);
     int alpha = 255 * qAbs(qSin(textCounter * 0.01));
     if (messageYPos < 0 || (textCounter % 314 == 0)) {
         messageYPos = qBound(fm.height(), rand() % viewportHeight,
                              viewportHeight - fm.height());
     }
     p->setPen(QColor(255, 255, 255, alpha));
     p->setBackground(QColor(50, 50, 50, qBound(0, alpha, 50)));
     p->setBackgroundMode(Qt::OpaqueMode);
     p->drawText((viewportWidth / 2) - (textWidth/ 2), messageYPos, text);
     ++textCounter;
     if (textCounter >= (314 * numSuggestiveMessages))
         textCounter = 0;
 }

 void GLPainter::initSwirlyItems()
 {
     swirlyCounter = swirlClockwise ? 0 : 360;
     scaleFactor = viewportWidth / 200.0;
     QRectF logoRect(0, 0, logo.width(), logo.height());

     for (int i=0; i<NUM_SWIRLY_ITEMS; ++i) {
         if (swirlClockwise) {
             swirlyItems[i] = QPainter::PixmapFragment::create(QPointF(0, 0), logoRect,
                                                               scaleFactor,
                                                               scaleFactor);
         } else {
             swirlyItems[i] = QPainter::PixmapFragment::create(QPointF(0, 0), logoRect,
                                                               scaleFactor * 1 / 360.0,
                                                               scaleFactor * 1 / 360.0);
         }
     }
 }

 void GLPainter::updateSwirlyItems()
 {
     swirlyCounter += swirlClockwise ? 1 : -1;

     for (int i=0; i<NUM_SWIRLY_ITEMS; ++i) {
         int factor;
         factor = swirlClockwise ? (swirlyCounter - i * 20) : (swirlyCounter + i * 20);
         if (factor > 0 && factor <= 360) {
             swirlyItems[i].x = viewportWidth / 2.0 + qSin(factor * 0.05) * (viewportWidth / 2.0) * (100.0 / factor);
             swirlyItems[i].y = viewportHeight / 2.0 + qCos(factor * 0.05) * (viewportHeight / 2.0) * (100.0 / factor);
             swirlyItems[i].rotation += -swirlyCounter * 0.01;
             swirlyItems[i].scaleX += swirlClockwise ? -scaleFactor * 1 / 360.0 : scaleFactor * 1 / 360.0;
             if (swirlClockwise) {
                 if (swirlyItems[i].scaleX < 0)
                     swirlyItems[i].scaleX = scaleFactor;
             } else {
                 if (swirlyItems[i].scaleX > scaleFactor)
                     swirlyItems[i].scaleX = scaleFactor * 1 / 360.0;
             }
             swirlyItems[i].scaleY = swirlyItems[i].scaleX;
         } else {
             swirlyItems[i].scaleX = swirlyItems[i].scaleY = 0;
         }
     }
     if (swirlClockwise) {
         if (swirlyCounter > (360 + NUM_SWIRLY_ITEMS * 20))
             swirlyCounter = 0;
     } else {
         if (swirlyCounter < -NUM_SWIRLY_ITEMS * 20)
             swirlyCounter = 360;
     }
 }

 class GLWidget : public QGLWidget
 {
 public:
     GLWidget(QWidget *parent, QGLWidget *shareWidget = 0);
     ~GLWidget();
     void startRendering();
     void stopRendering();

 protected:
     void resizeEvent(QResizeEvent *event);
     void paintEvent(QPaintEvent *event);
     QSize sizeHint() const { return QSize(200, 200); }

 private:
     GLPainter glPainter;
     QThread glThread;
 };

 GLWidget::GLWidget(QWidget *parent, QGLWidget *share)
     : QGLWidget(QGLFormat(QGL::SampleBuffers), parent, share)
     , glPainter(this)
     , glThread(this)
 {
     setAttribute(Qt::WA_PaintOutsidePaintEvent);
     setAttribute(Qt::WA_DeleteOnClose);
 }

 GLWidget::~GLWidget()
 {
     stopRendering();
 }

 void GLWidget::startRendering()
 {
     glPainter.moveToThread(&glThread);
     connect(&glThread, SIGNAL(started()), &glPainter, SLOT(start()));
     glThread.start();
 }

 void GLWidget::stopRendering()
 {
     glPainter.stop();
     glThread.wait();
 }

 void GLWidget::resizeEvent(QResizeEvent *event)
 {
     glPainter.resizeViewport(event->size());
 }

 void GLWidget::paintEvent(QPaintEvent *)
 {
     // Handled by GLPainter.
 }

 class MainWindow : public QMainWindow
 {
     Q_OBJECT
 public:
     MainWindow();

 private slots:
     void newThread();
     void killThread();

 private:
     QMdiArea mdiArea;
     QGLWidget shareWidget;
 };

 MainWindow::MainWindow()
     : QMainWindow(0)
     , mdiArea(this)
     , shareWidget(this)
 {
     setWindowTitle("Qt GL Hypnotizer");
     QMenu *menu = menuBar()->addMenu("&Hypnotizers");
     menu->addAction("&New hypnotizer thread", this, SLOT(newThread()), QKeySequence("Ctrl+N"));
     menu->addAction("&End current hypnotizer thread", this, SLOT(killThread()), QKeySequence("Ctrl+K"));
     menu->addSeparator();
     menu->addAction("E&xit", qApp, SLOT(quit()), QKeySequence("Ctrl+Q"));

     setCentralWidget(&mdiArea);
     shareWidget.resize(1, 1);
     newThread();
 }

 void MainWindow::newThread()
 {
     static int windowCount = 1;
     if (mdiArea.subWindowList().count() > 9)
         return;
     GLWidget *widget = new GLWidget(&mdiArea, &shareWidget);
     mdiArea.addSubWindow(widget);
     widget->setWindowTitle("Thread #" + QString::number(windowCount++));
     widget->show();
     widget->startRendering();
 }

 void MainWindow::killThread()
 {
     delete mdiArea.activeSubWindow();
 }

 int main(int argc, char *argv[])
 {
     // Make Xlib and GLX thread safe under X11
     QApplication::setAttribute(Qt::AA_X11InitThreads);
     QApplication application(argc, argv);

     // Using QPainter to draw into QGLWidgets is only supported with GL 2.0
     if (!((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) ||
           (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0))) {
         QMessageBox::information(0, "Qt GL Hypnotizer",
                                  "This system does not support OpenGL 2.0 or OpenGL ES 2.0, "
                                  "which is required for this example to work.");
         return 0;
     }

     MainWindow mainWindow;
     mainWindow.show();
     return application.exec();
 }

 #include "main.moc"