百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

比QT更高效的开源免费嵌入式linux图形GUI

ztj100 2025-04-08 20:52 10 浏览 0 评论

@ZHangZMo

  • EGT-Ensemble Graphics Toolkit介绍
  • EGT具备非常高的图形渲染效率EGT采用了非常优秀的开源2D图形处理引擎-Cairo开源2D图形处理引擎Cairo的优势Cairo 2D图像引擎的性能Cairo 2D图像引擎的实际应用案例彩蛋 - 开源EDA软件KiCAD也在使用Cairo
  • EGT高效的秘诀还有哪些Cairo需要依赖PixmanPixman针对不同平台有优化
  • EGT vs QT5实际效果PK
  • 代码贴图

有些介绍资料直接来自豆包,仅代表个人意见和理解,不喜勿喷

EGT-Ensemble Graphics Toolkit介绍

The Ensemble Graphics Toolkit (EGT)是MIcrochip针对旗下ARM9、SAMA5处理器推出来的一款运行于嵌入式Linux的C++ GUI开发工具套件。EGT(嵌入式图形工具包)提供了现代化的图形用户界面(GUI)功能、外观样式,并在嵌入式 Linux 应用中尽可能贴近底层硬件的同时最大限度地提升性能。关键词是开源、免费商用

Ensemble Graphics Toolkit: Introduction

官方给出的EGT组件依赖框图

EGT具备非常高的图形渲染效率 EGT采用了非常优秀的开源2D图形处理引擎-Cairo

开源2D图形处理引擎Cairo的优势

Cairo 2D图像引擎的性能

Cairo 2D图像引擎的实际应用案例

彩蛋 - 开源EDA软件KiCAD也在使用Cairo

EGT高效的秘诀还有哪些

Cairo使用Pixman来加速底层像素的操作,Pixman能够提供图像的合成、alpha 通道处理、色彩空间转换等基本的像素级别的操作

Cairo需要依赖Pixman

Pixman针对不同平台有优化

Pixman针对ARM SIMD架构、带NEON或者MIPS、X86等架构,都有专门针对性的优化代码,来尽可能利用处理器硬件特性,加速像素的处理速度

EGT vs QT5实际效果PK

在Microchip SAMA5D27开发板上,跑Microchip EGT提供的潜水员例程,该例程会有潜水员的动态图片,同时也有2条鱼在界面上从左到右在游动,另外还不断有泡泡从底部冒出。同时将该例程功能移植到QT5上,然后两者在实际硬件上运行,对比CPU占用率的差异

QT vs Cairo性能大对比 视频对比 基于EGT开发的demo,全程CPU占用率在22%左右,而QT5基本都在70%以上

代码贴图

个人对QT开发不是很熟悉,欢迎提出更好的优化方案 以下是QT5

Bash
#include 
#include 
#include 
#include 
#include 
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
    , fishFrame(0)
    , fish2Frame(0)
    , diverFrame(0)
    , bubblePixmap(":/images/smallbubble.png")
{
    ui->setupUi(this);

    // Load fish images and split them into 6 parts
    QPixmap fishPixmap(":/images/fish.png");
    QPixmap fish2Pixmap(":/images/fish2.png");
    QPixmap diverPixmap(":/images/diver.png");

    int frameWidth  = fishPixmap.width() /4;
    int frameHeight = fishPixmap.height()/2;

    for (int j = 0; j < 2; j++) {
        for (int i = 0; i < 4; ++i) {
            fishPixmaps.push_back(fishPixmap.copy(i * frameWidth, frameHeight * j, frameWidth, frameHeight));
        }
    }

    frameWidth  = fish2Pixmap.width() /2;
    frameHeight = fish2Pixmap.height()/3;

    for (int j = 0; j < 3; j++) {
        for (int i = 0; i < 2; ++i) {
            fish2Pixmaps.push_back(fish2Pixmap.copy(i * frameWidth, frameHeight * j, frameWidth, frameHeight));
        }
    }

    frameWidth  = diverPixmap.width();
    frameHeight = diverPixmap.height()/16;

    for (int i = 0; i < 16 i diverpixmaps.push_backdiverpixmap.copy0 frameheight i framewidth frameheight set the background image ui->backgroundLabel->setPixmap(QPixmap(":/images/water_1080.png"));
    ui->backgroundLabel->setScaledContents(true);

    // Set the initial fish images
    ui->fishLabel->setPixmap(fishPixmaps[0]);
    ui->fishLabel->setScaledContents(true);

    ui->fish2Label->setPixmap(fish2Pixmaps[0]);
    ui->fish2Label->setScaledContents(true);

    // Set the diver image
    ui->diverLabel->setPixmap(diverPixmaps[0]);
    ui->diverLabel->setScaledContents(true);

    // Create timers for moving the fish
    moveTimer = new QTimer(this);
    connect(moveTimer, &QTimer::timeout, this, &MainWindow::moveFish);
    moveTimer->start(50);

    moveTimer2 = new QTimer(this);
    connect(moveTimer2, &QTimer::timeout, this, &MainWindow::moveFish2);
    moveTimer2->start(50);

    // Create timers for animating the fish
    animateTimer = new QTimer(this);
    connect(animateTimer, &QTimer::timeout, this, &MainWindow::animateFish);
    animateTimer->start(100);

    animateTimer2 = new QTimer(this);
    connect(animateTimer2, &QTimer::timeout, this, &MainWindow::animateFish2);
    animateTimer2->start(100);

    // Create timers for animating the diver
    animateTimer3 = new QTimer(this);
    connect(animateTimer3, &QTimer::timeout, this, &MainWindow::animateDiver);
    animateTimer3->start(100);

    cpuTimer = new QTimer(this);
    connect(cpuTimer, &QTimer::timeout, this, &MainWindow::updateClock);
    cpuTimer->start(1000); // 每秒更新一次时钟

    // Create timer for creating bubbles
    bubbleTimer = new QTimer(this);
    connect(bubbleTimer, &QTimer::timeout, this, &MainWindow::createBubble);
    bubbleTimer->start(1000);

    // 显示 CPU 使用率的标签
    cpuLabel = new QLabel(this);
    cpuLabel->setGeometry(580, 5, 200, 40);
    cpuLabel->setStyleSheet("font-size: 20px; color: red;");
    cpuLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);

    cpuUsage = new CPUUsage(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::moveFish()
{
    int x = ui->fishLabel->x() + 5;
    if (x > this->width()) {
        x = -ui->fishLabel->width();
        int y = QRandomGenerator::global()->bounded(this->height() - ui->fishLabel->height());
        ui->fishLabel->move(x, y);
    } else {
        ui->fishLabel->move(x, ui->fishLabel->y());
    }
}

void MainWindow::moveFish2()
{
    int x = ui->fish2Label->x() + 5;
    if (x > this->width()) {
        x = -ui->fish2Label->width();
        int y = QRandomGenerator::global()->bounded(this->height() - ui->fish2Label->height());
        ui->fish2Label->move(x, y);
    } else {
        ui->fish2Label->move(x, ui->fish2Label->y());
    }
}

void MainWindow::animateFish()
{
    fishFrame = (fishFrame + 1) % fishPixmaps.size();
    ui->fishLabel->setPixmap(fishPixmaps[fishFrame]);
}

void MainWindow::animateFish2()
{
    fish2Frame = (fish2Frame + 1) % fish2Pixmaps.size();
    ui->fish2Label->setPixmap(fish2Pixmaps[fish2Frame]);
}

void MainWindow::animateDiver()
{
    diverFrame = (diverFrame + 1) % diverPixmaps.size();
    ui->diverLabel->setPixmap(diverPixmaps[diverFrame]);
}

void MainWindow::updateClock()
{
    cpuLabel->setText(QString("CPU Usage: %1%").arg(cpuUsage->getCPUUsage(), 0, 'f', 2));
    update();
}

void MainWindow::createBubble()
{
    int bubbleCount = QRandomGenerator::global()->bounded(1, 4); // Random number of bubbles between 1 and 5
    for (int i = 0; i < bubblecount i int x='QRandomGenerator::global()-'>bounded(this->width());
        int y = this->height();
        int size = QRandomGenerator::global()->bounded(5, 32); // Random size between 10 and 50
        int xspeed = 0;
        int yspeed = -QRandomGenerator::global()->bounded(5, 20); // Random speed between 1 and 10
        Bubble* bubble = new Bubble(bubblePixmap, xspeed, yspeed, QPoint(x, y), size, this);
        bubbles.push_back(bubble);
        bubble->show();
        if (bubble->getcount() >= 20) {
            break;
        }
    }
}

以下是EGT

Bash
/*
 * Copyright (C) 2018 Microchip Technology Inc.  All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#include 
#include 
#include 
#include 
#include 
#include <egt/ui>
#include 
#include 
#include 
#include 

using namespace std;
using namespace egt;

class Bubble : public ImageLabel
{
public:
    Bubble(int xspeed, int yspeed, const Point& point) noexcept
        : ImageLabel(Image("file:smallbubble.png"), "", Rect(point, Size())),
          m_xspeed(xspeed),
          m_yspeed(yspeed)
    {
        flags().set(Widget::Flag::no_layout);
    }

    Bubble(const Bubble&) = default;
    Bubble(Bubble&&) = default;
    Bubble& operator=(const Bubble&) = default;
    Bubble& operator=(Bubble&&) = default;
    virtual ~Bubble() = default;

    bool animate()
    {
        bool visible = Rect(Point(0, 0), Application::instance().screen()->size()).intersect(box());

        if (visible)
        {
            Point to(box().point());
            to += Point(m_xspeed, m_yspeed);
            move(to);
        }

        return visible;
    }

private:

    Bubble() = delete;

    int m_xspeed;
    int m_yspeed;
};

class MainWindow : public TopWindow
{
public:
    MainWindow()
        : TopWindow(Size()),
          e1(r())
    {
        background(Image("file:water.png"));

        m_label = make_shared("Objects: 0",
                                     Rect(Point(10, 10),
                                          Size(150, 40)),
                                     AlignFlag::left | AlignFlag::center_vertical);
        m_label->color(Palette::ColorId::text, Palette::white);
        m_label->color(Palette::ColorId::bg, Palette::transparent);
        add(top(left(m_label)));

        m_sprite = make_shared(Image("file:diver.png"), Size(390, 312), 16, Point(0, 0));
        m_sprite->no_layout(true);
        add(m_sprite);
        m_sprite->show();
    }

    void handle(Event& event) override
    {
        TopWindow::handle(event);

        switch (event.id())
        {
        case EventId::raw_pointer_move:
            spawn(display_to_local(event.pointer().point));
            break;
        default:
            break;
        }
    }

    void spawn(const Point& p)
    {
        auto xspeed = 0;
        auto yspeed = speed_dist(e1);
        auto offset = offdist(e1);
        auto size = size_dist(e1);

        // has to move at some speed
        if (yspeed == 0)
            yspeed = 1;

        m_images.emplace_back(make_shared(xspeed, yspeed, p));
        auto& image = m_images.back();
        add(image);
        image->image_align(AlignFlag::expand_horizontal | AlignFlag::expand_vertical);
        image->resize_by_ratio(size);
        image->move(Point(p.x() - image->box().width() / 2 + offset,
                          p.y() - image->box().height() / 2 + offset));
        objects_changed();
    }

    void animate()
    {
        for (auto x = m_images.begin(); x != m_images.end();)
        {
            auto& image = *x;
            if (!image->animate())
            {
                image->detach();
                x = m_images.erase(x);
                objects_changed();
            }
            else
            {
                x++;
            }
        }
    }

    void objects_changed()
    {
        ostringstream ss;
        ss << "Objects: " << m_images.size m_label->text(ss.str());
    }

    vector<shared_ptr> m_images;
    shared_ptr m_label;
    shared_ptr m_sprite;

    std::random_device r;
    std::default_random_engine e1;
    std::uniform_int_distribution speed_dist{-20, -1};
    std::uniform_int_distribution offdist{-20, 20};
    std::uniform_int_distribution size_dist{10, 100};
};

int main(int argc, char** argv)
{
    Application app(argc, argv, "water");
#ifdef EXAMPLEDATA
    add_search_path(EXAMPLEDATA);
#endif
    MainWindow win;

    vector sprites;

#define SPRITE1
#ifdef SPRITE1
    Sprite sprite1(Image("file:fish.png"), Size(252, 209), 8, Point(0, 0));
    sprite1.no_layout(true);
    win.add(sprite1);
    sprite1.show();
    sprites.push_back(&sprite1);
#endif

#define SPRITE2
#ifdef SPRITE2
    Sprite sprite2(Image("file:fish2.png"), Size(100, 87), 6, Point(0, 0));
    sprite2.no_layout(true);
    win.add(sprite2);
    sprite2.show();
    sprites.push_back(&sprite2);
#endif

    sprites.push_back(win.m_sprite.get());

    PeriodicTimer animatetimer(std::chrono::milliseconds(30));
    animatetimer.on_timeout([&win]()
    {
        win.animate();
    });
    animatetimer.start();

    PeriodicTimer animatetimer2(std::chrono::milliseconds(100));
    animatetimer2.on_timeout([&sprites]()
    {
        for (auto& sprite : sprites)
            sprite->advance();
    });
    animatetimer2.start();

    PeriodicTimer spawntimer(std::chrono::seconds(1));
    spawntimer.on_timeout([&win]()
    {
        if (win.m_images.size() > 30)
            return;

        static std::uniform_int_distribution xoffdist(-win.width() / 2, win.width() / 2);
        int offset = xoffdist(win.e1);

        static std::uniform_int_distribution count_dist(1, 10);
        int count = count_dist(win.e1);

        Point p(win.box().center());
        p.y(win.box().height());
        p.x(p.x() + offset);

        while (count--)
            win.spawn(p);
    });
    spawntimer.start();

#ifdef SPRITE1
    PropertyAnimator a1(-sprite1.size().width(), Application::instance().screen()->size().width(),
                        std::chrono::milliseconds(10000),
                        easing_linear);
    a1.on_change([&sprite1](PropertyAnimator::Value value){
            sprite1.x(value);
        });
    a1.start();

    PeriodicTimer floattimer(std::chrono::milliseconds(1000 * 12));
    floattimer.on_timeout([&a1, &sprite1, &win]()
    {

        static std::uniform_int_distribution yoffdist(0, win.height() - sprite1.size().height());
        int y = yoffdist(win.e1);

        sprite1.move(Point(-sprite1.size().width(), y));
        a1.start();
    });
    floattimer.start();
#endif

#ifdef SPRITE2
    PropertyAnimator a2(-sprite2.size().width(), Application::instance().screen()->size().width(),
                        std::chrono::milliseconds(12000),
                        easing_linear);
    a2.on_change([&sprite2](PropertyAnimator::Value value){
            sprite2.x(value);
        });
    a2.start();

    PeriodicTimer floattimer2(std::chrono::milliseconds(1000 * 15));
    floattimer2.on_timeout([&a2, &sprite2, &win]()
    {
        static std::uniform_int_distribution yoffdist(0, win.height() - sprite2.size().height());
        int y = yoffdist(win.e1);

        sprite2.move(Point(-sprite2.size().width(), y));
        a2.start();
    });
    floattimer2.start();
#endif

    Label label1("CPU: ----");
    label1.color(Palette::ColorId::text, Palette::red);
    label1.color(Palette::ColorId::bg, Palette::transparent);
    win.add(bottom(left(label1)));

    egt::experimental::CPUMonitorUsage tools;
    PeriodicTimer cputimer(std::chrono::seconds(1));
    cputimer.on_timeout([&label1, &tools]()
    {
        tools.update();
        ostringstream ss;
        ss << "CPU: " << static_cast(tools.usage()) << "%";
        label1.text(ss.str());
    });
    cputimer.start();

    win.show();

    return app.run();
}

相关推荐

Sublime Text 4 稳定版 Build 4113 发布

IT之家7月18日消息知名编辑器SublimeText4近日发布了Build4113版本,是SublimeText4的第二个稳定版。IT之家了解到,SublimeTe...

【小白课程】openKylin便签贴的设计与实现

openKylin便签贴作为侧边栏的一个小插件,提供便捷的文本记录和灵活的页面展示。openKylin便签贴分为两个部分:便签列表...

“告别 Neovim!为什么我投奔了刚开源的 Zed 编辑器?”

...

壹啦罐罐 Android 手机里的 Xposed 都装了啥

这是少数派推出的系列专题,叫做「我的手机里都装了啥」。这个系列将邀请到不同的玩家,从他们各自的角度介绍手机中最爱的或是日常使用最频繁的App。文章将以「每周一篇」的频率更新,内容范围会包括iOS、...

电气自动化专业词汇中英文对照表(电气自动化专业英语单词)

专业词汇中英文对照表...

Python界面设计Tkinter模块的核心组件

我们使用一个模块,我们要熟悉这个模块的主要元件。如我们设计一个窗口,我们可以用Tk()来完成创建;一些交互元素,按钮、标签、编辑框用到控件;怎么去布局你的界面,我们可以用到pack()、grid()...

以色列发现“死海古卷”新残片(死海古卷是真的吗)

编译|陈家琦据艺术新闻网(artnews.com)报道,3月16日,以色列考古学家发现了死海古卷(DeadSeaScrolls)新残片。新出土的羊皮纸残片中包括以希腊文书写的《十二先知书》段落,这...

鸿蒙Next仓颉语言开发实战教程:订单列表

大家上午好,最近不断有友友反馈仓颉语言和ArkTs很像,所以要注意不要混淆。今天要分享的是仓颉语言开发商城应用的订单列表页。首先来分析一下这个页面,它分为三大部分,分别是导航栏、订单类型和订单列表部分...

哪些模块可以用在 Xposed for Lollipop 上?Xposed 模块兼容性解答

虽然已经有了XposedforLollipop的安装教程,但由于其还处在alpha阶段,一些Xposed模块能不能依赖其正常工作还未可知。为了解决大家对于模块兼容性的疑惑,笔者尽可能多...

利用 Fluid 自制 Mac 版 Overcast 应用

我喜爱收听播客,健身、上/下班途中,工作中,甚至是忙着做家务时。大多数情况下我会用MarcoArment开发的Overcast(Freemium)在iPhone上收听,这是我目前最喜爱的Po...

Avalonia日志组件实现与优化指南(ar日志表扣)

...

浅色Al云食堂APP代码(三)(手机云食堂)

以下是进一步优化完善后的浅色AI云食堂APP完整代码,新增了数据可视化、用户反馈、智能推荐等功能,并优化了代码结构和性能。项目结构...

实战PyQt5: 121-使用QImage实现一个看图应用

QImage简介QImage类提供了独立于硬件的图像表示形式,该图像表示形式可以直接访问像素数据,并且可以用作绘制设备。QImage是QPaintDevice子类,因此可以使用QPainter直接在图...

滚动条隐藏及美化(滚动条隐藏但是可以滚动)

1、滚动条隐藏背景/场景:在移动端,滑动的时候,会显示默认滚动条,如图1://隐藏代码:/*隐藏滚轮*/.ul-scrool-box::-webkit-scrollbar,.ul-scrool...

浅色AI云食堂APP完整代码(二)(ai 食堂)

以下是整合后的浅色AI云食堂APP完整代码,包含后端核心功能、前端界面以及优化增强功能。项目采用Django框架开发,支持库存管理、订单处理、财务管理等核心功能,并包含库存预警、数据导出、权限管理等增...

取消回复欢迎 发表评论: