Username: Password:

在Unix系统下用shell制作通用界面
来源:作者: 发布时间:2007-12-05 06:21:48

现在在Unix系统下用shell编写的菜单程式大都还是采用多级菜单的模式,这种模式的弊端在于菜单的层次多,界面本身不直观,而且在编程过程中,将菜单的显示格式和内容连同所调用的子程式包括在菜单主程式中,使得程式只能满足某个方面的需求,菜单程式本身不具备通用性。本程式设计采用了一种新的设计思路,将下拉菜单界面作为二维表格来处理,把下拉菜单的内容连同所调用的子程式名称分别存放在这两个二维表中,通过对表的读取,实现了控制光标移动、选择菜单内容连同调用子程式的目的。采用这种方式编写出来的程式易于维护,通用性强。在程式本身不做任何改变的情况下,能够在同一操作平台中进行任意移植,因而具备广泛的应用价值。这种思维模式并不局限在Unix系统下的shell编程,而且对于像C这样的过程化语言也具备一定的借鉴意义。
设计思路
在下拉菜单制作过程中,整个下拉菜单界面所包含的菜单名称连同所调用的子程式名之间的相互关系构成了二维表,其中子菜单名称和子程式名称作为表的元素,通过选择光标在表中上下左右移动,将表中元素读出来,再进行处理运算,从而达到控制菜单的选择连同子程式调用等目的。









































表1 菜单项
菜单1 菜单2 菜单3 …… 菜单n
菜单11 菜单12 菜单13 …… 菜单1n
菜单21 菜单22 菜单23 菜单2n
菜单31 菜单32 菜单33 菜单3n
……
菜单m1 菜单m2 菜单m3 菜单mn




































表2 对应各菜单项的子程式
子程式11 子程式12 子程式13 …… 子程式1n
子程式21 子程式22 子程式23 子程式2n
子程式31 子程式32 子程式33 子程式3n
……
子程式m1 子程式m2 子程式m3 子程式mn

从上面的两个表中不难看出除表1中的第一行为标题行(菜单栏),表1和表2有相同结构,两个表之间的元素存在着一一对应的关系,即每个菜单名称下对应着所调用的程式名(备注: 由于每个菜单标题栏下的子菜单的内容是不相同的,因而每个子菜单下的菜单数目也各不相同,表中一些元素能够是空值,他表示在此没有菜单选择项)。
文中介绍方法的技术难点在于选择光标位置和实际光标位置的关系。所谓选择光标位置是指在上下左右键的控制下,光标在菜单界面的位置,也就是光标在表中的行和列的位置。而实际光标位置是指光标在电脑屏幕上的实际位置。怎样通过选择光标位置计算出实际光标位置是本程式的一个难点。本程式的处理办法是将选择光标的行列位置分别作为计算函数的参数,通过函数计算出实际光标的位置。
实现步骤
先将菜单的内容按照一定的格式显示在电脑屏幕上。显示格式要依据表的结构和内容而定,而不能固定不变。假如事先固定下来,会使显示格式和内容之间产生矛盾,难以达到相互之间的统一,程式就不具备通用性。
选择光标在菜单栏左右移动确定选择项目的同时将菜单栏下所包含的子菜单内容显示出来。菜单栏最右端的菜单选择项一般情况下表示“退出”,当选择光标处于这个位置时,回车后退出整个菜单的选择。
在菜单栏中回车或按↓键进入菜单栏下一级子菜单,按照所显示的子菜单内容,选择光标上下移动确定所选定的子菜单内容,回车执行所调用的子程式,←、→两个键退出子菜单的选择。
需要说明的是由于在Unix系统中,光标在上下左右移动时, Unix系统的read命令无法捕获←、↑、→、↓键的控制字符,无法对光标进行有效的控制,为了获取移动光标的控制字符,这里需要用C语言编写一个函数,其主要功能是在光标进行上下左右移动时,能够准确地返回←、↑、→、↓控制键的ASCII值,函数名为getchar。
程式分析
由于光标移动过程中涉及光标的行列位置等重复运算,运用函数可减少程式自身的长度,使程式变得短小、精悍。这里涉及以下一些函数:
1. 画框函数
前面提到显示格式依据表的结构而定,对菜单的边框长度的配置不能固定不变,他要依据菜单标题栏的长度连同标题栏的标题个数而定。这个函数的功能就是依据菜单界面宽度画边框,参数$1表示边框的横线和竖线。
menu_x()
{
_R=$1
col_x=1
while [ col_x -le ${S_LENGTH} ]
do
if [ $_R ]; then echo $_R“\c”
else echo “\c”
fi
col_x=‘expr $col_x + 2’
done
}
2. 计算实际光标在屏幕上的行列位置函数
选择光标在标题栏左右移动的过程中,需要计算光标在屏幕上的实际位置,通过这个函数能够准确地计算出这个实际位置。其运算过程是将选择光标在表中的行列位置作为函数的参数,依据这两个参数计算出光标在屏幕上的准确位置,并将选择光标按照计算出的位置在屏幕上准确显示。其中变量SCREEN- CUR表示表1的元素内容,也就是菜单界面的菜单名称,变量SCREEN-R和SCREEN-C分别表示实际光标在屏幕上位置。执行的结果是将选择光标的内容按实际光标的位置显示在屏幕上。
menu_c()
{ _C=$1 # 选择光标在菜单界面的列位置
_R=$2 # 选择光标在菜单界面的行位置
SCREEN_CUR=‘awk -F“|” “NR==$_R { print }”menu|cut -d“|” -f$_C’
if [ $_C -gt 1 ]; then F_C=‘expr $_C - 1’
SCREEN_LENG=‘head -1 menu| cut -d“|” -f0-$F_C|sed -e ‘s/|//g’ \ | awk ‘{ print length($0)}’’
else
SCREEN_LENG=0
fi
SCREEN_R=‘expr $_R + 2’
SCREEN_C=‘expr $C_COL + $SCREEN_LENG + 2’
SCREEN_CUR_X=“\033[${SCREEN_R};${SCREEN_C}H${SCREEN_CUR}”
}
3. 计算选择光标在移动过程中位置的函数
选择光标在上下左右的移动过程中,其在菜单界面的位置也随之发生变化,需要通过运算,以确定选择光标在菜单界面的准确位置。其中参数$1表示上下左右键所返回的ASCII值,当参数$1等于2或3时,表示选择光标在上移或左移; 等于1或4时表示选择光标在向下移动或向右移动。参数$2表示选择光标移动过程中在表1中的位置,参数$3表示选择光标移动过程中所限定的区间范围。
menu_x_y()
{ _Z=$1
_S=$2
_L=$3
case $_Z in
2|3) if [ $_S -gt 1 ]
then _S=‘expr $_S - 1’
else _S=$_L
fi ;;
1|4) if [ $_S -lt $_L ]
then _S=‘expr $_S + 1’
else _S=1
fi;;
esac
return $_S
}
4. 计算菜单界面每个菜单栏下的菜单数目函数
通常情况下每个菜单标题下所包含的内容是不相同的,因而每个菜单栏下菜单的数目也是不相同的,需要对每个菜单栏下的菜单数目进行计算,参数$1表示选择光标在菜单栏下的列位置。
menu_row_number()
{ _H=$1
S_NUMBER=‘cut -d“|” -f$_H menu|sed -e ‘s/ //g’-e ‘/^$/d’|\ awk ‘END { print NR}’’
}
5. 执行子程式函数
子程式名存在prg文档中,表2中的元素就是子程式名。调用子程式的过程实际就是根据选择光标在菜单界面的行列位置将相应位置的元素读出来,然后依据表2所提供的程式名判断是否真实存在,假如存在则执行。
menu_prg()
{ _C=$1 # 选择光标在菜单界面的列位置
_R=$2 # 选择光标在菜单界面的行位置
prg_name=‘awk -F“|” “NR==$_R { print }” prg|cut -d“|” -f$_C’
if [ -s $prg_name ]
then
eval $prg_name
# 执行所调用的子程式
else
echo “\007”
fi
}
下面是主程式:
# 配置菜单界面前景和背景颜色
COLOR1=“\033[32;44;1m” # 菜单界面的前景色
COLOR2=“\033[33;45;1m” # 菜单界面的背景色
COLOR3=“\033[37;40;1m” # 选择光标的颜色
# 对程式中所用的一些变量进行初始化配置
CUR_R=1 #选择光标在菜单界面的行位置
CUR_C=1 #选择光标在菜单界面的列位置
S_LENGTH=‘head -1 menu|sed -e ‘s/|//g’ | \ awk ‘{ print length($0)}’’
# 确定菜单界面的宽度
S_MENU=‘head -1 menu| \ awk -F“|” ‘{ print NF}’’
# 确定菜单标题栏的字段数
C_COL=‘expr \( 80 - $S_LENGTH - 4 \) / 2 ’ # 确定菜单界面的起始位置
echo ${COLOR1}; clear # 按格式显示菜单界面
row=2 # 显示行 [2-23]
while [ row -le 23 ]
do
case $row in
2) echo “\033[${row};${C_COL}H┏\c”; menu_x “━”; echo “┓” ;;
3) echo “\033[${row};${C_COL}H┃\c”;
head -1 menu |sed -e ‘s/|//g’ |awk ‘{ print $0 “┃” }’;;
23) echo “\033[${row};${C_COL}H┗\c”; menu_x “━”; echo “┛\c” ;;
*) echo “\033[${row};${C_COL}H┃\c”; menu_x “ ”; echo “┃” ;;
esac
row=‘expr $row + 1’
done
while true
do
menu_c $CUR_C $CUR_R # 计算选择光标的位置
echo “${COLOR2}${SCREEN_CUR_X}\c”
stty -echo
getchar # 等待选择
ANS_X=$? # 返回ASCII值
stty echo
echo “${COLOR1}${SCREEN_CUR_X}\c”
case $ANS_X in
3|4) menu_x_y $ANS_X $CUR_C $S_MENU
#选择光标在菜单标题栏中左右移动
CUR_C=$?;;
1|10) if [ $CUR_C = $S_MENU ] #按回车键或↓键进入子菜单
then setcolor -n ; clear; break
fi
menu_row_number $CUR_C
# 在菜单标题栏下将所包含子菜单内容显示在屏幕上
row=2
while [ row -le ${S_NUMBER} ]
do
menu_c $CUR_C $row
echo “${COLOR3}${SCREEN_CUR_X}\c”
row=‘expr $row + 1 ’
done
while true do
menu_c $CUR_C $CUR_R
echo “${COLOR2}${SCREEN_CUR_X}\c”
stty -echo
getchar
ANS_Y=$?
stty echo
echo “${COLOR3}${SCREEN_CUR_X}\c”
case $ANS_Y in
1|2) menu_x_y $ANS_Y $CUR_R $S_NUMBER #上下移动选择光标
CUR_R=$?;;
3|4) menu_x_y $ANS_Y $CUR_C $S_MENU #左右移动选择光标退出子菜单选择
CUR_C=$?
CUR_R=1
break;;
10) menu_prg $CUR_C $CUR_R;; #回车后执行子程式
*) echo “\007”;;
esac
done
;;
*) echo “\007\c”;;
esac
done
小结
本文所论述的是怎样在Unix系统下利用shell制作通用的下拉菜单。这种通用性集中体现在实现了菜单下的菜单名称连同所调用的子程式名称和菜单主程式的分离,菜单界面下子菜单名称连同所调用的子程式名称分别存放在两个文本文档中,主程式通过对这两个文档的读取实现了菜单程式的正确显示和选择功能。只要对这两个文本文档进行编辑,无需对主程式进行任何改变,即可完成Unix系统下拉菜单的制作,使得菜单制作很快捷、灵活。同时能够很方便地进行移植,因而有较强的通用性。而且采用这种方式制作出来的下拉菜单界面比较直观、明了,操作起来更加简单、方便。
备注:在编辑menu和prg文档时,子菜单名称和子程式名称是一一对应的关系,所以子菜单和子程式在文档中位置要摆放正确,不能乱放。由于在本程式中awk语句的任何分隔符都是“|”,而不是空格,因而文本文档中的分隔符也是“|”,而不能用空格,这一点在编辑这两个文档时要特别注意。

喜欢本文,那就收藏到:

    Del.icio.us Google书签 Digg Live Bookmark Technorati Furl Yahoo书签 Facebook 百度搜藏 新浪ViVi 365Key网摘 天极网摘 和讯网摘 博拉网 POCO网摘 添加到饭否 QQ书签 Digbuzz我挖网
相关评论  我也要评论
还没有关于此文章的相关评论!
  • 昵称: (为空则显示guest)
  • 评论分数: ★ ★ ★★★ ★★★★ ★★★★★
  • 评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
  • 导航
    赞助商
    文章类别
    订阅