编辑
2025-03-03
记录知识
0

一、前言

  在音频开发中,窗体多半为半透明、圆角窗体,如下为Qt 5.5 VS2013实现半透明方法总结。

二、半透明方法设置

2.1 窗体及子控件都设置为半透明

  • setWindowOpacity(0.8);//参数范围为0-1.0,通过QSlider控件做成透明度控制条

  • 无边框设置 setWindowFlags(Qt::FramelessWindowHint);

  • 窗体圆角设置

setAttribute(Qt::WA_TranslucentBackground);   通过paintEvent绘制窗体背景色与圆角   void Widget::paintEvent(QPaintEvent *event)   {     QPainter painter(this);     painter.setRenderHint(QPainter::Antialiasing); // 反锯齿;     painter.setBrush(QBrush(QColor("#616F76")));  //窗体背景色     painter.setPen(Qt::transparent);     QRect rect = this->rect();        //rect为绘制大小     rect.setWidth(rect.width() - 1);     rect.setHeight(rect.height() - 1);     painter.drawRoundedRect(rect, 15, 15);  //15为圆角角度     //也可用QPainterPath 绘制代替 painter.drawRoundedRect(rect, 15, 15);     //QPainterPath painterPath;     //painterPath.addRoundedRect(rect, 15, 15);//15为圆角角度     //painter.drawPath(painterPath);     QWidget::paintEvent(event);   }

2.2、通过图片贴图,设置局部透明

  • 窗体设置
setAttribute(Qt::WA_TranslucentBackground);//背景半透明属性设置 setWindowFlags(Qt::FramelessWindowHint);//无边框窗体设置
  • 采用样式加载图片
  ui->m_BgWidget->setStyleSheet("background-image:url(:/images/bg.png);");

注意:m_BgWidget为窗体对象的子窗体,不能直接设置QWidget

  • 效果图如下

image.png

2.3 通过paintEvent重绘背景色透明度

  • 窗体属性设置
setAttribute(Qt::WA_TranslucentBackground);//背景半透明属性设置   setWindowFlags(Qt::FramelessWindowHint);//无边框窗体设置   m_BgColor = QColor("#616F76");//默认背景色   m_BgColor.setAlphaF(0.8);   this->setContextMenuPolicy(Qt::CustomContextMenu);   connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),       this, SLOT(showContextMenuSlot(const QPoint &)));//右击出现菜单
  • 右击出现菜单
void Widget::showContextMenuSlot(const QPoint &pos)   {     QAction *act = NULL;     if(NULL == m_Menu)     {       m_Menu = new QMenu();//菜单       m_Actions.clear();//记录所有Action       act = m_Menu->addAction("1.0", this, SLOT(funcSlot()));       m_Actions << act;       act->setCheckable(true);       act = m_Menu->addAction("0.8", this, SLOT(funcSlot()));       m_Actions << act;       act->setCheckable(true);  //设置可选中       act->setChecked(true);  //设置被选中       act = m_Menu->addAction("0.5", this, SLOT(funcSlot()));       m_Actions << act;       act->setCheckable(true);       act = m_Menu->addAction("0.3", this, SLOT(funcSlot()));       m_Actions << act;       act->setCheckable(true);       act = m_Menu->addAction("0.1", this, SLOT(funcSlot()));       m_Actions << act;       act->setCheckable(true);     }     m_Menu->exec(mapToGlobal(pos));//弹出菜单   }
  • 选择菜单Action,修改背景颜色透明度
void Widget::funcSlot()   {     QAction *act = qobject_cast<QAction *>(sender());//获取选中的Action     if(act)     {       double alpha = act->text().toDouble();       m_BgColor.setAlphaF(alpha);//背景色透明度修改       foreach(QAction *action, m_Actions)//去除其余选中,互斥       {         if(act != action)           action->setChecked(false);       }       this->update();//刷新界面     }   }
  • 通过paintEvent重绘背景色
void Widget::paintEvent(QPaintEvent *event)   {     QPainter painter(this);     painter.setRenderHint(QPainter::Antialiasing); // 反锯齿;     painter.setBrush(QBrush(m_BgColor));//修改后的背景色     painter.setPen(Qt::transparent);     QRect rect = this->rect();      //rect为绘制窗体大小     rect.setWidth(rect.width() - 1);     rect.setHeight(rect.height() - 1);     painter.drawRoundedRect(rect, 15, 15);//15为圆角角度     //也可用QPainterPath 绘制代替 painter.drawRoundedRect(rect, 15, 15);     //QPainterPath painterPath;     //painterPath.addRoundedRect(rect, 15, 15);     //painter.drawPath(painterPath);     QWidget::paintEvent(event);   }
  • 效果如下

image.png

2.4 通过paintEvent采用Clear模式绘图,实现局部透明

  • 窗体属性设置
m_Margin = 60;//各个绘制图形与边框的距离   m_BgColor = QColor("#00BFFF");//窗体背景色   installEventFilter(this);//事件过滤器,用于鼠标按下后界面移动   setWindowFlags(Qt::FramelessWindowHint);//无边框窗体设置   setAttribute(Qt::WA_TranslucentBackground);//背景半透明属性设置
  • 画笔属性设置
void Widget::paintEvent(QPaintEvent *)   {     QPainter painter(this);     painter.setPen(Qt::NoPen);     painter.setBrush(m_BgColor);     painter.drawRoundedRect(this->rect(), 15, 15);//设置整体窗体圆角为15°     painter.setCompositionMode(QPainter::CompositionMode_Clear);//设置Clear绘图模式     //绘制三角形     drawTriangle(&painter);     //绘制圆     drawCircular(&painter);     //绘制矩形     drawRectangle(&painter);   }
  • 绘制三角形
void Widget::drawTriangle(QPainter *painter)   {     QPainterPath path;     int width = this->width() / 2;     int height = this->height() / 2;     //顶点     int topX = width / 2;     int topY = m_Margin;     //左下顶点     int leftX = m_Margin;     int leftY = height - m_Margin;     //右下顶点     int rightX = width - m_Margin;     int rightY = height - m_Margin;     path.moveTo(topX, topY);//起点     path.lineTo(leftX, leftY);//画线段1     path.lineTo(rightX, rightY);//画线段2     path.lineTo(topX, topY);//画线段3     painter->fillPath(path, QBrush(m_BgColor));//绘制三角形   }
  • 绘制圆
void Widget::drawCircular(QPainter *painter)   {     int width = this->width() / 2;     int height = this->height() / 2;     int x = width + width / 2;//X向坐标     int y = height / 2; //Y向坐标     int r = width / 2 - m_Margin;     //第一个参数为中心点,r为x向、y向长度(不一致时可绘制椭圆)     painter->drawEllipse(QPoint(x, y), r, r);   }
  • 绘制矩形
void Widget::drawRectangle(QPainter *painter)   {     int width = this->width() / 2;     int height = this->height() / 2;     int rectWidth = width - 2 * m_Margin;//矩形宽度     int rectHeight = height - 2 * m_Margin;//矩形高度     int rectX = width - rectWidth / 2;//矩形X向长度     int rectY = height + m_Margin;//矩形Y向长度     painter->drawRect(QRect(rectX, rectY, rectWidth, rectHeight));   }
  • 运行效果

v2-5db345f67cb9fde8eb91f5c62eda5d9c_b.gif

编辑
2025-03-03
记录知识
0

一:重写QSplashScreen

#include <QSplashScreen> #include <QMouseEvent> class AppSplashScreen : public QSplashScreen { Q_OBJECT public: explicit AppSplashScreen(QPixmap pixmap); ~AppSplashScreen(); void mousePressEvent(QMouseEvent *event); }; AppSplashScreen::AppSplashScreen(QPixmap pixmap) : QSplashScreen(pixmap) { this->setAttribute(Qt::WA_TransparentForMouseEvents, false); this->setWindowFlags(Qt::FramelessWindowHint); this->setAttribute(Qt::WA_TranslucentBackground); } /* 重写mousePressEvent */ void AppSplashScreen::mousePressEvent(QMouseEvent *event) { return; } AppSplashScreen::~AppSplashScreen() { }
  1. 重写mousePressEvent 为了屏蔽鼠标点击事件
  2. 设置窗口属性:
  this->setAttribute(Qt::WA_TransparentForMouseEvents, false); 设置禁止鼠标事件到父窗口   this->setWindowFlags(Qt::FramelessWindowHint); 设置窗口无边框   this->setAttribute(Qt::WA_TranslucentBackground); 设置窗口透明

二:初始化splash

/** * @brief 在应用启动过程中附上gif作为开启动画 */ void MainWindow::InitSplash(void) { int width,height; setFixedSize(800, 460); splash = new AppSplashScreen(QPixmap(":/png/png/splash.gif").scaled(width, height)); label = new QLabel(splash); mv = new QMovie(":/png/png/splash.gif"); bool istablet = QGSettings("org.ukui.SettingsDaemon.plugins.tablet-mode").get("tablet-mode").toBool(); if(istablet == true){ width = QGuiApplication::primaryScreen()->geometry().width(); height = QGuiApplication::primaryScreen()->geometry().height(); }else{ width = this->width(); height = this->height(); } splash->setFixedSize(width,height); label->setFixedSize(QSize(width , height)); mv->setScaledSize(label->size()); QScreen *screen = QGuiApplication::primaryScreen(); splash->move((screen->geometry().width() - width) / 2,(screen->geometry().height() - height) / 2); splash->showMessage(QObject::tr("正在启动程序..."), Qt::AlignRight | Qt::AlignTop, Qt::white); label->setMovie(mv); mv->start(); if(istablet == true){ splash->showFullScreen(); }else{ splash->show(); } for(int i=0; i<100; i++) { qApp->processEvents(); QThread::msleep(1); } }
  • setFixedSize 设置了父窗体的最大大小和最小大小为 800,460 它相当于设置了setMinimumSize和setMaximumSize都为同一个值
  • setScaledSize 将图片缩放大小设置为固定size。也就是这个QLabel的fixedsize
  • 为了与应用程序保持一致的位置,需要和应用程序一样,将splash移动到屏幕中间
  QScreen *screen = QGuiApplication::primaryScreen();   splash->move((screen->geometry().width() - width) / 2,(screen->geometry().height() - height) / 2);
  • showMessage 在splash播放的时候,显示文本信息
  • 绑定QMovie到QLabel上,并且开始播放gif
  label->setMovie(mv);   mv->start();
  • qApp->processEvents();代表当前的事情不重要,其他线程任务仍继续进行。用于不断刷新splash的事件,从而正常播放gif动画这里故意循环了100次,msleep1,也就意味着,当前至少100ms用于播放动画。然后再继续处理其他事情
  • 综合上面代码,initsplash过程中,初始化和播放了一个gif,并且让他默认走了100ms。

三:销毁SplashScreen

/** * @brief 销毁new出来的 QMovie QLabel AppSplashScreen */ void MainWindow::DestorySplash(void) { splash->finish(this); delete mv; mv = NULL; delete label; label = NULL; delete splash; splash = NULL; }

四:让动画继续播放

/** * @brief 不断设置动画消息 */ void MainWindow::SplashShowmsg(const QString msg) { label->setMovie(mv); mv->start(); Qt::Alignment topRight = Qt::AlignRight | Qt::AlignTop; splash->showMessage(msg,topRight, Qt::white); for(int i=0;i<10;i++) { qApp->processEvents(); splash->repaint(); } }

五:如何调用动画

MainWindow::MainWindow(QStringList str,QWidget *parent) :QWidget(parent) { mainWid = new QWidget(); InitSplash(); /* 主界面做其他事情 */ mainWid->show(); DestorySplash(); }

总结:

上面描述了如何设置一个QSplashScreen,从而让你的程序开启时出现动画效果。但对于效率来说,它为了绘制动画做了很多不必要的sleep和repaint/show。

编辑
2025-03-03
记录知识
0

一、使用惯例

1.1 使用QTimer实例实现可重复定时器

#include <QTimer> #include <QCoreApplication> #include <QDebug> int main(int argc, char** argv) { QCoreApplication app(argc, argv); //创建事件循环,用于分发QTimerEvent超时事件 auto timer = new QTimer; //新建定时器 //以QT信号-槽方式设置超时回调函数,这里槽的为lambda函数 QObject::connect(timer, &QTimer::timeout, [timer] () { static int cnt = 0; //定时器重复次数 cnt++; qDebug() << "当前定时器超时/重复时间(豪秒):" << timer->interval(); if(cnt > 4) timer->stop(); //停止计时器 }); timer->start(1000); //启动定时器,超时时间1000毫秒 return app.exec(); //进入事件循环,分发处理超时事件 }

1.2 使用QTimer::singleShot实现一次性定时器

#include <QTimer> #include <QCoreApplication> #include <QDebug> int main(int argc, char** argv) { QCoreApplication app(argc, argv); //创建事件循环 //创建一次性定时器,超时时间2000毫秒,并指定超时回调函数(lambda匿名函数) QTimer::singleShot(2000, [](){ qDebug() << "SingleTimer timeout!!"; }); app.exec(); //进入事件循环 return 0; }

二、QTimer注意事项

2.1 只能在QTimer所属线程中启动(thread() == QThread::currentThread())

2.2 在启动时QTimer所属线程必须具备事件循环(d->threadData->hasEventDispatcher())

主线程:在构建QCoreApplication对象之后

副线程:执行QThread::start完并进入run函数之后(此时线程才真正创建完成)

2.3 QTimer所属线程进入事件循环后才能触发超时信号。

主线程:执行QCoreApplication::exec

副线程:执行QThread::exec

三、 QTimer基本原理

借助QTimerEvent事件实现,当启动(QTimer::start)一个定时器时Qt调用底层操作系统接口注册一个定时器给当前线程,当线程接收到超时信号时会构造一个QTimerEvent事件发送会给QTimer对象,QTimer接收到该事件后会调用QObject::timerEvent函数,QTimer类通过覆盖(override)该虚函数,触发QTimer::timeout信号。

四、QTimer实现源码

https://code.woboq.org/qt5/qtbase/src/corelib/kernel/qtimer.cpp.html
编辑
2025-03-03
记录知识
0

一、工具简介

Qt 提供了一个打包工具,叫做 deployqt,可以将应用程序所依赖的库文件都提取出来。在 Windows 系统叫 windeployqt,在 Linux 系统叫 linuxdeployqt,在 Mac 下叫 macdeployqt。

二、linuxdeployqt 源码下载与编译

2.1 源码下载地址 :

git clone https://github.com/probonopd/linuxdeployqt.git

2.2 Qt环境:

本工程在172.25.130.130服务器chroot环境下,Qt环境是配好的,因此这里不需要配置Qt的环境变量。

image.png

2.3 安装patchelf

sudo apt install patchelf

该工具的作用:patchelf是一个用于修改ELF(可执行和链接格式)二进制文件的工具。它可以更改二进制文件中的符号表、动态段、程序头等部分,以便在运行时重新定位或修改它们。这对于动态库加载、插件系统和其他需要自定义运行时行为的场景非常有用。

工程中使用如下:

image.png

2.4 源码编译:

linuxdeployqt/main.cpp 去掉glibc判断

image.png

  • linuxdeployqt/share.cpp中修改变量copyCopyrightFiles 默认值为false
  • bool copyCopyrightFiles = false;//不拷贝版权文件

进入工程目录下:

image.png

因为这是qt工程,我们可以qmake 生成makefile 文件 ,再make编译(网上有使用cmake教程,其实大可不必)

编译有报错:

image.png

可以把这些打印全注掉,make clean&& make。

编译完成,把这个可执行文件扔进/usr/local/bin目录,这样,我们就可以在其他目录访问了。

通过命令查看可执行程序的信息:

image.png

2.5 工具的使用:

在需要打包的文件夹下执行 linuxdeployqt xxx(应用程序名)-bundle-non-qt-libs即可。

三、制作成deb包

这里可以参考新人文档如何使用编译deb包。

编辑
2025-03-03
记录知识
0

在网络负载非常重的情况下,对于文件服务器、高流量Web服务器这样的应用来说,把不同的网卡IRQ均衡地绑定到不同的CPU核心上,将会减轻单个CPU的负担,提高多CPU、多核心的整体处理中断的能力。对于数据库服务器这样的应用来说,把磁盘控制器绑到一个CPU核心,把网卡绑定到另一个CPU核心上,将会提高数据库的响应时间,达到优化性能的目的。合理地根据自己的生产环境和应用的特点来平衡IRQ中断有助于提高系统的整体吞吐能力和性能。这里介绍一下网络设备的绑核操作。

一:中断的affinity

在proc文件系统中,为中断提供了smp_affinity和smp_affinity_list接口,允许给指定的IRQ源绑定目标的CPU,而在/proc/irq/default_smp_affinity中,通过掩码的方式指定了IRQ的默认配置掩码。一般是ff,也就是所有CPU(0-15)。主要示例如下:

# cat /proc/irq/default_smp_affinity ff # cat /proc/irq/1/smp_affinity ff # cat /proc/irq/1/smp_affinity_list 0-7

二:实践绑核

2.1 查看硬中断号

# cat /proc/interrupts | grep eth1 110: 69925 0 0 0 0 0 0 14520 GICv3 259 Level eth1 111: 0 0 0 0 0 0 0 0 GICv3 258 Level eth1

2.2 smp_affinity_list设置

echo 7 > /proc/irq/110/smp_affinity_list echo 7 > /proc/irq/111/smp_affinity_list

这里意思为将110和111中断绑定在CPU序号为7上

2.3 smp_affinity 设置

如smp_affinity_list已经设置,则smp_affinity 可无需设置

smp_affinity 按照CPU掩码计算,如下

cpu0 0001 0 1 cpu1 0010 1 2 cpu2 0100 2 4 cpu3 1000 3 8 cpu4 10000 4 10. cpu5 100000 5 40 cpu6 1000000 6 80 cpu7 10000000 7 100

如果绑定为第8个CPU,则设置

echo 100 > /proc/irq/110/smp_affinity echo 100 > /proc/irq/111/smp_affinity

2.4 测试

ping 测试

$ ping -I eth1 0.0.0.0 PING 0.0.0.0 (172.25.80.124) from 172.25.80.124 eth1: 56(84) bytes of data. 64 bytes from 172.25.80.124: icmp_seq=1 ttl=64 time=0.064 ms 64 bytes from 172.25.80.124: icmp_seq=2 ttl=64 time=0.035 ms

查看中断

# cat /proc/interrupts | grep eth1 110: 69925 0 0 0 0 0 0 17112 GICv3 259 Level eth1 111: 0 0 0 0 0 0 0 0 GICv3 258 Level eth1

这里确定已经绑定成功了