Лабораторная работа: Организация интерфейса пользователя
int movetext(int left, int top, int right, int bottom, int destleft, int desttop)
Копирует содержимое прямоугольной области на экране, определяемой значениями left (левая граница), top (верхняя граница), right (правая граница) и bottom (нижняя граница), в новую прямоугольную область, определяемую аналогичным образом. Левый верхний угол нового прямоугольника задается парой параметров destleft и desttop. Копирование для перекрывающихся окон выполняется корректно.
int gettext(int left, int top, int right, int bottom, void *destin)
Заносит содержимое прямоугольной области на экране, заданной значениями параметров left, top, right, bottom в область памяти, на которую указывает destin.
Функция считывает содержимое прямоугольника в память последовательно, слева направо и сверху вниз. Каждая позиция экрана занимает 2 байта памяти. Первый байт соответствует символу данного знакоместа, а второй – его атрибутам.
Пространство, необходимое для прямоугольника в w столбцов шириной и h строк высотой определяется следующим образом:
размер в байтах = (h строк) × (w столбцов) × 2.
int puttext(int left, int top, int right, int bottom, void *source)
Выводит содержимое области памяти, на которую указывает source, в прямоугольник на экране, координаты которого задаются значениями left, top, right и bottom. Функция выводит содержимое области памяти в заданный прямоугольник в последовательности слева направо и сверху вниз.
5. Реализация пользовательского интерфейса в Borland C ++
5.1 Общие принципы
При внимательном рассмотрении перечисленных в разделе 2 видов пользовательского интерфейса можно заметить, что во всех случаях ввод и вывод информации производятся отдельно от основных вычислений в программе. Общий подход к проектированию приложений заключается в том, чтобы отделить интерфейсную часть от вычислительной. Вычислительная часть должна быть построена таким образом, чтобы как можно меньше зависеть от пользовательского интерфейса, от способов ввода и вывода информации. Все исходные данные передаются в вычислительное ядро стандартными методами, как правило, через параметры функций. Ядро возвращает результаты и, возможно, информацию о возникших ошибках. Интерфейсная часть самостоятельно интерпретирует эту информацию и сообщает пользователю результаты выполнения.
Вычислительная часть программы не должна самостоятельно выводить на экран какие-либо сообщения (исключение составляют сообщения, выводимые в отладочных целях, но их в финальной версии программы также быть не должно). Кроме того, она не должна завершать работу программы (за исключением ситуаций, когда нормальное продолжение работы в принципе невозможно, например, при сильных повреждениях служебных структур в памяти).
Для отделения одной части от другой в BorlandC++ их следует просто вынести в отдельные функции. Основное правило при распределении кода по функциям заключается в том, чтобы для каждой функции определить ее принадлежность к интерфейсу или вычислительной части. По возможности следует не допускать появления функций, которые по поведению можно отнести и в ту, и в другую часть.
Основное требование к интерфейсной части – обеспечивать максимальную защиту от ошибок ввода. Контроль сложных зависимостей между вводимыми данными, которые определяются типом выполняемой задачи, обычно выполняется ядром. В то же время интерфейс в состоянии отследить простые ошибки: ввод строки вместо числа, ввод числа за пределами заданного диапазона и т.п. Проверкам «на глупость» должны подвергаться все данные, вводимые пользователем.
5.2 Консольный интерфейс
Консольный интерфейс достаточно просто реализуется с помощью стандартных функций ввода-вывода: printf, scanf, puts, getch. Перед выводом можно очистить экран, чтобы предыдущие сеансы работы с программой не отвлекали пользователя. Для некоторых видов программ, особенно для системных утилит, очистка экрана является нежелательной, т.к. информация о предыдущих запусках может потребоваться пользователю. Впрочем, к учебным программам это отношения не имеет, и экран лучше все-таки очистить.
Перед каждым запросом данных необходимо вывести пользователю приглашение для ввода этих данных, например: «Введите коэффициент a». Если ввод производится по сравнительно сложным правилам, их необходимо также вкратце описать перед вводом (например, при вводе массива нужно сначала спросить число элементов N , а потом указать, что элементы вводятся N раз).
Поскольку после завершения программы на экране появляется оболочка BorlandC++ и закрывает результаты расчетов, желательно приостанавливать выполнение программы до нажатия клавиши после вывода результатов. При этом пользователю нужно вывести соответствующее сообщение.
Кроме того, ввод данных можно осуществлять не стандартными функциями, а рассмотренными ниже функциями ввода с редактированием. Это позволит сделать интерфейс более дружественным пользователю, особенно в случае, когда расчеты можно повторить (во второй и последующих итерациях можно предлагать пользователю по умолчанию предыдущие значения параметров).
В соответствии с принципами разделения интерфейса и вычислительного ядра ввод, вывод и вычисления следует размещать в разных функциях. Общую логику работы «ввод – вычисления – вывод» можно также вынести в отдельную функцию:
void run()
{
input_data();
calculate();
show_results();
}
Разумеется, прототипы функций ввода input_data(), вычислений calculate() и вывода show_results() могут быть (и будут) другими, зависящими от конкретной программы и способов передачи данных в ядро. Такой подход позволяет легко организовать циклическое выполнение расчетов. Для этого функцию run() нужно вызывать в do…while-цикле, выход из которого осуществляется при отрицательном ответе на вопрос «продолжить?».
В остальном программирование консольного интерфейса, как правило, не вызывает затруднений.
5.3 Простое меню
Простое меню отличается от консольного интерфейса в первую очередь последовательностью выполнения операций. Программа обязательно зацикливается, условие выхода из цикла – выбор пункта меню «Выход». Внутри цикла размещается запрос номера меню и его обработка. Запрос может производиться функциями scanf, getch и т.п. Обработка обычно реализуется оператором switch, в котором для каждого пункта меню предусмотрена отдельная ветка case, а все остальные значения «тихо» игнорируются. Уведомлять пользователя о выборе неправильного пункта меню необязательно, это может его раздражать (например, при случайном промахе в процессе нажатия клавиши).
Основная интерфейсная функция может выглядеть так:
void run()