Python 与 Curses

2015-03-25 Wednesday     python , program

curses

在 C 提供的库中,除了提供标准库之外,还包括了如下的内容。

/usr/lib64/libform.so.5
/usr/lib64/libformw.so.5
/usr/lib64/libmenu.so.5
/usr/lib64/libmenuw.so.5
/usr/lib64/libncurses++.so.5
/usr/lib64/libncurses++w.so.5
/usr/lib64/libncurses.so.5
/usr/lib64/libncursesw.so.5
/usr/lib64/libpanel.so.5
/usr/lib64/libpanelw.so.5
/usr/lib64/libtic.so.5
/usr/lib64/libtinfo.so.5

除了上述的库之外,可以从 github mirro 下载代码,其中包含了很多的测试或者示例代码。

curses.panel

如下是一个简单的示例,会绘制两个 panel ,而且第二个 panel 会自动移动。

from time import sleep
import curses, curses.panel

def make_panel(h, l, y,x, str):
    win = curses.newwin(h, l, y, x)
    win.erase()
    win.box()
    win.addstr(2, 2, str)

    panel = curses.panel.new_panel(win)
    return win, panel

def test(stdscr):
    try:
        curses.curs_set(0)
    except:
        pass

    stdscr.box()
    stdscr.addstr(2, 2, "panels everywhere")
    win1, panel1 = make_panel(10,12, 5,5, "Panel 1")
    win2, panel2 = make_panel(10,12, 8,8, "Panel 2")
    curses.panel.update_panels(); stdscr.refresh()
    sleep(1)

    panel1.top(); curses.panel.update_panels(); stdscr.refresh()
    sleep(1)

    for i in range(20):
        panel2.move(8, 8+i)
        curses.panel.update_panels(); stdscr.refresh()
        sleep(0.1)

    sleep(1)

if __name__ == '__main__':
    curses.wrapper(test)

异步程序

默认来说,当从键盘获取输入时,会阻塞读取,如下一步步介绍异步实现调用。

同步程序

如下是一个同步程序。

#include <stdlib.h>
#include <curses.h>

int main(void)
{
  initscr();
  crmode();
  noecho();
  clear();

  char c;
  while((c = getch()) != 'q'){
    if(c == 'w')
      mvaddch(20, 20, '!');
    else if(c == 'e')
      mvaddch(20, 20, '0');
    else if(c == 'r')
      mvaddch(20, 20, 't');
  }

  move(20, 21);
  addstr("do other thing");
  getch();

  endwin();
  return EXIT_SUCCESS;
}

上述程序会一直阻塞在 getch() 函数中,当输入 q 后会退出 while 循环,然后才执行 addstr() 函数。如果要实现异步调用,可以通过 A) 设置输入O_ASYNC位;B) 使用 aio_read() 实现。

O_ASYNC

接下来,看看如何通过设置 O_ASYNC 标志位来实现异步读取。

#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <curses.h>
#include <sys/types.h>

void on_input (  )
{
  char c;
  c = getch();
  if(c == 'w')
    mvaddch(20, 20, '!');
  else if(c == 'e')
    mvaddch(20, 20, '0');
  else if(c == 'r')
    mvaddch(20, 20, 't');
}

int main(void)
{
  initscr();
  crmode();
  noecho();
  clear();

  int fd_flags;
  fcntl(0, F_SETOWN, getpid());            // 当输入就绪的时候发送信号
  fd_flags = fcntl(0, F_GETFL);
  fcntl(0, F_SETFL, (fd_flags | O_ASYNC)); // 设置输入为O_ASYNC
  signal(SIGIO, on_input);                 // IO信号回调函数

  while(1){
    move(20, 22);
    addstr("do other thing");
    refresh();
    sleep(1);
  }

  endwin();
  return EXIT_SUCCESS;
}

上面这个例子可以看到,会先输出 do other thing 字符串,也就是运行 addstr() 函数,同时在你输入字符程序可以通过信号处理函数 on_input 来接受输入进行响应,从而达到了异步的效果。

aio_read()

异步输入的第二种方法是通过 aio_read() 实现,相比来说更加灵活,但是设置起来也比较复杂,其设置步骤如下:

  1. 设置信号处理函数,接受用户输入;
  2. 设置 struct aiocb 中的变量指明等待什么类型的输入,当输入的时候产生什么信号;
  3. struct aiocb 传递给 aio_read() 来递交读入请求。

其中 struct aiocb 定义如下:

struct aiocb {
  /* The order of these fields is implementation-dependent */
  int             aio_fildes;     /* File descriptor */
  off_t           aio_offset;     /* File offset */
  volatile void  *aio_buf;        /* Location of buffer */
  size_t          aio_nbytes;     /* Length of transfer */
  int             aio_reqprio;    /* Request priority */
  struct sigevent aio_sigevent;   /* Notification method */
  int             aio_lio_opcode; /* Operation to be performed; lio_listio() only */
  /* Various implementation-internal fields not shown */
};
struct sigevent {
    int          sigev_notify; /* Notification method */
    int          sigev_signo;  /* Notification signal */
    union sigval sigev_value;  /* Data passed with notification */
    void       (*sigev_notify_function) (union sigval);
                     /* Function used for thread notification (SIGEV_THREAD) */
    void        *sigev_notify_attributes;
                     /* Attributes for notification thread (SIGEV_THREAD) */
    pid_t        sigev_notify_thread_id;
                     /* ID of thread to signal (SIGEV_THREAD_ID) */
};

下面是一个简单的例子:

#include <aio.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <curses.h>
#include <unistd.h>
#include <signal.h>

struct aiocb kbcbuf;

void on_input (  )
{
  char c, *cp = (char *)kbcbuf.aio_buf;
  if(aio_error(&kbcbuf) != 0) {
    perror("reading faild");
  } else if(aio_return(&kbcbuf) ==1) {
    c = *cp;
    if(c == 'w')
      mvaddch(20, 20, '!');
    else if(c == 'e')
      mvaddch(20, 20, '0');
    else if(c == 'r')
      mvaddch(20, 20, 't');
  }
  aio_read(&kbcbuf);
}

int main(void)
{
  initscr();
  crmode();
  noecho();
  clear();

  signal(SIGIO, on_input);

  static char input[1];
  kbcbuf.aio_fildes = 0;
  kbcbuf.aio_offset = 0;
  kbcbuf.aio_buf = input; // 设置接受输入的buf
  kbcbuf.aio_nbytes = 1;  // 设置接受输入的字节大小
  // 设置处理输入的方法,SIGE_SIGNAL代表通过发送信号来处理
  kbcbuf.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
  // 设置要发送的信号
  kbcbuf.aio_sigevent.sigev_signo = SIGIO;

  aio_read(&kbcbuf);

  while(1){
    move(20, 22);
    addstr("do other thing");
    refresh();
    sleep(1);
  }

  endwin();
  return EXIT_SUCCESS;
}

可以通过 gcc -o main main.c -Wall -lncurses -lrt 编译。

异步函数

实际上,也可以直接使用类似 select()、poll()、epoll() 函数。

参考

可以查看下官方的介绍文档 Curses Programming with Python,以及 Python 中的 curses 模块,可查看 Terminal handling for character-cell displays

关于 C 语言中 ncurses 的相关内容可以参考 NCURSES Programming HOWTO 文档,以及官方网站 www.gnu.org 中的介绍。



如果喜欢这里的文章,而且又不差钱的话,欢迎打赏个早餐 ^_^


About This Blog

Recent Posts

Categories

Related Links

  • RTEMS
    RTEMS
  • GNU
  • Linux Kernel
  • Arduino

Search


This Site was built by Jin Yang, generated with Jekyll, and hosted on GitHub Pages
©2013-2018 – Jin Yang