天天财汇 购物 网址 万年历 小说 | 三峰软件 小游戏 视频
TxT小说阅读器
↓小说语音阅读,小说下载↓
一键清除系统垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放,产品展示↓
首页 淘股吧 股票涨跌实时统计 涨停板选股 股票入门 股票书籍 股票问答 分时图选股 跌停板选股 K线图选股 成交量选股 [平安银行]
股市论谈 均线选股 趋势线选股 筹码理论 波浪理论 缠论 MACD指标 KDJ指标 BOLL指标 RSI指标 炒股基础知识 炒股故事
商业财经 科技知识 汽车百科 工程技术 自然科学 家居生活 设计艺术 财经视频 游戏--
  天天财汇 -> 设计艺术 -> 这个开源的6千行UI框架,能打败QT,MFC吗? -> 正文阅读

[设计艺术]这个开源的6千行UI框架,能打败QT,MFC吗?

[收藏本文] 【下载本文】
超轻量UI框架,C/C++编写,可以在Android,Linux,Windows,虚拟现实上面运行。 I am GuiLite-在线播放-优酷网,视频…
凡是以为Qt/MFC只是用来写界面的,一开始就输了。核心部分不开源只有lib是几个意思?“由于代码量极少,它便于开发人员扩展自己的UI风格”。没有native-handle,没有虚方法,没有事件系统,你让我拿头去扩展啊?core_include里我数数,13个头文件,数数Qt里类似的组件,QtCore + QtGui有多少类吧。thread还要自己撸接口蛋疼不蛋疼?而且还不是什么上层接口比如async、coroutine、promise、future,就是统一封装了下pthread,连std::thread都不如。没找到任何render相关。请问如何扩展?如何做样式?请问有QGraphicsView的画布功能吗?没有的话你倒是画个visio给我看看?请问有TextEdit/TextBrowser吗?连notepad.exe都写不出来也好意思叫UI框架?回去重修四人帮设计模式吧。paint接口里,有任何变换方式吗?比如旋转缩放?比如矩阵变换?我没看到哟。文本绘制里,有对齐吗?有抗锯齿吗?有换行控制吗?能区分字形中宫和完整的字形区域吗?有字体管理吗?有图片处理吗?我给你一张bmp、一张png、一张jpg、一张svg、一张ico,你倒是给我draw出来呀?话说你那个GUI_BITMAP我都找不到定义,你让我怎么编译?请问我如何把有交互的控件,或者哪怕图片放进table?我看接口只能放string。table只能直接setItem,没有做model-view分离,那我就不要求百万行了,请问能放得下十万行数据不卡么?请问我想设置快捷键怎么办?没任何事件系统的封装,莫非只能撸平台API?那你倒是把native handle给我啊!请问有任何layout布局管理吗?我没找到哦,大概不是一拉窗口边框就露陷?GitHub主页上的那个图层管理,没任何价值,因为代码里根本找不到任何与之相关的东西。另外这个图层管理设计的也是一团糟。不会写你不会抄啊,Customizing Qt Widgets Using Style Sheets Box-Model拿去抄,不谢。 请问有任何界面动画功能吗?我想要个动态弹出菜单,想点关闭按钮后渐隐,怎么做?哦对了,连菜单我都没找到。我都不好意思说双缓冲、局部更新这种GUI绘制的基础机制了,目测根本没有。敢问支不支持accessibility?就目前看来,native handle都没有,那么可以认为是0支持了。所以欧美市场别想了,因为人家法定要支持。做大项目也别想了,因为没法自动化测试。换肤功能呢?翻译功能呢?类型private成员都没隐藏起来,想让大家跟着你commit一次就全盘重编译一次?你的事件框架在哪?别告诉我全是用重载哦,而且gui的所有头文件都没找到任何虚函数,请问界面如何推送事件出来?无任何命名规范,Camel、Pascal、下划线、匈牙利全都有。请问有多屏和dpi支持吗?我没看到。请问有渐变绘制的api吗?答:没有。视频里的曲线是怎么画的?范例工程的source里我怎么找不到捏?另外连曲线控件都没也好意思叫UI框架?所谓的跨平台,还是每个平台上都写native啊,那请问这框架要你何用?我貌似翻出了个叫MainPage.xaml的东东,啧……buildWin32的sample里,我居然看到了 #ifdef __linux__以及backtrace以及backtrace_symbols,啧……我都没兴趣下下来跑benchmark了。信不信我手头随便一个项目的界面搬运过来,分分钟给你跑崩溃?哦不对,这UI库连我手头的任何一个项目的需求都不能满足。综上所属,这个所谓的UI框架,也就是把各平台的GDI之类的基础绘制接口封装了下,做成了core/wnd.h里的c_wnd类,其他没任何东西了。别丢脸了,真的。这东西连MFC 1.0都比不上。难怪不好意思贴GitHub链接,只敢贴个视频。港真,你把core.lib原码放出来我都懒得抄,这种东西我一个周末就能撸出来。真当我一个周末撸不出这种水平的6000行代码?算了还是不写了,我自己开的坑都还没填呢,GitHub页面连个大学生都比不上,心疼问题关注列表里的
@齐亮
@龙泉寺扫地僧
@刘雨培
和我自己。污染时间线啊。喂喂谁把题主举报了呀,我还等着多邀请我几个问题刷点赞呢。话说这题主问的这种问题还没必要被永封吧?
回答作者的最新回复

腾讯qq用QT/MFC吗?微软的office用MFC吗?百度,阿里用QT/MFC吗?
大公司更务实,QT/MFC的商业洗脑对他们几乎没有用,大家不妨深度思考一下其中的原因

除了微软,BAT的重点都不是桌面app,他们的ui就是duilib,实际就是被qt吊打,比如你还好意思挂出来的QQ,在高分屏上依旧糊的一塌糊涂,还不如用UWP写的win10版,你这是在黑友军啊。
office则是微软内部有一套自家的ui框架,根本不是mfc那种古董,至少也是winform级别直追uwp了。(据轮子哥评论,office是因为mfc太新,写office时还没mfc)
我倒是反过来问你,为什么photoshop要用qt?为什么亿图图示要用qt?为什么wps要用qt?
不是微软那级别的公司,凭什么认为自己能做出比qt好的ui框架?除了Qt,你倒是给我找一个通吃全平台的被广泛使用用的第三方框架啊?
人家是windows+linux第三方ui框架里市场占有率最高的,是靠真本事还是靠洗脑了全世界程序猿,你自己心里就没点哔数么?
虽然看到题主的提问记录,我已经有80%置信度怀疑题主是民科了。
不过看到这句“商业洗脑”,我觉得可以把置信度提高到100%了。
这个开源的一行代码App框架,能打败QT,MFC,Android,iOS吗?
跨平台、超轻量、易扩展,框架代码如下:

#define CALL(fn, ...) fn(__VA_ARGS__)

使用示例(使用CALL输出Hello,World!):

CALL(printf, "Hello, World!\n");

这是自己来问的吧。我看了,就是个很简单的界面库。这种库github上随便搜索能出来上百个。6000行,就想干掉这个干掉那个的,跟动不动要推翻相对论是差不多的意思。
qt一大堆库,对cpp侵入性强,qml学习成本高
mfc那封装好浅,一大坨大写字母宏。同样传统界面,我宁可直接上win32
c#额外装framework,devexpress一大堆库,winform还凑合,uwp连个看图都做不好,wpf行将就木
duilib,bug不少,没人维护,设计器也不行
electron全身肿大,没有世界级水平跑不顺畅
cef得兼顾各种语言,需要团队分工
老delphi7,不支持64位
其余的又太小众
各有各的痛点
如果出现一个现代化体验,轻量级,依赖少,跨平台的UI库,还是很有机会的。明天看看题主的库怎么样。
我很佩服 这个项目的勇气 。。。。
敢于挑战 肯定是一件好事,毕竟任何新的伟大项目 都是 从小做大,挑战 权威开始的。
但是能不那麻烦 给点 更加详细的 图片或者视频?就官方的截图和视频 来说 除了那点 检测仪的 曲线图形 之外 我是真没看到 有什么东西了。。。。
这样的作品 又岂能称之为强大呢?说句实在的 要和 qt 以及 mfc 相比 真是差远了。
没错就算只相比UI部分也是差得老远了。
建议去看看 imgui 吧 人家 那 demo(samples) 做得。各种截图 展现 各种界面元素。
讲个笑话,界面库 Qt。
提醒题主一下,你就算再看不起 Qt,人家有 Qt Quick,自己研究研究这玩意是你多长时间能打败的吧。
哦,好像还想和 UWP 比,哎。
不能。UI这块很容易变成炫技的项目。
实际市场需要的是,紧贴业务层面的UI库。
这个需求基本确定了好用的UI库一定不会太轻。
我曾经在大厂的应用部门做开发,维护过基于传统的MVC框架的桌面应用。
这种老牌传统MVC框架将Model、View和Controller拆分的很彻底,界面层就是界面层,业务层就是业务层。
绝对不会在界面层写逻辑业务的代码,也不会在业务层里面出现任何一个窗口控件操作。
这样写的好处当然有很多,耦合性低、拓展性高、业务分离,但是也带来很多麻烦。
最麻烦的地方莫过于代码量的庞大复杂,往往简单一个赋值变量透传,可能都会跳转十几个不同的类对象。
想修改一个简单的业务场景,可能都要花费一天的时间去解决。
在传统MVC框架基础上,我基于Qt一些好用的特性,自己研究一套适合快速开发的MVC框架。
这套框架里面可能有些变形,不完全符合MVC的结构,但是容易简单上手。


首先,先给定一个业务场景,一个天气显示相关的软件,现在界面上只有一个天气按钮,还有一个隐藏的天气信息,点击天气按钮就触发隐藏信息显示今天天气。
先按照最简单的实现方法来

void MainWindow::initWindow()
{
    auto label = new QLabel();
    label->setVisible(false);

    auto button = new QPushButton("天气");
    button->setCheckable(true);
    button->setChecked(false);
    connect(button, &QPushButton::clicked, [=](bool checked) {
        QString weather = getTodayWeather();
        label->setText(weather);
        label->setVisible(checked);
    });

    auto widget = new QWidget();
    auto layout = new QVBoxLayout(widget);
    layout->addWidget(button, 0, Qt::AlignCenter);
    layout->addWidget(label, 0, Qt::AlignCenter);

    this->resize(400, 400);
    this->setWindowTitle(tr("小林通"));
    this->setCentralWidget(widget);
}

QString MainWindow::getTodayWeather()
{
    return QString("今日天气:晴");
}

实现后效果后就是这样


这种写法就是不按照框架来,想到哪就写到哪的写法,等控件一旦增多,交互一旦变复杂。
代码就会变得臃肿无比,到后期拓展维护和调试问题都是令人头大。
这时候就引入三个中心的概念:场景中心、事件中心、数据中心。
顾名思义,场景中心就是管理所有界面配置相关的类,ConfigCenter。
事件中心就是管理所有事件订阅通知的类,EventCenter。
数据中心就是管理所有数据的类,其中分为内部数据和外部数据,DataCenter。
1. 首先,先介绍下第一个场景中心ConfigCenter的功能,他的功能很简单就是响应场景配置,管理所有受控组件。
一般我会把配置类ConfigBase封装成一个基类,通过主界面类继承ConfigBase方式来实现场景配置。

class ConfigBase
{
public:
    ConfigBase();

protected:
    void readConfig();
    void writeConfig();

    QVariant getConfig(const QString &name);
    void setConfig(const QString &name, const QVariant& data);

    virtual void onConfig(const QString &name, const QVariant& data) = 0;

protected:
    QVariantMap m_config;
};

为什么要引入ConfigCenter这个概念呢?
在过去桌面应用的发展中,对大多数人来说界面就是一个数据显示的窗口,很少人会关心页面的交互的逻辑。
所以把页面的修改逻辑写得很随意,这里写一下,那里改一下,导致很多时候交互逻辑复杂一点场景,写起来就很容易大量出错。
为了解决这个问题,我引入一个控件的类型概念,来方便理解场景配置中心的作用。
把所有控件分成两种类型,一种是控制组件,另一种受控组件,其中受控组件被控制组件所控制。
在上面例子中,button就是控制组件,label就是受控组件,简单描述就是button控制label显示和隐藏。
理解上面两种控件的意思后,接下来把上面两个界面图片理解成两个场景。
场景一:无信息
场景二:有天气信息

    enum Weather_Type {
        Weather_None = 0x00,
        Weather_Exist
    };

场景名称currentWeacher表示当前有无天气场景,对应一个枚举、字符串、其他方式表示场景配置内容。
这样就把页面的控件的交互,转化成不同场景的切换,控制组件修改配置中心setConfig来切换场景配置,在onConfig来响应受控组件变化。
复杂的交互,被拆分成一个个场景,通过控制组件去修改场景配置,在根据配置内容去修改相应的受控组件。
这样写法控制组件可以不用关心场景的实现,而受控组件也不用考虑会被哪些控件所触发。
同时只需要通过getConfig获取场景配置内容,我就能知道当前界面应该是啥样子。

void MainWindow::initWindow()
{
    auto label = new QLabel();

    auto button = new QPushButton("天气");
    button->setCheckable(true);
    button->setChecked(false);
    connect(button, &QPushButton::clicked, [=](bool checked) {
        QString weather = getTodayWeather();
        label->setText(weather);
        Weather_Type type = checked ? Weather_Exist : Weather_None;
        setConfig("currentWeacher", type);
    });

    auto widget = new QWidget();
    auto layout = new QVBoxLayout(widget);
    layout->addWidget(button, 0, Qt::AlignCenter);
    layout->addWidget(label, 0, Qt::AlignCenter);

    this->resize(400, 400);
    this->setWindowTitle(tr("小林通"));
    this->setCentralWidget(widget);

    ui_label_weather = label;
}

void MainWindow::initConfig()
{
    setConfig("currentWeacher", Weather_None);
}

QVariant MainWindow::getConfig(const QString &name)
{
    return property(name.toStdString().c_str());
}

void MainWindow::setConfig(const QString &name, const QVariant &data)
{
    setProperty(name.toStdString().c_str(), data);
    onConfig(name, data);
}

void MainWindow::onConfig(const QString &name, const QVariant &data)
{
    if(name == "currentWeacher")
    {
        onConfigCurrentWeather(data);
    }
}

void MainWindow::onConfigCurrentWeather(const QVariant &data)
{
    if(ui_label_weather)
    {
        switch (data.toInt()) {
        case Weather_None:
            ui_label_weather->setVisible(false);
            break;
        case Weather_Exist:
            ui_label_weather->setVisible(true);
            break;
        default:
            break;
        }
    }
}

QString MainWindow::getTodayWeather()
{
    return QString("今日天气:晴");
}

2. 其次,介绍下事件中心EventCenter,管理所有事件订阅通知,通知数据中心更新。
经常写应用的开发应该对事件中心不陌生了,很多界面和后端通信都是通过事件订阅的方式。

    connect(button, &QPushButton::clicked, [=](bool checked) {
        QString weather = getTodayWeather();
        label->setText(weather);
        Weather_Type type = checked ? Weather_Exist : Weather_None;
        setConfig("currentWeacher", type);
    });

在上面代码button的触发中,我并没有把天气内容的修改放到场景切换接口里面实现。
因为这个部分是属于数据中心的活,不是配置中心需要处理的事情。而通知数据中心干活,这就需要事件中心出手。
不同于纯C++为了实现事件中心的订阅方式,大量使用回调函数指针,还要索引事件跟指针之间匹配。
用过Qt里面信号槽,就会感觉就是天生为订阅通信而生,尤其QAction这个类高度匹配事件触发器。
一般我会把事件类EventBase封装成一个基类,通过主界面类继承EventBase方式来实现事件触发。

class EventBase
{
public:
    EventBase();

public:
    QAction* getEvent(const QString &event);

    bool sendEvent(const QString &event);
    bool sendEvent(const QString &event, const QVariant &param);

    QVariant getEventParam(const QString &event);
    void setEventParam(const QString &event, const QVariant &param);

    template <typename Func1, typename Func2>
    inline QAction* addEvent(const QString &event, const QString &text, Func1 item, Func2 slot)
    {
        auto action = new QAction(text);
        QObject::connect(action, &QAction::triggered, item, std::move(slot));
        QObject::connect(action, &QAction::triggered, [this, event](bool checked = false) {
            onEventAction(event, checked, getEventParam(event));
        });
        d_eventActions.insert(event, action);
        return action;
    }

protected:
    virtual void onEventAction(const QString &event, int status, const QVariant &param)=0;

private:
    QMap<QString, QAction*> d_eventActions;
    QMap<QString, QVariant> d_eventParams;
};

习惯事件名称开头大写,配置名称开头小写。

void MainWindow::initWindow()
{
    auto label = new QLabel();

    auto button = new QPushButton("天气");
    button->setCheckable(true);
    button->setChecked(false);
    connect(button, &QPushButton::clicked, [=](bool checked) {
        sendEvent("UpdateWeather");
        Weather_Type type = checked ? Weather_Exist : Weather_None;
        setConfig("currentWeacher", checked ? Weather_Exist : Weather_None);
    });

    auto widget = new QWidget();
    auto layout = new QVBoxLayout(widget);
    layout->addWidget(button, 0, Qt::AlignCenter);
    layout->addWidget(label, 0, Qt::AlignCenter);

    this->resize(400, 400);
    this->setWindowTitle(tr("小林通"));
    this->setCentralWidget(widget);

    ui_label_weather = label;
}

void MainWindow::initEvent()
{
    addAction(addEvent("UpdateWeather", QString(), this, &MainWindow::onActionUpdateWeather));
}

void MainWindow::onEventAction(const QString &event, int status, const QVariant &param)
{
    EventCenter::instance()->addEvent(Event_Action, event, status, param);
}

通过主界面继承EventBase基类,addEvent添加事件,在通过sendEvent触发事件。
关于事件中心EventCenter大多数都是写成单例类去实现,网上有很多可以参考的例子,这里就不细说了。
3. 最后来说下数据中心DataCenter,一般数据中心分类成两种内部数据和外部数据。
内部数据更新,一定会响应受控组件更新。
外部数据更新,可能会影响其他内部数据更新,可能会影响其他外部数据更新。

class InsideDataCenter
{
public:
    static InsideDataCenter* instance();

public:
    DataWeather d_weather;
};

class ExternDataCenter
{
public:
    static ExternDataCenter* instance();

public:
    DataCurrentSite d_currentSite;
    DataCurrentMood d_currentMood;
};

内部数据就是跟界面绑定的数据,一旦今日天气更新,界面ui_label_weather就会马上更新。
外部数据就是跟程序内存的数据,一旦当前位置更新,就会影响到今日天气的变化,进而导致界面ui_label_weather更新。
也有不影响内部数据的情况,比如当前心情变化,就不会影响到今日天气。

void MainWindow::initData()
{
    auto dataManager = InsideDataCenter::instance();
    connect(&dataManager->d_weather, &DataWeather::changedToView, this, &MainWindow::onDataWeather);
}

void MainWindow::onDataWeather(const QVariant &data)
{
    if(ui_label_weather)
    {
        QString weather = QString("今日天气:%1").arg(data.toString());
        ui_label_weather->setText(weather);
    }
}

我会将大量的逻辑代码写到DataCenter外部数据中心,比如:
DataCurrentSite里面通过百度地图api获取当前位置信息和当前天气信息。
DataCurrentMood里面通过本机摄像头采集人脸表情数据,通过表情识别获取当前心情。
这样写法,可以极大拓展了数据层,一个个数据模块都独立出来,从订阅事件到修改内部数据,从而影响到界面显示。
只要我内部数据跟界面绑定好,数据层就可以很好跟界面剥离开,外部数据模板化,做成插件的形式。
当然弊端还是有的,如果内部数据跟界面是同步绑定的,需要考虑耗时时间,界面卡顿的情况。
如果内部数据跟界面是异步绑定的,需要考虑数据不同步,脏数据的出现。
外部数据相互可以影响,调用逻辑关系要考虑好,会不会嵌套死循环。
我这种方法肯定不能说是正规的MVC框架,只能说借鉴下MVC用法,在Qt一些特性加持下改动不少。
之前尝试过固定60帧刷新界面数据,但比较吃电脑配置,消耗资源大。
也尝试过模型视图结构,在model和view添加代理delegate,代码量繁琐,修改头疼。
也尝试过数据和界面写在一起,前期开发有多开心,后期维护就有多难过。
尝试越多越烦躁。
最后准备模仿MVP框架去写,但是model不能直接操作view刷新,只能通过presenter去通知view刷新,导致中间层代码量变多。
旦借鉴MVP的思路,我把跟界面交互的部分界面刷新操作封装成场景配置中心去管理,把数据更新交给原本的MVC框架中。
拆分了controller代码,把部分界面刷新逻辑放在config,让view本身实现,只专心做给model数据通知即可。
把model拆分成两个inside和extern,其中inside直接更新view,另一个extern做数据获取和逻辑处理。
并且extern可以直接更新inside的数据,进而更新界面。


整理几条思路:
界面交互不可以直接操作受控组件,通过切换场景来影响界面控件。界面交互不可以直接修改数据中心的数据,通过触发事件来影响被订阅的数据。数据层数据分类为内部数据和外部数据,只有内部数据修改才可以影响界面控件。对于高频率更新的场景和内部数据,单独拎出来,另外安排处理,防止阻塞。
也不用按照我想法来,这个框架还不太成熟,在你写界面端代码的时候,有可以借鉴的地方就挺好。
用视频开源的项目我倒真是头一次见。
单凭这一点你就已经赢麻了。
看了一眼,感觉应该对标 SDL (而不是 QT/MFC)。
当然,显然跟 SDL 也没啥可比性……
[收藏本文] 【下载本文】
   设计艺术 最新文章
有哪些对你很有冲击力的设计?
「英语流利说」的使用体验如何?
为什么设计院出的图纸一堆错误?
保时捷中国总裁首度回应「米时捷」:或许好
为什么很多JRPG游戏战斗中可操控角色一般是
设计师都觉得宋体很难看吗?
有哪些看着像 PS 过的照片,实际却没有?
为什么galgame普遍使用自研引擎而不是unity
“角色也有自己的生活”是什么时候开始成为
写代码用哪种字体看起来最舒适?
上一篇文章      下一篇文章      查看所有文章
加:2024-02-21 23:09:58  更:2024-02-21 23:16:17 
 
 
股票涨跌实时统计 涨停板选股 分时图选股 跌停板选股 K线图选股 成交量选股 均线选股 趋势线选股 筹码理论 波浪理论 缠论 MACD指标 KDJ指标 BOLL指标 RSI指标 炒股基础知识 炒股故事
网站联系: qq:121756557 email:121756557@qq.com  天天财汇