本文已参与「新人创作礼」活动,一起开启掘金创作之路。
一般需要启动界面的程序会把加载工作放到子线程,在主线程播放启动界面,但是有时候又确实需要在主界面耗时加载一些东西,就没办法播放动态启动界面了,而QT又不支持子线程刷新UI。 所以写了一个多进程启动界面组件。
main.cpp
#include <QApplication>
#include <QWidget>
#include "YSplashScreen.h"
#include <QSplashScreen>
#include <thread>
#include <chrono>
int main(int argc, char* argv[])
{
QApplication application(argc, argv);
QWidget w;
YSplashScreenApplication ssa(w);
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
w.show();
ssa.stop();
return application.exec();
}
YSplashScreen.h
#ifndef YSPLASHSCREEN_H
#define YSPLASHSCREEN_H
#include <QWidget>
#define YSplashScreenApplicationSimple(widOrWidget) \
YSplashScreenApplication __simpleYSplashScreenApplication(widOrWidget);\
Q_UNUSED(__simpleYSplashScreenApplication);
class QProcess;
class YSplashScreenApplication{
public:
~YSplashScreenApplication();
YSplashScreenApplication() = default;
explicit YSplashScreenApplication(WId wid);//默认设置
inline YSplashScreenApplication(QWidget* widget): YSplashScreenApplication(widget->winId()){}
inline YSplashScreenApplication(QWidget& widget): YSplashScreenApplication(widget.winId()){}
void check();//检查是否是启动界面进程
void start(WId wid);//打开启动界面
inline void start(QWidget* widget) { start(widget->winId()); }
inline void start(QWidget& widget) { start(widget.winId()); }
void stop();//关闭启动界面
inline void setSplashScreenWidget(QWidget* widget) { splashScreenWidget = widget; }//自定义设置动态启动界面
private:
WId wid;
void exec(WId wid);
QProcess* process = nullptr;
QWidget* splashScreenWidget = nullptr;
bool isChecked = false;
};
class YSplashScreen : public QWidget
{
public:
explicit YSplashScreen(QWidget *parent = nullptr);
public:
void paintEvent(QPaintEvent *) override;
void timerEvent(QTimerEvent*) override;
int currentIndex = 0;
};
#endif // YSPLASHSCREEN_H
YSplashScreen.cpp
#include "YSplashScreen.h"
#include <QApplication>
#include <QScreen>
#include <QProcess>
#include <QTimer>
#include <QPainter>
YSplashScreenApplication::~YSplashScreenApplication()
{
if(process){
stop();
}
}
YSplashScreenApplication::YSplashScreenApplication(WId wid)
{
check();
start(wid);
}
void YSplashScreenApplication::check()
{
const QStringList& arguments = qApp->arguments();
if(arguments.size() == 3 && arguments.last() == "YSplashScreen"){
WId wid = arguments.at(1).toULongLong();
exec(wid);
}
isChecked = true;
}
void YSplashScreenApplication::start(WId wid)
{
if(!isChecked){
check();
}
process = new QProcess;
process->start(qApp->applicationFilePath(), QStringList{QString::number(wid), "YSplashScreen"});
}
void YSplashScreenApplication::stop()
{
if(process){
process->close();
process->deleteLater();
process = nullptr;
}
}
void YSplashScreenApplication::exec(WId wid)
{
this->wid = wid;
QTimer* timer = new QTimer;
QObject::connect(timer, &QTimer::timeout, [this]{
QScreen* screen = qApp->primaryScreen();
QPixmap pixmap = screen->grabWindow(this->wid, 0, 0, 1, 1);
if(pixmap.isNull()){
qApp->quit();
}
});
timer->start(300);
if(!splashScreenWidget){
setSplashScreenWidget(new YSplashScreen);
}
splashScreenWidget->setWindowFlag(Qt::ToolTip);
splashScreenWidget->setWindowFlag(Qt::FramelessWindowHint);
splashScreenWidget->setAttribute(Qt::WA_TranslucentBackground, true);
splashScreenWidget->setAutoFillBackground(false);
splashScreenWidget->show();
exit(qApp->exec());
}
YSplashScreen::YSplashScreen(QWidget *parent) : QWidget(parent)
{
setAutoFillBackground(false);
setWindowFlag(Qt::ToolTip);
setWindowFlag(Qt::FramelessWindowHint);
setFocusPolicy(Qt::StrongFocus);
setAttribute(Qt::WA_TranslucentBackground, true);
QSize desktopSize = qApp->primaryScreen()->size();
setGeometry((desktopSize.width() - 200) / 2, (desktopSize.height() - 200) / 2, 200, 200);
startTimer(100);
}
void YSplashScreen::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setRenderHints(QPainter::Antialiasing);
painter.setPen(Qt::NoPen);
QLinearGradient gradient(width() / 2, 0, width() / 2, height());
gradient.setColorAt(0, QColor(1,209,255));
gradient.setColorAt(1, QColor(0,135,191));
painter.setBrush(gradient);
painter.drawRoundedRect(rect(), 10, 10);
painter.setPen(Qt::white);
painter.setFont(QFont("楷体", 20, 100));
painter.drawText(QRect(0, 0, 200, 80), Qt::AlignCenter, "正在启动...");
painter.setPen(Qt::NoPen);
painter.translate(100, 130);
painter.setBrush(Qt::white);
painter.rotate(36 * currentIndex);
for(int i = 0; i < 10; ++i){
painter.rotate(-36);
painter.drawEllipse(QRect(33 + i, i - 12, 24 - 2 * i, 24 - 2 * i));
}
}
void YSplashScreen::timerEvent(QTimerEvent *)
{
currentIndex = (currentIndex + 1) % 10;
update();
}