DIY一个高大上带提醒的计时器,简单实用,你还在等什么
你還在為錯過重要的事情而心煩不已嗎?
你還在為沒搶到某米手機而扼腕嘆息嗎?
你還在為沒領到美食免單而空遺口水嗎?
從今天起,這些都不是問題,且看小編我給你一一道來!
咳咳咳~上菜啦~
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\n$c_time\nPlease set the deadtime\n" 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\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_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 numberfor(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")){// 將傳遞進的計時時間字符串(以秒為單位的時間)轉換為lang型 left_time = atol(argv[1]);if(argc == 3)strcpy(str, argv[2]);}else if(!strcmp(argv[1], "-c")){// 將傳遞進的計時時間字符串(以秒為單位的時間)轉換為lanleft_time = atol(argv[2]);if (argc == 4)strcpy(str, argv[3]);}else{printf("Usage: %s [options] <seconds> [info strings]\n", argv[0]);return 0;}// 初始化ncursesinitscr();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 wininit_pair(2, COLOR_BLUE, COLOR_BLACK); // use for clock wininit_pair(3, COLOR_BLUE, COLOR_BLUE); // use for clock digitinit_pair(4, COLOR_RED, COLOR_RED); // use for clock digitinit_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(¤t_time);end_seconds = current_time + left_time;// 進入主循環,開始計時while (left_time){// 休眠一秒鐘,減少cpu占用時間sleep(1);time(¤t_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(¤t_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中去
3.在shell腳本中調用倒計時程序
小編我來說一句,這段小小的代碼相信難不倒大家,小編也就不在這里多做敘述了,當然,如果有看不懂得地方歡迎登陸實驗樓官方網站http://www.shiyanlou.com/courses/?course_type=project&tag=all
里面有更多詳細的內容~
小編先行告退,各位看官請繼續~4.計時完成后,播放音樂和彈窗提醒
if [ $? -eq 0 ];thenif [ -n "$music" ];thenmocp -S $musicmocp -pfizenity --info --width=100 --text=$infoif [ $? -eq 0 ];then_exitfifi注意這段代碼,應緊接著上面那段代碼的后面,因為這里用到了$?,你如果學過shell編程,應該能明白這是為什么,$?表示上一個程序的返回值。我們這里判斷它是否的等于0,表示上面的計時程序是否計時結束,如果結束正常返回0,就表示該開始提醒你了。播放音樂就用到了那個mocp程序,你如果不熟悉它的話,可以先安裝好這個程序,并在命令行單獨運行它來熟悉它的使用,遇到任何困難,最好的解決方法就是使用--help參數和man文檔。
$ sudo apt-get install moc上面mocp的-S的參數表示以服務的形式運行程序,-p為play,-x為退出服務,當然也會停止播放當前音樂,我們使用單獨的函數完成程序退操作
function _exit(){if [ -n "$music" ];thenmocp -xficlearexit 0}上面的zenity即為一個基于gtk的彈出式對話框程序,后多種構件樣式可選,我們這里使用的是--info構件
5.程序參數處理
如你預見的一樣,這個程序多種參數,如果處理不好可能出現不必要的麻煩,所以這里單獨說明一下這個程序是如何處理參數傳遞的。這里采用了一個很通用的方法,使用shift命令移位傳遞參數
while [ -n "$1" ];docase "$1" in-c) option="-c" ;;-m) music="$2" ;;-i) info="$2" ;;*) show_erro ;;esacif [ "$1" == "-c" ];thenshiftelseshift 2fidone從上面你可以看到,對-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.gitC程序使用下面的命令生成
$ 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,這里有在實驗樓學習的孩紙們分享的干貨和學習心得,最后小編祝大家在碼農的康莊大道上一帆風順!
總結
以上是生活随笔為你收集整理的DIY一个高大上带提醒的计时器,简单实用,你还在等什么的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python聊天室
- 下一篇: C语言基于GTK+Libvlc实现的简易