FLTK的UI设计工具FLUID使用方法总结

news2025/1/14 1:02:43

tags: FLTK C++ GUI

写在前面

终于又捡起来FLTK了, 先来看看怎么通过FLUID创建一个图形界面并完成回调函数的创建, 参考的是官方教程中关于创建一个CubeView程序的例子, 教程里面很多都与最新版本的FLTK界面不太一致, 但是通过我的摸索还是找出了方法. 下面来分享一下.

创建类

直接点New新建各种类型即可, 一些注意事项在官方文档中给出了, 一些类或者组件的名称都用粗体标出来了(如下图), 需要对应, 不然后面回调函数连接不上.

CubeViewUI类中主要实现了构造函数以及一个show方法, 用于显示窗体.

截屏2022-12-26 17.30.09

最后的结果如下, 这里其实主要是要看清楚是水平组件还是垂直组件, 这个很重要, 其次就是主窗体的大小, 配比要合适.

截屏2022-12-26 17.30.40

回调函数

其实就是两行, 主要用于五个主要组件中, 分为两组:

slider

  1. Zoom:

    cube->size=((Fl_Value_Slider *)o)->value();
    cube->redraw();
    
  2. ypan:

    cube->pany(((Fl_Value_Slider *)o)->value()); 
    cube->redraw();
    
  3. xpan:

    cube->panx(((Fl_Value_Slider *)o)->value()); 
    cube->redraw();
    

roller

  1. vrot:

    cube->v_angle(((Fl_Roller *)o)->value()); 
    cube->redraw();
    
  2. hrot

    cube->h_angle(((Fl_Roller *)o)->value()); 
    cube->redraw();
    

CubeView类(显示图形)

最后是主要的显示窗体的类, 这需要在extra Code部分添加头文件声明:

#include "CubeView.h"

这部分代码如下:

// CubeView.h
#include <FL/gl.h>
#include <FL/Fl_Gl_Window.H>
#include <math.h>

class CubeView : public Fl_Gl_Window {
public:
    CubeView(int x, int y, int w, int h, const char *l = 0);
    // this value determines the scaling factor used to draw the cube.
    double size;
    /* Set the rotation about the vertical (y ) axis.
     *
     * This function is called by the horizontal roller in CubeViewUI
     * and the initialize button in CubeViewUI.
     */
    void v_angle(float angle) { vAng = angle; };
    // Return the rotation about the vertical (y ) axis.
    float v_angle() { return vAng; };
    /* Set the rotation about the horizontal (x ) axis.
     *
     * This function is called by the vertical roller in CubeViewUI
       and the
     * initialize button in CubeViewUI.
     */
    void h_angle(float angle) { hAng = angle; };
    // the rotation about the horizontal (x ) axis.
    float h_angle() { return hAng; };
    /* Sets the x shift of the cube view camera.
     *
     * This function is called by the slider in CubeViewUI and the
     * initialize button in CubeViewUI.
     */
    void panx(float x) { xshift = x; };
    /* Sets the y shift of the cube view camera.
     *
     * This function is called by the slider in CubeViewUI and the
     * initialize button in CubeViewUI.
     */
    void pany(float y) { yshift = y; };
    /* The widget class draw() override.
     * The draw() function initialize Gl for another round of
     * drawing then calls specialized functions for drawing each
     * of the entities displayed in the cube view.
     */
    void draw();

private:
    /* Draw the cube boundaries
     * Draw the faces of the cube using the boxv[] vertices, using
     * GL_LINE_LOOP for the faces. The color is #defined by
     * CUBECOLOR.
     */
    void drawCube();
    float vAng, hAng;
    float xshift, yshift;
    float boxv0[3];
    float boxv1[3];
    float boxv2[3];
    float boxv3[3];
    float boxv4[3];
    float boxv5[3];
    float boxv6[3];
    float boxv7[3];
};
// CubeView.cxx
#include "CubeView.h"

CubeView::CubeView(int x, int y, int w, int h, const char *l)
    : Fl_Gl_Window(x, y, w, h, l) {
    vAng = 0.0;
    hAng = 0.0;
    size = 10.0;
    /* The cube definition. These are the vertices of a unit cube
     * centered on the origin.*/
    boxv0[0] = -0.5;
    boxv0[1] = -0.5;
    boxv0[2] = -0.5;
    boxv1[0] = 0.5;
    boxv1[1] = -0.5;
    boxv1[2] = -0.5;
    boxv2[0] = 0.5;
    boxv2[1] = 0.5;
    boxv2[2] = -0.5;
    boxv3[0] = -0.5;
    boxv3[1] = 0.5;
    boxv3[2] = -0.5;
    boxv4[0] = -0.5;
    boxv4[1] = -0.5;
    boxv4[2] = 0.5;
    boxv5[0] = 0.5;
    boxv5[1] = -0.5;
    boxv5[2] = 0.5;
    boxv6[0] = 0.5;
    boxv6[1] = 0.5;
    boxv6[2] = 0.5;
    boxv7[0] = -0.5;
    boxv7[1] = 0.5;
    boxv7[2] = 0.5;
};
// The color used for the edges of the bounding cube.
#define CUBECOLOR 255, 255, 255, 255
void CubeView::drawCube() {
    /* Draw a colored cube */
#define ALPHA 0.5
    glShadeModel(GL_FLAT);
    glBegin(GL_QUADS);
    glColor4f(0.0, 0.0, 1.0, ALPHA);
    glVertex3fv(boxv0);
    glVertex3fv(boxv1);
    glVertex3fv(boxv2);
    glVertex3fv(boxv3);
    glColor4f(1.0, 1.0, 0.0, ALPHA);
    glVertex3fv(boxv0);
    glVertex3fv(boxv4);
    glVertex3fv(boxv5);
    glVertex3fv(boxv1);
    glColor4f(0.0, 1.0, 1.0, ALPHA);
    glVertex3fv(boxv2);
    glVertex3fv(boxv6);
    glVertex3fv(boxv7);
    glVertex3fv(boxv3);
    glColor4f(1.0, 0.0, 0.0, ALPHA);
    glVertex3fv(boxv4);
    glVertex3fv(boxv5);
    glVertex3fv(boxv6);
    glVertex3fv(boxv7);
    glColor4f(1.0, 0.0, 1.0, ALPHA);
    glVertex3fv(boxv0);
    glVertex3fv(boxv3);
    glVertex3fv(boxv7);
    glVertex3fv(boxv4);
    glColor4f(0.0, 1.0, 0.0, ALPHA);
    glVertex3fv(boxv1);
    glVertex3fv(boxv5);
    glVertex3fv(boxv6);
    glVertex3fv(boxv2);
    glEnd();
    glColor3f(1.0, 1.0, 1.0);
    glBegin(GL_LINES);
    glVertex3fv(boxv0);
    glVertex3fv(boxv1);
    glVertex3fv(boxv1);
    glVertex3fv(boxv2);
    glVertex3fv(boxv2);
    glVertex3fv(boxv3);
    glVertex3fv(boxv3);
    glVertex3fv(boxv0);
    glVertex3fv(boxv4);
    glVertex3fv(boxv5);
    glVertex3fv(boxv5);
    glVertex3fv(boxv6);
    glVertex3fv(boxv6);
    glVertex3fv(boxv7);
    glVertex3fv(boxv7);
    glVertex3fv(boxv4);
    glVertex3fv(boxv0);
    glVertex3fv(boxv4);
    glVertex3fv(boxv1);
    glVertex3fv(boxv5);
    glVertex3fv(boxv2);
    glVertex3fv(boxv6);
    glVertex3fv(boxv3);
    glVertex3fv(boxv7);
    glEnd();
}; // drawCube
void CubeView::draw() {
    if (!valid()) {
        glLoadIdentity();
        glViewport(0, 0, w(), h());
        glOrtho(-10, 10, -10, 10, -20000, 10000);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glPushMatrix();
    glTranslatef(xshift, yshift, 0);
    glRotatef(hAng, 0, 1, 0);
    glRotatef(vAng, 1, 0, 0);
    glScalef(float(size), float(size), float(size));
    drawCube();
    glPopMatrix();
};

主文件(main)

#include "CubeViewUI.h"

static int my_handler(int event) {
    if (event == FL_SHORTCUT) return 1;
    // eat all shortcut keys
    return 0;
}

int main(int argc, char **argv) {
    CubeViewUI ui;
    Fl::add_handler(my_handler);
    ui.show(argc, argv);
    return Fl::run();
}

这里我加上了句柄函数, 这个函数用来使esc键失效, 否则在窗体中按下esc会直接关闭窗口.

编译链接

重点要说一下编译链接这块.

官方Makefile

先给出官方文档1中的Makefile, 其实用Cmake要方便一些, 但是Makefile也要会写才行.

CXX      = $(shell fltk-config --cxx)
DEBUG    = -g
CXXFLAGS = $(shell fltk-config --use-gl --use-images --cxxflags ) -I.
LDFLAGS  = $(shell fltk-config --use-gl --use-images --ldflags )
LDSTATIC = $(shell fltk-config --use-gl --use-images --ldstaticflags )
LINK     = $(CXX)
 
TARGET = cube
OBJS =  CubeMain.o CubeView.o CubeViewUI.o
SRCS =  CubeView.cxx CubeViewUI.cxx
 
.SUFFIXES: .o .cxx
%.o: %.cxx
	$(CXX) $(CXXFLAGS) $(DEBUG) -c $<

all: $(TARGET)
	$(LINK) -o $(TARGET) $(OBJS) $(LDSTATIC)

$(TARGET): $(OBJS)
CubeMain.o: CubeMain.cxx CubeViewUI.h
CubeView.o: CubeView.cxx CubeView.h CubeViewUI.h
CubeViewUI.o: CubeViewUI.cxx CubeView.h
 
clean: $(TARGET) $(OBJS)
	rm -f *.o 2> /dev/null
	rm -f $(TARGET) 2> /dev/null

CmakeLists

再给出Cmake文件(之前其实就介绍过)

cmake_minimum_required(VERSION 3.24)
project(fltk_proj)

set(CMAKE_CXX_STANDARD 17)

FIND_PACKAGE(FLTK REQUIRED)

# 相当于gcc `-I`参数
include_directories("/opt/homebrew/include")
# 相当于gcc `-L`参数
link_directories("/opt/homebrew/lib")

# 相当于gcc `-l`参数
link_libraries("fltk")
link_libraries("Xext")
link_libraries("X11")
link_libraries("m")

# 添加可执行程序
add_executable(test1 CubeMain.cxx CubeViewUI.cxx CubeView.cxx)

# MESSAGE(${FLTK_LIBRARIES})
TARGET_LINK_LIBRARIES(test1 ${FLTK_LIBRARIES})

最后如果想生成MacOS下的test.app目录, 那么只需要在命令行中输入:

fltk-config --post <前面生成的可执行文件名>

就会在同级目录下生成xxx.app了, 还是相当方便的.

部署.app目录的Shell脚本

后来我发现这个步骤可以通过Shell脚本来完成(fltk-config本质是就是一个可执行的脚本文件)

具体参考了fltk-config源码, 以及2. 脚本如下:

#!/bin/sh

compile=$1
case "$compile" in
*.cxx)
	prog=$(basename "$compile" .cxx)
	;;
*.cpp)
	prog=$(basename "$compile" .cpp)
	;;
*.cc)
	prog=$(basename "$compile" .cc)
	;;
*.C)
	prog=$(basename "$compile" .C)
	;;
*)
	echo "ERROR: Unknown/bad C++ soure file extension on \"$compile\"!"
	exit 1
	;;
esac

post=$prog
echo Creating "$post.app" bundle for desktop...

id=$(echo $post | tr ' ' '_')
echo $id

# Make the bundle directory and move the executable there
rm -rf "$post.app/Contents/MacOS"
mkdir -p "$post.app/Contents/MacOS"
mv "$post" "$post.app/Contents/MacOS"

# Make a shell script that runs the bundled executable
echo "#!/bin/sh" >"$post"
echo 'dir="`dirname \"$0\"`"' >>"$post"
echo 'exec "$dir/'"$post.app/Contents/MacOS/$post"'" "$@"' >>"$post"
chmod +x "$post"

# Make the simplest Info.plist needed for an application
cat >"$post.app/Contents/Info.plist" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<plist version="0.9">
    <dict>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleExecutable</key>
    <string>$post</string>
    <key>CFBundleIdentifier</key>
    <string>org.fltk.$id</string>
    <key>CFBundleName</key>
    <string>$post</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>NSHighResolutionCapable</key>
    <true/>
    </dict>
</plist>
EOF

echo "done!"

用法为:(保存为gen_app.sh)

./gen_app.sh test.cpp

终极版Makefile(集合编译链接与部署)

但是不够简洁, 于是想着把Shell集成在Makefile里面, 于是就有: (部分较官方文档有改动)

CXX      = $(shell fltk-config --cxx)
DEBUG    = -g
CXXFLAGS = $(shell fltk-config --use-gl --use-images --cxxflags ) -I.
LDFLAGS  = $(shell fltk-config --use-gl --use-images --ldflags )
LDSTATIC = $(shell fltk-config --use-gl --use-images --ldstaticflags )
LINK     = $(CXX)
 
TARGET = cube
OBJS =  CubeMain.o CubeView.o CubeViewUI.o
SRCS =  CubeMain.cxx CubeView.cxx CubeViewUI.cxx

APP_NAME= $(TARGET)

define info_plist
<?xml version="1.0" encoding="UTF-8"?> \
<plist version="0.9"> \
	<dict> \
	<key>CFBundleInfoDictionaryVersion</key> \
	<string>6.0</string> \
	<key>CFBundleExecutable</key> \
	<string>APP_NAME</string> \
	<key>CFBundleIdentifier</key> \
	<string>org.fltk.APP_NAME</string> \
	<key>CFBundleName</key> \
	<string>APP_NAME</string> \
	<key>CFBundlePackageType</key> \
	<string>APPL</string> \
	<key>NSHighResolutionCapable</key> \
	<true/> \
	</dict> \
</plist>
endef

.SUFFIXES: .o .cxx
compile: $(SRCS)
	@echo "compiling..."
	@$(CXX) $(CXXFLAGS) $(DEBUG) -c $^

all: compile link run

bundle_app: clean_app package_app

run: compile link $(TARGET)
	./$(TARGET)

package_app: compile link
	@echo "bundling..."
	@mkdir -p $(APP_NAME).app/Contents/{MacOS,Resources}
	@echo '$(info_plist)' > "$(APP_NAME).app/Contents/Info.plist"
	@sed -e "s/APP_NAME/$(APP_NAME)/g" -i "" "$(APP_NAME).app/Contents/Info.plist"
	@cp $(TARGET) "$(APP_NAME).app/Contents/MacOS/$(APP_NAME)"
#	cp -R "$(FRAMEWORK_PATH)/SDL2.framework" "$(APP_NAME).app/Contents/Resources/"

link: $(OBJS)
	@echo "linking..."
	@$(LINK) -o $(TARGET) $(OBJS) $(LDSTATIC)

clean: clean_app
	@echo "Delete *.o $(TARGET)..."
	@rm -f *.o 2> /dev/null
	@rm -f $(TARGET) 2> /dev/null

clean_app:
	@echo "Delete $(APP_NAME).app..."
	@rm -rf $(APP_NAME).app

用这套Makefile, 配合clion, 简直无敌!

效果

bb

几点注意

  1. Makefile缩进需要用制表符, 如果要用空格需要在开头加上.RECIPEPREFIX := $(.RECIPEPREFIX)<space>, 其中<space>是一个空格.
  2. FLUID界面也会被esc关闭, 注意保存, 可以通过快捷键⌘+S保存, 然后⌘+⇧+C生成头文件和源码.
  3. 控件通过拖放完成布局, 需要调整部分控件的范围与step, 例如roller和slider.
  4. 布局应该和回调函数分离, 便于维护, 后续会写相关文章.

ref


  1. FLTK 1.3.8: FLTK Basics; ↩︎

  2. 带有生成文件的应用程序捆绑包 - Joseph Long (joseph-long.com); ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/117681.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

centos7搭建mysql5.6

检查是否安装mysql rpm -qa | grep mysql 检查已安装的Mariadb rpm -qa | grep -i mariadb 卸载已安装的Mariadb数据库 rpm -qa | grep mariadb | xargs rpm -e --nodeps 下载mysql5.6安装包文件 wget http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm安…

详解什么是Polygon跨链桥| Tokenview

Polygon是一个Layer2网络&#xff0c;用于解决以太坊的吞吐量和主权挑战。尽管以太坊是最受欢迎的区块链开发平台&#xff0c;但它的吞吐量很低&#xff0c;不适合某些应用程序。Polygon提供特定于应用程序的、与以太坊兼容的侧链&#xff0c;将独立链的可扩展性和独立性与以太…

自学开发技术,从入门到入行

今天我们不谈技术&#xff0c;也不聊业务&#xff0c;说说学习技术的心得。 说到学习这种事情&#xff0c;无论是学什么&#xff0c;都需要持之以恒&#xff0c;拥有坚持的决心才有可能会学到一些东西。如果只是三天打鱼&#xff0c;两天晒网的态度&#xff0c;不出差错的话&a…

ORB-SLAM2 --- Tracking::TrackLocalMap函数

目录 1.函数作用 2.函数步骤 3.code 4.函数解释 ORB-SLAM2 --- Tracking::UpdateLocalKeyFrames函数https://blog.csdn.net/qq_41694024/article/details/128307627 1.函数作用 在跟踪得到当前帧初始姿态后&#xff0c;现在对local map进行跟踪得到更多的匹配&#xff0…

《迷你世界》亿级玩家都在用的游戏场景推荐系统长啥样?

导读&#xff1a;中国TOP1沙盒游戏&#xff0c;如何解决数据存储难题&#xff1f; 提到推荐系统&#xff0c;很多人都在电商购物、资讯或娱乐平台中体验过。比如&#xff0c;你刚在某电商APP买了一部手机&#xff0c;过两天再登录时&#xff0c;首页推荐中必定有耳机、手机壳等…

【信息安全】seed-labs实验-Secret-Key Encryption Lab

目录Task 1: Frequency AnalysisTask 2: Encryption using Different Ciphers and ModesTask 4: PaddingTask 5: Error Propagation – Corrupted Cipher TextTask 6: Initial Vector (IV) and Common MistakesTask 6.1. IV ExperimentTask 6.2. Common Mistake: Use the Same …

python源代码打包成为exe文件

目录标题前言下载安装模块&#xff08;pyinstaller&#xff09;执行打包命令1、在cmd中进入项目文件2、执行pyinstaller命令成exe文件3、找到exe文件报错以及解决报错X1解决方法&#xff1a;报错X2解决方法&#xff1a;写在最后前言 大家都知道我们在pycharm中编写的源代码&am…

如何在 15 分钟内度量 DORA 指标?

在这篇文章中&#xff0c;我们将介绍 DevOps 四个关键指标——DORA 指标是什么&#xff0c;其度量难点&#xff0c;以及如何基于开源工具快速实现 DORA 指标的持续追踪。如果你熟悉 DORA 指标&#xff0c;可以直接跳到本文第二部分。 什么是 DORA 指标&#xff1f; DORA 的全…

设备树的使用

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、设备树的概念&#xff1f;二、设备树的作用三、如何使用设备树1.设备树的位置2.设备树的语法认识DTC、DTS、DTSI、DTB设备节点信息3.添加自己的设备树节点4.…

STM32MP157驱动开发——SPI驱动

STM32MP157驱动开发——SPI驱动一、简介1.SPI介绍2.STM32MP1 SPI介绍3. ICM-20608 简介4.Linux下的SPI框架二、驱动开发1&#xff09;IO 的 pinctrl 子节点创建与修改2&#xff09;SPI 设备节点的创建与修改3&#xff09;ICM20608驱动4&#xff09;测试App5&#xff09;运行测试…

【分享】订阅用友U8集简云连接器同步费用审批数据至用友U8系统

方案简介 集简云通过与钉钉连接平台深度融合&#xff0c;实现OA审批通过后&#xff0c;将采购、报销、收款、售后等费用审批单数据自动同步至用友U8系统&#xff0c;大大提高了企业日常采购、报销、付款等的工作效率&#xff0c;实现企业业务流程的自动化&#xff0c;为企业降…

Docker的数据管理

目录 一、数据卷 二、数据卷容器 三、容器互联 容器中管理数据主要有两种方式&#xff1a; 数据卷&#xff08;Data Volumes&#xff09;数据卷容器&#xff08;Data Volume Dontainers&#xff09; 一、数据卷 数据卷是一个供容器使用的特殊目录&#xff0c;位于容器中。可…

5点电容式触摸屏控制芯片GT811介绍

5点电容式触摸屏控制芯片GT811简介 5点电容式触摸屏控制芯片GT811&#xff0c;采用投射式电容检测原理&#xff0c;由16个驱动通道与10个感应通道组成触摸检测网络&#xff0c;通过内置模拟放大电路、数字运算模块&#xff0c;及高性能MPU得到实时准确的触摸信息&#xff0c;并…

rocketmq安装

链接&#xff1a;https://pan.baidu.com/s/14ziQH62MeYmM8N6JsH5RcA 提取码&#xff1a;yyds 下载rocketmq-all-4.9.3-bin-release.zip 下载、修改配置 mkdir -p /app/rocketmq cd /app/rocketmqunzip rocketmq-all-4.9.3-bin-release.zip cd rocketmq-4.9.3/修改 配置文件&…

F28335第十六篇——Flash操作

文章目录前言配置方法擦除函数编写和校验函数前言 本文主要介绍如何对DSP28335进行Flash操作。 本文主要参考资料&#xff1a; TI.Flash2833x_API_Readme DSP的Flash操作需要借助官方提供API。官方提供的API主要完成Flash的擦除&#xff0c;编写&#xff0c;校验三种功能。…

八、Java 15 新特性

八、Java 15 新特性 JDK 15 在 2020 年 9 月 15 号正式发布了&#xff01;根据发布的规划&#xff0c;这次发布的 JDK 15 将是一个短期的过度版&#xff0c;只会被 Oracle 支持&#xff08;维护&#xff09;6 个月&#xff0c;直到明年 3 月的 JDK 16 发布此版本将停止维护。而…

VUE3-生命周期钩子《六》

目录 1.onMounted() 2.onUpdated() 生命周期的作用&#xff0c;页面加载的时候&#xff0c;主动执行某些程序。生命周期钩子有很多种&#xff0c;每一种都是有顺序的&#xff0c;如果不按照顺序执行的话&#xff0c;那么就不会触发某种效果&#xff0c;所以先要了解生命周期钩…

Java Web高级面试题(一)

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&#xff1a;Java面试题…

常用的工具网站(网址 + 效果图)

一&#xff0c;阿里图标库 https://www.iconfont.cn/?spma313x.7781069.1998910419.d4d0a486a 二&#xff0c;AI人工智能图片放大 https://bigjpg.com/zh 三&#xff0c;一个有情怀的免费PPT模板下载网站&#xff01; https://www.ypppt.com/ 四&#xff0c;照片抠图…

AOP技术

目录 一、简介 1.1、OCP原则 1.2、AOP介绍及使用场景 二、AOP在项目中的使用 2.1、集成使用 2.2、定义全局异常通知 2.3、AOP注解的含义 2.4、多个切面的执行顺序 三、通知的执行顺序&#xff08;基于spring-aop5版本&#xff09; 四、常用的四种切入点表达式 4.1、…