Ввод и вывод в ncurses

15 комментариев

ncurses предоставляет набор функций ввода\вывода, который идентичен функциям ввода\вывода из stdio, но кроме этого добавляет свои функции. Рассмотрим их подробнее.

Функции вывода

Все функции вывода условно можно поделить на три группы:

  • addch()-функции. Вывод одного символа с заданными атрибутами.
  • printw()-функции. Форматированный вывод.
  • addstr()-функции. Вывод строки.

addch()-функции

Функция addch() выводит один символ в текущую позицию курсора с заданными атрибутами.

Атрибуты — это параметры вывода символа, такие как мерцание, подчёркнутость, выделение жирным шрифтом и т.п. В ncurses за каждый атрибут отвечает константа, объявленная в ncurses.h. Все такие константы начинаются с приставки «A_». Вот список некоторых атрибутов:

A_NORMAL    - обычный режим
A_STANDOUT  - самое яркое выделение из всех возможных
A_UNDERLINE - подчёркивание
A_BLINK     - мигание
A_DIM       - тусклый символ
A_BOLD      - выделение жирным шрифтом

addch() принимает ncurses-символ, длинна которого равна 32 битам. Первые 16 бит отводятся под символ, а остальные 16 под атрибуты, поэтому чтобы составить такой символ придётся применять логическое ИЛИ(|), чтобы объединить несколько атрибутов. Например так:

addch( 'A' | A_BOLD | A_UNDERLINE );

Вызов такой функции выведет на экран подчёркнутый символ «А» жирным шрифтом.

Пример

В качестве примера сравним, как выводится обычный символ «А» и как он выводится с атрибутами A_BOLD | A_UNDERLINE.

#include <ncurses.h>

int main()
{
    initscr();

    //Вывод символа "А" в обычном режиме
    printw("Typical symbol \'A\': "); 
    addch('A');

    //Вывод подчёркнутого и жирного символа "А"
    printw("\nUnderline and bold symbol \'A\': ");
    addch('A' | A_BOLD | A_UNDERLINE);

    printw("\nPress any key...");
    getch();
    endwin();
    return 0;
}

Вот как это выглядит:

addch

waddch(), mvaddch() и mvwaddch()

Эти функции аналогичны addch() за исключением того, что waddch() выводит символ не на stdscr а на заданное окно, mvaddch() перед выводом символа переводит координаты курсора, примерно следующим образом:

move(y, x);
addch(ch);

Функция move() меняет расположение корсора. Особое внимание нужно уделить тому, что в ncurses координаты принимаются в порядке (y, x), а не (x, y) как обычно. Помните об этом.

mvwaddch() является «суммой» функции mvaddch() и waddch().

printw()-функции

Эти функции практически полностью аналогичны функции printf(). Только у printw(), как у любой уважающей себя ncurses-функции, есть модификации для форматированного вывода на окно — wprintw(), вывода, начиная с определённых координат — mvprintw(), ну и вывод на окно, начиная с определённых координат — mvwprintw().

Пример

Эта программа выводит строку «Just a string» по центру экрана, а так же информацию о размере экрана в левом нижнем углу, вне зависимости от размеров экрана.

#include <ncurses.h>
#include <cstring>

int main()
{
    const char *mesg = "Just a string";

    //Количество строк и столбцов на экране терминала
    int row, col;   

    initscr();

    //Получаем максимально возможное количество строк и столбцов для окна stdscr и записываем эти данные в row и col
    getmaxyx(stdscr, row, col); 

    //Выводим сообщение в центре окна stdscr
    mvwprintw(stdscr, row / 2, (col - strlen(mesg)) / 2, "%s", mesg);

    //Выводим информацию о количестве строк и столбцов в левом нижнем углу экрана
    mvwprintw(stdscr, row - 1, 0, "The number of rows - %d and columns - %d\n", row, col);

    getch();    
    endwin();
    return 0;
}

Вызовем программу со стандартным размером окна: printw()1

Немного поколдуем с размерами окна (например, с помощью мыши) и снова выполним программу: printw()2

Как видно, программа получилась нечувствительной к размерам окна. Сделать её такой нам помогли функции getmaxx(), getmaxy() и getmaxyx(), запомните эти функции, они часто пригождаются.

addstr()-функции

Функция addstr() (а также mvaddstr(), waddstr() и mvwaddstr() ) просто выводят строку символов на экран.

Пример

Эта программа выведет на экран строку «Just a string»:

#include <ncurses.h>

int main()
{
    const char *mesg = "Just a string";

    initscr();

    addstr(mesg);

    getch();    
    endwin();
    return 0;
}

Функции ввода

Вывод без ввода смысла не имеет, поэтому разберём функции ввода. Как и функции вывода, функции ввода условно можно поделить на три группы:

  • getch()-функции. Считывание символа.
  • scanw()-функции. Форматированный ввод.
  • getstr()-функции. Считывание строки.

getch()-функции

Эта функция считывает только один символ с терминала. Имеет модификации wgetch(), mvgetch() и mvwgetch(). Совместно с функцией keypad() предоставляет удобное средство для управления клавиатурой.

Пример

В качестве примера напишем каркас для создания простого интерактивного меню:

#include <ncurses.h>

const char items[4][6] = {
    "Item1", 
    "Item2",
    "Item3", 
    "Item4"
};

int main()
{
    initscr();

    unsigned choice = 0; //Выбор пользователя

    curs_set(0); //"Убиваем" курсор
    //Включаем режим удобной работы с функциональными клавишами, другими словами KEY_UP и KEY_DOWN без этого не работали бы
    keypad(stdscr, true); 

    while ( true )
    {
        clear();
        for ( unsigned i = 0; i < 4; i++ ) //Проходим по всем элементам меню
        {
            if ( i == choice ) //Если текущий элемент совпадает с выбором пользователя
                addch('>'); //Выводим указатель
            else          
                addch(' '); //Иначе выводим " ", для равновесия

            printw("%s\n", items[i]);
        }

        //Получаем нажатие пользователя
        switch ( getch() )
        {
            case KEY_UP:
                if ( choice ) //Если возможно, переводим указатель вверх
                    choice--; 
                break;
            case KEY_DOWN:
                if ( choice != 3 ) //Если возможно, переводим указатель вниз
                    choice++;
                break;
        }
    }

    endwin();
    return 0;
}

menu

scanw()-функции

Аналог scanf() из stdio, то есть форматированный ввод с клавиатуры. Как всегда, имеется wscanw(), mvscanw(), mvwscanw().

Пример

В качестве примера напишем простой калькулятор:

#include <ncurses.h>

int main()
{
    initscr();

    int a, b;
    char c;

    scanw("%d%c%d", &a, &c, &b);

    printw("%d %c %d = ", a, c, b);
    switch ( c )
    {
        case '+':
            printw("%d", a + b);
            break;

        case '-':
            printw("%d", a - b);
            break;

        case '*':
            printw("%d", a * b);
            break;

        case '/':
            if ( !b )
                printw("ERROR");
            else printw("%f", static_cast<double>(a) / b);
            break;
        default:
            printw("ERROR");
            break;
    }

    getch();
    endwin();
    return 0;
}

getstr()-функции

Как известно, scanf(), а значит и scanw() некорректно считывают строки — только до первого символа-разделителя. Чтобы считывать строки вместе с символом разделителем, была создана функция getstr() (имеются, соответственно, wgetstr(), mvgetstr() и mvwgetstr()).

Пример

В качестве примера напишем программу, которая считает строку, а затем будет гонять эту строку по центру экрана. Обратите внимание на использование функций getmaxy(), getmaxx() и refresh(). Использование этих функций необходимо, особенно последней, поскольку без неё, программа на некоторых машинах (например, на моей) может «повиснуть» в глазах пользователя, хотя на самом деле она что-то выводит. Так же обратите внимание на макрос задержки. До этого времени все наши программы можно было назвать, хотя и с натяжкой, кроссплатформенными, благодаря PDcurses под windows. Но функцию задержки нам придётся придётся прописывать для каждой платформы отдельно, потому что unistd.h, как и windows.h это системозависимые библиотеки.

#include <ncurses.h>

#if defined(_WIN32) || defined(_WIN64) 
    #include <windows.h>
    #define msleep(msec) Sleep(msec)
#else
    #include <unistd.h>
    #define msleep(msec) usleep(msec*1000)
#endif

int main()
{
    initscr();

    char str[100];
    addstr("Enter string: ");
    getstr(str); //Считваем строку
    curs_set(0); //"Убиваем" курсор, чтобы не мешался
    while ( true )
    {
    //Перемещаем х-координату как можно дальше вправо, и будем уменьшать её, пока она != 0
        for ( unsigned x = getmaxx(stdscr); x; x-- ) 
        {
            clear();
            mvaddstr(getmaxy(stdscr) / 2, x, str);
            refresh();
            msleep(200);
        }
    }

    endwin();
    return 0;
}

Автор статьи: Porshe

Комментарии к статье: 15

Подождите, загружаются комментарии...

Возможность комментировать эту статью отключена автором. Возможно, во всем виновата её провокационная тематика или большое обилие флейма от предыдущих комментаторов.

Если у вас есть вопросы по содержанию статьи, рекомендуем вам обратиться за помощью на наш форум.