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

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

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

一、说明

这次项目课要实现的东西功能很简单,但是却用到了好几个东西,包括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脚本中实现

我们创建了一个函数

functionSetTime()
{
    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"00$c_hour$c_minute00`
}

上面代码中再SetTime函数内部定义了三个局部变量,用于通过date命令分别获取系统当前时间的时、分,并完整时间显示为构件的信息, 这里dialog的第一参数--stdout指定绘制窗口的设备,--title指定构件的标题,--time-format %H\:%M\:%S,用于指定设置完成后dialog返回的时间格式,--timebox就指定了当前dialog构件的类型(因为dialog支持多种构件类型,所以这里必须指定)。最后5个参数0 0 $c_hour $c_minute 00,分别以空格区分开,表示构件的宽度,高度,小时,分钟,秒,具体请参看dialogman文档。完成后你可以按键盘的Tab键切换选中项,然后移到你要修改的时间上,使用键盘的上下键更改数值。

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

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

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

实现显示大号字体(数字)如前面的效果图,因为ncurese本身是没有关于字体设置的实现的,所以这需要我们自己来绘制,我们考虑可以为每个数字建立一个基于父窗体口的子窗口,然后在每个窗口中绘制一个数字,要绘制数字,我们还需要自定义一组字模(只包含10个数字和一个冒号)。这里我们一共需要 8个子窗体,下面这个函数讲创建多个子窗体

voidcreate_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 } }
};
 
voiddisplay_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);
            }
        }
    }
}

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

再看主程序

intmain(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]);
        return0;
    }
    if (strcmp(argv[1], "-c")){
        // 将传递进的计时时间字符串(以秒为单位的时间)转换为lang型 
        left_time = atol(argv[1]);
        if(argc == 3)
            strcpy(str, argv[2]);
    }elseif(!strcmp(argv[1], "-c")){
        // 将传递进的计时时间字符串(以秒为单位的时间)转换为lan
        left_time = atol(argv[2]);
        if (argc == 4)
            strcpy(str, argv[3]);
    }else{
        printf("Usage: %s [options] <seconds> [info strings]\n", argv[0]);
        return0;
    }
    // 初始化ncurses
    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){
        // 休眠一秒钟,减少cpu占用时间
        sleep(1);
        time(&current_time);
        left_time = end_seconds - current_time;
        // 设置接近计时结束的标志,这里设置的为120s,即分钟,用于当计时只有两分钟时讲数字颜色换成红色
        isnear = (left_time < 120)?TRUE:FALSE;
        // 当传入"-c"参数时,使用正常显示时间模式
        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();
    return0;

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

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

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

小编我来说一句,这段小小的代码相信难不倒大家,小编也就不在这里多做叙述了,当然,如果有看不懂得地方欢迎登陆实验楼官方网站http://www.shiyanlou.com/courses/?course_type=project&tag=all

里面有更多详细的内容~

小编先行告退,各位看官请继续~

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

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

注意这段代码,应紧接着上面那段代码的后面,因为这里用到了$?,你如果学过shell编程,应该能明白这是为什么,$?表示上一个程序的返回值。我们这里判断它是否的等于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
    exit0
}

上面的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
        shift2
    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,这里有在实验楼学习的孩纸们分享的干货和学习心得,最后小编祝大家在码农的康庄大道上一帆风顺!


时间: 2024-11-08 23:40:18

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

SQL Server中带事务的存储过程简单举例

先来看一个概念: 数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完整地执行,要么完全地不执行.那么在存储过程里添加事务,则可以保证该事务里的所有sql代码要么完全执行要么完全不执行. 举个简单的带事务的存储过程: Begin Set NOCOUNT ON; --不返回影响行数 Set XACT_ABORT ON; --使用存储过程执行事务需要开启XACT_ABORT参数(默认为OFF) delete from table1 where n

【php增删改查实例】第四节 -自己 DIY 一个数据库管理工具

本节介绍如何自己DIY一个数据库管理工具,可以在页面输入sql 进行简单的增删改查操作. 首先,找到xampp的安装目录,打开htdocs: 新建一个php文件,名称为 mysqladmin.php 1.编写php服务器代码 1.1 写上php标签 首先,还是在这个页面,要写php代码,就需要有一个php标签: 我们的php代码要写在这个标签内. 1.2 数据库连接操作 xampp安装的mysql默认没有密码,不写就行. 1.3 获取form表单传过来的sql语句 1.4 用mysql_quer

2、基于wsgiref模块DIY一个web框架

一 web框架 Web框架(Web framework)是一种开发框架,用来支持动态网站.网络应用和网络服务的开发.这大多数的web框架提供了一套开发和部署网站的方式,也为web行为提供了一套通用的方法.web框架已经实现了很多功能,开发人员使用框架提供的方法并且完成自己的业务逻辑,就能快速开发web应用了.浏览器和服务器的是基于HTTP协议进行通信的.也可以说web框架就是在以上十几行代码基础张扩展出来的,有很多简单方便使用的方法,大大提高了开发的效率. 二 wsgiref模块 最简单的Web

一个故事带你理解if __name__ == &#39;__main__&#39;

如果你刚刚接触python,相信会在看别人的程序的时候会遇到if __name__ == '__main__'酱紫的语法,如果当时没看懂现在也一知半解的话,看下去,本文可以帮你解决这个问题. 大家都知道:Python的一大优点就是里面的模块非常多--包括内置的模块与自定义模块.我们可以直接利用别人写好的模块去实现自己的需求,酱紫大大的提高了自己的开发效率.也就是说,有时候你可能只需要一些文献阅读的能力与基本的编码能力也可以实现很强大的功能!~~这也许就是Python在各行各业各处开花的原因吧/叹

分享一个自控小技巧--小任务--简单却十分的有效

场景,话不多说 记不记得自己想做某件事情,比如每天锻炼,但是时常难以开始? 记不记得自己本来计划好的某段时间用来做某件事情,但是一不小心就沉浸于游戏或者电视剧忘了自拔? 记不记得自己计划好的每天晚上花一小时学习某项技能,但是坚持了三天就抛到脑后难以记起了? 我来分享一个关于自控的小技巧,对别人有没有效果我不知道,反正对多年来想要自控多一些的我反正发生了奇效! 技巧就是设定小任务 这个技巧十分的简单,易于执行.就是 设定几个特变简单的.但是每天都要做的小任务. 比如,如果你想要背单词:可以试试设定

一个"收纳箱"带你走进数据库的世界

一个"收纳箱"带你走进数据库的世界 一.前言 1.1时势造英雄--数据时代,未来已来 ? 如今,我们已然"从IT时代迈入DT时代",马云也说过:未来的资源不是石油,不是天然气,而是"数据".这是因为,在信息技术迭代如此之快的时代,会产生各种各样的大量数据.信息,那么伴随而来的问题就是:我们应该如何实现安全.有效地存储.检索以及管理数据? ? 数据时代,对数据的有效存储.高效访问.方便共享和安全控制已经成为目前亟待解决的问题. 1.2问题引出--如

BASH 创建一个命令 f1 使之可以简单管理文件版本

创建命令f1 创建一个shell脚本 f1,放到 $PATH 可以找到的位置: f1用法 1. 创建一个文件 foo.txt,并 backup 它 $ cat foo.txt 1 $ f1 backup foo.txt 2. 修改它 $ cat foo.txt 2 3. 还原到上一个版本 $ f1 rollback foo.txt $ cat foo.txt 1 BASH 创建一个命令 f1 使之可以简单管理文件版本,布布扣,bubuko.com

一个统计代码行数的简单方法

安装Git, 到项目目录下右击->Git Bash, 输入命令: find . -name "*.cs" | xargs wc -l 效果如下, 还是挺简便的. 一个统计代码行数的简单方法,布布扣,bubuko.com

DIY一个基于树莓派和Python的无人机视觉跟踪系统

DIY一个基于树莓派和Python的无人机视觉跟踪系统 无人机通过图传将航拍到的图像存储并实时传送回地面站几乎已经是标配.如果想来点高级的--在无人机上直接处理拍摄的图像并实现自动控制要怎么实现呢?其实视觉跟踪已经在一些高端的消费级无人机上有了应用,不过玩现成的永远没有自己动手来劲;).前段时间DIY了一个无人机三轴云台的视觉跟踪系统,除去云台花了¥370,本文将设计思路与实验效果分享出来. 一.基本配置 1.1 硬件 计算平台:树莓派3 (¥219.00) 摄像头:USB网络摄像头(¥108.