用Qt实现一个桌面弹幕程序(一)--实现一个弹幕①



  • 本来想写一篇Qt安装呀,新建项目呀的教程,不过在Qt Dream这个平台,相信大家都已经会了,所以就不废话了。

    简单讲:快快点我 去下载相应系统下的安装包,开始你的Qt程序之旅吧~

    创建一个QMainWindow为模板的程序,注意安装路径与项目路径都不要带空格和中文就好。

    下面进入正式环节~

    弹幕是什么呢?

    要说到弹幕,或许你会联想到这么几个东西:

    杰洛君想到的是:

    • 即(si)时(bi)评(da)论(lian)
    • N站,A站,B站等同性交友网站中一个热门流行功能
    • 弹(dan)幕不是弹(tan)幕啦
    • 额,有那么复杂吗?不就是一串飞过去的文字吗。。。

    综上所有的联想哪个最有助于我们编程实现呢?

    没错就是最后一个了:弹幕很简单,一串飞过去的文字。

    如何显示文字?

    杰洛君可是非常孤陋寡闻滴,随便查了查一些博客就学到了一点点内容,显示文字的话Qt里面可以用QLabel呀~

    (此处建议不要学杰洛君的学习方式,建议多多使用Qt的帮助文档进行类的学习,用光标选中你要查找的内容,毫不犹豫地按下F1吧,勇敢滴骚年呀,快去创造JJ)

    于是事不宜迟,打开项目中的main.cpp文件中添加自己的代码吧~

    #include "mainwindow.h"
    #include <QApplication>
    #include <QLabel>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        //添加的代码
        QLabel label("这是一个小弹幕");  //这里label的parent指针为NULL
        //QLabel label("这是一个小弹幕",&w);
        label.move(100,200);
        label.show();
    
        w.show();
    
        return a.exec();
    }
    

    上面的代码是非常耿直滴,就是新建一个Label ,然后设置Label上的文字,利用以屏幕左上角为(0,0)的坐标系,把Label移动到了x坐标为100,y坐标为200的位置。

    ps:小伙伴可以试试那行被封印代码,体验一下parent指针如果不为空会发生什么事情

    运行吧~你建造出了一个弹幕呀!!!

    0_1456406991895_pic10.png

    小C:我读书读的少,你不要骗我,这TM叫弹幕?我要报警了ヾ(。`Д´。)

    是的,你一定会很不满意,这能叫弹幕?

    再次讨论什么是弹幕?

    这次我们具体一点,弹幕具有什么属性?

    • 自己的字体
    • 自己的颜色
    • 自己的大小
    • 自己的飞行快慢
    • 自己的透明度
    • 不应被遮挡
    • 没有最大最小关闭按钮
    • 背景应该是透明的
    • 等等

    呼~这么看来,一个弹幕也有好多问题要解决呀。

    让我们来修改弹幕的字体吧

    Qt 提供了QFont类,这个类管理字体的类型,Label可以通过setFont(const QFont & )函数进行字体的设置

    修改上面的代码:

    QLabel label("这是一个小弹幕");
    label.setFont(QFont("SimHei",20,100));
    

    在这里杰洛君直接调用QFont的构造函数,将构造的对象作为参数传递给了setFont(const QFont &)函数。还有,杰洛君参考B站弹幕的设置把黑体(SimHei)传给了字体构造函数。

    QFont参数的含义如下:
    QFont(const QString &family, int pointSize = -1, int weight = -1, bool italic = false);

    • family:字体名称
    • pointSize:字体点大小,小于等于0时,自动设置为12
    • weight:字体的粗细
    • italic:字体是否为斜体

    让我们来修改弹幕的颜色吧

    如何设置字体的颜色呢?Qt提供了调色板类-- -- QPalette

    继续修改代码:

    QLabel label("这是一个小弹幕");
    label.setFont(QFont("SimHei",20,100));
    QPalette pa;
    pa.setColor(QPalette::Foreground,QColor(255,0,0));
    label.setPalette(pa);
    

    QPalette类

    此处使用了setColor(ColorRole role, const QColor & color) 方法

    其中前者ColorRole是QPalette中的枚举类型,建议查看帮助文档了解每一个的具体含义,这里用到的是前景色,修改了字体的颜色。

    后者就是具体的QColor颜色了,传入相应的RGB (Red,Green,Blue)值即可

    此时你会发现你的弹幕变成了这个样子

    0_1456407184367_pic11.png

    是不是很开心?O(∩_∩)O~~

    小C:教练,能不能更给力呀?←_←

    杰洛君:。。。

    让我们来修改弹幕的背景吧

    弹幕的背景应该是透明的,只看到字体。这个怎么做?

    很简单,加上这一句:

    label.setAttribute(Qt::WA_TranslucentBackground);
    

    在windows下编程的小伙伴可能就不愉快了:杰洛君骗人,加上这行之后,背景是黑的,怎么会是透明的呢?

    让我们按下F1 看文档是怎么说明的:

    Indicates that the widget should have a translucent background, i.e., any non-opaque regions of the widgets will be translucent because the widget will have an alpha channel. Setting this flag causes WA_NoSystemBackground to be set.

    On Windows the widget also needs the Qt::FramelessWindowHint window flag to be set. This flag is set or cleared by the widget's author.

    哈哈,知道问题所在了吧,需要设置Qt::FramelessWindowHint,才能正常显示为透明。

    这个window flag 该如何设置呢?

    让我们来设置弹幕的一些特性吧

    弹幕应该不被遮挡,应该没有标题栏,也不应该有任务栏,这些该如何设置呢?

    对,就是用到上面 杰洛君 提到的window flag

    试试加上这个:

    label.setWindowFlags(Qt::FramelessWindowHint|Qt::Tool|Qt::WindowStaysOnTopHint);
    

    这里Qt::FramelessWindowHint表示无标题栏

    这里Qt::Tool 表示这个是一个Tool 窗体,去掉了任务栏 (什么是Tool窗体,快打开F1看看文档描述,在windows和mac 下有不同表现,相当有意思呢,在mac上实现弹幕时,是不需要让它成为Qt::Tool窗体的)

    Qt::WindowStaysOnTopHint表示窗体置于顶层,不会被普通应用遮盖 ,有点类似QQ桌面端一样一直在顶层。

    经过这么多设置,终于它看上去更像弹幕了

    0_1456407357059_pic12.png

    小C:教练,能不能再给力一点呀→_→

    杰洛君:现在的读者真是越来越不好伺候了。╮(╯_╰)╭

    不过确实,距离B站的弹幕还是有很大差距,比如描边,比如移动,我们都还没实现呢。

    而且现在这样写代码,代码太散乱了,不好管理。

    后续

    Qt提供的Label 已经满足不了杰洛君了〒▽〒,怎么办呢?

    让我们在下一篇文章中一起愉快地重载它吧!

    今天就到这里啦~早点休息啦,啦啦啦~O(∩_∩)O哈哈~



  • @杰洛飞 好好玩



  • @momognu 快来跟随蜀黍做一些奇♂♂♂♂的事情,更好玩 ⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄



  • @杰洛飞
    能否支持高级弹幕呢?



  • @qyvlik 是怎样的高级呢?先剧透吧我打算写三篇文章于实现弹幕,第二篇重载QLabel将会实现弹幕的飞行。里面会用到QPropertyAnimation,通过这个类可以实现横飞,顶部,底端,逆向等等(甚至贝塞尔( -_-|| 什么鬼,有必要吗?)),第三篇是一些gif或者图片飞行弹幕,敬请期待~😝



  • @qyvlik 哦,高级弹幕是指代码弹幕是吧,这个我还不会弄😥 没有想过,回去想想试试。



  • 当时我记得有javascript代码控制的,而这一点呢,Qt Widgets好像做得不是很好。
    如果我不了解Qt Quick的话,我会倾向于使用Qt Graphics View来制作这样的弹幕。



  • 弹幕还应该有个活动范围,飞出播放器飞到桌面上就没必要了



  • @jiangcaiyang 这个还没有学,学得比较浅。以后可以做为优化方案。



  • @momognu 这个程序不是弹幕播放器哦,这个是一个桌面的弹幕程序。是那种晚会活动现场类似微信墙的应用,不过是以弹幕的形式。



  • @杰洛飞 以前我好像看到一个走马灯的程序,我想可以通过修改一下走马灯的程序来实现弹幕。



  • 好厉害!!!~~



  • 赞b( ̄▽ ̄)d~~


Log in to reply
 

走马观花

最近的回复

  • C

    Qt for MCU需要商业授权的

    read more
  • Qt for MCUs

    搭建Qt for MCUs PC端开发环境。qt for mcus提供了一个完整的图形框架和工具包,包含了在MCUs上设计、开发和部署gui所需的一切。它允许您在裸机或实时操作系统上运行应用程序。

    先决条件

    开发主机环境支持仅限于Windows 10

    MSVC compiler v19.16 (Visual Studio 2017 15.9.9 or newer) x64

    CMake v3.13 or newer (you can install it using the Qt Online installer) x64

    使用Qt联机安装程序安装Qt for MCUs,该安装程序可通过Qt帐户下载

    安装Qt 5.14和Qt Creator 4.11 or higher

    安装链接

    › Qt: https://account.qt.io/downloads
    › CMake: https://cmake.org/download/
    › Python 2.7 32-bit: https://www.python.org/downloads/release/python-2716/
    › Arm GCC: https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnutoolchain/gnu-rm/downloads
    › J-Link Software Pack: https://www.segger.com/downloads/jlink/JLink_Windows.exe
    › J-Link OpenSDA Firmware: https://www.segger.com/downloads/jlink/OpenSDA_MIMXRT1050-EVKHyperflash
    › STM32CubeProgrammer: https://www.st.com/en/development-tools/stm32cubeprog.html
    › STM32 ST-LINK Utility: https://www.st.com/en/development-tools/stsw-link004.html​​​​​​​

    Qt Creator设置 启用Qt Creator插件 选择“帮助>关于插件”,然后从列表中选择“MCU支持(实验性)”插件,重新启动Qt Creator以应用更改
    替代文字 为MCU创建Qt工具包

    选择工具>选项>设备>MCU

    选择Qt for MCUs-Desktop 32bpp作为目标

    如果尚未设置,请提供Qt for MCUs安装目录的路径。

    单击Apply应用。

    替代文字

    替代文字
    替代文字

    注意:

    编译器要选X64,Qt版本要选64bit,CMake Tool选x64

    打开恒温器项目demo

    选择文件>打开文件或项目。。。

    打开CMakefiles.txt文件来自thermo文件夹的文件。

    选择Qt作为MCU-桌面32bpp套件。

    单击“配置项目”以完成。

    替代文字

    问题

    开发主机环境支持仅限于Windows 10

    C++编译失败,文本大字体.pixelSize.

    文本类型无法正确呈现需要复杂文本布局的unicode序列。对复杂文本使用StaticText

    read more
  • H

    hi 有问题请教你,方便加个联系方式吗

    read more
  • boost.asio是一个很棒的网络库,这回儿我也开始系统地学习起来了。想想当年接触boost,也有八年多了。这次开始接触boost,觉得既熟悉又陌生。熟悉的是小写字母+下划线的命名方式、晦涩的模板、很慢的编译速度以及较大的程序体积,陌生的是asio的各种概念:io服务、接收器、套接字等等:我之前对网络编程不是非常了解。

    于是根据我的理解,参考《Boost.Asio C++网络编程》实现了这样一个简单的客户端和服务端通信的例子,例子非常简单,还不完善,但是幸运的是,可以在本机上互通了。
    下面是客户端的代码:

    #include <iostream> #include <boost/asio.hpp> #include <boost/proto/detail/ignore_unused.hpp> using namespace std; using namespace boost::asio; using namespace boost::system; using namespace boost::proto::detail;// 提供ignore_unused方法 void writeHandler( const boost::system::error_code& ec, size_t bytesTransferred ) { if ( ec ) { cout << "Write data error, code: " << ec.value( ) << "transferred: " << bytesTransferred << endl; } else { cout << "OK! " << bytesTransferred << "bytes written. " << endl; } } int main(int argc, char *argv[]) { ignore_unused( argc ); ignore_unused( argv ); io_service service; ip::tcp::socket sock( service ); ip::tcp::endpoint ep( ip::address::from_string( "127.0.0.1" ), 6545 ); boost::system::error_code ec; sock.connect( ep, ec ); if ( ec ) { cout << "Connect error, code: " << ec.value( ) << ", We will exit." << endl; return ec.value( ); } else { char buf[1024] = "Hello world!"; sock.async_write_some( buffer( buf ), writeHandler ); sock.close( ); } return service.run( ); }

    下面是服务端的代码:

    #include <iostream> #include <boost/asio.hpp> #include <boost/proto/detail/ignore_unused.hpp> using namespace std; using namespace boost::asio; using namespace boost::system; using namespace boost::proto::detail;// 提供ignore_unused方法 void acceptHandle( const boost::system::error_code& code ) { cout << "Accepted." << endl; } int main(int argc, char *argv[]) { ignore_unused( argc ); ignore_unused( argv ); io_service service; ip::tcp::endpoint ep( ip::address::from_string( "127.0.0.1" ), 6545 ); boost::system::error_code ec; ip::tcp::socket sock( service ); ip::tcp::acceptor acceptor( service, ep ); acceptor.async_accept( sock, acceptHandle ); if ( ec ) { cout << "There is an error in server. code: " << ec.value( ) << endl; } return service.run( );// 阻塞运行 }

    运行结果是这样的:
    78448d7b-b3ae-42fc-9e2e-4dd2fbdac2c2-image.png

    我对boost.asio中几个概念的理解:

    io_service,这就是一个类似事件循环的东西,它为io设备提供服务,故名。不管是套接字、文件还是串口设备,都要使用它的服务。它的run()函数相当于启动了一个事件循环。一旦有消息了,即进行响应。这也是实现异步编程的重要基础。 socket,这个类则是套接字,可以处理TCP或者是UDP请求。有同步以及异步的处理方式,也有带异常以及不带异常的处理方式。 acceptor,接收器,仅仅是服务端使用。相当于其余框架中的listener,作接收用的。

    比较浅显,如果有不当之处,敬请指正。

    read more

关注我们

微博
QQ群