• 用VS Code搞Qt 6:Gui基础类型——QGuiApplication和QWindow


    在99.996%的情况下,我们弄 Qt 应用都会使用 QApplication 类和 QWidget 类,即直接用 Widgets 库中的组件/控件。为了方便开发人员自己造轮子,Qt 也提供了一套基础的 GUI 组件。这些组件位于 Gui 库中。

    实际上,Widgets 也是在 Gui 库上实现的,算是官方默认为咱们实现的图形组件库。若是我们自己也想实现一套图形组件库,就得从 Gui 库入手。当然,此行为需要决心、恒心、耐心、信心、专心、勇气、朝气、力气、努力、神力、洪荒之力。毕竟是一项大工程,没有上述精神元素是很难做成的。因此,许多时候,我们压根儿不会去弄,就算要写个自定义的可视化组件,那一般也是从 QWidget 派生。

    尽管不怎么用,但还得了解一下的。QGuiAppliction 类用于管理整个应用程序的消息循环,初始化完各类组件后,开启主消息循环,直到循环结束,程序归西。QApplication 类就是它的子类。

    QWindow 类是各种窗口部件的基类。它不仅指窗体,也包括窗口上的控件什么的。控件会被视为子窗口(就是没有了标题栏和边框这些)。所以,从 QWindow 类派生既能实现窗体逻辑,也能自定义控件。不过,咱们一般不会直接从 QWindow 类派生,而是使用以下两个家伙:

    1、QRasterWindow :这个很好使,就是我们最最最的绘图方式来画控件的可视化部分。Raster 是“光栅”的意思。

    2、QOpenGLWindow :看名字也猜到,该类使用 OpenGL 来绘制控件的可视化部分。

    下面老周弄一个简单的演示,实现一个 MyCustWindow 类,派生自 QRasterWindow。里面也没啥逻辑,就是先把窗口的背景刷成红色,显得异常地喜庆和活泼。然后当鼠标移到窗口上时,会在窗口上画一个饼……不,是一个圆,这个圆会跟着鼠标指针走——其实,圆并不会动,只不过在鼠标移动后不断地将窗口重新绘制,以制造出圆会动的效果。

    总体的代码文件有四个,其实就两个,C/C++都有头文件,于是就四个了。

    /* CMakeLists.txt */
    
    project(MyApp LANGUAGES CXX)
    
    find_package(Qt6 REQUIRED COMPONENTS Core Gui)
    set(CMAKE_CXX_STANDARD 17)
    set(CMAKE_AUTOMOC ON)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)
    
    add_executable(MyApp WIN32
                app.h
                app.cpp
                MyCustWindow.h
                MyCustWindow.cpp)
    
    target_link_libraries(MyApp PRIVATE
                         Qt6::Core
                         Qt6::Gui)

    app.cpp 是写 main 的,自定义窗口是 MyCustWindow.h 和 MyCustWindow.cpp。老周有个习惯,写C++代码通常会新建个头文件,把要用到的所有头文件一次性弄上去,然后项目中其他地方包含这个头文件就行了。比如这里的 app.h。

    #ifndef _APP_H_
    #define _APP_H_
    
    #include <QGuiApplication>
    #include <QRasterWindow>
    #include <QRect>
    #include <QSize>
    #include <QPainter>
    #include <QColor>
    #include <QEvent>
    #include <QMouseEvent>
    
    #endif

    Qt 这厮基本就是这样的,每个类一个头文件,所以用到哪些类,直接上名字。_APP_H_ 只是个宏,为了防止多次包含此头文件时被重复定义,没有别的用途。这里老周没有用 #pragma once,据说这个不太通用。当然也没说不能用,只要不报错啥也能上。

    接下来是自定义窗口类的定义。

    /* MyCustWindow.h */
    
    #include "app.h"
    
    class MyCustWindow : public QRasterWindow {
        Q_OBJECT
    public:
        explicit MyCustWindow(QWindow* parent = nullptr);
        ~MyCustWindow();
    protected:
        void paintEvent(QPaintEvent *event) override;   
        bool event(QEvent *event) override;
    
    private:
        QPoint _curr_pos;
    };

    用在Qt对象树上的类型(说白了是 QObject 的直接或间接子类)都要加个宏 Q_OBJECT,信号和 Cao 需要它。不知道是啥也没事,照搬就行了。

    这里,_curr_pos 是私有的成员,主要用来记录当前鼠标指针的坐标,以便在 paintEvent 中画图用。这个示例需要重写两个成员:

    1、paintEvent:绘制窗口内容。

    2、event:处理事件,捕捉鼠标指针移动事件—— MouseMove。这里其实可以用 eventFilter,也能起来相同的效果,不过,直接重写 event 最划算。

    下面看 event 方法的实现。

    bool MyCustWindow::event(QEvent *event)
    {
        // 分析一下事件类型
        if(event -> type() == QEvent::MouseMove)
        {
            QMouseEvent* msevent = dynamic_cast<QMouseEvent*>(event);
            // 拿到当前鼠标指针的位置
            _curr_pos = msevent->pos();
    
            // 重新绘制窗口
            update();
            // 已处理,返回true
            return true;
        }
    
        // 剩下的留给基类默认处理
        return QRasterWindow::event(event);
    }

    如果是鼠标事件,那么,event 参数的指针实际指向的是 QMouseEvent 对象,所以我这里用 dynamic_cast 转换了一下,这个运算符虽然不太安全,但指针之间转换比较好用。这里其实不会有啥问题,因为如 event type 确定为 MouseMove,那么 event 参数就是 QMouseEvent* 类型。接着,访问 pos() 获得当前坐标,赋值给 _curr_pos。最后一步是调用 update 方法,这个一定要调用,这样才能强制窗口重新绘制,那个圆才会跟着鼠标走。

    当然,最后一行也少不了,毕竟我们这里只关心鼠标事件,可能还有很多事件,于是这些我们不感兴趣的事件交给基类处理。

    return QRasterWindow::event(event);

    下一步,我们画窗口内容。

    void MyCustWindow :: paintEvent(QPaintEvent *event)
    {
        QColor bkColor(255,0,0);   // 背景色
        QColor ptColor(255,255,0);  // 跟随鼠标指针的颜色
    
        // 开始表演
        QPainter painter(this);
        // 设置默认背景色
        painter.setBackgroundMode(Qt::OpaqueMode);
        painter.setBackground(QBrush(bkColor));
        painter.setBrush(QBrush(ptColor));
        // 擦除画布
        painter.eraseRect(0, 0, width(), height());
        // 画圆
        painter.drawEllipse(_curr_pos, 20, 20);
    
        //event -> accept();
    }

    最后一行的 accept 方法调用,此处可以有也可以省略。accept 后事件就不再传播到父对象了,不是父类,是对象树上的父对象。窗口算是这里的顶层对象了,所以传不传上去无所谓。

    这里有坑,有不少同志说,调用 setBackground 方法无法设置背景,全是黑的。这里要注意两点:

    1、setBackgroundMode 方法将模式设置为 OpaqueMode 时才能上背景,如果是 TransparentMode,表明是透明背景,此时设置不了背景色的。程序默认就是 OpaqueMode,所以 setBackgroundMode 一行可以省略。

    2、设置背景色后,在绘制前一定要清空画布——调用 eraseRect 方法,本示例中要擦除整个窗口的区域,所以,矩形的大小和窗口大小相同。

    调用 drawEllipse 方法直接就可以画圆了,我们这里画的不是椭圆,而是正圆,只要让 x 和 y 轴方向上的半径相等(都是 20)就可以了。

    回到 app.cpp,写 main 入口函数。

    #include "app.h"
    #include "MyCustWindow.h"
    
    int main(int argc, char* argv[])
    {
        QGuiApplication app(argc, argv);
    
        // 实例化窗口
        MyCustWindow window;
        // 标题
        window.setTitle("喜狼狼和灰太羊");
        // 大小
        window.resize(300, 270);
        // 位置
        window.setPosition(670,364);
        // 显示窗口
        window.show();
    
        return app.exec();
    }

    app.exec 开启主消息循环,要在所有初始化工作完成再调用,因为它不会马上返回,而是等消息循环结束才返回。

    末了,完工,咱们看看效果。

    通过这个演示,大伙伴们对 QWindow 的用处,想必有所了解了。若某天你发现你要干自造轮子的大活,那就得这样弄了。

  • 相关阅读:
    测试策略如何制定
    python atexit模块和register函数
    使用Redis实现异步消息队列
    python 处理中文 读取数据库输出全是问号
    TCP和UDP的区别和优缺点
    怎样ping网络
    ImportError: libpng12.so.0: cannot open shared object file: No such file or directory
    tensorrtx/retinaface/calibrator.cpp:4:31: 致命错误:opencv2/dnn/dnn.hpp:没有那个文件或目录
    编译tensorrtx/retinaface遇到报错/usr/local/cuda/include/vector_types.h(421): error: identifier "constexpr" is undefined
    RetinaFace.cpp:112:37: 错误:‘std::chrono’尚未声明
  • 原文地址:https://www.cnblogs.com/tcjiaan/p/16727570.html
Copyright © 2020-2023  润新知