Краткие теоретические сведения 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Краткие теоретические сведения



Используется две технологии вывода информации на экран в алфавитно – цифровом режиме: псевдографика и ncurses.

Псевдографика. В данном режиме формируется массив символов по числу символов на экране. Объект: график или рисунок, переносится в массив, и указанный массив выводится на печать.

Ncurses. Используется специальнаябиблиотека.

Для того, чтобы предоставить интерфейс «укажи и щелкни» пользователям текстовых терминалов, была разработана библиотека curses (ее название происходит от ее важнейшей функции – управления курсором, а вовсе не от проклятья, которая она накладывает на программистов). Изначально библиотека curses создавалась для BSD UNIX. В Linux используется открытый (на условиях MIT License) клон curses – библиотека ncurses (new curses).

Основными концепциями пользовательского интерфейса программы, использующей ncurses, являются экран (screen), окно (window) и под - окно (sub-window). Экраном называется все пространство, на котором ncurses может выводить данные. С точки зрения ncurses, экран – это матрица ячеек, в которые можно выводить символы. Если монитор работает в текстовом режиме, экран ncurses совпадает с экраном монитора. Если терминал эмулируется графической программой, экраном является рабочая область окна этой программы. Окном ncurses называется прямоугольная часть экрана, для которой определены особые параметры вывода. В частности, размеры окна влияют на перенос и прокрутку строк, выводимых в этом окне. В каком- то смысле окно можно назвать «экраном в экране». На уровне интерфейса программирования окна представлены структурами данных, по этой причине мы будем часто говорить об окне как о структуре.

В процессе инициализации ncurses автоматически создается окно stdscr, размеры которого совпадают с размерами экрана. Кроме структуры stdscr по умолчанию создается еще одна структура – curscr. Операции вывода данных ncurses модифицируют содержимое структуры stdscr, однако, на экране всегда отображается содержимое окна curscr. Иначе говоря, данные, которые выводит программа в окно stdscr (или в другое окно), не отображаются на экране монитора автоматически. Для того чтобы сделать результаты вывода видимыми, вы должны вызывать специальные функции обновления экрана (refresh() или wrefresh()). Эти функции сравнивают содержимое окон stdscr и curscr и на основе различий между ними вносят изменения в структуру curscr, а затем обновляют экран. Благодаря наличию окна curscr, ncurses-программе не требуется «помнить» весь свой предыдущий вывод и перерисовывать его всякий раз, когда в этом возникает необходимость. Этим программы ncurses отличаются от графических программ

Хотя ваша программа может пользоваться для вывода данных исключительно окном stdscr, ваша задача по проектированию интерфейса существенно упростится, если вы будете создавать собственные окна, расположенные «внутри» stdscr. Программа, использующая ncurses, может работать с несколькими окнами одновременно, выполняя вывод в каждое из них. Кроме окон (windows) программы ncurses могут создавать под – окна (subwindows), поведение которых несколько отличается от поведения стандартных окон.

Важнейшей особенностью ncurses является возможность указать произвольную позицию курсора для вывода (и ввода) данных. Позиция курсора отсчитывается от левого верхнего угла текущего окна. Ячейка в верхнем левом углу имеет координаты (0, 0). При работе с функциями ncurses важно помнить, что первой координатой является номер строки, (что соответствует y, в терминах графического программирования), а второй координатой – номер столбца (что соответствует x в графическом режиме).

В случае ошибки функции ncurses обычно возвращают константу ERR. Если функция не должна возвращать какое-то информативное значение (как, например, функция getch()), в случае успешного выполнения она возвращает значение OK.

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

Когда размеры окна терминала меняются, выполняющаяся в нем программа получает сигнал SIGWINCH. Это одновременно и хорошо и плохо. Хорошо – потому, что терминал информирует программу об изменении своих размеров, плохо – потому, что сигналы имеют особенность вмешиваться в работу программы. Например, если вы напишете программу, использующую ncurses, и не позаботитесь об обработке сигнала SIGWINCH, при изменении размеров окна терминала ваша программа может неожиданно завершиться, оставив терминал в неканоническом состоянии. Рассмотрим, как обрабатывается сигнал SIG_WINCH в программе cursed.

void sig_winch(int signo)

{

struct winsize size;

ioctl(fileno(stdout), TIOCGWINSZ, (char *) &size);

resizeterm(size.ws_row, size.ws_col);

}

Функция sig_winch() представляет собой обработчик сигнала SIGWINCH.

Следует отметить, что изменение размеров окна программы, работающей в текстовом режиме, представляет собой довольно нетривиальную задачу и стандартного рецепта, описывающего, что должна делать программа, когда размеры окна изменились, не существует. Разработчики ncurses постарались упростить решение этой задачи, введя функцию resizeterm(). Функцию resizeterm() следует вызывать сразу после изменения размеров окна терминала. Аргументами функции resizeterm() должны быть новые размеры экрана, заданные в строках и столбцах. Функция resizeterm() старается сохранить внешний вид и порядок работы приложения в изменившемся окне терминала, но это ей удается не всегда, с чем мы столкнемся ниже. Необходимые для resizeterm() значения размеров окна мы получаем с помощью специального вызова ioctl(). При этом первым параметром функции ioctl() должен быть дескриптор файла устройства, представляющего терминал. Вторым параметром ioctl() является константа TIOCGWINSZ, а третьим параметром – адрес структуры winsize. Структура winsize определенная в файле <sys/ioctl.h>, включает в себя поля ws_row и ws_col, в которых возвращается число строк и столбцов окна терминала.

Перейдем теперь к функции main() программы cursed:

int main(int argc, char ** argv)

{

initscr();

signal(SIGWINCH, sig_winch);

cbreak();

noecho();

curs_set(0);

attron(A_BOLD);

move(5, 15);

printw("Hello, brave new curses world!\n");

attroff(A_BOLD);

attron(A_BLINK);

move(7, 16);

printw("Press any key to continue...");

refresh();

getch();

endwin();

exit(EXIT_SUCCESS);

}

Работа с ncurses начинается с вызова функции initscr(). Эта функции инициализирует структуры данных ncurses и переводит терминал в нужный

режим. По окончании работы с ncurses следует вызвать функцию endwin(), которая восстанавливает то состояние, в котором терминал находился до инициализации ncurses. После вызова initscr() мы устанавливаем обработчик сигнал SIGWINCH. Устанавливать обработчик SIGWINCH следует только после инициализации ncurses, поскольку в обработчике используется функция resizeterm(), предполагающая, что библиотека ncurses уже инициализирована.

Функция noecho() отключает отображение символов, вводимых с клавиатуры. Функция cur_set() управляет видимостью курсора. Если вызвать эту функцию с параметром 0, курсор станет невидимым, вызов же функции с ненулевым параметром снова «включает» курсор.

Функция attron() позволяет указать некоторые дополнительные атрибуты выводимого текста. Этой функции можно передать одну или несколько констант, обозначающих атрибуты (в последнем случае их следует объединить с помощью операции «|»). Например, атрибут A_UNDERLINE включает подчеркивание текста, атрибут A_REVERSE меняет местами цвет фона и текста, атрибут A_BLINK делает текст мигающим, атрибут A_DIM снижает яркость текста по сравнению с нормальной, атрибут A_BOLD делает текст жирным в монохромном режиме и управляет яркостью цвета в цветном режиме работы монитора. Специальный атрибут COLOR_PAIR() применяется для установки цветов фона и текста.

Окно ncurses представляет собой матрицу ячеек для вывода символов. Помимо кода символа каждая ячейка содержит дополнительные атрибуты символа.

Режимы вывода

Для символов типа chtype можно устанавливать такие атрибуты, как мигание или цвет символа и фона. Для добавления символу атрибута мигания нужно включить флажок A_BLINK. Далается это так: chtype ch = 'w' | A_BLINK; Теперь при выводе этого символа он будет мигать, если конечно это позволяет сделать терминал. (A_DIM - пониженная яркость, A_BOLD - повышенная яркость, A_NORMAL - нормальное отображение, A_UNDERLINE - подчёркнутый, A_REVERSE - инверсный)

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

...

chtype ch;

...

if (!has_colors())

{

endwin();

printf("Цвета не поддерживаются");

exit(1);

}

start_color();

 

// 1 цвет в палитре - красные символы на чёрном фоне

init_pair(1, COLOR_RED, COLOR_BLACK);

 

// 2 цвета в палитре - зелёные символы на желтом фоне

init_pair(2, COLOR_GREEN, COLOR_YELLOW);

 

...

ch = 'w' | COLOR_PAIR(1); // символ с цветом 1 из палитры

Функция has_colors позволяет узнать можно ли использовать цвета. Функция start_color() должна вызываться до задания палитры. Функция init_pair() нужна чтобы задать какой цифре какой цвет будет соответствовать от 1 до COLOR_PAIRS-1 (0 зарезервирован для стандартного отображения). Для использования цвета в символе нужно включить флажок COLOR_PAIR(номер из палитры).

Список цветов:
COLOR_BLACK
COLOR_RED
COLOR_GREEN
COLOR_YELLOW
COLOR_BLUE
COLOR_MAGENTA
COLOR_CYAN
COLOR_WHITE

Следующие функции позволяют установить атрибуты вывода по умолчанию:

Включение атрибутов

int attron(int attrs)

включает атрибуты attrs. (Например attron(COLOR_PAIR(1)); устанавливает цвет 1 из палитры)

Установка атрибутов
int attrset(int attrs)

Заменяет текущие атрибуты атрибутами attrs (Например attrset(A_NORMAL); заменяет текущие атрибуты на A_NORMAL)

Установка атрибутов очистки
void bkgdset(chtype ch)
Устанавливает атрибуты с которыми очищается экран такими функциями как clear(). (Например bkgdset(COLOR_PAIR(1)); очистка будет осуществляться цветом 1 из палитры)

 

Сбросить атрибуты можно с помощью функции attroff().

Так же, как и в случае с attron(), функции attroff() можно передать несколько констант, обозначающих атрибуты, разделенных символом «|». Так же, как и установка атрибута, сброс атрибута влияет только на текст, напечатанный после сброса (текст, напечатанный ранее с установленным атрибутом, остается без изменений). В нашей программе мы сначала устанавливаем атрибут A_BOLD. Теперь, до тех пор, пока мы не сбросим этот атрибут, весь текст будет печататься жирным шрифтом.

Функция getch() предназначена для считывания символов из потока ввода терминала. Функция считывает по одному символу и может работать в двух режимах: блокирующем (режим по умолчанию) и неблокирующем. В блокирующем режиме функция приостанавливает выполнение программы до появления символа в потоке ввода, а в неблокирующем – возвращает значение сразу же, независимо от того, есть ли символ в потоке ввода или нет (если символа в потоке ввода нет, функция getch() в неблокирующем режиме возвращает значение ERR). В режиме cbreak() (о котором подробнее будет рассказано во второй части статьи) функция, считавшая символ, передает его программе, не дожидаясь, пока пользователь нажмет [Enter]. Таким образом, программа cursed завершается сразу же после нажатия на любую клавишу.

Интерфейс программирования ncurses не является частью glibc, а вынесен в отдельную библиотеку libncurses, поэтому во время сборки программы эту библиотеку нужно подключать явным образом, например: gcc cursed.c –o cursed –lncurses

Окна

В текстовых интерфейсах, построенных на основе ncurses, окна играют такую же важную роль, что и в графических интерфейсах. Прежде чем переходить к созданию приложений, использующих окна, необходимо внести некоторые уточнения в описание интерфейса ncurses. Прежде всего, вы должны понимать, что при работе с ncurses вы всегда имеете дело с окнами. В рассмотренной выше программе cursed мы работали с окном stdscr. Выше уже отмечалось, что каждое окно ncurses описано структурой данных, однако при работе с stscr нам не приходилось иметь дело ни с какими специальными структурами. Объясняется это тем, что в программе cursed мы использовали функции (attron(), move(), printw(), attroff(), getch()), специально предназначенные для работы с окном stdscr. Поскольку эти функции работают исключительно с окном stdscr, передавать им структуру, описывающую окно, не требуется. Для работы с другими окнами нам придется использовать обобщенные варианты функций. Списки параметров обобщенных функций совпадают со списками параметров функций, предназначенных для работы с stdscr, за исключением того, что первым параметром обобщенной функции должен быть указатель на структуру WINDOW, определяющую окно, для которого вызывается функция. Например, для установки атрибутов текста в произвольном окне применяется функция wattron(). Первым параметром этой функции служит указатель на структуру WINDOW, а второй параметр wattron() полностью аналогичен параметру функции attron().

Как получить указатель на структуру WINDOW, соответствующую некоторому окну? Переменная stdscr, которую экспортирует библиотека ncurses, содержит указатель на структуру WINDOW, представляющую корневое окно stdscr. Эта переменная определена как

extern WINDOW * stdscr;

Из того, что stdscr является обычным окном ncursees, следует, что вместо функций, предназначенных специально для stdscr, мы можем использовать их обобщенные аналоги, указывая переменную stdscr в качестве идентификатора окна. Например, вызов attron(A_BOLD) эквивалентен вызову wattron(stdscr, A_BOLD).

Обобщенным вариантом функции attroff() является функция wattroff(), а обобщенным вариантом функции move() функция wmove(). Вызов move(5, 15); из программы cursed можно заменить вызовом wmove(stdscr, 5, 15);

Функции printw() соответствует обобщенная функция wprintw(). Функции getch() соответствует функция wgetch(), аргументом которой должен быть все тот же указатель на WINDOW. Отметим, что функциями getch()/wgetch() и printw()/wprintw() не исчерпывается многообразие функций ввода/вывода символов ncurses.

Смысл создания дополнительных окон заключается в том, что мы ограничиваем область вывода текста (и область применения различных атрибутов) отдельными участками экрана. Создав в некоторой области экрана окно, мы можем быть уверены, что вывод данных, который мы выполняем в этом окне, никак не повлияет на остальной экран.

Библиотека ncurses предоставляет в наше распоряжение несколько функций, создающих новые окна. Самой простой и часто используемой является функция newwin(). У функции newwin() четыре параметра. Первые два параметра соответствуют количеству строк и столбцов в создаваемом окне, а вторые два указывают положение верхнего левого угла нового окна (строка и столбец) относительно окна stdscr.

Функция newwin() возвращает указатель на структуру WINDOW (или NULL в случае ошибки). После завершения работы с окном, выделенные ему ресурсы следует высвободить с помощью функции delwin(). Единственный параметр этой функции – указатель на структуру WINDOW, которую следует удалить.

Отметим, что удаление окна с помощью delwin() само по себе не влияет на содержимое экрана. Данные, выведенные в окно останутся на экране до тех пор, пока не будут перезаписаны другими данными.

Помимо функции newwin() нам будет полезно познакомиться еще с двумя функциями: subwin() и derwin(). Эти две функции предназначены для создания под-окон. Списки параметров у этих функций такие же, как и у newwin(), с той разницей, что первым параметром каждой функции является указатель на структуру WINDOW, соответствующую родительскому окну.

Последние два аргумента у subwin() и derwin() интерпретируются по-разному. У функции subwin() они задают положение верхнего левого угла окна относительно экрана, а у функции derwin() – относительно родительского окна. Чем же под-окно отличается от обычного окна? Окно и его под-окно разделяют массив, в котором хранятся символы и их атрибуты. Новое под- окно наследует все атрибуты своего родителя. Эти атрибуты затем могут быть изменены, что не повлияет на атрибуты родительского окна.

Займемся созданием окон в программе cursedwindows. Список заголовочных файлов и обработчик сигнала SIG_WINCH у программы cursedwindows такие же, как и у программы cursed, так что в листинге мы их пропустим и рассмотрим только функцию main().

int main(int argc, char ** argv)

{

WINDOW * wnd;

WINDOW * subwnd;

initscr();

signal(SIGWINCH, sig_winch);

cbreak();

curs_set(0);

refresh();

wnd = newwin(6, 18, 2, 4);

box(wnd, '|', '-');

subwnd = derwin(wnd, 4, 16, 1, 1);

wprintw(subwnd, "Hello, brave new curses world!\n");

wrefresh(wnd);

delwin(subwnd);

delwin(wnd);

move(9, 0);

printw("Press any key to continue...");

refresh();

getch();

endwin();

exit(EXIT_SUCCESS);

}

В функции main() мы инициализируем ncurses с помощью функции initscr() и устанавливаем обработчик SIGWINCH. Далее делаем курсор невидимым. После этого мы должны обновить экран с помощью refresh().

Мы создаем новое окно с помощью функции newwin(). Наше окно насчитывает 6 строк и 18 столбцов и его верхний левый угол находится в ячейке (2, 4) окна stdscr. Указатель на структуру WINDOW, который возвращает функция newwin(), мы сохраняем в переменной wnd. Функция box(), которую мы вызываем далее, позволяет создать рамку вдоль границы окна. Аргументами этой функции должны быть идентификатор окна и символы, используемые, соответственно, для рисования вертикальной и горизонтальной границы. Теперь было бы логично вывести какой-нибудь текст в окно, обрамленное рамкой, но тут возникает одна сложность. Поскольку символы рамки сами находятся внутри окна, символы текста могут затереть их в процессе вывода. Мы решаем эту проблему с помощью создания под-окна subwnd внутри окна wnd и вывода текста в это под-окно. Поскольку окно subwnd по размерам меньше, чем окно wnd, символы рамки не будут стерты.

Теперь мы можем распечатать текст, что мы и делаем с помощью функции wprintw(), указав ей идентификатор окна subwnd. Для того чтобы символы, напечатанные в окне, стали видимыми, мы должны вызвать функцию wrefresh(). Мы вызываем эту функцию только для окна wnd, поскольку оно содержит символьный массив и своего под-окна subwnd.

Обратите внимание на то, что символы строки "Hello, brave new curses world!", которую мы печатаем в под-окне subwnd с помощью функции wprintw(), переносятся при достижении границы под-окна (рис. 2). После завершения работы с окнами мы можем удалить структуры wnd и subwnd с помощью функции delwin(). Весь вывод, выполненный в окне wnd, останется на экране (точнее в окне curscr) до тех пор, пока вы не перезапишете его другим выводом.

Порядок выполнения работы.

1. Определить иерархию классов рисования (в соответствии с вариантом).

2. Определить методы классов

3. Реализовать классы.

4. Сформировать в виде псевдокода технологию задания и обработки размеров окон и вида знаков.

5. Написать демонстрационную программу, в которой рисуются требуемые объекты в псевдографике и ncurses технологиях.

Методические указания.

Сформировать облик класса (свойства и методы), необходимые для получения графика. Допустим, нужно нарисовать окружность и линию.

Формируется класс

//файл Drawing.h

#pragma once

class Drawing 

{

public: virtual void drawLine (

   double xl, double yl,

   double x2, double y2) = 0;

virtual void drawCircle (

   double x, double y,

   double r) = 0;

};

с виртуальными методами drawLine и drawCircle.

Далее формируются потомки класса Drawing – класс V1Drawing, отвечающий за методы псевдографики

//Файл V1Drawing.h

#include " Drawing.h"

class V1Drawing: public Drawing

{

public: void drawLine(

   double xl, double yl,

   double x2, double y2);

public:

void drawCircle(

   double x, double y, double r);

};

и класс V2Drawing, отвечающий за методы ncurses

//Файл V2Drawing.h

include "Drawing.h"

class V2Drawing: public Drawing {

public: void drawLine (

   double xl, double yl,

   double x2, double y2);

public:

 void drawCircle(

      double x, double y, double r);

};

В основной программе задать возможность изменения окон и выбора метода рисования.

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

Содержание отчета.

1. Титульный лист: название дисциплины; номер и наименование работы; фамилия, имя, отчество студента; дата выполнения.

2. Постановка задачи. Следует дать конкретную постановку, т.е. указать, какие классы должны быть реализованы, какие должны быть в них конструкторы, компоненты-функции и т.д.

3. Иерархия классов в виде графа.

4. Определение пользовательских классов с комментариями.

5..Реализация конструкторов с параметрами и деструктора.

6. Реализация методов.

7. Описание используемой технологии обработки изменяемых размеров окон и характеристик знаков.

8. Листинг демонстрационной программы.

9. Объяснение необходимости виртуальных функций. Следует показать, какие результаты будут в случае виртуальных и не виртуальных функций.

Варианты заданий.

Выбираются два объекта из задач на графику по задачнику 2 семестра 1- го курса. Берется одна задача на рисование и одна на движение.

Шаблоны программирования

Цель.

Получить практические навыки работы с шаблонами в программах С++.

8.2 Основное содержание работы.

Сформировать систему построения графических объектов средствами псевдографики и ncurses с использованием шаблона Bridge.



Поделиться:


Последнее изменение этой страницы: 2021-11-27; просмотров: 57; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.221.13.173 (0.093 с.)