文档章节

DIY一个高大上带提醒的计时器,简单实用,你还在等什么~

实验楼
 实验楼
发布于 2014/12/19 14:10
字数 2981
阅读 96
收藏 0


小编心语:锵锵锵!小编我又来了!昨天发了一篇比较实用的《Python聊天室》,鉴于反响还不错,成电的校友如此给力,SO ,小编也想给大家多分享点有用的干货,让大家边学边用。好了,闲话不多说,今天要给各位看官们介绍的是基于C语言的带提醒功能计时器。
你还在为错过重要的事情而心烦不已吗?
你还在为没抢到某米手机而扼腕叹息吗?
你还在为没领到美食免单而空遗口水吗?
从今天起,这些都不是问题,且看小编我给你一一道来!

dialog+ncurse实现命令行带提醒功能计时器

我们将那个结合shell编程与C语言编程使用dialog工具与ncurses库实现一个仿tmux时间风格的带提醒功能的计时器

一、说明

这次项目课要实现的东西功能很简单,但是却用到了好几个东西,包括dialogncurses库,zenity, moc, 用了这么多东西最后也就是个定时器-_-||。不过我想先告诉你的是为什么是它。这原因呢有两个,一个是之前看到有用户反应希望项目课有一些综合性稍强的 内容,这个项目课就是了,结合了shell编程和C语言编程,C语言编程除了包含本身的内容还涉及到了如何使用外部库来实现我们想要的功能;这第二个原因 是,我自己需要一个这样的定时器啊,我要每周二中午12点的时候准时抢小米手机(抢了几次没抢到啊,就是因为时间没掐准,不够快),所以就想要一个定时器 了,我想的是除了要实现基本功能外还要求“看起来,高大上”。

二、功能介绍

既然我是要实现一个命令行的定时器,要想“高大上”,那就必须要用到命令行的图形库了,一开始想到的是ncurses,这就得完全用C语言来开发了,后来又想到可以用shell下的dialog(基于ncurses实现的命令行图形对话框,包含一些常用的构件)来完成一部分功能,比如设定时间的界面可以用dialogtimebox构件来实现,如下图

本想是不是可以用dislog实现全部我想要的效果,比如在设定时间之后,进入倒计时界面,类似下面的效果

这实际是tmux内建时钟效果。然后翻翻dialog的文档,发现连字体大小都没法设置,也无法设置构件内文本的显示位置,那就没办法了,不过 dislog底层就是基于ncurses来实现的,我也就用ncurses来实现倒计时显示的这个功能吧。最后实现的效果如下,是不是山寨得跟原版 tmux, ctrl+b t)一样。

这样就满足了嘛,当然不,我们还要再加点东西,让它像闹钟一样时间到放歌音乐如何,或者再弹个窗,免得我埋头工作忘了"正事"(抢手机),播放音乐我用moc(一个流行的命令行音乐播放器),弹窗用zentiy(基于gtk构件的简单弹出式对话框)

 

功能介绍完了,下面就让我们来实现吧

三、具体实现

1.使用dialog设定时间

这个功能在shell脚本中实现

我们创建了一个函数

function SetTime()
{
    local c_hour=`date +%H`
    local c_minute=`date +%M`
    local c_time=`date +%H:%M:%S`
    s_time=`dialog --stdout --title "Set Time" --time-format %H\:%M\:%S --timebox\
        "Current time is\n$c_time\nPlease set the deadtime\n" 0 0 $c_hour $c_minute 00`
}

我们首先用shell实现的功能就这些,暂时把它放下,我们再来实现用C语言实现的功能。

2.使用ncurses库实现倒计时显示

关于ncurses库的使用,如果你完成过实验楼上面的另一个项目课C语言贪吃蛇,那么你应该比较熟悉它的使用了,因为这个项目课不是专门介绍ncuses的,所以这里也不会用更多其它关于ncurses的内容了,当然除了我们用到的。

void create_clock_subwindow(WINDOW *win)
{
    int top_win_width, top_win_height;
    int starty, startx;
    int i;
    getmaxyx(win, top_win_height, top_win_width);
    if (top_win_height < D_HEIGHT || top_win_width < D_WIDTH){
        printf("window is too small, please relize the window\n");
        return;
    }
    starty = (top_win_height - D_HEIGHT) / 2;
    startx = (top_win_width - D_WIDTH*8-7) / 2;
    for(i = 0; i < 8; i++){
        digit_win[i] = subwin(win, D_HEIGHT, D_WIDTH, starty, startx+i*(D_WIDTH+1));
    }
}

八个子窗体都是基于主窗体的相对位置创建的,以便适应你重新更改终端的大小,这其中digit_win是预先声明的一个全局数组,D_HEIGHTD_WIDTH,为预先定义的两个指示每个数字的宽和高的宏定义

WINDOW *digit_win[8];

相信你更关心的是如何定义一个字模,呵呵,自己在纸上画好一个5*5的格子,然后画上数字,像你小时候看过的电子表上的数字那样画(点阵字体),然后被覆盖的格子的位置填上1,否则为0,然后基于这11个格子建立一个三维数组就像下面这样:

char digit[11][5][5] = {
    { { 1,1,1,1,1 }, /* 0 */
      { 1,0,0,0,1 },
      { 1,0,0,0,1 },
      { 1,0,0,0,1 },
      { 1,1,1,1,1 } },
    { { 0,0,0,0,1 }, /* 1 */
      { 0,0,0,0,1 },
      { 0,0,0,0,1 },
      { 0,0,0,0,1 },
      { 0,0,0,0,1 } },
    { { 1,1,1,1,1 }, /* 2 */
      { 0,0,0,0,1 },
      { 1,1,1,1,1 },
      { 1,0,0,0,0 },
      { 1,1,1,1,1 } },
    { { 1,1,1,1,1 }, /* 3 */
      { 0,0,0,0,1 },
      { 1,1,1,1,1 },
      { 0,0,0,0,1 },
      { 1,1,1,1,1 } },
    { { 1,0,0,0,1 }, /* 4 */
      { 1,0,0,0,1 },
      { 1,1,1,1,1 },
      { 0,0,0,0,1 },
      { 0,0,0,0,1 } },
    { { 1,1,1,1,1 }, /* 5 */
      { 1,0,0,0,0 },
      { 1,1,1,1,1 },
      { 0,0,0,0,1 },
      { 1,1,1,1,1 } },
    { { 1,1,1,1,1 }, /* 6 */
      { 1,0,0,0,0 },
      { 1,1,1,1,1 },
      { 1,0,0,0,1 },
      { 1,1,1,1,1 } },
    { { 1,1,1,1,1 }, /* 7 */
      { 0,0,0,0,1 },
      { 0,0,0,0,1 },
      { 0,0,0,0,1 },
      { 0,0,0,0,1 } },
    { { 1,1,1,1,1 }, /* 8 */
      { 1,0,0,0,1 },
      { 1,1,1,1,1 },
      { 1,0,0,0,1 },
      { 1,1,1,1,1 } },
    { { 1,1,1,1,1 }, /* 9 */
      { 1,0,0,0,1 },
      { 1,1,1,1,1 },
      { 0,0,0,0,1 },
      { 1,1,1,1,1 } },
    { { 0,0,0,0,0 }, /* : */
      { 0,0,1,0,0 },
      { 0,0,0,0,0 },
      { 0,0,1,0,0 },
      { 0,0,0,0,0 } }
};
 
void display_a_digit(WINDOW *win, char digit[5][5], char isnear)
{
    int y, x;
    int color;
    color = isnear?COLOR_PAIR(4):COLOR_PAIR(3);
    werase(win); // erase the window before re-put the number
    for(y = 0; y < 5; y++){
        for(x = 0; x < 5; x++){
            if (digit[y][x] == 1){
                wattron(win, color);
                mvwprintw(win, y, x, "a");
                wattroff(win, color);
            }
        }
    }
}

这样我们边实现了可能最让你觉得困难的一部分代码

再看主程序

int main(int argc, char *argv[])
{
    char isnear;
    int screen_width, screen_height;
    int i;
    int hour_tens, hour_units, min_tens, min_units, sec_tens, sec_units;
    time_t current_time;
    long left_time, end_seconds;
    char *str;
    struct tm *c_time;
    WINDOW *win1;

    str = malloc(256*sizeof(char));
    memset(str, '\0', sizeof(char)*256);

    if (argc < 2 || argc > 4){
        printf("Usage: %s [options] <seconds> [info strings]\n", argv[0]);
        return 0;
    }
    if (strcmp(argv[1], "-c")){

        left_time = atol(argv[1]);
        if(argc == 3)
            strcpy(str, argv[2]);
    }else if(!strcmp(argv[1], "-c")){

        left_time = atol(argv[2]);
        if (argc == 4)
            strcpy(str, argv[3]);
    }else{
        printf("Usage: %s [options] <seconds> [info strings]\n", argv[0]);
        return 0;
    }

    initscr();
    cbreak();
    noecho();
    start_color();
    init_color(COLOR_BLACK, 46, 52, 54);
    init_color(COLOR_BLUE, 52, 101, 164);
    init_color(COLOR_WHITE, 128, 128, 128);
    init_pair(1, COLOR_BLACK, COLOR_BLACK); // use for main win
    init_pair(2, COLOR_BLUE, COLOR_BLACK); // use for clock win
    init_pair(3, COLOR_BLUE, COLOR_BLUE); // use for clock digit
    init_pair(4, COLOR_RED, COLOR_RED); // use for clock digit
    init_pair(5, COLOR_WHITE, COLOR_BLACK); // use for clock digit

    getmaxyx(stdscr, screen_height, screen_width);
    win1 = newwin(screen_height, screen_width, 0, 0);

    wbkgd(win1, COLOR_PAIR(1));
 
    time(&current_time);
    end_seconds = current_time + left_time;

    while (left_time){
        
        sleep(1);
        time(&current_time);
        left_time = end_seconds - current_time;
        
        isnear = (left_time < 120)?TRUE:FALSE;
       
        if (strcmp(argv[1], "-c")){
            hour_tens = left_time/3600/10;
            hour_units = left_time/3600%10;
            min_tens = left_time/60%60/10;
            min_units = left_time/60%60%10;
            sec_tens = left_time%60/10;
            sec_units = left_time%60%10;
        }else{ 
            c_time = localtime(&current_time);
            hour_tens = c_time->tm_hour/10;
            hour_units = c_time->tm_hour%10;
            min_tens = c_time->tm_min/10;
            min_units = c_time->tm_min%10;
            sec_tens = c_time->tm_sec/10;
            sec_units = c_time->tm_sec%10;
        }
        
        werase(win1);
       
        create_info_subwindow(win1); // info window
      
        wattron(info_win, COLOR_PAIR(5));
        mvwprintw(info_win, 0, 0, "%s", str);
        wattron(info_win, COLOR_PAIR(5));
      
        create_clock_subwindow(win1);
        
        for(i = 0; i < 8; i++){
            wbkgd(digit_win[i], COLOR_PAIR(2));
        }
      
        display_a_digit(digit_win[0], digit[hour_tens], isnear);
        display_a_digit(digit_win[1], digit[hour_units], isnear);
        display_a_digit(digit_win[2], digit[10], isnear);
        display_a_digit(digit_win[3], digit[min_tens], isnear);
        display_a_digit(digit_win[4], digit[min_units], isnear);
        display_a_digit(digit_win[5], digit[10], isnear);
        display_a_digit(digit_win[6], digit[sec_tens], isnear);
        display_a_digit(digit_win[7], digit[sec_units], isnear);
        
        touchwin(win1);
     
        wrefresh(win1);
    }
    free(str);
    
    destory_all_win(win1);
    endwin();
    return 0;

主程序最开始完成一些初始化工作,然后在一个主循环完成了主要的倒计时功能。

使用C语言通过ncurses库实现倒计时的功能完成了,我们再回到shell中去。

小编插播一句,查看详细释义可登陆实验楼官方网站:http://www.shiyanlou.com/courses/?course_type=project&tag=all

3.shell脚本中调用倒计时程序

SetTime
s_seconds=`date -d $s_time +%s`
c_seconds=`date +%s`
e_seconds=$((s_seconds - c_seconds))

4.计时完成后,播放音乐和弹窗提醒

if [ $? -eq 0 ];then
    if [ -n "$music" ];then
        mocp -S $music
        mocp -p
    fi
    zenity --info --width=100 --text=$info
    if [ $? -eq 0 ];then
        _exit
    fi
fi


我们这里判断它是否的等于0,表示上面的计时程序是否计时结束,如果结束正常返回0,就表示该开始提醒你了。 播放音乐就用到了那个mocp程序,你如果不熟悉它的话,可以先安装好这个程序,并在命令行单独运行它来熟悉它的使用,遇到任何困难,最好的解决方法就是使用 --help参数和 man文档。
$ sudo apt-get install moc

上面mocp-S的参数表示以服务的形式运行程序,-p为play,-x为退出服务,当然也会停止播放当前音乐,我们使用单独的函数完成程序退操作

function _exit()
{
    if [ -n "$music" ];then
        mocp -x
    fi
    clear
    exit 0
}

上面的zenity即为一个基于gtk的弹出式对话框程序,后多种构件样式可选,我们这里使用的是--info构件

5.程序参数处理

如你预见的一样,这个程序多种参数,如果处理不好可能出现不必要的麻烦,所以这里单独说明一下这个程序是如何处理参数传递的。这里采用了一个很通用的方法,使用shift命令移位传递参数

while [ -n "$1" ];do
    case "$1" in
    -c) option="-c" ;;
    -m) music="$2" ;;
    -i) info="$2" ;;
     *) show_erro ;;
    esac
    if [ "$1" == "-c" ];then
        shift
    else
        shift 2
    fi
done

从上面你可以看到,对-c参数的处理有点不一样,因为其他几个参数都会有一个值对应,而-c参数没有,所以它的移位操作只移动了一个位置。现在我们可以使用如下的方式传递参数

$ ./countdown -i "hello shiyanlou" -m music.mp3
$ ./countdown -c -i "hello shiyanlou" -m music.mp3
$ ./countdown -i "hello shiyanlou"
$ ./countdown
至此主要的一些麻烦的问题我们都解决了,你在熟悉了上面的实现过程之后,参考以下完成代码( 小编在这里插播一句,下面的是需要在我们实验楼网站的虚拟平台下才能实现的,不过看到这里,大家应该对于这个程序有了大致的认识,自己稍作修改就可完成属于自己的一个带提醒功能的计时器):
$ git clone https://github.com/shiyanlou/countdown.git

C程序使用下面的命令生成

$ gcc disclock.c -o disclock -lncurses

你可能需要安装如下程序

$ sudo apt-get install libncurses5-dev moc dialog zenity

感谢各位看官的支持~小编以后尽量会在每个工作日来与大家分享小编整理的干货,小编会整理一些自己觉得比较实用的程序分享给大家,当然如果大家有需求也可以私信我,或者也可以登陆实验楼的课程版块http://www.shiyanlou.com/courses/?course_type=all,如果这些还满足不了看官您的需求,可以登录实验楼的分享版块http://forum.shiyanlou.com/forum.php?mod=guide&view=newthread,这里有在实验楼学习的孩纸们分享的干货和学习心得,最后小编祝大家在码农的康庄大道上一帆风顺!
现在登陆实验楼,更有感恩好礼相送!
活动页面http://www.shiyanlou.com/huodong/thanks.html


© 著作权归作者所有

实验楼
粉丝 31
博文 31
码字总数 60531
作品 0
成都
私信 提问
XMind6中定时器的妙用

 头脑风暴是我们在进行会议,项目管理时常用到的XMind工具,但是,不知你有没有注意到其中的定时器设置,虽然小,却是很实用的一个功能。本文就教你怎样使用XMind 6头脑风暴中的定时器。  ...

xmind
2015/10/08
135
0
TPYBoard超全DIY案例一览:轻松玩转MicroPython开发!

TPYBoardv102是一款比较成熟和经典的MicroPython开发板,不仅适用于学习实验还能应用于大型的工业级开发,是MicroPython入门和研发的不二之选。下面我们就扒开v102的“层层外衣”来一次知识大...

bodasisiter
2018/07/05
1K
0
具有天气提醒功能的闹钟--WeatherAlarmClock

天气闹钟是一款具有天气提醒功能的闹钟,灵感来自于出门前经常忘记看天气预报没有带伞而淋雨的尴尬情况。 主要功能:闹钟、天气预报、计时器、录音、城市管理、城市搜索、自动定位、主题切换...

咖枯
2016/06/13
684
0
利用 Arduino 玩出来的发烧级玩具

随着“开源硬件”成为一个流行的词汇,各种开发平台自然而然的走进了大家的视野。但实话说,“原生”的开源硬件就是一块块没有外壳的“电路板”,在一般的人看来显得十分乏味。 不过,目前有...

oschina
2013/05/22
9.5K
21
DIY活动第二弹!带你一起打造基于RT-Thread的智能家居系统

项目简介 Hi~ 各位小伙伴们,还在为初学RT-Thread不知如何下手而感到烦恼吗?有没有想过利用RT-Thread来打造属于自己的一个智能家居系统? 现在,基于RT-Thread的分布式无线温度监控系统DIY...

RTThread物联网操作系统
07/10
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Mysql的sql_mode模式

sql_mode 是一个很容易被忽视的配置,宽松模式下可能会被输入一些非准确数据,所以生产环境下会要求为严格模式,为了保持生产环境和开发环境,测试环境一致性,我们开发环境和测试环境也要配...

贾峰uk
34分钟前
4
0
Qt程序打包发布方法(使用官方提供的windeployqt工具)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/toTheUnknown/article/details/81748179 如果使用到了Qt ...

shzwork
今天
7
0
MainThreadSupport

MainThreadSupport EventBus 3.0 中的代码片段. org.greenrobot.eventbus.MainThreadSupport 定义一个接口,并给出默认实现类. 调用者可以在EventBus的构建者中替换该实现. public interface ...

马湖村第九后羿
今天
3
0
指定要使用的形状来代替文字的显示

控制手机键盘弹出的功能只能在ios上实现,安卓是实现不了的,所以安卓只能使用type类型来控制键盘类型,例如你要弹出数字键盘就使用type="number",如果要弹出电话键盘就使用type="tel",但这...

前端老手
今天
8
0
总结:Raft协议

一、Raft协议是什么? 分布式一致性算法。即解决分布式系统中各个副本数据一致性问题。 二、Raft的日志广播过程 发送日志到所有Followers(Raft中将非Leader节点称为Follower)。 Followers收...

浮躁的码农
今天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部