Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QSplashScreen animation
QtWS25 Last Chance

QSplashScreen animation

Scheduled Pinned Locked Moved Solved General and Desktop
splashscreenanimation
13 Posts 5 Posters 7.1k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • J.HilkJ Online
    J.HilkJ Online
    J.Hilk
    Moderators
    wrote on last edited by
    #1

    Hello everyone, a simple question, I believe.

    I have Q QSplashScreen for my application, to bridge loadup and show some credits, firm logo etc.

    To give it a bit of an extra flair, I derived QSplashScreen to add an animation.

    The problem, while the app is setting itself up, the SplashScreen, visible to the animation, freezes.

    Even placed in a seperate Thread I have to call QApplication::processEvents() during the Setup to see anything of my animation.
    I was surpised it compiled at all, as there is the only one MainThread my handle the UI thing.

    The problem is that ui->setupUi(this); in my classes takes a significant amount of time that results in an obvious freeze of the QSplashScreen.

    So my question is it possible to have an animated QSplashscreen without regulary calling QApplication::processEvents(); Or do I have to make it a seperated program, start is started before my main programm to have that effect?

    Code example:

    //Main
    QElapsedTimer time; time.start();
    
        QApplication::setAttribute(Qt::AA_Use96Dpi);
        QApplication a(argc, argv);
    
        QImage pMap (":/images/Logo.png");
        QPainter painter(&pMap);
        QString credits("Credits");
        QFont f = painter.font(); f.setPixelSize(16); painter.setFont(f);
        QFontMetrics fMetric (painter.font());
        QRect rec = fMetric.boundingRect(credits);
        painter.drawText((pMap.width()-rec.width())/2,(pMap.height()-rec.height()*1),credits);
    
        jhSplashScreen *splash = new jhSplashScreen();
        splash->setImage(pMap);
    
        splash->setWindowFlags( Qt::WindowStaysOnTopHint | Qt::SplashScreen | Qt::FramelessWindowHint );
    
        splash->show();
    
        QThread *t = new QThread();
        splash->moveToThread(t);
        QObject::connect(t, &QThread::started, splash, &jhSplashScreen::init);
        QObject::connect(t, &QThread::finished, splash, &jhSplashScreen::deleteLater);
        QObject::connect(t, &QThread::finished, t,      &QThread::deleteLater);
        t->start();
    
        myApp w;
    
        if(!w.lock())
            return -42;
    
        while(time.elapsed() < 5450){
            QApplication::processEvents();
        }
    
        w.show();
        t->quit();
    
        return a.exec();
    

    Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


    Q: What's that?
    A: It's blue light.
    Q: What does it do?
    A: It turns blue.

    1 Reply Last reply
    0
    • J.HilkJ Online
      J.HilkJ Online
      J.Hilk
      Moderators
      wrote on last edited by
      #4

      So, I think I found a solution that works.
      I'm not sure however, if it is the most elegant solution.

      I turned the SplshScreen animation into its own program, compiled that as an executable:

      The Splash.exe and my main program have each a QSharedMemory object.
      The QSharedMemory is aparently also freed, when I kill my main program via TaskManager.

      After 5 seconds, I check from my splash app if the Object was created/is maintained from my main program. If it isn't -> cancel program else wait for cancle from main program.

      jhSplash::jhSplash(QWidget *parent) : QWidget(parent)
      {
          _MainApp = new QSharedMemory("myAppSingleInstance",this);
          this->setWindowFlags( Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::WindowTransparentForInput);
          this->setAttribute(Qt::WA_TranslucentBackground,true);
          
          m_Img = QImage(":/Logo.png");
          
          this->resize(m_Img.size());
          
          m_updateTimer = new QTimer(this);
          connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(repaint()));
          m_updateTimer->start(6);
      
          this->show();
          QTimer::singleShot(5000,this,[=]{m_CheckMemory = true;});
      }
      
      jhSplash::~jhSplash()
      {
      
      }
      
      void jhSplash::paintEvent(QPaintEvent *event)
      {
          int penWidth = 6;
          QRectF oRect (0,0,this->width(), this->height());
          QPainter painter(this);
          painter.setBackgroundMode(Qt::TransparentMode);
          painter.drawImage(0,0,m_Img);
          QString credits("Credits");
          QFont f = painter.font(); f.setPixelSize(16); painter.setFont(f);
          QFontMetrics fMetric (painter.font());
          QRect rec = fMetric.boundingRect(credits);
          painter.drawText((oRect.width()-rec.width())/2,(oRect.height()-rec.height()*1),credits);
      
          QPen pen;
          pen.setWidth(penWidth);
          pen.setColor(Qt::black);
          painter.setPen(pen);
      
          QRect arcRect(penWidth/2,penWidth/2,this->width()-penWidth,this->height()-penWidth);
      
          m_drawAngle = m_drawAngle +16;
          int startAngle = 0+m_drawAngle;
          int spanAngle = (5760 -960)*(-1);
          painter.drawArc(arcRect,startAngle,spanAngle);
      
          pen.setColor(Qt::red);
          painter.setPen(pen);
          startAngle = 0 + m_drawAngle;
          spanAngle  = 960;
          painter.drawArc(arcRect,startAngle,spanAngle);
      
          if(m_CheckMemory){
              if(!_MainApp->attach(QSharedMemory::ReadOnly)){
                  this->close();
              }
              _MainApp->detach();
          }
      
          QWidget::paintEvent(event);
      }
      
      //Main Application main.cpp
      
      QElapsedTimer time; time.start();
      
          QApplication::setAttribute(Qt::AA_Use96Dpi);
          QApplication a(argc, argv);
      
          QProcess splash;
          splash.start("path/to/Splash/Splash.exe");
      
          uConfig w;
      
          if(!w.lock())
              return -42;
      
          while(time.elapsed() < 5450){
          }
      
          w.show();
          splash.close();
      
          QObject::connect(&a, &QApplication::aboutToQuit, &splash, &QProcess::close);
      
          return a.exec();
      

      Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


      Q: What's that?
      A: It's blue light.
      Q: What does it do?
      A: It turns blue.

      1 Reply Last reply
      4
      • VRoninV Offline
        VRoninV Offline
        VRonin
        wrote on last edited by
        #2

        this

        while(time.elapsed() < 5450){
                QApplication::processEvents();
            }
        

        is useless.

        GUI operations are only allowed in the same thread the QApplication lives so the multi-thread is not feasible.

        I'd say the fastest way here is to move the splash to a separate binary altogether and use QProcess to control it

        "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
        ~Napoleon Bonaparte

        On a crusade to banish setIndexWidget() from the holy land of Qt

        J.HilkJ 1 Reply Last reply
        2
        • VRoninV VRonin

          this

          while(time.elapsed() < 5450){
                  QApplication::processEvents();
              }
          

          is useless.

          GUI operations are only allowed in the same thread the QApplication lives so the multi-thread is not feasible.

          I'd say the fastest way here is to move the splash to a separate binary altogether and use QProcess to control it

          J.HilkJ Online
          J.HilkJ Online
          J.Hilk
          Moderators
          wrote on last edited by
          #3

          @VRonin said in QSplashScreen animation:

          this

          while(time.elapsed() < 5450){
                  QApplication::processEvents();
              }
          

          is useless.

          Actually, not entirely useless, this garanties at least 5 seconds of splashscreen. And processEvents was so only way I managed to update the animation.

          GUI operations are only allowed in the same thread the QApplication lives so the multi-thread is not feasible.

          I'd say the fastest way here is to move the splash to a separate binary altogether and use QProcess to control it

          That was what I suspected. The question I have to that, is there a way to also close the seperated Process if the user terminated the app in an unusual way, e.g. forces a close via taskmanager?


          Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


          Q: What's that?
          A: It's blue light.
          Q: What does it do?
          A: It turns blue.

          1 Reply Last reply
          0
          • J.HilkJ Online
            J.HilkJ Online
            J.Hilk
            Moderators
            wrote on last edited by
            #4

            So, I think I found a solution that works.
            I'm not sure however, if it is the most elegant solution.

            I turned the SplshScreen animation into its own program, compiled that as an executable:

            The Splash.exe and my main program have each a QSharedMemory object.
            The QSharedMemory is aparently also freed, when I kill my main program via TaskManager.

            After 5 seconds, I check from my splash app if the Object was created/is maintained from my main program. If it isn't -> cancel program else wait for cancle from main program.

            jhSplash::jhSplash(QWidget *parent) : QWidget(parent)
            {
                _MainApp = new QSharedMemory("myAppSingleInstance",this);
                this->setWindowFlags( Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::WindowTransparentForInput);
                this->setAttribute(Qt::WA_TranslucentBackground,true);
                
                m_Img = QImage(":/Logo.png");
                
                this->resize(m_Img.size());
                
                m_updateTimer = new QTimer(this);
                connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(repaint()));
                m_updateTimer->start(6);
            
                this->show();
                QTimer::singleShot(5000,this,[=]{m_CheckMemory = true;});
            }
            
            jhSplash::~jhSplash()
            {
            
            }
            
            void jhSplash::paintEvent(QPaintEvent *event)
            {
                int penWidth = 6;
                QRectF oRect (0,0,this->width(), this->height());
                QPainter painter(this);
                painter.setBackgroundMode(Qt::TransparentMode);
                painter.drawImage(0,0,m_Img);
                QString credits("Credits");
                QFont f = painter.font(); f.setPixelSize(16); painter.setFont(f);
                QFontMetrics fMetric (painter.font());
                QRect rec = fMetric.boundingRect(credits);
                painter.drawText((oRect.width()-rec.width())/2,(oRect.height()-rec.height()*1),credits);
            
                QPen pen;
                pen.setWidth(penWidth);
                pen.setColor(Qt::black);
                painter.setPen(pen);
            
                QRect arcRect(penWidth/2,penWidth/2,this->width()-penWidth,this->height()-penWidth);
            
                m_drawAngle = m_drawAngle +16;
                int startAngle = 0+m_drawAngle;
                int spanAngle = (5760 -960)*(-1);
                painter.drawArc(arcRect,startAngle,spanAngle);
            
                pen.setColor(Qt::red);
                painter.setPen(pen);
                startAngle = 0 + m_drawAngle;
                spanAngle  = 960;
                painter.drawArc(arcRect,startAngle,spanAngle);
            
                if(m_CheckMemory){
                    if(!_MainApp->attach(QSharedMemory::ReadOnly)){
                        this->close();
                    }
                    _MainApp->detach();
                }
            
                QWidget::paintEvent(event);
            }
            
            //Main Application main.cpp
            
            QElapsedTimer time; time.start();
            
                QApplication::setAttribute(Qt::AA_Use96Dpi);
                QApplication a(argc, argv);
            
                QProcess splash;
                splash.start("path/to/Splash/Splash.exe");
            
                uConfig w;
            
                if(!w.lock())
                    return -42;
            
                while(time.elapsed() < 5450){
                }
            
                w.show();
                splash.close();
            
                QObject::connect(&a, &QApplication::aboutToQuit, &splash, &QProcess::close);
            
                return a.exec();
            

            Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


            Q: What's that?
            A: It's blue light.
            Q: What does it do?
            A: It turns blue.

            1 Reply Last reply
            4
            • VRoninV Offline
              VRoninV Offline
              VRonin
              wrote on last edited by VRonin
              #5

              While I like your solution best, below an alternative based on a heartbeat socket:

              Splash App

              splashwidget.h

              #ifndef SPLASHWIDGET_H
              #define SPLASHWIDGET_H
              
              #include <QWidget>
              #include <QTimer>
              #include <QLocalSocket>
              #include <QApplication>
              #include <QDataStream>
              class SplashWidget : public QWidget
              {
                  Q_OBJECT
                  Q_DISABLE_COPY(SplashWidget)
                  enum {ContinueChar = 88};
              public:
                  SplashWidget(const QString& serverName, QWidget* parent= Q_NULLPTR)
                      :QWidget(parent)
                      ,m_serverName(serverName)
                  {
                      init();
                  }
                  virtual ~SplashWidget() {}
              protected:
                  void init(){
                      setWindowFlags( Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::WindowTransparentForInput);
                      setAttribute(Qt::WA_TranslucentBackground,true);
                      m_hbTimer = new QTimer(this);
                      connect(m_hbTimer,&QTimer::timeout,this,&SplashWidget::resetTimer);
                      m_hbTimer->setInterval(1000); // 1 sec
                      m_hbReceiver = new QLocalSocket(this);
                      connect(m_hbReceiver, static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error),qApp,&QApplication::quit);
                      connect(m_hbReceiver, &QLocalSocket::disconnected,qApp,&QApplication::quit);
                      connect(m_hbReceiver, &QLocalSocket::readyRead, this, &SplashWidget::resetTimer);
                      connect(m_hbReceiver, &QLocalSocket::connected, m_hbTimer, static_cast<void(QTimer::*)()>(&QTimer::start));
                      m_hbReceiver->connectToServer(m_serverName,QIODevice::ReadOnly);
                  }
                  Q_SLOT void resetTimer(){
                      if(m_hbReceiver->bytesAvailable()>=sizeof(qint8)){
                          QDataStream hbin(m_hbReceiver);
                          hbin.setVersion(QDataStream::Qt_5_0);
                          qint8 readHb=0;
                          hbin >> readHb;
                          if(readHb == static_cast<qint8>(ContinueChar))
                              return m_hbTimer->start();
                          qApp->quit();
                      }
                  }
              private:
                  const QString m_serverName;
                  QTimer* m_hbTimer;
                  QLocalSocket* m_hbReceiver;
              
              };
              #endif
              

              main.cpp

              #include "splashwidget.h"
              #include <QApplication>
              
              int main(int argc, char *argv[])
              {
                  QApplication a(argc, argv);
                  const QStringList argList = a.arguments();
                  if(argList.size()<2)
                      return -1;
                  if(argList.at(1).isEmpty())
                      return -1;
                  SplashWidget w(argList.at(1));
                  w.show();
              
                  return a.exec();
              }
              

              Main Application

              splashcontroller.h

              #ifndef SPLASHCONTROLLER_H
              #define SPLASHCONTROLLER_H
              #include <QObject>
              #include <QUuid>
              #include <QLocalServer>
              #include <QLocalSocket>
              #include <QTimer>
              #include <QProcess>
              #include <QDataStream>
              class SplashController : public QObject
              {
                  Q_OBJECT
                      Q_DISABLE_COPY(SplashController)
              public:
                  SplashController(QObject* parent = Q_NULLPTR)
                      :QObject(parent)
                      , m_localSocket(Q_NULLPTR)
                      , m_localServer(Q_NULLPTR)
                      , m_splashProcess(Q_NULLPTR)
                      , m_splashProcessPath("SplashScreenApp")
                  {
                      m_hbTimer = new QTimer(this);
                      m_hbTimer->setInterval(500);
                  }
                  QString splashProcessPath() const
                  {
                      return m_splashProcessPath;
                  }
              
                  void setSplashProcessPath(const QString &splashProcessPath)
                  {
                      m_splashProcessPath = splashProcessPath;
                  }
                  Q_SLOT void start()
                  {
                      if (m_localServer)
                          return;
                      const QString serverName = QUuid::createUuid().toString();
                      m_localSocket = Q_NULLPTR;
                      m_localServer = new QLocalServer(this);
                      if (!m_localServer->listen(serverName)) {
                          m_localServer->deleteLater();
                          m_localServer = Q_NULLPTR;
                          error();
                      }
                      QObject::connect(m_localServer, &QLocalServer::newConnection, this, &SplashController::splashConnected);
                      m_splashProcess = new QProcess(this);
                      QObject::connect(m_splashProcess, &QProcess::errorOccurred, this, &SplashController::stopComponents);
                      QObject::connect(m_splashProcess, &QProcess::errorOccurred, this, &SplashController::error);
                      QObject::connect(m_splashProcess, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &SplashController::stopComponents);
                      QObject::connect(m_splashProcess, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &SplashController::error);
                      m_splashProcess->start(m_splashProcessPath, QStringList(serverName));
              
                  }
                  Q_SLOT void stop()
                  {
                      stopComponents();
                      finished();
                  }
                  Q_SIGNAL void error();
                  Q_SIGNAL void finished();
              protected:
                  Q_SLOT void stopComponents()
                  {
                      if (!m_localServer)
                          return;
                      m_hbTimer->stop();
                      disconnect(m_splashProcess);
                      disconnect(m_localServer);
                      if (m_localSocket)
                          disconnect(m_localSocket);
                      m_splashProcess->deleteLater();
                      m_localServer->deleteLater();
                      m_splashProcess = Q_NULLPTR;
                      m_localServer = Q_NULLPTR;
                      m_localSocket = Q_NULLPTR;
                  }
              
                  Q_SLOT void splashConnected()
                  {
                      if (m_localSocket)
                          return;
                      m_localSocket = m_localServer->nextPendingConnection();
                      QObject::connect(m_localSocket, &QLocalSocket::disconnected, this, &SplashController::stopComponents);
                      QObject::connect(m_localSocket, &QLocalSocket::disconnected, this, &SplashController::error);
                      QObject::connect(m_localSocket, static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error), this, &SplashController::stopComponents);
                      QObject::connect(m_localSocket, static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error), this, &SplashController::error);
                      QObject::connect(m_hbTimer, &QTimer::timeout, this, &SplashController::spalshHB);
                      m_hbTimer->start();
                  }
                  Q_SLOT void spalshHB()
                  {
                      if (!m_localSocket)
                          return;
                      QDataStream hbin(m_localSocket);
                      hbin.setVersion(QDataStream::Qt_5_0);
                      hbin << static_cast<qint8>(88);
                  }
              
              private:
                  QTimer* m_hbTimer;
                  QLocalServer* m_localServer;
                  QLocalSocket* m_localSocket;
                  QProcess* m_splashProcess;
                  QString m_splashProcessPath;
              };
              
              #endif // SPLASHCONTROLLER_H
              

              an example of main.cpp

              #include <QApplication>
              #include <QThread>
              #include "splashcontroller.h"
              
              int main(int argc, char *argv[])
              {
              
                  QApplication app(argc, argv);
                  QThread splashThread;
                  SplashController* splashScreen = new SplashController;
                  splashScreen->setSplashProcessPath("C:/temp/SplashScreenApp");
                  splashScreen->moveToThread(&splashThread);
                  QObject::connect(splashScreen, &SplashController::finished, &splashThread, &QThread::quit);
                  QObject::connect(splashScreen, &SplashController::error, &splashThread, &QThread::quit);
                  QObject::connect(splashScreen, &SplashController::finished, splashScreen, &SplashController::deleteLater);
                  QObject::connect(splashScreen, &SplashController::error, splashScreen, &SplashController::deleteLater);
                  QObject::connect(splashScreen, &SplashController::finished, [&splashScreen]()->void {splashScreen = Q_NULLPTR; });
                  QObject::connect(splashScreen, &SplashController::error, [&splashScreen]()->void {splashScreen = Q_NULLPTR; });
                  QObject::connect(&splashThread, &QThread::started, splashScreen, &SplashController::start);
                  splashThread.start();
                  QTimer::singleShot(10000, [&splashScreen]()->void {
                      QMetaObject::invokeMethod(splashScreen, "stop", Qt::QueuedConnection);
                  });
                  QTimer::singleShot(10100, &app, &QApplication::quit);
                  return app.exec();
              }
              

              Of course you'll have to subclass SplashWidget and implement your own paint

              "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
              ~Napoleon Bonaparte

              On a crusade to banish setIndexWidget() from the holy land of Qt

              J.HilkJ 1 Reply Last reply
              3
              • VRoninV VRonin

                While I like your solution best, below an alternative based on a heartbeat socket:

                Splash App

                splashwidget.h

                #ifndef SPLASHWIDGET_H
                #define SPLASHWIDGET_H
                
                #include <QWidget>
                #include <QTimer>
                #include <QLocalSocket>
                #include <QApplication>
                #include <QDataStream>
                class SplashWidget : public QWidget
                {
                    Q_OBJECT
                    Q_DISABLE_COPY(SplashWidget)
                    enum {ContinueChar = 88};
                public:
                    SplashWidget(const QString& serverName, QWidget* parent= Q_NULLPTR)
                        :QWidget(parent)
                        ,m_serverName(serverName)
                    {
                        init();
                    }
                    virtual ~SplashWidget() {}
                protected:
                    void init(){
                        setWindowFlags( Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::WindowTransparentForInput);
                        setAttribute(Qt::WA_TranslucentBackground,true);
                        m_hbTimer = new QTimer(this);
                        connect(m_hbTimer,&QTimer::timeout,this,&SplashWidget::resetTimer);
                        m_hbTimer->setInterval(1000); // 1 sec
                        m_hbReceiver = new QLocalSocket(this);
                        connect(m_hbReceiver, static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error),qApp,&QApplication::quit);
                        connect(m_hbReceiver, &QLocalSocket::disconnected,qApp,&QApplication::quit);
                        connect(m_hbReceiver, &QLocalSocket::readyRead, this, &SplashWidget::resetTimer);
                        connect(m_hbReceiver, &QLocalSocket::connected, m_hbTimer, static_cast<void(QTimer::*)()>(&QTimer::start));
                        m_hbReceiver->connectToServer(m_serverName,QIODevice::ReadOnly);
                    }
                    Q_SLOT void resetTimer(){
                        if(m_hbReceiver->bytesAvailable()>=sizeof(qint8)){
                            QDataStream hbin(m_hbReceiver);
                            hbin.setVersion(QDataStream::Qt_5_0);
                            qint8 readHb=0;
                            hbin >> readHb;
                            if(readHb == static_cast<qint8>(ContinueChar))
                                return m_hbTimer->start();
                            qApp->quit();
                        }
                    }
                private:
                    const QString m_serverName;
                    QTimer* m_hbTimer;
                    QLocalSocket* m_hbReceiver;
                
                };
                #endif
                

                main.cpp

                #include "splashwidget.h"
                #include <QApplication>
                
                int main(int argc, char *argv[])
                {
                    QApplication a(argc, argv);
                    const QStringList argList = a.arguments();
                    if(argList.size()<2)
                        return -1;
                    if(argList.at(1).isEmpty())
                        return -1;
                    SplashWidget w(argList.at(1));
                    w.show();
                
                    return a.exec();
                }
                

                Main Application

                splashcontroller.h

                #ifndef SPLASHCONTROLLER_H
                #define SPLASHCONTROLLER_H
                #include <QObject>
                #include <QUuid>
                #include <QLocalServer>
                #include <QLocalSocket>
                #include <QTimer>
                #include <QProcess>
                #include <QDataStream>
                class SplashController : public QObject
                {
                    Q_OBJECT
                        Q_DISABLE_COPY(SplashController)
                public:
                    SplashController(QObject* parent = Q_NULLPTR)
                        :QObject(parent)
                        , m_localSocket(Q_NULLPTR)
                        , m_localServer(Q_NULLPTR)
                        , m_splashProcess(Q_NULLPTR)
                        , m_splashProcessPath("SplashScreenApp")
                    {
                        m_hbTimer = new QTimer(this);
                        m_hbTimer->setInterval(500);
                    }
                    QString splashProcessPath() const
                    {
                        return m_splashProcessPath;
                    }
                
                    void setSplashProcessPath(const QString &splashProcessPath)
                    {
                        m_splashProcessPath = splashProcessPath;
                    }
                    Q_SLOT void start()
                    {
                        if (m_localServer)
                            return;
                        const QString serverName = QUuid::createUuid().toString();
                        m_localSocket = Q_NULLPTR;
                        m_localServer = new QLocalServer(this);
                        if (!m_localServer->listen(serverName)) {
                            m_localServer->deleteLater();
                            m_localServer = Q_NULLPTR;
                            error();
                        }
                        QObject::connect(m_localServer, &QLocalServer::newConnection, this, &SplashController::splashConnected);
                        m_splashProcess = new QProcess(this);
                        QObject::connect(m_splashProcess, &QProcess::errorOccurred, this, &SplashController::stopComponents);
                        QObject::connect(m_splashProcess, &QProcess::errorOccurred, this, &SplashController::error);
                        QObject::connect(m_splashProcess, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &SplashController::stopComponents);
                        QObject::connect(m_splashProcess, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &SplashController::error);
                        m_splashProcess->start(m_splashProcessPath, QStringList(serverName));
                
                    }
                    Q_SLOT void stop()
                    {
                        stopComponents();
                        finished();
                    }
                    Q_SIGNAL void error();
                    Q_SIGNAL void finished();
                protected:
                    Q_SLOT void stopComponents()
                    {
                        if (!m_localServer)
                            return;
                        m_hbTimer->stop();
                        disconnect(m_splashProcess);
                        disconnect(m_localServer);
                        if (m_localSocket)
                            disconnect(m_localSocket);
                        m_splashProcess->deleteLater();
                        m_localServer->deleteLater();
                        m_splashProcess = Q_NULLPTR;
                        m_localServer = Q_NULLPTR;
                        m_localSocket = Q_NULLPTR;
                    }
                
                    Q_SLOT void splashConnected()
                    {
                        if (m_localSocket)
                            return;
                        m_localSocket = m_localServer->nextPendingConnection();
                        QObject::connect(m_localSocket, &QLocalSocket::disconnected, this, &SplashController::stopComponents);
                        QObject::connect(m_localSocket, &QLocalSocket::disconnected, this, &SplashController::error);
                        QObject::connect(m_localSocket, static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error), this, &SplashController::stopComponents);
                        QObject::connect(m_localSocket, static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error), this, &SplashController::error);
                        QObject::connect(m_hbTimer, &QTimer::timeout, this, &SplashController::spalshHB);
                        m_hbTimer->start();
                    }
                    Q_SLOT void spalshHB()
                    {
                        if (!m_localSocket)
                            return;
                        QDataStream hbin(m_localSocket);
                        hbin.setVersion(QDataStream::Qt_5_0);
                        hbin << static_cast<qint8>(88);
                    }
                
                private:
                    QTimer* m_hbTimer;
                    QLocalServer* m_localServer;
                    QLocalSocket* m_localSocket;
                    QProcess* m_splashProcess;
                    QString m_splashProcessPath;
                };
                
                #endif // SPLASHCONTROLLER_H
                

                an example of main.cpp

                #include <QApplication>
                #include <QThread>
                #include "splashcontroller.h"
                
                int main(int argc, char *argv[])
                {
                
                    QApplication app(argc, argv);
                    QThread splashThread;
                    SplashController* splashScreen = new SplashController;
                    splashScreen->setSplashProcessPath("C:/temp/SplashScreenApp");
                    splashScreen->moveToThread(&splashThread);
                    QObject::connect(splashScreen, &SplashController::finished, &splashThread, &QThread::quit);
                    QObject::connect(splashScreen, &SplashController::error, &splashThread, &QThread::quit);
                    QObject::connect(splashScreen, &SplashController::finished, splashScreen, &SplashController::deleteLater);
                    QObject::connect(splashScreen, &SplashController::error, splashScreen, &SplashController::deleteLater);
                    QObject::connect(splashScreen, &SplashController::finished, [&splashScreen]()->void {splashScreen = Q_NULLPTR; });
                    QObject::connect(splashScreen, &SplashController::error, [&splashScreen]()->void {splashScreen = Q_NULLPTR; });
                    QObject::connect(&splashThread, &QThread::started, splashScreen, &SplashController::start);
                    splashThread.start();
                    QTimer::singleShot(10000, [&splashScreen]()->void {
                        QMetaObject::invokeMethod(splashScreen, "stop", Qt::QueuedConnection);
                    });
                    QTimer::singleShot(10100, &app, &QApplication::quit);
                    return app.exec();
                }
                

                Of course you'll have to subclass SplashWidget and implement your own paint

                J.HilkJ Online
                J.HilkJ Online
                J.Hilk
                Moderators
                wrote on last edited by
                #6

                @VRonin Thanks for the detailed and complex answer! I'll defenitly look into your heartbeat approach a bit more!

                I wanted to add a few lines, of code that I forgot in my post above.

                The QShared Memory part of the main-app main.cpp:

                 QApplication::setAttribute(Qt::AA_Use96Dpi);
                    QApplication a(argc, argv);
                //!New Part start
                QSharedMemory _singular("myAppSingleInstance");
                    if(_singular.attach(QSharedMemory::ReadOnly)){
                        _singular.detach();
                        return -42;
                    }else{
                        _singular.create(1);
                    }
                //!New Part End
                    QProcess 
                

                And also a small addition, so that noone accidentally starts Splash.exe without a main App:

                //Main Application, main.cpp
                QProcess splash;
                QStringList s{"openByApp"};
                splash.start("path/to/Splash/Splash.exe",s);
                
                //Splash App, main.cpp
                QApplication a(argc, argv);
                    if(a.arguments().last() != "openByApp"){
                        return -42;
                    }
                

                Also on a second thought, this would also be the perfect situtation to test Qt 5.9's brand new Technology Preview Module : Qt Remote Objects ?

                It seems to fit, according to the quick description I read.


                Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                Q: What's that?
                A: It's blue light.
                Q: What does it do?
                A: It turns blue.

                VRoninV 1 Reply Last reply
                0
                • J.HilkJ J.Hilk

                  @VRonin Thanks for the detailed and complex answer! I'll defenitly look into your heartbeat approach a bit more!

                  I wanted to add a few lines, of code that I forgot in my post above.

                  The QShared Memory part of the main-app main.cpp:

                   QApplication::setAttribute(Qt::AA_Use96Dpi);
                      QApplication a(argc, argv);
                  //!New Part start
                  QSharedMemory _singular("myAppSingleInstance");
                      if(_singular.attach(QSharedMemory::ReadOnly)){
                          _singular.detach();
                          return -42;
                      }else{
                          _singular.create(1);
                      }
                  //!New Part End
                      QProcess 
                  

                  And also a small addition, so that noone accidentally starts Splash.exe without a main App:

                  //Main Application, main.cpp
                  QProcess splash;
                  QStringList s{"openByApp"};
                  splash.start("path/to/Splash/Splash.exe",s);
                  
                  //Splash App, main.cpp
                  QApplication a(argc, argv);
                      if(a.arguments().last() != "openByApp"){
                          return -42;
                      }
                  

                  Also on a second thought, this would also be the perfect situtation to test Qt 5.9's brand new Technology Preview Module : Qt Remote Objects ?

                  It seems to fit, according to the quick description I read.

                  VRoninV Offline
                  VRoninV Offline
                  VRonin
                  wrote on last edited by VRonin
                  #7

                  @J.Hilk said in QSplashScreen animation:

                  Also on a second thought, this would also be the perfect situtation to test Qt 5.9's brand new Technology Preview Module : Qt Remote Objects ?

                  My solution uses the principles behind this. It uses the "local" connection and, given the simplicity of the inter-process communication in this case, You probably do not need the overhead of that module. QSharedMemory is perfect in this case even though your current code probably doesn't handle opening the main app twice well

                  A side note for curious readers:
                  You might ask why we are using QSharedMemory or QLocalSocket instead of using stdin/stdout to communicate between processes and the answer is that on Windows stdin is not a socket so you can't use QSocketNotifier to get a sort of readyRead() in the child process. In this specific case you probably can have a thread in the child just stuck on a std::cin call until the server responds, but I'm not going to try it

                  "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                  ~Napoleon Bonaparte

                  On a crusade to banish setIndexWidget() from the holy land of Qt

                  1 Reply Last reply
                  1
                  • 6thC6 Offline
                    6thC6 Offline
                    6thC
                    wrote on last edited by
                    #8

                    Not sure this will help. But I only wished to have messages animate for my splash. And this is what I do:

                    In my main I define a QApplication derived class:

                    MyAppObject app(argc, argv);
                    

                    I construct the object, I have an init method that emits progress signal messages such as:

                    message="Reading configurations: " % settings.fileName();
                    emit initMessage(message);
                    

                    Back in main, after app is declared but before app.init():

                        const QImage image(":/Images/Splash.png");
                        QPixmap pixmap = QPixmap::fromImage(image).scaled(QSize(300,300)
                                           ,Qt::AspectRatioMode::KeepAspectRatio
                                           ,Qt::SmoothTransformation);
                        QSplashScreen *splash = new QSplashScreen(pixmap);
                        splash->show();
                    // bind app init messsage signals/updates to splash message
                    QObject::connect(&app, &ClassName::initMessage,
                        [=]( const QString message){
                            int alignment = Qt::AlignCenter;
                            const QColor &color = Qt::darkYellow;
                            splash->showMessage(message, alignment, color);
                            } 
                    // start the fun
                    app.Init();
                    // everything is ready, kill off the splash
                    splash->close();
                    // start the QApp
                    return app.exec();
                    
                    mzimmersM 1 Reply Last reply
                    0
                    • 6thC6 6thC

                      Not sure this will help. But I only wished to have messages animate for my splash. And this is what I do:

                      In my main I define a QApplication derived class:

                      MyAppObject app(argc, argv);
                      

                      I construct the object, I have an init method that emits progress signal messages such as:

                      message="Reading configurations: " % settings.fileName();
                      emit initMessage(message);
                      

                      Back in main, after app is declared but before app.init():

                          const QImage image(":/Images/Splash.png");
                          QPixmap pixmap = QPixmap::fromImage(image).scaled(QSize(300,300)
                                             ,Qt::AspectRatioMode::KeepAspectRatio
                                             ,Qt::SmoothTransformation);
                          QSplashScreen *splash = new QSplashScreen(pixmap);
                          splash->show();
                      // bind app init messsage signals/updates to splash message
                      QObject::connect(&app, &ClassName::initMessage,
                          [=]( const QString message){
                              int alignment = Qt::AlignCenter;
                              const QColor &color = Qt::darkYellow;
                              splash->showMessage(message, alignment, color);
                              } 
                      // start the fun
                      app.Init();
                      // everything is ready, kill off the splash
                      splash->close();
                      // start the QApp
                      return app.exec();
                      
                      mzimmersM Offline
                      mzimmersM Offline
                      mzimmers
                      wrote on last edited by
                      #9

                      Hi guys - I'm going to pull this topic out of mothballs, just to ask...in the intervening years, has there been any developments that would make this task more straightforward? The customer wants an animated splash screen...will have to work on Android as well as desktops.

                      Thanks...

                      J.HilkJ 1 Reply Last reply
                      0
                      • mzimmersM mzimmers

                        Hi guys - I'm going to pull this topic out of mothballs, just to ask...in the intervening years, has there been any developments that would make this task more straightforward? The customer wants an animated splash screen...will have to work on Android as well as desktops.

                        Thanks...

                        J.HilkJ Online
                        J.HilkJ Online
                        J.Hilk
                        Moderators
                        wrote on last edited by
                        #10

                        @mzimmers hi, as far as I know, there is no easier way in Qt than the QProcess one. Due to the nature of Qt and all GUI Stuff in one thread concept. Any long, consecutive, GUI Creation will cause freezes of the animation.

                        -> Standalone SplashScreen executable launched via QProcess.

                        Android is even more complicated. Because you actually have 2 different phases, the launch, which is purely OS handled and then the initialisation of your app where you can influence stuff.

                        You may have noticed when starting a bare bone application on android, it starts with a black screen transitioning into a white one with your application name written on it.

                        You where always able to replace the black screen with a static splash screen of your choice and only with android 12 they added the possibility to make this animated:
                        https://developer.android.com/develop/ui/views/launch/splash-screen

                        IIRC the white one you could make animated in the past


                        Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                        Q: What's that?
                        A: It's blue light.
                        Q: What does it do?
                        A: It turns blue.

                        mzimmersM 1 Reply Last reply
                        0
                        • J.HilkJ J.Hilk

                          @mzimmers hi, as far as I know, there is no easier way in Qt than the QProcess one. Due to the nature of Qt and all GUI Stuff in one thread concept. Any long, consecutive, GUI Creation will cause freezes of the animation.

                          -> Standalone SplashScreen executable launched via QProcess.

                          Android is even more complicated. Because you actually have 2 different phases, the launch, which is purely OS handled and then the initialisation of your app where you can influence stuff.

                          You may have noticed when starting a bare bone application on android, it starts with a black screen transitioning into a white one with your application name written on it.

                          You where always able to replace the black screen with a static splash screen of your choice and only with android 12 they added the possibility to make this animated:
                          https://developer.android.com/develop/ui/views/launch/splash-screen

                          IIRC the white one you could make animated in the past

                          mzimmersM Offline
                          mzimmersM Offline
                          mzimmers
                          wrote on last edited by mzimmers
                          #11

                          @J-Hilk it occurs to me that there's more than one purpose for a splash screen:

                          1. it displays some desired information for a minimum period of time
                          2. it gives the user something to look at while the app is loading

                          What this would be (in non-technical terms) is: the app starts by playing a movie, while all the behind-the-scenes stuff is doing whatever it does. After some pre-set period of time, the movie player will accept a signal to end. This signal would come from the worker. The worker would then take over the display.

                          The difficulty here, IMO, is to get the movie player and the rest of the app to share one window. I'm not sure how I'd go about that.

                          I haven't built for Android in a few months, but as I remember, Qt Creator provides the hooks to install an animated splash screen for Android, no?

                          JoeCFDJ 1 Reply Last reply
                          0
                          • mzimmersM mzimmers

                            @J-Hilk it occurs to me that there's more than one purpose for a splash screen:

                            1. it displays some desired information for a minimum period of time
                            2. it gives the user something to look at while the app is loading

                            What this would be (in non-technical terms) is: the app starts by playing a movie, while all the behind-the-scenes stuff is doing whatever it does. After some pre-set period of time, the movie player will accept a signal to end. This signal would come from the worker. The worker would then take over the display.

                            The difficulty here, IMO, is to get the movie player and the rest of the app to share one window. I'm not sure how I'd go about that.

                            I haven't built for Android in a few months, but as I remember, Qt Creator provides the hooks to install an animated splash screen for Android, no?

                            JoeCFDJ Offline
                            JoeCFDJ Offline
                            JoeCFD
                            wrote on last edited by JoeCFD
                            #12

                            @mzimmers I did it with two loaders inside qml ApplicationWindow on Android. The splash screen part is an animated widget and stopped by a timer.

                            mzimmersM 1 Reply Last reply
                            0
                            • JoeCFDJ JoeCFD

                              @mzimmers I did it with two loaders inside qml ApplicationWindow on Android. The splash screen part is an animated widget and stopped by a timer.

                              mzimmersM Offline
                              mzimmersM Offline
                              mzimmers
                              wrote on last edited by mzimmers
                              #13

                              @JoeCFD yeah, that's what I had in mind (I think). What QML class did you use for your animated widget? (EDIT: I imagine it's AnimatedImage.) And, does yours have a minimum play time before it can be interrupted?

                              1 Reply Last reply
                              0

                              • Login

                              • Login or register to search.
                              • First post
                                Last post
                              0
                              • Categories
                              • Recent
                              • Tags
                              • Popular
                              • Users
                              • Groups
                              • Search
                              • Get Qt Extensions
                              • Unsolved