Linux - 可视化菜单界面设计

优化屏幕刷新

问题

刷新多个窗体需要一些繁琐,在一个慢速的链接上,屏幕的绘制相当的慢

目标就是要尽量减少要在屏幕上的绘制的字符数

基本函数
int wnoutrefresh(WINDOW *window_ptr);
决定哪些字符需要发送到屏幕,但是并不实际的发送
int doupdate(void);
向终端发送实际的改变

说明

如果只是简单地调用wnoutrefresh,其后立即调用doupdate,其效果就如同调用wrefresh一样

如果希望重新绘制一个窗体栈,可以在每一个窗体(以正确的顺序)上调用wnoutrefresh函数,然而在最后一个wnoutrefresh函数之后调用doupdate函数

这使得curses按顺序在每一个窗体上执行屏幕更新计算,并且只输出更新的屏幕。这会使得curses尽量减少需要发送的字符数

WINDOW *subwin(WINDOW *parent, int lines, int cols, int y, int x);
subwin函数具有与newwin几乎相同的参数列表
子窗体的删除方式也与其他的窗体使用一个delwin调用方式相同
与新窗体类似,可以使用一系列的 mvw函数将数据写入子窗体中,但有一点重要区别
子窗体本身并不会存储一个单独的屏幕字符集,他们与子窗体创建时所指定的父窗体共享存储空间
这就意味着子窗体中的任何改动也同时会发生在底层的父窗体中,所以当一个子窗体被删除时,屏幕并不会发生变化
int delwin(WINDOW *window_to_delete);

在将sub_window_ptr指向subwin的调用结果之后,就将子窗体变得可以滚动

甚至是在子窗体被删除而基窗体(strdcr)已经刷新之后,屏幕上的文本仍然保持原样

这是因为子窗体实际更新的是stdscr的字符数据

keypad模式

功能键的处理

在大多数终端会发送一个以转义字符开始的字符串

这些程序所具有的不仅是单击Escape键和由按下一个功能键所引起的字符串之间区别的问题,而且必须使用相同逻辑按键的不同序列来处理不同的终端

curses提供了一个优雅的实用功能来管理这些功能按键

对于每一个终端,每一个功能键所发送的字符序列都会被存储,通常是存储在一个terminfo结构中

所包含的头文件curses.h具有一个以KEY_为前缀的定义部分定义了逻辑按键

当curses启动时,序列与逻辑按键之间的转换就被禁止,必须使用keypad函数来打开

int keypad(WINDOW *window_ptr, bool keypad_on);

如果函数调用成功则会返回OK,否则返回ERR

keypad模式的三个限制

转义序列的识别是时间相关的

许多网络协议将字符组装到数所包中(会导致不能正确识别转义序列)

或者是分割他们(从而会导致功能按键序列会被识别为Escape与单个的字符)

唯一解决办法

进行编程,使用信号来处理希望使用的每一个功能按键,为其发送单一、唯一的字符

为使curses可区分按下Escape与以Escape开头的键盘序列,必须等待一小段时间

有时,一旦打开keypad模式,Escape按键处理上的一个非常小的延时也会被注意到

curses不能处理不唯一的转义序列

如果终端有可以发送相同序列的两个不同的按键,curses只是简单的不处理这个序列,因为他不能确定应返回哪一个逻辑按键

彩色显示

大多数早期的curses版本不支持颜色

颜色被ncurses以及大多数现在的curses实现所支持

curses中的颜色支持有一些不同,其原因在于每一个字符的颜色并不是独立于其底色而定义的

所以必须同时定义前景色与背景色,即所谓的颜色对

bool has_colors(void);
如果支持颜色,has_colors就会返回真
int start_color(void);
进行颜色初始化,如果颜色初始化成功,则返回OK
初始化COLORS和COLOR_PAIR
COLORS:终端所支持的最多的颜色数目
COLOR_PAIR:用户可以定义的色彩对的最大数目
系统颜色
COLOR_BLACK 0 黑色
 COLOR_RED 1 红色
COLOR_GREEN 2 绿色
COLOR_YELLOW 3 黄色
COLOR_BLUE 4 蓝色
COLOR_MAGENTA 5 洋红色
COLOR_CYAN 6 蓝绿色, 青色
COLOR_WHITE 7 白色

初始化颜色对

int init_pair(short pair_number, short foreground, short background);
用于更改一个彩色对的定义
彩色对是Curses的一个概念,它用一个整型数值去标志一对前景/背景彩色
pair_number:彩色对数值,其范围从1到 COLOR_PAIRS-1
f:指定前景彩色
b:指定背景彩色
int COLOR_PAIR(int pair_number);
int pair_content(short pair_number, short *foreground, short *background);

初始化颜色过程

定义颜色对1,使其背景色为绿色而前景色为红色
init_pair(1, COLOR_RED, COLOR_GREEN);
使用COLOR_PAIR将这个颜色作为一个属性来进行访问
wattron(window_ptr, COLOR_PAIR(1));
将屏幕设置为绿色的背景色以及红色的前景色
说明
因为COLOR_PAIR是一个属性,所以可以将其与其他的属性进行组合
在PC上,经常可以通过使用位或操作符组合COLOR_PAIR属性与A_BOLD属性来获得屏幕的亮度
wattron(window_ptr, COLOR_PAIR(1) | A_BOLD);

重新定义颜色

在初始化颜色的时候改变某个颜色的RGB值
init_color(COLOR_RED, 700, 0, 0);
 参数1 : 颜色名称
参数2, 3, 4 : 分别为R(red),G(green),B(blue)的数值
最小值:0
最大值:1000
如果显示终端无法改变颜色设置,函数将返回ERR
can_change_color()
监测终端是否可以支持颜色改变

pad

背景

期望得到一个比实际物理屏幕更大的逻辑屏幕,并且每次只显示逻辑屏幕的部分内容

窗口缺陷

所有窗体必须不大于物理屏幕

pad功能

操作并不适合普通窗体的逻辑屏幕信息

pad结构与WINDOW结构相类似

所有可以用于向窗体输出的函数也可以用于pad

但pad具有其特殊的创建与刷新例程

WINDOW *newpad(int lines, int columns);
返回一个指向WINDOW的指针,与newwin函数相同
删除pad使用delwin函数,与窗体相同
pad并没有限定一个特定的屏幕位置,必须指定希望pad出现在屏幕上的区域
int prefresh(WINDOW *pad_ptr, int pad_row, int pad_column, int screen_row_min, int screen_col_min, int screen_row_max, int screen_col_max);
创建一个pad区域,由(pad_row, pad_column)开始
所定义的区域为(screen_row_min, screen_col_min)到(screen_row_max, screen_col_max)

可视化菜单界面设计

init_curses()函数
initscr初始化指针接着
start_color来显示彩色
curs_set(0)屏蔽掉物理指针
noecho()将终止键盘上的输入会在屏幕上显示出来
void init_cures(){
    initscr();
    start_color();
    init_pair(1,COLOR_WHITE,COLOR_BLUE);
    init_pair(2,COLOR_BLUE,COLOR_WHITE);
    init_pair(3,COLOR_RED,COLOR_WHITE);
    curs_set(0);
    noecho();
    keypad(stdscr,TRUE);
}
void draw_menubar(WINDOW *menubar){
    wbkgd(menubar,COLOR_PAIR(2));
    waddstr(menubar,"Menu1");
    wattron(menubar,COLOR_PAIR(3));
    waddstr(menubar,"(F1)");
    wattroff(menubar,COLOR_PAIR(3));
    wmove(menubar,0,20);
    waddstr(menubar,"Menu2");
    wattron(menubar,COLOR_PAIR(3));
    waddstr(menubar,"(F2)");
    wattroff(menubar,COLOR_PAIR(3));
}

draw_menubar()函数说明

定义一个显示在屏幕最顶部的菜单栏

实际上是stdscr窗体的一个子窗体,该子窗体只有 一行

程序将指向该子窗体的指针作为它的参数

首先改变它的背景色,接着定义菜单的文字

使用waddstr定义菜单 的文字

wattron调用另一不同颜色对(序号3)以取代缺省的颜色对(序号2)

2号颜色对在最开始就 由wbkgd设置成缺省的颜色对

wattroff函数可以让我们切换到缺省的颜色对状态

WINDOW **draw_menu(int start_col){
    int i;
    WINDOW **items;
    items=(WINDOW **)malloc(9*sizeof(WINDOW *));
    items[0]=newwin(10,19,1,start_col);
    wbkgd(items[0],COLOR_PAIR(2));
    box(items[0],ACS_VLINE,ACS_HLINE);
    items[1]=subwin(items[0],1,17,2,start_col+1);
    items[2]=subwin(items[0],1,17,3,start_col+1);
    items[3]=subwin(items[0],1,17,4,start_col+1);
    items[4]=subwin(items[0],1,17,5,start_col+1);
    items[5]=subwin(items[0],1,17,6,start_col+1);
    items[6]=subwin(items[0],1,17,7,start_col+1);
    items[7]=subwin(items[0],1,17,8,start_col+1);
    items[8]=subwin(items[0],1,17,9,start_col+1);

draw_menu()函数说明

显示当按下F1或者F2键显示的菜单

定义一个在蓝色背景上 菜单栏颜色一样的白色背景窗体

新窗口不应被显示在背景色上的字覆盖掉,它应该停留在那里直到 关闭菜单

因此菜单窗体不能定义为stdscr的子窗体

窗体items[0]是用newwin函数定义的,其他8个窗体则都是定义成items[0]窗体的子窗体

items[0]被用来绘制一个围绕在菜单旁边的边框,其他窗体则用来显示菜单中选中的单元

同样的,他们不会覆盖掉菜单上的边框

函数中倒数第三句使得菜单中的第一个单元背景色和其他的不一样,这是因为菜单弹出来后,第一个单元是选中状态(区别选中和没选中的状态)

void delete_menu(WINDOW **items,int count)
{
    int i;
    for (i=0;i<count;i++)
        delwin(items[i]);
    free(items);
}
int scroll_menu(WINDOW **items,int count,int menu_start_col){
    int key;
    int selected=0;
    while (1) {
        key=getch();
        if (key==KEY_DOWN || key==KEY_UP) {
            wbkgd(items[selected+1],COLOR_PAIR(2));
            wnoutrefresh(items[selected+1]);
  if (key==KEY_DOWN) {
      selected=(selected+1) % count;
  } else {
      selected=(selected+count-1) % count;
  }
  wbkgd(items[selected+1],COLOR_PAIR(1));
  wnoutrefresh(items[selected+1]);
  doupdate();
        } else if (key==KEY_LEFT || key==KEY_RIGHT) {
  delete_menu(items,count+1);
  touchwin(stdscr);
  refresh();
  items=draw_menu(20-menu_start_col);
  return scroll_menu(items,8,20-menu_start_col);
        } else if (key==ESCAPE) {
  return -1;
        } else if (key==ENTER) {
  return selected;
        }
    }
}

scroll_menu()函数说明

允许在菜单选择项上上下移动

通过getch读取键盘上的键值

如果按下上移或下移方向键,菜单选择项的上一个项或者下一个项被选中

如果是向左或向右 方向键,当前菜单将会关闭,另一个菜单打开

如果按下回车键,则返回选中的单元值

如果按下ESC键,菜单将会被关闭,并且没有任何选择项

getch能从键盘上读取键值

这是因为程序开始使用keypad(stdscr,TRUE)

将返回值赋给一个int型变量而不是char型变量,这是因为int型变量能表示比char型更大的值

int main(){
    int key;
    WINDOW *menubar,*messagebar;
    init_curses();
    bkgd(COLOR_PAIR(1));
    menubar=subwin(stdscr,1,80,0,0);
    messagebar=subwin(stdscr,1,79,23,1);
    draw_menubar(menubar);
    move(2,1);
    printw("Press F1 or F2 to open the menus. ");
    printw("ESC quits.");
    refresh();
 do {
        int selected_item;
        WINDOW **menu_items;
        key=getch();
        werase(messagebar);
        wrefresh(messagebar);
        if (key==KEY_F(1)) {
  menu_items=draw_menu(0);
  selected_item=scroll_menu(menu_items,8,0);
  delete_menu(menu_items,9);
  if (selected_item<0)
      wprintw(messagebar,"You haven‘t selected any item.");
  else
        wprintw(messagebar,
          "You have selected menu item %d.",selected_item+1);
touchwin(stdscr);
  refresh();
        } else if (key==KEY_F(2)) {
  menu_items=draw_menu(20);
  selected_item=scroll_menu(menu_items,8,20);
  delete_menu(menu_items,9);
  if (selected_item<0)
      wprintw(messagebar,"You haven‘t selected any item.");
  else
      wprintw(messagebar,
          "You have selected menu item %d.",selected_item+1);
  touchwin(stdscr);
  refresh();
       }
    } while (key!=ESCAPE);
时间: 2024-10-14 20:08:59

Linux - 可视化菜单界面设计的相关文章

游戏菜单界面设计 与 游戏整合

小时候一直觉得4399小游戏的那些游戏菜单好low,现在做完我的菜单界面回去去看看感觉还不错... 菜单制作过程 完整代码 https://paste.ubuntu.com/p/HZBWXMWT8K/ 详细介绍: <!--more--> 前言 从走迷宫起,到五子棋,再到做完象棋,再到去学下图书馆管理系统程序设计.其实我对做游戏或管理系统不是有很大兴趣,之前觉得设计界面麻烦,觉得c语言书上那个图书馆管理系统程序好无聊,还觉得做游戏又麻烦又累,然后自从那天学会简单的wasd移动得知原来可以这么巧妙

wxWidgets界面设计工具DialogBlocks(转载)

wxWidgets界面设计工具DialogBlocks // * create by zyzx // * 2009-3-12 // * 转载请注明来源:http://www.cppblog.com/zyzx 工欲善其事,必先利其器.在MFC上开发,有VS系列强大的可视化工具,基于wx的开发工具虽然没有VS.QT的界面设计器强大,但在灵活度上VS的界面设计器是没法比的(QT的没有用过,不做评论).          如果选择了wx库,了解几款基于其上的界面设计工具,会大大提高开发效率. 一.wx界

UI界面设计需要遵循哪些规范

一:遵循一致的准则,确立标准并遵循 无论是控件使用,提示信息措辞,还是颜色.窗口布局风格,遵循统一的标准,做到真正的一致. 设计 这样得到的好处: 1:使用户使用起来能够建立起精确的心里模型,使用熟练了一个界面后,切换到另外一个界面能够很轻松的推测出各种功能,语句理解也不需要费神理解 2:降低培训.支持成本,支持人员不会行费力逐个指导. 3:给用户统一感觉,不觉得混乱,心情愉快,支持度增加 做法: 项目组有经验人士,确立UI规范: ·美工提供色调配色方案,提供整体配色表 ·界面控制程序人员.用户

#这个测试程序有助于我们理解wxPython的界面设计,基本的控件和事件调用都有

#!/bin/env python # -*- coding: utf-8 -*- ################################################################################# #这个测试程序有助于我们理解wxPython的界面设计,基本的控件和事件调用都有 ################################################################################# imp

好的用户界面-界面设计的一些技巧

原文地址:http://www.cnblogs.com/Wayou/p/goodui.html 如此有用的文章我已记不得是什么时候发现的了,但在看完的那一刻便想将之翻译,分享给大家自己也受用. 时间过了很久,来到了2014年,终于静下心来花了大把时间连同图片一起译成了中文.像我这样业余的翻译六级分数只够及格的程序员,不敢说做到信雅达,但求意思到位. 1 尽量使用单列而不是多列布局 单列布局能够让对全局有更好的掌控.同时用户也可以一目了然内容.而多列而已则会有分散用户注意力的风险使你的主旨无法很好

Android Design Support Library(二)用NavigationView实现抽屉菜单界面

NavigationView在MD设计中很重要,之前Google也提出了使用DrawerLayout来实现导航抽屉. 这次,在Android Design Support Library中,Google提供了NavigationView来实现导航菜单界面. 这次我们写的代码在Android用TabLayout实现相似网易选项卡动态滑动效果这篇文章代码的基础上进行改动,所以最好先看看上面这篇文章 首先仍旧是配置build.gradle: dependencies { compile fileTre

一些关于界面设计的技巧

最近做项目有些忙,百忙之中老师让我给新生讲下界面设计的知识o(╯□╰)o(只因自己会小弄些PS戳我),这就尴尬了讲什么好呢?偶然间看到刘哇勇前辈翻译过这篇文章,但是发现前辈第40点以后就没译了.我就打算在前辈的翻译的基础上把剩下的部分给翻译完(自己英语不是特别好,所以就模糊翻译了),一来加深自己对设计的理解,二来到时也可以和朋友一起分享这篇文章.做好一名合格的程序员,我们应该有一些自己对设计的理解,这样我们大局观才能更加宽广. 1 尽量使用单列而不是多列布局 单列布局能够让对全局有更好的掌控.同

有关于界面设计的技巧

1 尽量使用单列而不是多列布局 单列布局能够让对全局有更好的掌控.同时用户也可以一目了然内容.而多列而已则会有分散用户注意力的风险使你的主旨无法很好表达.最好的做法是用一个有逻辑的叙述来引导用户并且在文末给出你的操作按钮. 2 放出礼品往往更具诱惑力 给用户一份精美小礼品这样的友好举动再好不过了.具体来讲,送出礼品也是之有效的获得客户忠诚度的战术,这是建立在人们互惠准则上的.而这样做所带来的好处也是显而易见的,会让你在往后的活动进展(不管是推销,产品更新还是再次搞活动)中更加顺利. 3 合并重复

Android用NavigationView实现抽屉菜单界面

NavigationView在MD设计中非常重要,之前Google也提出了使用DrawerLayout来实现导航抽屉.这次,在Android Design Support Library中,Google提供了NavigationView来实现导航菜单界面. 这次我们写的代码在Android用TabLayout实现类似网易选项卡动态滑动效果这篇文章代码的基础上进行修改,所以最好先看看上面这篇文章 首先仍旧是配置build.gradle: dependencies { compile fileTre