FLTK学习笔记3-随机点名器
前面学习了基本控件以及回调函数的创建,在这里我们利用之前的知识编写一个随机点名程序。
随机点名程序应当有一个显示文本的控件用来显示被点到的人,这里我们用Fl_Input
控件;还应该有一个按钮,点击后开始随机显示人名,再点击一下停止更新人名,这里我们用Fl_Button
控件。
所需知识
C++ 随机数
随机点名需要产生随机数来代表被点到的人。关于C++随机数可以参考这里。本文中用以下方式产生随机数:
std::mt19937_64 engine(2020); /* 定义随机数引擎,种子为2020 */
std::uniform_int_distribution<> udist(min_number, max_number); /* 创建均匀分布 */
int random_number = udist(engine); /* 产生随机数 */
FLTK中自动重复执行指令
在FLTK中可以通过Fl::add_timeout
和Fl::repeat_timeout
来实现自动重复执行指令。
Fl::add_timeout
的作用是添加运行一次的回调函数,其函数原型如下,具体说明见官方文档。
void Fl::add_timeout (double t, Fl_Timeout_Handler cb, void* argp=0);
这里的t
代表每t
秒触发一次,cb
是要重复执行的回调函数。
Fl::repeat_timeout
的作用是重复运行回调函数,其函数原型如下,具体说明见官方文档。
void Fl::repeat_timeout (double t, Fl_Timeout_Handler cb, void* argp=0);
这里的t
和cb
含义与上面一样。
因此,可以用如下代码实现自动重复执行指令:
void repeat_cb(Fl_Widget* pw, void* pv){
/* some codes */
Fl::repeat_timeout(repeat_cb); /* 重复执行自身指令 */
}
int main(){
/* some codes */
Fl::add_timeout(1.0/5, repeat_cb); /* 执行repeat_cb指令 */
/* some codes */
}
在不需要重复指令时可以通过Fl::remove_timeout
来终止自动重复执行。
void Fl::remove_timeout (Fl_Timeout_Handlercb, void* argp=0);
随机点名器
首先,随机点名器中需要一些全局变量来储存状态:
std::mt19937_64 engine; /* 存储随机数产生器 */
std::vector<std::string> vs; /* 存储点名名单 */
Fl_Input* pText; /* 存储文本控件的地址 */
然后需要读取点名名单与产生随机数:(这里的名单是一个.txt文件,每一行是一个人名)
/* 读取点名名单 */
void loadNames(const std::string& dest){
std::fstream fs;
std::string name;
fs.open(dest, std::ios::in);
if (!fs){
return;
}
while (std::getline(fs, name)){
vs.push_back(name);
}
fs.close();
}
int getRandom(int min_number, int max_number){
std::uniform_int_distribution<> udist(min_number, max_number);
return udist(engine);
}
之后是画我们的GUI界面。整个界面非常简单,仅由一个输入框和一个按钮组成:
Fl_Double_Window* window = new Fl_Double_Window(300, 200, "随机点名测试");
pText = new Fl_Input(40, 50, 150, 30);
Fl_Button* pButton = new Fl_Button(190, 50, 70, 30, "开始点名");
window->end();
最后是写回调函数。这里我们需要三个回调函数:start_cb
,stop_cb
和rollCall_cb
。
初始按钮上的文字是“开始点名”,当被点击时调用第一个回调函数start_cb
,其作用是将按钮上的文字变为“停止点名”,并且将按钮的回调函数变为第二个回调函数stop_cb
,同时将第三个用于随机点名的回调函数rollCall_cb
设为重复执行。
void start_cb(Fl_Widget* pw, void* pv){
Fl_Button* o1 = (Fl_Button*)pw;
o1->label("停止点名"); /* 变更文字描述 */
o1->callback(stop_cb, nullptr); /* 改变按钮绑定的回调函数 */
Fl::add_timeout(1.0/5, rollCall_cb); /* 将点名回调函数设为重复执行 */
}
在开始点名后,按钮被点击时调用第二个回调函数stop_cb
,其作用是将按钮上的文字变为“开始点名”,并且将按钮的回调函数改回第一个回调函数start_cb
,同时结束第三个回调函数rollCall_cb
的重复执行。
void stop_cb(Fl_Widget* pw, void* pv){
Fl_Button* o1 = (Fl_Button*)pw;
o1->label("开始点名"); /* 变更文字描述 */
o1->callback(start_cb, nullptr); /* 改变按钮绑定的回调函数 */
Fl::remove_timeout(rollCall_cb); /* 结束点名回调函数的重复执行 */
}
之后是随机点名回调函数:
void rollCall_cb(void*){
int i = getRandom(0, vs.size()-1); /* 产生代表某个人的随机数 */
pText->value(vs[i].c_str()); /* 将对应人名显示到输入框内 */
Fl::repeat_timeout(1.0/5, rollCall_cb); /* 重复执行自身指令 */
}
程序示例
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Button.H>
#include <fstream>
#include <random>
#include <string>
#include <vector>
std::mt19937_64 engine(2020); /* 存储随机数产生器 */
std::vector<std::string> vs; /* 存储点名名单 */
Fl_Input* pText; /* 存储文本控件的地址 */
void loadNames(const std::string& dest){
std::fstream fs;
std::string name;
fs.open(dest, std::ios::in);
if (!fs){
return;
}
while (std::getline(fs, name)){
vs.push_back(name);
}
fs.close();
}
int getRandom(int min_number, int max_number){
std::uniform_int_distribution<> udist(min_number, max_number);
return udist(engine);
}
void start_cb(Fl_Widget* pw, void* pv);
void stop_cb(Fl_Widget* pw, void* pv);
void rollCall_cb(void*);
void start_cb(Fl_Widget* pw, void* pv){
Fl_Button* o1 = (Fl_Button*)pw;
o1->label("停止点名"); /* 变更文字描述 */
o1->callback(stop_cb, nullptr); /* 改变按钮绑定的回调函数 */
Fl::add_timeout(1.0/5, rollCall_cb); /* 将点名回调函数设为重复执行 */
}
void stop_cb(Fl_Widget* pw, void* pv){
Fl_Button* o1 = (Fl_Button*)pw;
o1->label("开始点名"); /* 变更文字描述 */
o1->callback(start_cb, nullptr); /* 改变按钮绑定的回调函数 */
Fl::remove_timeout(rollCall_cb); /* 结束点名回调函数的重复执行 */
}
void rollCall_cb(void*){
int i = getRandom(0, vs.size()-1); /* 产生代表某个人的随机数 */
pText->value(vs[i].c_str()); /* 将对应人名显示到输入框内 */
Fl::repeat_timeout(1.0/5, rollCall_cb); /* 重复执行自身指令 */
}
int main(){
loadNames("Names.txt");
Fl_Double_Window* window = new Fl_Double_Window(300, 200, "随机点名测试");
pText = new Fl_Input(40, 50, 150, 30);
Fl_Button* pButton = new Fl_Button(190, 50, 70, 30, "开始点名");
pButton->callback(start_cb, pText);
window->end();
window->show();
return Fl::run();
}
这里注意在编译的时候记得加上-lfltk命令(在MSYS2上还要加上-mwindows -DWIN32 -lole32 -luuid -lcomctl32)。
运行结果:
相关链接:
专栏:FLTK
FLTK学习笔记2-回调函数
FLTK学习笔记4-在FLTK中显示图片(上)