Литмир - Электронная Библиотека
A
A

{

  reference_to<Lines_window>(pw).next();

}

Функция

next()
определяет действие, которое действительно выполняется после щелчка на кнопке Next point: она считывает пару координат, обновляет объект
Open_polyline
и позицию считывания, а также перерисовывает окно.

void Lines_window::next()

{

  int x = next_x.get_int();

  int y = next_y.get_int();

  lines.add(Point(x,y));

  // обновляем текущую позицию считывания:

  ostringstream ss;

  ss << '(' << x << ',' << y << ')';

  xy_out.put(ss.str());

  redraw();

}

Все это совершенно очевидно. Функция

get_int()
позволяет получить целочисленные координаты из объектов класса
In_box
; поток
ostringstream
форматирует строки для вывода в объект класса
Out_box
; функция-член
str()
позволяет вставить строку в поток
ostringstream
. Финальная функция,
redraw()
, необходима для представления результатов пользователю; старое изображение остается на экране, пока не будет вызвана функция
redraw()
из класса
Window
.

А что нового в этой программе? Посмотрим на ее функцию

main()
.

#include "GUI.h"

int main()

try {

  Lines_window win(Point(100,100),600,400,"lines");

  return gui_main();

}

catch(exception& e) {

  cerr << "Исключение: " << e.what() << '\n';

  return 1;

}

catch (...) {

  cerr << "Какое-то исключение\n";

  return 2;

}

Так ведь здесь, по существу, ничего нет! Тело функции

main()
содержит лишь определение нашего окна
win
и вызов функции
gui_main()
. Ни других функций, ни операторов
if
или
switch
, ни цикла — ничего из того, чтобы изучали в главах 6–7, — только определение переменной и вызов функции
gui_main()
, которая сама вызывает функцию
run()
из библиотеки FLTK. Изучая программу далее, увидим, что функция
run()
— это просто бесконечный цикл.

while(wait());

За исключением некоторых деталей реализации, описание которых вынесено в приложение Д, мы просмотрели весь код, запускающий программу рисования линий. Мы увидели всю логику этой программы. Что же произошло? 

16.6. Инверсия управления

А произошло вот что: мы передали поток управления от самой программы элементам управления окном: теперь программа возобновляет свою работу каждый раз, когда активизируется какой-нибудь из этих элементов. Например, щелкните на кнопке, и программа начнет работать. После возврата обратного вызова программа “отключается”, ожидая, пока пользователь сделает что-нибудь еще. По существу, функция

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

Обычная программа организована следующим образом:

Программирование. Принципы и практика использования C++ Исправленное издание - _175.png

Программа графического пользовательского интерфейса организована иначе.

Программирование. Принципы и практика использования C++ Исправленное издание - _176.png

 

Программирование. Принципы и практика использования C++ Исправленное издание - _003.png
 Одна из сложностей такой инверсии управления проявляется в том, что порядок выполнения программы теперь полностью определяется действиями пользователя. Это усложняет как организацию, так и отладку программы. Трудно себе представить, что сделает пользователь, но еще труднее представить себе возможные результаты случайной последовательности обратных вызовов. Это превращает систематическое тестирование в ночной кошмар (подробнее об этом — в главе 26). Методы решения этой проблемы выходят за рамки рассмотрения нашей книги, но мы просим читателей быть особенно осторожными, работая с кодом, управляемым пользователями с помощью обратных вызовов. Кроме очевидных проблем с потоком управления, существуют проблемы, связанные с видимостью и отслеживанием связей между элементами управления окном и данными. Для того чтобы минимизировать трудности, очень важно не усложнять часть программы, отвечающую за графический пользовательский интерфейс, и создавать ее постепенно, тестируя каждую часть. Работая с программой графического пользовательского интерфейса, почти всегда необходимо рисовать небольшие диаграммы объектов и взаимодействия между ними.

Как взаимодействуют части программы, активизированные разными обратными вызовами? Проще всего, чтобы функции оперировали данными, хранящимися в окне, как показано в примере из раздела 16.5. В нем функция

next()
класса
Lines_window
активизировалась щелчком на кнопке Next point, считывала данные из объектов класса
In_box
(
next_x
и
next_y
), а затем обновляла переменную-член
lines
и объект класса
Out_box (xy_out)
. Очевидно, что функция, активизированная обратным вызовом, может делать все, что угодно: открывать файлы, связываться с сетью веб и т.д. Однако пока мы рассмотрим простой случай, когда данные хранятся в окне. 

216
{"b":"847443","o":1}