惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

有赞技术团队
有赞技术团队
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
P
Palo Alto Networks Blog
C
Cisco Blogs
The Hacker News
The Hacker News
T
Threatpost
S
Schneier on Security
K
Kaspersky official blog
Spread Privacy
Spread Privacy
博客园_首页
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
NISL@THU
NISL@THU
量子位
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Google DeepMind News
Google DeepMind News
Security Latest
Security Latest
博客园 - 司徒正美
云风的 BLOG
云风的 BLOG
博客园 - 叶小钗
H
Hackread – Cybersecurity News, Data Breaches, AI and More
N
News and Events Feed by Topic
爱范儿
爱范儿
P
Proofpoint News Feed
C
CERT Recently Published Vulnerability Notes
Project Zero
Project Zero
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Cisco Talos Blog
Cisco Talos Blog
GbyAI
GbyAI
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Apple Machine Learning Research
Apple Machine Learning Research
T
Tenable Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
V
Vulnerabilities – Threatpost
Forbes - Security
Forbes - Security
博客园 - 三生石上(FineUI控件)
C
Cyber Attacks, Cyber Crime and Cyber Security
N
News and Events Feed by Topic
V
V2EX
Webroot Blog
Webroot Blog
The Register - Security
The Register - Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
阮一峰的网络日志
阮一峰的网络日志
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Blog — PlanetScale
Blog — PlanetScale
M
MIT News - Artificial intelligence
Scott Helme
Scott Helme
Simon Willison's Weblog
Simon Willison's Weblog
L
LangChain Blog
W
WeLiveSecurity
Cloudbric
Cloudbric

博客园 - 立体风

当AI构建自身 nana gui库的回调函数 介绍 MSVC 的编译命令/ Zc CMake 变量 ${CMAKE_CURRENT_BINARY_DIR} 学习 nana c++ 库 (一) AutoHotkey v2 的源码学习路线 Visual Studio 中行号右边的黄色竖条 大O表示法推导 allocate 中为什么有两个L vs的cl.exe 编译器 c++20 基本配置 IT高频词 address Python Install Manager (PIM,官方安装管理器)简介 debian 中 contrib 软件仓库 debian 版本号及 apt 命令对软件的区分 sysctl的历史及常用命令 极限运算的法则 TeX 的 ctex 宏包的基本用法 XeLaTeX 的基本用法 XeLaTeX 介绍 为什么梯度向量 ∇f 指向函数值增长最快的方向 三角函数的余弦定理的证明 向量点积的几何定义的证明 方向导数公式的证明 仿射函数的定义及用途 凸集的定义及证明 在 LaTeX 公式中处理英文单词 梯度下降法 (Gradient Descent)的数学原理。 梯度下降法简介 向量形式表达最小二乘法 线性回归模型的核心数学表达式 risidual 音标和本义 coefficients的音标及本义(线性模型)
Nana c++ gui库的对话框函数 -- 理解 nana 的运行机制
立体风 · 2026-06-19 · via 博客园 - 立体风
#include <nana/gui.hpp>                  // always include this
#include <nana/gui/widgets/button.hpp>
#include <nana/gui/widgets/group.hpp>
#include <nana/gui/widgets/label.hpp>
#include <nana/gui/msgbox.hpp>
int main()
{
    using namespace nana;
    form   fm;                             // Our main window
    fm.caption("My first NANA demo");       // (with this title)
    place  fm_place{ fm };                    // have automatic layout
    bool   really_quick{ false };             // and a default behavior
    label  hello{ fm,"Hello World" };         // We put a label on our window
    button btn{ fm,"Quit" };                // and a button
    btn.tooltip("I will ask first");       // that show a tip upon mouse hover
    group  act{ fm, "Actions" };            // Add a group of "options"
    act.add_option("Quick quickly")         // and two options that control quick behavior
        .events().click([&]() {   really_quick = true;
    btn.tooltip("Quick quickly");       });
    act.add_option("Ask first")
        .events().click([&]() {   really_quick = false;
    btn.tooltip("I will ask first");    });
    btn.events().click([&]()               // now the button know how to respond
        {
            if (!really_quick)     // not really quick !
            {
                msgbox m(fm, "Our demo", msgbox::yes_no);
                m.icon(m.icon_question);
                m << "Are you sure you want to quick?";
                auto response = m();
                if (response != m.pick_yes) return;   // return to the demo
            }
            API::exit();           // or really quick
        });
    act.radio_mode(true);                   // Set "radio mode" (only one option selected)
    // let divide fm into fields to holds the other controls.
    // for example, let split fm into two fields separated by a movable vertical barre.
    fm_place.div("vertical <label margin=10>|70% <actions>");
    fm_place["label"] << hello << btn;        // and place the controls there
    fm_place["actions"] << act;
    fm_place.collocate();                      // and collocate all in place
    fm.show();
    exec();
}

在上述代码中,关于 click 事件的代码中,有一段调用对话框函数 msgbox 的调用方法,表达了 nana 的设计风格。

btn.events().click([&]()               // now the button know how to respond
        {
            if (!really_quick)     // not really quick !
            {
                msgbox m(fm, "Our demo", msgbox::yes_no);
                m.icon(m.icon_question);
                m << "Are you sure you want to quick?";
                auto response = m();
                if (response != m.pick_yes) return;   // return to the demo
            }
            API::exit();           // or really quick
        });

当执行 m() 时,到底发生什么?

m() 在这里表示执行并弹出对话框。

在 C++ 中,这种写法利用了运算符重载(Operator Overloading),具体来说是重载了**函数调用运算符 ()**(也称为重载 operator())。

彻底地理解这几行代码,我们可以把它拆解开来看:

1. m() 背后发生了什么?

当你创建了一个 msgbox 对象 m 并设置好它的各种属性(如标题、图标、文本)后,它只是一个存在于内存中的对象,并没有真正显示在屏幕上。

当你调用 m() 时,实际上是触发了类似下面的过程:

  • 阻塞运行: 弹出一个模态对话框,并暂停(阻塞)后面代码的执行。
  • 等待交互: 等待用户点击 “是 (Yes)” 或 “否 (No)”。
  • 返回结果: 当用户点击了某个按钮关闭对话框后,m()返回一个值,代表用户的选择。

2. 代码逐行拆解

auto response = m();

  • m():显示对话框,等用户点击。
  • auto response:自动推导类型,用来接收 m() 返回的结果(在 Nana 库中,这个返回类型通常是 msgbox::pick_t 或者是它的内部枚举)。
if (response != m.pick_yes) return;

  • m.pick_yes:是 msgbox 预定义的一个常量,代表用户点击了“是”。
  • 整个 if 语句:如果用户的选择 response 不是!=)“是”,说明用户反悔了(点了“否”或者直接关闭了对话框),此时直接 return 退出当前 lambda 函数,取消退出程序,返回到主界面。

拓展:为什么不写成 m.show() 呢?

很多 GUI 库(比如 Qt 或 Windows API)通常会使用 m.exec() 或 m.showDialog()。

而 Nana 库选择重载 () 运算符,是一种更偏向现代 C++ 仿函数(Functor) 的设计风格。它把“显示对话框并获取结果”这整个动作,抽象得就像调用一个普通函数一样自然:

"我想从对话框 m 获取一个结果,那我就像调用函数一样调用它:m()"

为什么在 click 事件的匿名函数没有使用 if...esle 结构

这是一个非常好的编程习惯问题!你敏锐地察觉到了这里的代码结构。

之所以没有写成常见的 if...else 结构,而是让 API::exit(); 直接“裸奔”在函数底部,背后主要有两层考虑:代码设计的清晰度(提前返回模式) 以及 逻辑的必然结果


1. 核心原因:避免“嵌套地狱”的卫语句(Guard Clauses)

在软件工程中,这种写法被称为卫语句(Guard Clause),或者叫提前返回(Early Return)

如果按照传统的 if...else 逻辑,代码会写成这样:

// 传统 if...else 结构
btn.events().click([&]() {
    if (!really_quick) { // 情况 A:需要确认
        msgbox m(fm, "Our demo", msgbox::yes_no);
        m.icon(m.icon_question);
        m << "Are you sure you want to quick?";
        if (m() == m.pick_yes) {
            API::exit(); // 真正退出
        }
    } else {             // 情况 B:不需要确认
        API::exit();     // 真正退出
    }
});

为什么传统的 if...else 不够好?

  1. 代码重复:你必须在 if 块和 else 块里面写两次 API::exit();
  2. 嵌套变深:随着逻辑变复杂,你的代码会不断向右缩进,形成“金字塔”或“嵌套地狱”,变得极难阅读。

2. 执行路径的逻辑拆解(为什么不需要 else)

我们来看看原本的代码逻辑是如何像一个“安检闸机”一样工作的:

btn.events().click([&]() 
{
    // 拦截层:只有在 "需要弹出提示" 时才进入
    if (!really_quick) 
    {
        msgbox m(fm, "Our demo", msgbox::yes_no);
        // ... 省略设置 ...
        auto response = m();
        
        // 如果用户点了“否”,直接 return 结束这个 lambda 函数!
        // 后面所有的代码(包括 API::exit())都会被跳过。
        if (response != m.pick_yes) return; 
    }

    // 通关层:能走到这一行的,只有两种清白的情况
    // 情况 1: really_quick 为 true,直接跳过了上面的整个 if 块。
    // 情况 2: really_quick 为 false,但用户在弹窗里勇敢地点击了“是 (Yes)”。
    API::exit();           
});

因为 if 块内部的 return 具有强烈的截断性(只要条件不满足,函数直接在这里彻底结束,后面的代码根本没机会执行)。

所以,代码运行到最后一行 API::exit(); 时,在逻辑上已经天然形成了 else 的效果,根本不需要再刻意写一个 else 关键字来包裹它。


总结

这种“先排除异常/反悔情况(通过 return 拦截),最后执行核心动作”的编写背景,是现代 C++(以及绝大多数现代编程语言)非常推崇的干净风格。它让代码:

  • 扁平化:没有深层的缩进。
  • 意图明显:主流程(退出程序)一目了然地放在函数最底部。