Читаем Программирование полностью

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

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

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

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

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

<p id="AutBody_Root291"><strong>16.7. Добавление меню</strong></p>

Исследуем вопросы управления и взаимодействия, поднятые в разделе “Инверсия управления”, на примере создания меню для программы, рисующей линии. Для начала опишем меню, позволяющее пользователю выбирать цвет всех линий в переменной lines. Добавим меню color_menu и обратные вызовы.

struct Lines_window:Window {

  Lines_window(Point xy,int w,int h,const string& title);

  Open_polyline lines;

  Menu color_menu;

  static void cb_red(Address,Address);   // обратный вызов

                                         // для красной кнопки

  static void cb_blue(Address,Address);  // обратный вызов

                                         // для синей кнопки

  static void cb_black(Address,Address); // обратный вызов

                                         // для черной кнопки

  // действия:

  void red_pressed() { change(Color::red); }

  void blue_pressed() { change(Color::blue); }

  void black_pressed() { change(Color::black); }

  void change(Color c) { lines.set_color(c); }

  // ...как и прежде...

};

Создание всех таких практически идентичных функций обратного вызова и функций “действия” — довольно утомительное занятие. Однако оно не вызывает никаких затруднений, а описание более простых средств выходит за рамки нашей книги. После щелчка на кнопке меню цвет линий изменяется на требуемый.

Определив член color_menu, мы должны его инициализировать.

Lines_window::Lines_window(Point xy,int w,int h,

      const string&title):Window(xy,w,h,title),

  // ...как и прежде...

  color_menu(Point(x_max()–70,40),70,20,Menu::vertical,"color")

  {

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже