小编心语:锵锵锵!小编我又来了!昨天发了一篇比较实用的《Python聊天室》,鉴于反响还不错,SO ,小编也想给大家多分享点有用的干货,让大家边学边用。好了,闲话不多说,今天要给各位看官们介绍的是基于C语言的带提醒功能计时器。
你还在为错过重要的事情而心烦不已吗?
你还在为没抢到某米手机而扼腕叹息吗?
你还在为没领到美食免单而空遗口水吗?
从今天起,这些都不是问题,且看小编我给你一一道来!
咳咳咳~上菜啦~
dialog+ncurse实现命令行带提醒功能计时器
我们将那个结合shell编程与C语言编程使用dialog工具与ncurses库实现一个仿tmux时间风格的带提醒功能的计时器
一、说明
这次项目课要实现的东西功能很简单,但是却用到了好几个东西,包括dialog
,ncurses
库,zenity
, moc
, 用了这么多东西最后也就是个定时器-_-||。不过我想先告诉你的是为什么是它。这原因呢有两个,一个是之前看到有用户反应希望项目课有一些综合性稍强的 内容,这个项目课就是了,结合了shell编程和C语言编程,C语言编程除了包含本身的内容还涉及到了如何使用外部库来实现我们想要的功能;这第二个原因 是,我自己需要一个这样的定时器啊,我要每周二中午12点的时候准时抢小米手机(抢了几次没抢到啊,就是因为时间没掐准,不够快),所以就想要一个定时器 了,我想的是除了要实现基本功能外还要求“看起来,高大上”。
二、功能介绍
既然我是要实现一个命令行的定时器,要想“高大上”,那就必须要用到命令行的图形库了,一开始想到的是ncurses,这就得完全用C语言来开发了,后来又想到可以用shell下的dialog(基于ncurses实现的命令行图形对话框,包含一些常用的构件)来完成一部分功能,比如设定时间的界面可以用dialog
的timebox
构件来实现,如下图
本想是不是可以用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 $c_time Please set the deadtime " 0 0 $c_hour $c_minute 00` }
上面代码中再SetTime
函数内部定义了三个局部变量,用于通过date
命令分别获取系统当前时间的时、分,并完整时间显示为构件的信息, 这里dialog
的第一参数--stdout
指定绘制窗口的设备,--title
指定构件的标题,--time-format %H:%M:%S
,用于指定设置完成后dialog
返回的时间格式,--timebox
就指定了当前dialog
构件的类型(因为dialog支持多种构件类型,所以这里必须指定)。最后5个参数0 0 $c_hour $c_minute 00
,分别以空格区分开,表示构件的宽度,高度,小时,分钟,秒,具体请参看dialog
的man
文档。完成后你可以按键盘的Tab
键切换选中项,然后移到你要修改的时间上,使用键盘的上下键更改数值。
我们首先用shell实现的功能就这些,暂时把它放下,我们再来实现用C语言实现的功能。
2.使用ncurses库实现倒计时显示
关于ncurses库的使用,如果你完成过实验楼上面的另一个项目课C语言贪吃蛇,那么你应该比较熟悉它的使用了,因为这个项目课不是专门介绍ncuses的,所以这里也不会用更多其它关于ncurses的内容了,当然除了我们用到的。
实现显示大号字体(数字)如前面的效果图,因为ncurese本身是没有关于字体设置的实现的,所以这需要我们自己来绘制,我们考虑可以为每个数字 建立一个基于父窗体口的子窗口,然后在每个窗口中绘制一个数字,要绘制数字,我们还需要自定义一组字模(只包含10个数字和一个冒号)。这里我们一共需要 8个子窗体,下面这个函数讲创建多个子窗体
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 "); 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_HEIGHT
,D_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, '