Text
                    Макс Шлее

программирование

•	Кроссплатформенная реализация
приложений для УУ|пс1о\л/8, Ыпих и Мас 08 X

•	Программирование графики, мультимедиа,
веб-приложений, баз данных, сети, таймера,
многопоточности ХМЬ

•	140 завершенных программ

•	Впервые! Описание интегрированной среды
разработки 01 Сгеа1ог

В ПОДЛИННИКЕ

Макс Шлее программирование наС++ Санкт-Петербург <БХВ-Петербург» 2010
УДК 681.3.06 ББК 32.973.26-018.2 Ш68 Шлее М. Ш68 р(4.5. Профессиональное программирование на С++. — СПб.: БХВ-Петербург, 2010. — 896 с.: ил. + ПУГ) — (В подлиннике) 18ВЫ 978-5-9775-0398-3 Книга посвящена разработке приложений для ХУтскжв, Ьтих и Мас 08 X с использова- нием библиотеки версии 4.5. Подробно рассмотрены возможности, предоставляемые этой библиотекой, и описаны особенности, выгодно отличающие ее от других библиотек. Впер- вые описана интегрированная среда разработки От СгеаЮг. Книга содержит исчерпывающую информацию о классах <2с4 и также практические рекомендации их применения, проиллюст- рированные на большом количестве подробно прокомментированных примеров. ИУО со- держит исходные тексты описанных в книге примеров, библиотеку 014.5 и интегрированную среду разработки О( Сгеасог для УУтбомге, Ьлпих и Мас 08 X. Для программистов УДК 681.3.06 ББК 32.973.26-018.2 Группа подготовки издания: Главный редактор Зам. главного редактора Зав. редакцией Редактор Компьютерная верстка Корректор Дизайн серии Оформление обложки Зав. производством Екатерина Кондукова Игорь Шишигин Григорий Добин Юрий Якубович Натальи Караваевой Виктория Пиотровская Инны Тачиной Елены Беляевой Николай Тверских Лицензия ИД № 02429 от 24.07.00. Подписано в печать 30.09.09. Формат 70х100’/1в. Печать офсетная. Усл. печ. л. 72.24. Тираж 1500 экз. Заказ № 3983 "БХВ-Петербург", 190005. Санкт-Петербург, Измайловский пр., 29. Санитарно-эпидемиологическое заключение на продукцию № 77.99.60.953.Д.005770.05.09 от 26.05.2009 г. выдано Федеральной службой по надзору в сфере защиты прав потребителей и благополучия человека. Отпечатано с готовых диапозитивов в ГУП "Типография "Наука" 199034, Санкт-Петербург, 9 линия, 12 15ВМ 978-5-9775-0398-3 ©ШлееМ, 2009 © Оформление, издательство "БХВ-Петербург". 2009
Оглавление Предисловие Маттиаса Эттриха Предисловие Введение 9 Структура книги..................................................16 ЧАСТЬ I. ОСНОВЫ Глава 1. Обзор иерархии классов 01 •••••••••••••••••••••••••••••*«••*•••••••••••••••••<•••••••«••• 29 Первая программа на 01................................................................................29 Модули 01.............................................................................................31 Резюме................................................................................................37 Глава 2. Философии объектной модели..........................................................38 Механизм сигналов и слотов..............................................................41 Организация объектных иерархий..........................................................55 Метаобъектная информация................................................................57 Резюме..................................................................................58 Глава 3. Работа с 01 Интегрированная среда разработки ГОЕ..............................60 01 А$8181ап1.................................................... 60 Работа с цгпаке...................................................62 Рекомендации для проекта с 01................................... 65 Метаобъектный компилятор МОС......................................66 Компилятор ресурсов КСС......................................... 67
Структура (^-проекта.......................................................68 Методы отладки.............................................................69 Глобальные определения (^1.................................................75 Резюме.....................................................................76 Глава 4. Библиотека контейнеров........................—........................................77 Контейнерные классы........................................................78 Итераторы..................................................................80 Последовательные контейнеры................................................85 Ассоциативные контейнеры................................................. 93 Алгоритмы..................................................................99 Строки....................................................................102 Произвольный тип (^Уапап!.................................................106 Модель общего использования данных........................................107 Резюме....................................................................109 ЧАСТЬ II. ЭЛЕМЕНТЫ УПРАВЛЕНИЯ___________________________________________________.................._______......________________111 Глава 5. С чего начинаются элементы управления...................................113 Класс ..........................................................114 Стек виджетов..................................................122 Рамки........................................................ 123 Виджет видовой прокрутки.......................................124 Резюме.........................................................126 Глава 6. Управление автоматическим размещением элементов ••••••••••ее 128 Менеджеры компоновки (1ауои1 тапа^егз)..........................129 Порядок следования табулятора...................................144 Разделители (28рИкег............................................145 Резюме..........................................................147 Глава 7. Элементы отображения.................................................................... 148 Надписи......................................................148 Индикатор прогресса..........................................154 Электронный индикатор........................................157 Резюме.......................................................160 Глава 8. Кнопки С чего начинаются кнопки. Класс ^АЬз^^ас^Випоп Кнопки..................................... 161 161 162
Флажки-........................................................166 Переключатели................................................ 168 Группировка кнопок.............................................169 Резюме....................................................... 174 Глава 9. Элементы настройки Класс (2АЬигаа81й1ег............................................175 Ползунок...................................................... 177 Полоса прокрутки................................................179 Установщик......................................................181 Резюме..........................................................183 Глава 10. Элементы ввода............................................................................... 184 Однострочное текстовое поле................................... 184 Редактор текста.................................................187 С чего начинаются виджеты счетчиков.............................198 Проверка ввода..................................................200 Резюме........................................................ 203 Глава 11. Элементы выбора............................................................................204 Простой список.........................................................•........204 Иерархические списки............................................................209 Таблицы.........................................................................212 Выпадающий список...............................................................214 Закладки........................................................................215 Виджет панели инструментов......................................................217 Резюме......................................................................... 218 Глава 12. Интервью или модель-представление ••«••••••••••«•••••••••••••••••••••••в* 220 Концепция.......................................................................221 Индексы модели..................................................................230 Иерархические данные............................................................231 Роли элементов..................................................................236 Создание собственных моделей данных.............................................237 Промежуточная модель данных (Ргоху тск1е1)......................................246 Модель элементно-базированных классов...........................................248 Резюме..........................................................................251 Глава 13. Цветовая палитра элементов управления .................................252 Резюме......................................................256
ЧАСТЬ III. СОБЫТИЯ И ВЗАИМОДЕЙСТВИЕ С ПОЛЬЗОВАТЕЛЕМ __257 Глава 14. События...... ....259 Перегрузка специализированных методов обработки событий........262 Реализация собственных классов событий.........................276 Переопределение метода еиеп1().................................2"П Сохранение работоспособности приложения........................281 Резюме.........................................................282 Глава 15. Фильтры событий ••••••••••••••*••••••«••••••••••••••••••••••••••••••••••«••••••••••«••••••• 283 Реализация фильтров событий........................................284 Резюме...........................................................287 Глава 16. Искусственное создание событий ••••••••••••••••••••••••••••••••••••••••••••••ее 288 Резюме.................................................... 292 1 'лава 17. Выведение в компьютерную грас^ику.....295 Классы геометрии...............................................295 Цвет.............................:............................300 Резюме.........................................................307 Глава 18. Легенда о короле Артуре и контексте рисования •••••••••«•••••«•ее**309 Класс (}Рат{ег................................................ 311 Перья и кисти.................................................. 313 Градиенты.......................................................316 Техника сглаживания (Ап11-АИа81п§)..............................318 Рисование.......................................................319 Запись команд рисования........................................ 326 Трансформация систем координат................................ 327 Графическая траектория (рапиег расЬ)............................330 Отсечения...................................................... 331 Режим совмещения (сотрозкюп пюде)................................333 Резюме........................................................ 336 Глава 19. Растровые изображения....... Форматы графических файлов........... Контекстно-независимое представление .337 337 340
Контекстно-зависимое представление...............................348 Резюме .........................................................355 Глава 20. Работа со шрифтами ••••••••«♦•«••••••••••••••••••••••••••••••••••••••••••••••••••««••••«•а356 Отображение строки...........................................................................358 Резюме..................................................................................... 361 Глава 21. Графическое представление •«••••а•••••••••**••••«••••••*••••*•««•••••••••••••••••••362 Сцена........................................................................................363 Представление......................................................................... .....364 Элемент................................................................................ ....365 События......................................................................................369 Виджеты в графическом представлении..........................................................375 Резюме..........................’.......................................................... 378 Класс ()Мсте................................................379 8УО-графика.................................................381 Резюме......................................................383 Глава 23. Работа с ОрепОЬ ааааеаоае«оааааееаеаааоаааааееааааеаааеаааааааееааоаааааааааааааа' Основные положения ОрепОЬ...................................385 Классы 01 для работы с ОрепОЬ...............................386 Реализация ОрепОЬ-программы.................................387 Разворачивание ОрепОЬ-программ во весь экран................391 Графические примитивы ОрепОЬ.............................. 392 Трехмерная графика........................................ 396 Резюме.................................................... 401 Класс ()Ргт:ег.......................................................402 Резюме.......................................................408 Глава 25. Разработка собственных элементов управления •••••••••••••••••••409 Примеры создания виджетов..................................... 409 Резюме........................................................ 416 Глава 26. Элементы со стилем ••••••••••••••••••••••«••••••••••••••••••••••••••••••«••«•••••••••••в Встроенные стили.............................................. 419 Создание собственных стилей................................... 424 Использование (}8(у1е для рисования виджетов..;.................432
Использование каскадных стилей документа...............................................433 Резюме.................................................................................441 Глава 27. Звук....................................................................................................442 Воспроизведение звука..................................................................442 Проверка возможности воспроизведения...................................................443 Пример программы, воспроизводящей звук.................................................444 Резюме.................................................................................448 Глава 28. Мультимедиа ...................................................................................449 Путешествие к истокам РЬопоп...........................................................450 Архитектура РЬопоп................................................................... 451 Быстрый старт........................................................................ 453 Создаем простой медиаплеер.............................................................455 Резюме..................«..............................................................459 ЧАСТЬ V. СОЗДАНИЕ ПРИЛОЖЕНИИ..............М........»..........»...............461 Глава 29. Сохранение настроек приложения ..............................................463 Управление сеансом............................................................... 471 Резюме................................................................................473 Глава 30. Буфер обмена и перетаскивание ..................................................474 Буфер обмена......................................................................... 474 Перетаскивание.........................................................................475 Резюме............................................................................... 490 Глава 31. Интернационализация приложения............................................491 Подготовка приложения к интернационализации............................................492 Утилита 1ир<1а1е................................................................. 492 Программа 0( 1лп§ш$1...................................................................494 Утилита 1ге1еа$е. Пример программы, использующей перевод...............................495 Резюме.................................................................................497 Глава 32. Создание меню................................................................498 Анатомия меню..........................................................................498 Отрывные меню...................................................................... 503 Контекстные меню.......................................................................504 Резюме............................................................................... 506
Глава 33. Диалоговые окна •••••••••••••••••••I ,507 Правила создания диалоговых окон................................507 Класс (2О1а1о§..................................................508 Стандартные диалоговые окна.....................................515 Окна сообщений..................................................524 Резюме..........................................................530 Глава 34. Предоставление помощи........«•«•••••«•«••••«••«•«•••«•••••••••••••««••••«•••••••••в 532 Воздушная подсказка.................................................... 532 Подсказка "Что это"......................................................533 Система помощи (ОпПпе Неф)...............................................535 Резюме...............'................................................. 538 Глава 35. Создание 801- и МШ-приложений ..............................................539 Класс главного окна (^МатЖп&лм...........................................539 Класс действия (2Ас!юп...................................................541 Панель инструментов......................................................542 Доки................................................................... 545 Строка состояния.........................................................545 Окно заставки............................................................548 801- и МОЬприложения.....................................................550 Резюме................................................................. 566 567 Глава 36. Рабочий стол (Оекк^ор)................................................................... Область оповещений........................................ 567 Виджет экрана............................................. 574 Резюме.................................................... 577 ЧАСТЬ VI. ОСОБЫЕ ВОЗМОЖНОСТИ ф___________579 Глава 37. Работа с файлами, директориями и потоками ввода-вывода•••••••••••••••••«••••••••••••••••••••••••••••••••••••••••••в 581 Ввод-вывод. Класс (2ЮОеу1се.......................................................581 Работа с директориями. Класс ()О1г................................................587 Информация о файлах. Класс ()ЕИе1п/о..............................................592 Наблюдение за файлами и директориями..............................................594 Потоки ввода-вывода...............................................................594 Резюме............................................................................597
Дата и время................................................ 598 Таймер.................................................. ....602 Резюме........................................................608 Глава 39. Процессы и потоки •••••••••••••••••••••*••••609 Процессы...............................................609 Потоки.................................................613 Резюме.................................................631 Глава 40.. Программирование поддержки сети..... 632 Сокетное соединение....................................632 Высокоуровневые классы............................... 646 Резюме.................................................648 Глава 41. Работа с ХМЬ 649 Основные понятая и структура ХМЬ-документа................650 ХМЬ и 01.....................................................................651 Работа с 1ЮМ............................................................... 652 Работа с ЗАХ.................................................................658 Резюме.................................................................. ...662 Глава 42. Программирование баз данных .....мм««......м......«...мм...........мм..«663 Основные положения ЗОЬ.......................................................664 Использование ЗОЬ в библиотеке 01............................................666 Резюме.......................................................................675 Глава 43. Динамические библиотеки и система расширений ••••••••••••••••ее 676 Динамические библиотеки......................................676 Расширения (рШ^-тз)..........................................680 Резюме.......................................................690 Глава 44. Совместное использование О< с платформозависимыми АР1 •*•••«»•••••••••••••••••••••••••••••••••••••••••••••*•• 691 Совместное использование с ^шдои'з АР1..................-............................. 692 Совместное использование с Ьтих.........................................................695 Резюме..................................................................................695 Глава 45.01 Иезщпег. Быстрая разработка прототипов....••••••••••••••••••••••а 696 Создание новой формы в 01 Е)е81§пег.....................................................696 Добавление виджетов.....................................................................700
Компоновка (1ауои1)......................................................... 702 Порядок следования табулятора.........................................................703 Сигналы и слоты.................................................................... ,704 Использование форм в проектах.........................................................706 Компиляция........................................................................ 708 Динамическая загрузка формы...........................................................709 Резюме................................................................................713 Глава 46. Проведение тестов••«••••••••••••••••••••••••••••«••••••«••••••••«••••••••••••••••••••••••••о714 Создание тестов..................................................................... 715 Создание тестов графического интерфейса............................................. 722 Параметры для запуска тестов..........................................................723 Резюме............................................................................... 724 Глава 47. УУеЬКИ •••••••••««•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••*••••••••« 725 Путешествие к истокам.................................................................727 А зачем?..............................................................................727 Быстрый старт....................................................................... 728 Написание простого ХУеЬ-браузера......................................................730 Резюме................................................................................737 Глава 48. Интегрированная среда разработки О! Сгеа1ог••••••••«••••••••••••••а 738 Первый запуск.........................................................................739 Создаем проект "Не11о 01 СгеаЮг"......................................................740 Пользовательский интерфейс 01 СгеаЮг..................................................745 Редактирование текста.................................................................748 Интерактивный отладчик и программный экзорцизм........................................156 Резюме.............................................................................. 766 Глава 49. Рекомендации по миграции программ из 013 в 014 767 Основные отличия 014 от 013...........................................................767 Начало перевода на 014............................................... -............771 Резюме.............................................................................. 774 ЧАСТЬ VII. ЯЗЫК СЦЕНАРИЕВ 018сНр1................................................775 Глава 50. Основы поддержки сценариев •••••«•••••••••••••«•••••••••••••••••••••••••••а_777 Принцип взаимодействия с языком сценариев.............................................778 Привет, сценарий......................................................................783 Резюме.............................................................................. 784
Глава 51. Синтаксис языка сценариев .........................................................785 Зарезервированные ключевые слова.............................................................785 Комментарии..................................................................................786 Переменные.................................................................................. 786 Константы....................................................................................791 Операции.....................................................................................791 Управляющие структуры...................................................................... 795 Функции......................................................................................801 Объектная ориентация.........................................................................804 Резюме.......................................................................................806 Глава 52. Встроенные объекты 018спр1 807 Объект С1оЬа1................................................................................807 Объект ^тЬег.................................................................................807 Объект Воо1еап...............................................................................808 Объект 8(ппц.................................................................................808 Объект Ке^Ехр................................................................................809 Объект Аггау.................................................................................810 Объект Оа1е..................................................................................811 Объект МагИ..................................................................................812 Объект Еипсйоп...............................................................................815 Резюме.......................................................................................816 Глава 53. Классы поддержки 018спр1 и практические примеры..........817 Класс (28сг1р1Уа1ие.............................................................................................. 817 Класс (}8спр1Соп1ех1.........................................................................817 Класс (28сг1р1Еп!>1пе........................................................................818 Практические примеры.........................................................................820 Отладчик 0г 8спр1............................................................................833 Резюме.......................................................................................836 Эпилог .................................................................................................................837 ПРИЛОЖЕНИЯ__________________________________________________________________________..______839 Приложение А. Таблицы семибитной кодировки А8СП ..........................841 Приложение В. Таблица простых чисел.... 845 Приложение С. Глоссарий............^.................................................................848 Приложение П. Описание ПУП-диска••••••••••••••••••••••«••••«••••••••«•««••••••••••••••••ее 853 Предметный указатель ••«••«•«••••••••••••«•••«•••••••••••««••••••••••••••••••••«•«•••••••••••••••••••••А 863
Предисловие Маттиаса Эттриха Ье!'з зсагс \укк а ПсСюпа! зСогу. 1та§1пе !еп уеагз а§о, зотеопе сате Со те апд азкес!: "1$ к розз!Ые (о \уп!е а ГеаСше-псЬ §гарЫса1 аррПсаСюп, апд !кеп сотрПе апд тип Лхз аррНсаНоп па!1Уё!у оп а!1 дИГегепС та]ог орегаСт§ зузсетз? Оп Ьтих, оп 1ЛЧ1Х, оп ХУтдохуз, апд оп Ле Мас!пСозк?" Васк !кеп — аз а уоип§ сотриСег 8с1еп!1зС— I \уои!д ргоЬаЫу Ьауе апзхуегед, "Мо, !ка!’з пос розз1Ые. Апд И11 \уаз, Ле зузСет хуоик! Ье уегу дИПсик № изе, апд Нткед Ьу Ске хуеакез! р1агГогт. ВеССег сЬоозе опе р1а!Гопп, от хугке уоиг соде зеуега! Птез." А Геху уеагз 1аСег I дгзсоуегед О! — апд коху хугоп§ I хуаз! 01 такез сгие сгозз-р1а!Гогт рго§гаттт§ а геаПСу, хуккои! Нткт§ уоиг сЬогсез апд сгеаруку. к §1уез изегз хука! изегз хуапС: Газ!, паНуе аррксакопз Ла!1оок апд Гее! ]изС п§к!. к ^суез деуе!орегз хука! деуе!орегз хуап!: а Ггатехуогк (ка! 1е!з из хугке 1езз соде, апд сгеа(е тоге. А Ггатехуогк Ска! такез рго^гаттт^ Гип а^ат, по такег хуке!кег хуе до соттепла! хуогк ог соп!пки!е Со Ореп Зоигсе рго]ес!з. Тоо ёоод !о Ье стае? Уои доп’! ЬеНеуе те? ХУе11, !ке ргооГ 1з еазу. ГП разз Ле хуогд оп Со Мах, \уко хуП1 Се11 уои ехасПу Ьоху к'з допе. Мах, уоиг сит. ВеГоге 11еауе, 1еС те ху!зк уои §оод 1иск \У1!к уоиг ПгзС Ос-зСерз. ВиС Ье сагеГи!, к тау уегу хуеП Сит тСо а 1ИеС1те аддккюп. Еккег хуау, I коре уои хуШ Ьауе аз тисЬ Гип из1п§ Ос аз хуе Ьауе сгеаСт§ к Гог уои. МаСсМаз Есспск ОсСоЬег 1зС, 2004, Оз1о Давайте пофантазируем. Представьте себе, будто бы 10 лет назад кто-то подо- шел ко мне и спросил: "Возможно ли создать многофункциональное приложение с графическим интерфейсом пользователя, а затем откомпилировать его и пользо- ваться на всех распространенных операционных системах? На Ипих, 11Ы1Х, У/1пАо^з, Мастюзй?" В то время я был молодым программистом, и я бы, наверное,
ответил: "Нет, это невозможно. А если это и было бы возможным, то такая сис- тема была бы очень трудна в обращении и ограничена возможностями самой сла- бой платформы. Лучше выбрать одну операционную систему или переписать свою программу несколько раз". Несколько лет спустя я открыл для себя От — и понял, как я был не прав! От делает платформонезависимое программирование действительностью, не огра- ничивая ваш выбор и творческие возможности. От предоставляет пользователям то, чего они хотят: быстрые программы, которые выглядят и работают должным образом. От предоставляет разработчикам программ то, чего они желают: среду, позволяющую писать меньше кода, создавая при этом больше. Благодаря этому программирование становится интереснее, и при этом неважно, является оно ком- мерческим или проектом с открытым исходным кодом (Ореп Зоигсе). Слишком хорошо, чтобы быть правдой? Вы мне не верите? Ну что же, доказать это просто. Я передаю слово Максу, который расскажет вам подробно, как это делается. Макс, теперь твоя очередь. Прежде чем я попрощаюсь, позвольте пожелать вам удачи в ваших первых шагах с От. Но осторожно, От может вызвать у вас зависимость на всю жизнь. В любом случае, я надеюсь, что вам будет также интересно работать с От, как нам было интересно создавать ее для вас. Маттиас Эттрих 1 октября 2004, Осло
Благодарности Автор выражает глубокую признательность своей первой наставнице в об- ласти информатики — Татьяне Дмитриевне Оболенцевой — преподавателю Новосибирского филиала Московского технологического университета лег- кой промышленности, разбудившей в нем творческий потенциал. А также профессору доктору Ульриху Айзэнекеру (ШпсЬ У/.Е18епескег), который по- мог определиться в многообразном мире информатики. Большую помощь в создании этой книги оказали самые близкие ему люди: Алена Шлее (О1епа ЗсЫее), родители Евгений и Галина Шлее (Еи^еп & СаНпа ЗсЫее), сестра Натали Гоуз (НаСа1у (Зоеа). Глубокую признательность и уважение испытывает автор ко всему коллекти- ву издательства БХВ-Петербург, а в особенности — к Игорю Владимирови- чу Шишигину, Андрею Геннадиевичу Смышляеву и Юрию Владимиро- вичу Якубовичу за их поддержку и сотрудничество. Особая благодарность Маттиасу Эттриху (МаПЫаз ЕипсЬ) — сотруднику фирмы ЫоЫа и основателю КОЕ — за его интерес и поддержку, проявленные во время написания книги. А также автор благодарит Кента Ханзена (Кеп1 Напзеп) и Андреаса Ардаль Ханссена (Ашкеаз Аагйа! Напззеп) за проверку примеров книги и остальных сотрудников фирмы Мок1а за замечательную бйблиотеку, которая вдохновила меня на написание этой книги. А также выражаю благодарность моим читателям, присылавшим свои заме- чания и предложения: Виталию Улыбину, Александру Климову, Артуру Акопяну, Ире Романенко, Вячеславу Гурковскому, Николаю Прокушину, Юрию Зинченко, Людмиле Брагиной, Алексею Старченко и Дмитрию Оленченко. И наконец, но не в последнюю очередь, большое спасибо Наталье Шмидт за мой портрет на обратной стороне книги.
“Любая достаточно передовая техноло- гия неотличима от магии.” Артур Кларк Предисловие Как и в предыдущих изданиях моей книги (первом и втором), мы продолжим разговор о дальнейшем развитии библиотеки СК (в дальнейшем просто СК)- Основной причиной для продолжения разговора на эту тему стали ваши от- клики и большой интерес, проявленный вами к первому и второму изданиям книги, за что я вам искренне благодарен. За последние два года произошли очень важные изменения, так что просто необходимо вовремя сообщить вам о них. И самое главное событие, которое, конечно же, не может вас не порадо- вать — это то, что компания ТгоПсесЬ перешла в "руки" к фирме Мо1аа. Даже в самом сладком сне мы не могли предполагать, что Мокла решит предоста- вить возможность использования СК под лицензией ЬСРЬ (ОМП Ьезкег Оепе- га! РиЬПс Ысепзе). Это ли не революция во благо пользователей библиотеки СК? Именно такое решение компании позволяет всем нам использовать СК в соответствии с этой лицензией совершенно бесплатно для разработки ком- мерческого программного обеспечения. Это делает СК еще более доступной и, следовательно, более распространенной и популярной. Что, фактически, превращает ее в стандарт платформонезависимой разработки программного обеспечения с графическим пользовательским интерфейсом на С++. Другое важное событие— это выход интегрированной среды разработки (К Сгеа(ог от Мок1а. Теперь, наконец, стало возможно использовать на раз- личных платформах один и тот же подход и одинаковый инструментарий, рассчитанный и созданный с учетом тонкостей и особенностей этой библио- теки. Это значительно повышает эффективность программирования. Ввиду важности этой темы, я посвятил ей целую главу. Мне очень хотелось, чтобы новое издание книги вместило в себя как можно больше информации по новым возможностям библиотеки (К. Поэтому мысли об этом никогда не покидали
меня, и каждую свободную минуту я посвящал работе над книгой. Я старался не упустить ни одного нюанса, касающегося новинок и изменений в 01. Наряду с 01 СгеаЮг в настоящее издание вошли так же и темы о новых воз- можностях, появившихся в 01 начиная с версии 4.4. Это описание модуля мультимедиа РЬопоп, с помощью которого вы сможете реализовать в ваших программах вывод видео и звука или просто предоставить мультимедиа- плеер. Отдельная глава посвящена модулю \УеЬКн, который широко используется фирмой Арр1е в Мас 08 X, 1РЬопе и 1РодТоисЬ. Он позволит вам легко и бы- стро реализовывать ХУеЬ-браузеры и другие ХУеЬ-клиенты. Новую тему о виджетах рабочего стола я также вынес в отдельную главу. Теперь стало возможным использовать область оповещения операционной системы, не используя платформозависимых решений, а при помощи только лишь одного Ос Структура самой книги осталось практически неизменной, некоторые главы претерпели небольшие изменения, обновления и дополнения. И теперь мне в очередной раз остается лишь пожелать вам счастливого пу- тешествия по главам третьего издания книги.
Введение Сегодня практически невозможно представить себе приложение, не обла- дающее интерфейсом пользователя. Понятия 8ой\уаге и О1Л (ОгарЫса! ГЗзег 1п1егГасе) неразрывно связаны друг с другом. Библиотеки для создания поль- зовательского интерфейса применяются в большом количестве операцион- ных систем, начиная с МойГ для ОС 1ЛЧ1Х и заканчивая широко известной МРС (М1СГО80Й Роипйабоп Оазкек) от Мгсгозой для ОС АУпккжз. Хотя УУшйохуз АР1 (АррПсаПоп Рго§гатпмп§ ййегГасе) — интерфейс про- граммирования приложений— обладает всем необходимым для создания графического интерфейса пользователя, использование этих доступных "ин- струментов" требует больших затрат времени и практического опыта. Даже библиотека МРС, призванная облегчить процесс написания программ для ОС \У1гк1о\У8, не дает той простоты и легкости в процессе создания программ, как хотелось бы. Поэтому и сегодня разработчики, по-прежнему, тратят мас- су времени на реализацию интерфейса пользователя. Но самый большой не- достаток, связанный с применением этих библиотек — это платформозави- симость. И если вы программируете только для ОС 5Ушскж8, то у вас, наверняка, воз- никнет вопрос — зачем мне испытывать что-то новое? Одна из причин, по- чему это стоит сделать — это реализация платформонезависимых приложе- ний. Платформонезависимая реализация приложений — это будущее программной индустрии. С каждый днем она будет приобретать все более возрастающее значение. В самом деле, задумайтесь — зачем оставлять без внимания пользователей ОС ЦМХ, только лишь потому, что вы являетесь программистом для ОС АУшбохуз. Позволив вашему приложению работать под разными ОС, вы заметно увеличите количество пользователей (клиен- тов). Выигрыш же от реализации платформонезависимых приложений нали- цо: значительно сокращается время разработки, так как нет необходимости писать код дважды, и, что не менее важно, отпадает необходимость знать
специфику каждой из платформ, для которой пишется программа. Также нет надобности во время разработки продукта формировать специальные подкоман- ды разработчиков для каждой платформы реализации, что может значительно сократить не только время разработки, но и себестоимость вашего продукта. И, вместе с тем, заметно улучшится и качество приложений, так как оно будет тестироваться на нескольких платформах, а ошибки будут исправляться центра- лизованным путем в одном и том же исходном коде программы. СИ — это луч надежды для программистов, пишущих на языке С++, вынуж- денных сейчас выполнять двойную работу по реализации своих приложений для ОС \У1ПЙо\У8, Ыпих и Мас 08 X. Выбор в пользу 01 избавит вас от этих проблем. 01 предоставляет поддержку большого числа операционных сис- тем: Мюгозой \У1пс1о\У8 (95/98/1ЧТ4/2000/ХР), Мас 08 X, Ьших, 8о1ап8, А1Х, 1пх, Ме(В8О, ОрепВЗЛ, НР-ЦХ, РгееВЗВ и другие клоны 1Л41Х с XII. Более того, благодаря 91 ЕтЬесМед — все возможности 01 доступны также и в ин- тегрированных системах (ЕтЬейдед 8у81ет8). 01 использует АР1 низкого уровня, что позволяет приложениям работать столь же эффективно, как и приложениям, разработанным специально для конкретной платформы. Несмотря на то, что предоставляемая платформонезависимость является од- ной из самых заманчивых возможностей библиотеки, многие разработчики используют 91 также для создания приложений, работающих только на од- ной платформе. Делают они это из тех соображений, что им нравится инст- рументарий и идейный подход самой библиотеки. Подобный подход предостав- ляет им дополнительную гибкость. Ввиду того что требования к программному продукту с течением времени постоянно подвергаются изменениям, при появ- лении необходимости предоставить продукт для какой-либо еще платформы это не составит большой сложности. Для ускорения и упрощения создания пользовательских интерфейсов, 9* предоставляет программу 91 Ве$1§пег, позволяющую делать это в интерак- тивном режиме. На сегодняшний день 91 — это продукт, широко используемый разработчи- ками всего мира. Компаний, использующих эту библиотеку, более 400В. Из числа некоторых активных пользователей 9( можно назвать такие известные компании, как: АбоЬе, АТ&Т, Саппоп, НР, ВозсЬ, Воеш§, 1ВМ, Мо1ого1а, ЫА8А, ЛЕС, Рюпеег, 8Ьагр, 81ешеп8,8опу и Хегох и др. Используя сегодня ту или иную программу, вы, возможно, даже и не догады- ваетесь, что при ее написании использовалась библиотека 91. Назову лишь несколько, на мой взгляд, самых ярких примеров: □ рабочий стол КОЕ (К ТЭезкЮр ЕпукошпеШ) (^кмг.кДе.о1%), используемый в 1лпих и ЕгееВЗО (рис. В. 1);
□ веб-браузер Орега (чумту.орега.сот), набирающий все большую популяр- ность (рис. В.2); □ интернет-пейджер Зкуре (ул?лу.8куре.сот), предназначенный для голосо- вой связи Уо1Р (Уогсе Оуег 1Р), звонков на обычные телефоны и проведе- ния видеоконференций через Интернет (рис. В.З); □ программа А(1оЬе РЬоЮзкор А1Ьшп (лу\у\УЛЙоЬе.сот) для обработки рас- тровых изображений (рис. В.4); □ сетевая карта мира Ооо§1е ЕаПЬ (еаг1Ь.§оо§1е.сош), которая позволяет рассматривать интересующие нас участки поверхности нашей планеты с высоты до 200 метров (рис. В.5); □ программа для виртуализации операционных систем У!гШа1Вох (и'\у\у.У1г1иа1Ьох.ог§) от 8ип М1сго8у81ет8 (рис. В.6); □ свободный проигрыватель УЬС Ме<Яа Р1ауег (1УУУ’иг.уШео1ап.оге/у1с/), на- чиная с версии В.9 (рис. В.7).
4? ЛЬ оШ шрега - С-рега Пк Ес1л мЪ?.' ЕШЫтагк *Л-'к1дв; Тоок Не1р Треп РгтпГ Рт<1 Нете Рапек Ые Сзссаск Токе П^иЬ ’□ - орём аЬоиг ^ОРЕЙА "'Ж^г .. - М<й1.ДСр^а 1| ЬегьотнЫп^ьп мег зюп Ви« РЫтогт М?51?ГТ| Лама ,, ^МТЫ1 4УГ.1Г* ; 9 02 8585 У'ЛпЗО УМгЙ0*5 Х₽ 5.ЦГ1 Лм;а Еигйтое Епмпопгт-егЛ меггюп 1.5 Р1»н.!пп,М|ай,М Рис. В.2. Орега ш ^-т^т ФлЪ Вид Н:+1ТЭ1ты Пистр-зинты Позвонить Помощь ь .1^ Мах 8сИ1ее - ' ....;{; г..... . . мой Зкуре & ЗВОНЯЩИХ - - Доп, возможности Продо пжительность разговораОО 29 11 У . < I ч ||(1|| # Разговор с Тест-ЗвонокЗкуре 7.639.358 Пол.
Рис. В.4. АдоЬе РЬоЮзЬор А1Ьит ' С<ж® 8еагЬе*еп АпясН ТооЬ НгшЛйдеп Ние > $исЬен птГч юсч $1сМЫИ» 8106 V к > *.-» Веявее<е I * ’ • А1егпй|уе СШпвтел ' .< ЭО-беЬаиав | ♦ >Н«е51шлН5 1пъ*8*С>«Св К АЗА 0<1е ' фмеЛеСИе тяйРгл _ОаггтйаЛ 1 7и1 ' 1Шв||е , Мо*овЬгв1| Ругятйеп ’ ЕЬ«неи Атйс№ ’ивимлегм? ₽н»е Рв*вг*агА □ »Л _ г ОеодюГ» шпУ^еЬ *' Ьпр!еЪкжтдеп € гоов Еи«ра Т#4Ьпо1йв»#« Ш-.ае# © 2С0в'Т#г!иМ»1Нс<
Рис. В.6. \Лг1иа1Вох (взято из тга.у<11иа1Ьох.огд)
Многие привыкли считать, что Ос— это средство для создания только ин- терфейса пользователя. Это неправильно, 01 — это полный инструментарий для программирования. Этот инструментарий состоит из отдельных модулей и предоставляет: □ поддержку двух- и трехмерной графики (фактически, являясь стандартом для платформонезависимого программирования на ОрепбЬ); □ возможность интернационализации, которая позволяет значительно рас- ширить рынок сбыта ваших программ; □ использование формата ХМЬ (еХсепыЫе Магкир Ьап§иа§е); □ 8ТЬ-совместимую библиотеку контейнеров; □ поддержку стандартных протоколов ввода/вывода; □ классы для работы с сетью; □ поддержку программирования баз данных, включая поддержку Огас1е, М1СГО8ОЙ 80Ь 8егуег, ГОМ ОВ2, Му8рЬ, 80Ьйе, ЗуЬазе, □ и многое другое. Ос— полностью объектно-ориентированная библиотека. Новая концепция ведения межобъектных коммуникаций, именуемая "сигналы и слоты", пол- ностью заменяет былую не вполне надежную модель обратных вызовов. Также имеется возможность обработки событий, например, нажатия клавиш клавиатуры, перемещения мыши и т. д. Предоставляемая система расширений (р1и§1пз) позволяет создавать модули, расширяющие функциональные возможности ваших приложений. Эти рас- ширения пользователи вашей программы могут получать не только от вас, но и от других разработчиков. Несмотря на то, что Ос изначально создавался для языка программирования С++, это вовсе не означает, что ее использование невозможно в других язы- ках. Напротив, во многих языках программирования существуют модули для работы с этой библиотекой, например, 011ашЫ в 1ауа, Ос# в С#, Рег10с в Рег1, РуОс в РуСЬоп и т. д. Программы, реализованные с использованием 01, могут использовать язык сценариев 01 8спрС. Эта технология позволяет пользователям вашего прило- жения расширить возможности без изменения исходного кода и без переком- поновки самого приложения, изменить "поведение" приложения. 0( прекрасно документирована, благодаря чему вы всегда можете почерп- нуть любую интересующую вас информацию об этой библиотеке, используя программу 01 АыззСапС. А если и этого окажется недостаточно, то не забы- вайте, что 01 — это библиотека с открытым исходным кодом (Ореп 8оигсе),
и вы всегда можете взглянуть на него и детально разобраться в том, как рабо- тает та или иная часть этой библиотеки. И если быть предельно кратким, то библиотеку 91 можно охарактеризовать в трех словах: Простота + Быстрота + Мощность. Добро пожаловать в мир 91! Макс Шлее Дармштадт 22 апреля 2009 г. Структура книги Книга состоит из семи частей. Хочу сразу обратить ваше внимание на то, что если вы уже имели опыт программирования с предыдущими версиями (X то полезнее всего будет начать ознакомление с материалом главы 49, которая описывает отличия 9^4 от 913. Часть I. Основы 01 Основная задача этой части — описать новый подход при программировании с использованием 91. Глава 1. Обзор иерархии классов 01 Первая глава — вводная, знакомящая с модульной архитектурой и классами 9^, а также с реализацией первой программы. Глава 2. Философия объектной модели Во вторую главу входит подробное описание механизма сигналов и слотов, организация объектов в иерархии, свойства объектов. Глава 3. Работа с 01 Третья глава описывает процесс создания проектных файлов, которые можно переработать на любой платформе в соответствующие таке-файлы, методы и средства отладки приложений. Глава 4. Библиотека контейнеров Четвертая глава содержит описание классов, которые в состоянии хранить в себе элементы различных типов данных и манипулировать ими. В этой главе
описываются также различные категории итераторов. Контейнерные классы в Ос являются составной частью основного модуля, и знания о них необхо- димы на протяжении всей книги. Данная глава содержит описание механизма "общих данных", дающего возможность экономично и эффективно использо- вать ресурсы. Все контейнерные классы— списки, словари, хэш-таблицы и др. — описаны в отдельности, особое внимание уделено классу строк озеггпд и мощному механизму для анализа строк, именуемому "регулярное выражение", а также осуществляется знакомство с классом ОУагхапъ, объек- ты которого способны содержать в себе данные разного типа. Часть II. Элементы управления Задача второй части — описание элементов, из которых строятся пользова- тельские интерфейсы. Эта часть дает навыки грамотного и обоснованного применения данных элементов. Глава 5. С чего начинаются элементы управления Пятая глава вводит понятие виджета как синонима элемента управления. Описываются три класса, от которых наследуются все элементы управления, самые важные методы этих классов, такие как изменение размера, местопо- ложения, цвета и др. Рассказывается, как управлять из виджета изменением изображения указателя мыши. Говорится и о классе озсаскеаихддеч, кото- рый способен показывать в отдельно взятый момент времени только лишь один из содержащихся в нем виджетов. ' Глава 6. Управление автоматическим размещением элементов Шестая глава описывает классы для размещений (Ьауоик), позволяющие управлять различными вариантами размещения виджетов на поверхности другого виджета, знакомит с классом разделителя озрИсгег. В качестве примера разрабатывается программа калькулятора. Глава 7. Элементы отображения Седьмая глава описывает элементы управления, не принимающие непосред- ственного участия в действиях пользователя и служащие только для отобра- жения информации. В группу этих элементов входят надписи, индикатор процесса выполнения и электронный индикатор. Подробно разбираются ос- новные особенности этих виджетов.
Глава 8. Кнопки В восьмой главе после описания основных возможностей базового класса кнопок рассматриваются следующие их типы: обычные кнопки, флажки и переключатели. Делается акцент на особенностях их применения. Описы- вается возможность группировки кнопок. Глава 9. Элементы настройки Девятая глава описывает группу виджетов, позволяющих производить настройки, не требующие большой точности: ползунков, полос прокрутки, установщиков. Глава 10. Элементы ввода В десятой главе описывается группа виджетов, представляющих собой фундамент для ввода пользовательских данных. Проводится детальное рас- смотрение каждого виджета этой группы: однострочные и многострочные текстовые поля, счетчик, элемент ввода даты и времени. Рассматривается ис- пользование класса ОУаИйасог для предотвращения неправильного ввода пользователя. Глава 11. Элементы выбора Одиннадцатая глава знакомит с группой виджетов, в которую входят списки, таблицы, закладки, инструменты и др. Глава 12. Интервью, модель-представление Двенадцатая глава знакомит с подходом модель-представление и преимуще- ствами, связанными с его использованием. Глава 13. Палитра элементов управления Тринадцатая глава описывает процесс изменения цветов как для каждого виджета в отдельности, так и для всех виджетов приложения. Часть III. События и взаимодействие с пользователем Цель третьей части — подробно ознакомить с тонкостями применения собы- тий при программировании на 01.
Глава 14. События В четырнадцатой главе разъясняется необходимость сосуществования двух моделей, связанных с оповещением — сигналы, слоты и события. После это- го следует описание целого ряда классов событий для мыши, клавиатуры, таймера и др. Отдельно рассматривается каждый из методов, предназначен- ных для получения и обработки этих событий. Глава 19. Фильтры событий Пятнадцатая глава знакомит с очень мощным механизмом, дающим возмож- ность объекту фильтра осуществлять перехват управлением событиями. Это позволяет объектам классов, унаследованных от ооЬэесс, реализовать, на- пример, один класс фильтра и устанавливать его в нужные объекты, что зна- чительно экономит время на разработку, так как отпадет необходимость на- следования или изменения класса, если при этом преследуется цель только переопределить методы для обработки событий. Глава 16. Искусственное создание событий В шестнадцатой главе рассказывается о способах производить события ис- кусственным образом, что может оказаться очень полезным, например, для имитации ввода пользователя. Часть IV. Г рафика и звук Задача четвертой части — познакомить с разнообразием возможностей, свя- занных с программированием компьютерной графики. Также затрагивается тема реализации приложений со звуком и мультимедиаприложений. Глава 17. Введение в компьютерную графику Семнадцатая глава описывает основные классы для геометрии, необходимые, прежде всего, для рисования. Дается понятие цвета и палитры. Глава 18. Легенда о короле Артуре и контексте рисования Восемнадцатая глава описывает перья и кисти, трансформации систем коор- динат, отсечения, градиентные заливки и многое другое. В ней содержатся примеры рисования различных графических примитивов от точек до поли- гонов, рассказывается о записи команд рисования при помощи класса 1 с сиге, о трансформации систем координат и о других аспектах, связан- ных с рисованием.
Глава 19. Растровые изображения Девятнадцатая глава содержит подробное описание двух классов для растро- вых изображений о₽1хтар и оттаде. Рассматриваются преимущества подоб- ного разделения растровых изображений на два класса и функциональные возможности этих классов. Вводится понятие "прозрачность" и перечисляют- ся поддерживаемые графические форматы. Глава 20. Работа со шрифтами Двадцатая глава демонстрирует использование шрифтов. Глава 21. Графическое представление Двадцать первая глава описывает иерархию классов обгарЫсзЗсепе, предос- тавляющих интерфейс рисования высокого уровня. Эти классы можно при- менять там, где необходимо предоставить пользователю возможность мани- пулировать большим количеством графических изображений, например, в качестве спрайтов для компьютерных игр. Глава 22. Анимация Двадцать вторая глава содержит описание класса омоухе, предназначенного для отображения анимированных изображений в форматах ОП7 и МКО. Глава 23. Работа с ОрепСЬ Двадцать третья глава описывает х совместное использование библиотеки ОрепОЬ с 01, в основном, в качестве дополнительного средства для вывода трехмерной графики. Подробно рассматриваются классы 01, созданные для поддержки ОрепОЬ. Для полноты производится краткое знакомство с воз- можностями самого ОрепОЬ: примитивами, проекциями, пикселами и изо- бражениями, трехмерной графикой и дисплейными списками. Глава 24. Вывод на печать Двадцать четвертая глава рассказывает о возможностях, связанных с выво- дом на печатающее устройство: об использовании принтера в качестве кон- текста рисования, о настройке параметров печати и о многом другом. Глава 25. Разработка собственных элементов управления Двадцать пятая глава описывает факторы, которые необходимо учитывать при создании собственных виджетов. Например, обсуждается, какой из клас- сов взять за базовый и какие методы нуждаются в переопределении.
Глава 26. Элементы со стилем Двадцать шестая глава рассказывает о механизме, позволяющем изменять внешний вид приложения и его поведение (Ьоок & Рее1). Глава знакомит со встроенными стилями, демонстрирует их применение, а также описывает ме- ханизм создания своих собственных стилей и использование С88 (Са$сасНп§ 81у1е 8Ьее1з) для этой цели. Глава 27. Звук В двадцать седьмой главе описываются возможности, предоставляемые СК для воспроизведения звука. Глава 28. Мультимедиа Двадцать восьмая глава знакомит с возможностями использования звука и видео, предоставляемыми модулем РЬопоп. Часть V. Создание приложений В пятой части описываются все необходимые составляющие для реализации профессиональных приложений. Глава 29. Сохранение настроек приложения В двадцать девятой главе объясняется механизм сохранения измененных пользователем настроек и их восстановления при дальнейших загрузках при- ложения. Также описываются механизм управления сеансом, сохранение на- строек и документов в тех случаях, когда пользователь, завершая сеанс рабо- ты с операционной системой, оставил приложение открытым, изменив его настройки или не записав измененный документ. Глава 30. Буфер обмена и перетаскивание В тридцатой главе демонстрируются возможности обмена данными между разными приложениями посредством буфера обмена и перетаскивания (с!га§ & йгор). Глава 31. Интернационализация приложения Хорошее приложение предоставляет многоязыковую поддержку для возмож- ности его комфортного использования в различных языковых средах. Три- дцать первая глава описывает технику, связанную с интернационализацией и локализацией создаваемых приложений. 2 Зак. 3983
Глава 32. Создание меню Меню — это неотъемлемая часть каждого приложения. Тридцать вторая гла- ва описывает процесс создания меню разных типов: строк меню, выпадаю- щих меню, отрывных меню, контекстных меню, а также ускорителей, пред- назначенных для быстрого доступа к отдельным пунктам меню. Глава 33. Диалоговые окна В тридцать третьей главе вводятся понятия модальных и немодальных диало- говых окон. Описываются стандартные диалоги для выбора файлов, шриф- тов, цвета, и др. Объясняется, как применять простые диалоги для выдачи сообщений и как создавать собственные диалоговые окна. Глава 34. Предоставление помощи Предоставление подсказок в программах необходимо для облегчения работы пользователя. В тридцать четвертой главе рассмотрены различные варианты помощи с методами их реализации. Глава 35. Создание 801- и МОЬприложений Тридцать пятая глава описывает технику создания панелей инструментов для приложении и использования строк состояния, знакомит с анатомией главно- го окна приложения и возможностями класса для главного окна приложения Омахпихпдо». Приведен пример создания полноценного текстового редактора сначала как приложение 8В1 (8т§1е Эоситеп! ЫегГасе), а затем МО1 (МиЬ Пр1е ИоситеЩ ЬНегГасе). Глава 36. Рабочий стол (ОевкСор) В тридцать шестой главе рассматриваются основные приемы использования классов работы с рабочим столом операционной системы. Операционные системы предоставляют возможность размещения иконок в области опове- щений (в \У1п<1о\У8 эта область находится в нижнем правом углу) и взаимо- действия пользователя с приложением из этой области. Рассматривается класс озузсештгаутсоп, в котором реализованы механизмы работы с обла- стью оповещений. Также рассматривается класс О0езкеорн1адее, предостав- ляющий доступ к графической области рабочего стола. Часть VI. Особые возможности 01 Задача шестой части — подробно ознакомить с теми возможностями (X ко- торые не обязательно связаны с программированием графики й пользова-
тельского интерфейса, но очень важны, так как предоставляют программисту набор функциональных возможностей практически на все случаи жизни, и, тем самым, позволяют добиться полной платформонезависимости. Глава 37. Работа с файлами, директориями и потоками ввода-вывода В тридцать седьмой главе описываются возможности, предоставляемые 01 для чтения и записи файлов, а также для просмотра директорий и получения подробной информации о файлах. Завершается глава примером реализации программы, осуществляющей поиск файлов в заданной директории (папке). Глава 38. Дата, время и таймер Тридцать восьмая глава'описывает область назначения и применения тайме- ров, а также знакомит с классами, предоставляющими информацию о теку- щей дате и времени и методы для работы с ними. Глава 39. Процессы и потоки Тридцать девятая глава рассказывает о назначении процессов, описывает ис- пользование многопоточности для параллельного выполнения задач, необхо- димые для этого классы и методы. Рассказывается о совместном использова- нии данных и сложностях, с этим связанных. Вводится понятия мьютекса (ти(ех) и задач синхронизации, а также семафора как обобщения мьютексов. Глава 40. Программирование поддержки сети Сороковая глава знакомит с классами, позволяющими реализовывать как ТСР-клиенты, так и серверы. После этого рассматриваются более специали- зированные классы: огер, который дает возможность доступа к данным через ИГР, и онеср, представляющий собой реализацию клиентской части протоко- ла НТТР. Глава 41. Работа с ХМЬ Сорок первая глава содержит краткий вводный курс в очень популярный формат для описания, хранения и обмена данными ХМЬ (еХ1еп$1Ые Магкир Ьап§иа§е). Анализируются преимущества и недостатки различных способов представления данных ХМЬ-документа. После небольшого введения в ПОМ (ОосшпепГ ОЬ]есС Моде1) объясняется, как можно осуществлять чтение и проводить операции с узлами ПОМ-представления ХМЬ-документа. Также говорится о чтении при помощи ЗАХ (81тр1е АР! Гог ХМЬ) и о записи ХМЬ- документов.
Глава 42. Программирование баз данных Сорок вторая глава содержит краткий вводный курс в базы данных. Описыва- ются процессы соединения с базой данных и ее открытия. Подробно говорится о классе 08д10иегу и. исполнении команд 80Ь (81гис1иге<1 Оиегу Ьап§иа§е), получении, удалении и добавлении данных. Рассматривается возможность использования классов, базирующихся на технологии "Интервью". Глава 43. Динамические библиотеки Сорок третья глава рассказывает, как объединить используемый различными Приложениями или их частями код в отдельные динамические библиотеки. Описывается процесс создания и загрузки динамических библиотек. Кроме того, рассказывается о системе расширений (р1и§ш$). Глава 44. Совместное использование 01 с платформозависимыми АР1 Сорок четвертая глава описывает включение платформозависимых функций ОС 5Упк1о№ и 1лпих в программы, базирующиеся на библиотеке Ос. Глава 45.01 Оее1дпег. Быстрая разработка прототипов В сорок пятой главе, после небольшого описания возможностей (^1 Ое81§пег, проводится разработка приложения средствами, предоставляемыми этой средой. Глава 46. Проведение тестов Тестирование — это залог правильной разработки программного обеспече- ния. Сорок шестая глава знакомит с возможностями, предоставляемыми <}1, для проведения модульного тестирования. Глава 47. УУеЬКП Сорок седьмая глава описывает модуль Р(АУеЬКк, который предоставляет инструментарий для получения и отображения информации из всемирной паутины (ЛУогЫ \Ук1е \УеЪ). Прочитав эту главу, вы научитесь быстро созда- вать \УеЬ-браузеры и другие ХУеЬ-клиенты. Глава 48. Интегрированная среда разработки 01 СгеаХог В сорок восьмой главе вы познакомитесь с новой интегрированной средой разработки, узнаете, какие преимущества дает ее использование и из каких компонентов она состоит. Каждый компонент отдельно описан, особое вни- мание уделяется встроенному интерактивному отладчику.
Глава 49. Рекомендации по миграции программ из 013 в 014 Четвертая версия 01 принесла с собой массу изменений, поэтому миграция разработанного с течением времени программ с 0(3 на 014 представляется нелегкой задачей. Сорок девятая глава призвана ознакомить читателя с ос- новными изменениями, сделанными в 0(4, и с проблемами, возникающими при миграции программ на 0(4. Описываются некоторые возможные реше- ния этих проблем, и производится знакомство со средствами, позволяющими автоматизировать, облегчить и ускорить процесс миграции. Часть VII. Язык сценариев 018спр1 Задача седьмой части — ознакомить с языком сценариев 01 8спр(, который базируется на стандарте’ ЕСМА8спр1 4.0 и известен также как 1ауа8спр( 2.0 и 18спр1.ЫЕТ. С предоставлением поддержки этого языка в своих програм- мах перед разработчиком открываются расширенные возможности. Глава 50. Основы поддержки сценариев Пятидесятая глава объясняет принцип работы и принцип взаимодействия 0( 8спр! с 01-программами и описывает, в каких случаях и какие преимущества дает использование языка сценариев. Глава 51. Синтаксис языка сценариев В пятьдесят первой главе описываются ключевые слова языка и приводятся примеры их использования: объявление переменных, операции присвоения, логические операции, циклы, определение функций, определение классов и многое другое. Глава 52. Встроенные объекты 018спр1 Пятьдесят вторая глава описывает встроенные в 8спр1 объекты. К ним от- НОСЯТСЯ ОЬ^есЪ, МаСИ, 5Сг1пд, Воо1еап, КедЕхр, ЫитЬег, ВаСе И Т. Д. Глава 53. Классы поддержки 018спр1 и практические примеры В пятьдесят третьей главе представлены классы, необходимые разработчику на С++ для предоставления поддержки языка сценариев в своих программах, и приводится несколько примеров, подытоживающих материал предшест- вующих глав и использование отладчика для языка сценария.
Часть I Основы 01
Глава 1 Обзор иерархии классов 01 "Если вы хотите знать территорию — нужно сначала изучить карту". Тони Быозен Первая программа на 01 . Как заведено, в самом начале знакомства нужно поздороваться и, чтобы ни- кого не оставить без внимания, лучше всего обратиться сразу ко всему миру. Давайте для этого напишем короткую программу "НеПо 5Уог1д" ("Здравствуй, Мир"), результат выполнения которой показан на рис. 1.1. Написание подобного рода программ стало уже традицией при знакомстве с новым языком или библиотекой. Хотя подобный пример не в состоянии про- демонстрировать весь потенциал невозможности самой библиотеки, он дает представление о базовых понятиях. Данный пример позволяет оценить объем и сложность процесса реализации программ, использующих эту библиотеку. Кроме того, при помощи примера можно убедиться, что все необходимое для компиляции и компоновки установлено правильно. Не11с Рис. 1.1. Окно программы "НеПо УУогкГ #1пс1ийе <0СОи1> 1П1; та1п(1пЪ агдс, сЪаг** агдрг)
ОАррНсаСхоп арр(агдс, агду) ; ОЬаЬе! 1Ы("Не11о, Иог1<1 !”); 1Ы.8Ьом(); гесит арр. ехес (); } В первой строке листинга 1.1 подключается заголовочный файл осей!, кото- рый представляет собой файл модуля, включающий в себя заголовочные файлы для используемых в нашей программе классов: ОАррИсаПоп и 0ЬаЬе1. Конечно, мы могли бы обойтись и без модуля оьси1, а непосредст- венно подключить заголовочные файлы для поддержки классов ОАррИсаЫоп и оьаЬе1, но при большем количестве классов разных модулей, задействованных в программе, читаемость самой программы заметно бы ухудшилась. Кроме того, подключение модулей дает возможность ускорить компиляцию самой программы за счет предварительно откомпилированных заголовочных файлов (РгесотрПед Неадегз) в том случае, если ваш компиля- тор позволяет это делать. Теперь давайте разберем наш пример. Сначала создается объект класса 0Арр11са(:1оп, который осуществляет контроль и управление приложением. Для его создания в конструктор этого класса необходимо передать два аргу- мента. Первый аргумент представляет собой информацию о количестве ар- гументов в командной строке, с которой происходит обращение к программе, а второй — это указатель на массив символьных строк, содержащих аргумен- ты, по одному в строке. Любая использующая 91 программа с графическим интерфейсом должна создавать только один объект этого класса, и он должен быть создан до использования операций, связанных с пользовательским ин- терфейсом. Затем создается объект класса оъаЬе1. После создания элементы управления 01 по умолчанию невидимы, и для их отображения необходимо вызвать ме- тод зЪомО. Объект класса оьаЬе! является основным управляющим элемен- том приложения, что позволяет завершить работу приложения при закрытии окна элемента. Если вдруг окажется, что в созданном приложении имеется сразу несколько независимых друг от друга элементов управления, то при закрытии окна последнего такого элемента управления завершится и само приложение. Это правильно, иначе приложение осталось бы в памяти ком- пьютера и использовало бы его ресурсы. Наконец, в последней строке программы приложение запускается вызовом ОАррИсасХоп: :ехес(). С его запуском приводится в действие цикл обработ- ки событий, определенный в классе осогеАррИсаъхоп, являющимся базовым
для <2Арр11сае1оп. Этот цикл передает получаемые от системы события на обработку соответствующим объектам. Он продолжается до тех пор, пока либо не будет вызван статический метод осогеАррИсаЫоп:: ех1е (), либо не закроется окно последнего элемента управления. По завершению работы приложения метод ОАррИсаЫоп: :ехес() возвращает значение целого типа, содержащее код, информирующий о его завершении. Модули 01 В начале изучения классов новой библиотеки создается ощущение перена- сыщения из-за большого объема информации. Иерархия классов Ос имеет четкую внутреннюю структуру, которую важно понять, чтобы уметь хорошо и интуитивно ориентироваться в этой библиотеке. Библиотека Ос — это множество классов (более 500), которые охватывают большую часть функциональных возможностей операционных систем, предос- тавляя разработчику мощные механизмы, расширяющие и, вместе с тем, упро- щающие разработку приложений. При этом не нарушается идеология операци- онной системы. Рс не является единым целым, она разбита на модули (табл. 1.1). Таблица 1.1. Модули 01 Библиотека Обозначение в проектном файле Назначение □ПСоге соге Основополагающий модуль, состоящий из классов, не связанных с графическим интерфейсом дих Модуль для программирования графи- ческого интерфейса пеЪюогк Модуль для программирования сети ОСОрепСЬ орепд! Модуль для программирования графики ОрепбС. 0Ь5д1 Модуль для программирования баз данных ОЁЗллд ЗVд Модуль для работы с 3\/6 (8са1аЫе Уес1ог ОгарЫсз, масштабируемая век- торная графика) Оъхш1 хт1 Модуль поддержки ХМЬ, классы, отно- сящиеся к ЗАХ и ООМ
Таблица 1.1 (окончание) Библиотека Обозначение в проектном файле Назначение ОСЗЗиррогС д^ЗзиррогС Модуль, содержащий классы для со- вместимости с предыдущей библиоте- кой О! ОеЗсгхрЪ зсгхрЬ Модуль поддержки языка сценариев РЪопоп рИопоп Модуль мультимедиа ОСИеЬКИ: Модуль для создания веб-приложений 0Ъ5сг1рЪТоо1з зсг!рееоо1з Модуль дополнительных возможностей поддержки языка сценария. На настоя- щий момент предоставляет отладчик ОЁТезЪ ПезЪ Модуль, содержащий классы для тести- рования кода Любая (^-программа так или иначе должна использовать хотя бы один из модулей, в большинстве случаев это фСоге и ОКЭш, поэтому эти два модуля определены в программе создания таке-файлов (см. главу 3) по умолчанию. Для использования других модулей в своих проектах необходимо перечис- лить их в проектном файле (см. главу 3). Например, чтобы добавить модули, нужно написать ОТ += орепд1 пеСюогк зд1 А чтобы исключить модуль из проекта: ОТ -= ди! Наиболее значимый из перечисленных в табл. 1.1 модулей — это 0(Соге, так как он является базовым для всех остальных модулей (рис. 1.2). Далее идут модули, которые непосредственно зависят от СИСоге, это — (^[НеГ’Л'огк, ()ССгш, и 91Хш1. И, наконец, модули, зависящие от только что упомяну- тых модулей — Р(38иррог1, рЮрепОЬ и <2(8у§. Для каждого модуля рс предоставляет отдельный заголовочный файл, со- держащий заголовочные файлы всех классов этого модуля. Название этого заголовочного файла соответствует названию самого модуля. Например, для включения О«Эш модуля нужно добавить в программу строку #1пс1ис1е <р1:(3и1>
Пространство имен 01 Пространство имен ос содержит ряд типов перечислений и констант, которые часто применяются при программировании. Если вам необходимо получить доступ к какой-либо константе этого пространства имен, то вы должны ука- зать префикс ос (например, не геа, а ос:: гей). Если вы все-таки хотите опус- кать префикс ос, то необходимо в начале файла с исходным кодом добавить следующую директиву: изхпд патезрасе Модуль СИСоге Как уже было сказано ранее, базовым является модуль 01Соге. Этот модуль является базовым для приложений и не содержит классов, относящихся к ин- терфейсу пользователя. Если вы собираетесь реализовать консольное прило-
жение, то, вполне возможно, вы можете ограничиться одним этим модулем. В модуль ОгСоге входят более 200 классов, вот некоторые из них: □ контейнерные классы оЫзе, ОУесъог, омар (см. главу 4); □ классы ДЛЯ ввода И вывода ОХОЦеухсе, ОТехЬЗЪгеат, 0Р11е (см. главу 37); □ классы процесса ОРгосегз и для программирования многопоточности ОТкгеаб, ОИахССопсЦЫоп, ОМиСех (см. главу 39); 1 □ классы для работы с таймером оваз!ст1тег и отйпег (см. главу 38); □ классы для работы с датой и временем ооаее и отйпе (см. главу 38); , □ класс ооЬдесе, являющийся краеугольным камнем объектной модели (}1 (см. главу 2); □ базовый класс событий ову-епе (см. главу 14); □ класс для сохранения настроек приложения озесыпдз (см. главу 29); □ класс приложения осогеАррИсаЫоп, из объекта которого, если требуется, можно запустить цикл событий. Давайте немного остановимся на классе осогеАррПсаЫоп. Объект класса при- ложения осогеАррИсаЫоп можно образно сравнить с сосудом, содержащим объекты, подсоединенные к контексту операционной системы. Срок жизни объекта класса осогеАррИсаЫоп соответствует продолжительности работы всего приложения, и он остается доступным в любой момент работы програм- мы. Объект класса осогеАррИсаЫоп должен создаваться в приложении только один раз. К задачам этого объекта можно отнести: □ управление событиями между приложением и операционной системой; □ передача и предоставление аргументов командной строки. Модуль 01<лШ Этот модуль содержит в себе классы, необходимые для программирования графического интерфейса пользователя. В этот модуль входят около 300 клас- сов. Вот некоторые из них: □ класс оихадеь — это базовый класс для всех элементов управления биб- лиотеки СИ. По своему внешнему виду это не что иное, как заполненный четырехугольник, но за этой внешней простотой скрывается большой по- тенциал непростых функциональных возможностей. Этот класс насчиты- вает 254 метода и 53 свойства. В главе 5 этому элементу уделено особое внимание; □ классы для автоматического размещения элементов ОУвохьауоиь, Онвохьауоие (см. главу б);
□ классы элементов отображения оьаье1, оьспыитЬег (см. главу 7); □ классы кнопок ОРиаЪВиссоп, осьесквох, овааховиееоп (см. главу 5); О классы элементов установок озиаег, озсгоПваг (см. главу 9); О классы элементов ввода оЫпеЕахе, озрХпВох (см. главу 10); □ классы элементов выбора осошЬоВох, 0Тоо1вох (см. главу 77); □ классы меню ома1пн1пао« и омепи (см. главы 32 и 55); □ классы окон сообщений и диалоговых окон омеззадеВох, <2П1а1од (см. главу 33); □ классы для рисования ОРахпЪег, овгизк, ОРеп, 0Со1ог (см. главу 18}; □ классы для растровых изображений охтаде, орххтар (см. главу 19); □ классы стилей омдс1гзгу1е, ои1пао»8зсу1е и другие (см. главу 26). Как отдельному элементу, так и всему приложению может быть присвоен оп- ределенный стиль, изменяющий их внешний облик; □ класс приложения <2АррИсаЫоп, который предоставляет цикл событий. Давайте рассмотрим немного поподробнее последний класс, класс ОАррИсаьХоп, с которым мы встречались в самом первом примере. Все, что было сказано ранее о классе осогеАррИсасхоп, относится также и к этому классу, так как он является прямым его наследником. Объект класса 0АррИсар1оп представляет собой центральный контрольный пункт всех Ое-приложений, имеющих пользовательский интерфейс. Данный объект ис- пользуется для получения событий клавиатуры, мыши, таймера и других со- бытий, на которые приложение должно реагировать соответствующим обра- зом. Например, окно даже самого простого приложения может быть изменено по величине или быть перекрыто окном другого приложения, и на все подобные события необходима правильная реакция. В назначение ОАррИсаьХоп входит: □ установка стиля приложения. Таким образом можно устанавливать стиль МоЫС, И1пс1о«з, а также многие; другие виды и поведения (Ьоок & Гее1) приложения, включая и свои собственные (см. главу 26); □ получение указателя на объект рабочего стола (йезкюр); □ получение доступа к буферу обмена (см. главу 30); □ управление глобальными манипуляциями с мышью (например, установка интервала двойного щелчка кнопкой мыши) и регистрация движения мы- ши в пределах и за пределами окна приложения; □ выдача предупреждающего звукового сигнала (см. главу 27);
□ обеспечение правильного завершения работающего приложения при за- вершении работы операционной системы (см. главу 29)', □ инициализация необходимых настроек приложения, например, палитры для расцветки элементов управления (см. главу 13). Модуль СНМеЪлюгк Сетевой модуль предоставляет инструментарий для программирования ТСР- и 1Л)Р-сокетов (классы отсрЗоскеп и оиарзоскес), а также для реализации программ-клиентов, использующих протоколы НТТР и РТР (классы онеср и огьр). Этот модуль описывается в главе 40. Модуль (МХт1 Этот модуль предназначен для работы с ХМЬ посредством 8АХ2- и ГЮМ- интерфейсов, которые определяют классы 91 (см. главу 41). Модуль СИ8д1 Этот модуль предназначен для работы с базами данных. В него входят клас- сы, предоставляющие возможность для манипулирования значениями баз данных (см. главу 42). Модуль ОЮрепб1_ Модуль (ЭЮрепОЕ делает возможным использование ОрепОЬ в (^-программах для двух- и трехмерной графики. Основным классом этого модуля является осьи1адес, который унаследован от омхадеь (см. главу 23). Модуль СПЗуд Модуль поддержки графического векторного формата 8УО, базирующегося на ХМЬ. Этот формат предоставляет возможность не только для вывода од- ного кадра векторного изображения, но может быть использован и для век- торной анимации (см. главу 22). Модуль СК38иррог1 Если сравнивать третью версию с четвертой, то можно заметить, что биб- лиотека подверглась очень большим изменениям. Некоторые классы были удалены из новой версии библиотеки, а другие претерпели такие изменения,
которые сделали их несовместимыми со старым программным кодом. Ос- новное назначение этого модуля состоит в облегчении адаптации старых Со- программ к <04. Разумеется, этот модуль не нужно использовать в ваших но- вых программах, написанных на базе 0(4. Некоторые рекомендации по адап- тации старых (0-программ к <04 можно найти в главе 49. Резюме Библиотека (О не является монолитной библиотекой, она разбита на отдель- ные модули: (ОСоге, <00ш, О^еМогк, (ООрепСЬ, 018ц1, ОсХт! и (0$у§. Каждый модуль имеет свое назначение, например, программирование интер- фейса пользователя, графики, баз данных и др. Классы модулей предостав- ляют разработчику механизмы, расширяющие возможности программистов и, вместе с тем, упрощающие создание приложений. Вершиной модульной иерархии является модуль дьсоге, который позволяет реализовывать прило- жения без графического интерфейса пользователя (консольные приложения). Объект класса осогеАррИсаЫоп должен быть создан в приложении только один раз. Для реализации приложений с графическим интерфейсом пользователя необ- ходим модуль оесих. Класс оАррИсаЫоп является стержнем любого <2г- приложения с графическим интерфейсом. Объект этого класса также не дол- жен создаваться в приложении больше одного раза. Данный объект исполь- зуется для получения событий клавиатуры, мыши и др.
Глава 2 Философия объектной модели "Те, кого первое знакомство с квантовой теорией не повергло в шок, скорее всего, вовсе ее не поняли". Макс Борн Объектная модель 01 подразумевает, что все построено на объектах. Факти- чески, класс ооьэесъ — основной, базовый класс. Подавляющее большинст- во классов Ос являются его наследниками. Классы, имеющие сигналы и сло- ты, должны быть унаследованы от этого класса. Примечание При множественном наследовании важно помнить, что при определении класса имя класса ооьдесс (или унаследованного от него) должно стоять первым, чтобы МОС (Ме1а ОЬ]ес1 СотрПег, метаобъектный компилятор) мог правильно распознать его. Другой порядок приведет к ошибке при компиляции. В листин- ге 2.1 приведен правильный порядок для множественного наследования. ПГ-----........................................... ............ _____......_____________........._____......;___....... с!азз МуС1азз : риЬИс ООЬэесЁ, риЬИс АпоЫгегСТазз { Примечание При множественном наследовании также важно учитывать, что от ооьдесс должен быть унаследован только один из базовых классов. Другими словами — нельзя производить наследование сразу от нескольких классов, наследующих 0ОЬэес11.
Класс ооьзесс содержит в себе поддержку: □ сигналов и слотов (81§па1/$1о<); □ таймера; □ механизма объединения объектов в иерархии; □ событий и механизма их фильтрации; □ организации объектных иерархий; □ метаобъектной информации; □ приведения типов; □ свойств. Сигналы и слоты — это средства, позволяющие эффективно производить об- мен информацией о событиях, вырабатываемых объектами. О них мы под- робно поговорим позже в этой главе. Поддержка таймера дает возможность каждому из классов, унаследованных от класса ооьзесс, не создавать дополнительно объект таймера. Тем самым экономится время на разработку. Подробнее о таймерах говорится в главе 38. Механизм объединения объектов в иерархические структуры позволяет резко сократить временные затраты при разработке приложений, не заботясь об освобождении памяти создаваемых объектов, так как объекты-предки сами отвечают за уничтожение своих потомков. Механизм фильтрации событий позволяет осуществить их перехват. Фильтр событий может быть установлен в любом классе, унаследованном от ооьзесс, благодаря чему можно изменять реакцию объектов на происходящие собы- тия без изменения исходного кода класса (см. главу 15). Метаобъектная информация включает в себя информацию о наследовании классов, что позволяет определять, являются ли классы непосредственными наследниками, а также узнать имя класса. Для приведения типов 01 предоставляет шаблонную функцию доЬзесе_саз<:<т> (), базирующуюся на метаинформации, создаваемой метаобъектным компиля- тором МОС (см. главу 3), для классов, унаследованных от ооьз'есъ. Свойства — это поля, для которых обязательно должны существовать мето- ды чтения. С их помощью можно получать доступ к атрибутам объектов из- вне, например из Ос 8спр( (см. часть VII). Свойства также широко использу- ются в визуальной среде разработки пользовательского интерфейса Ос Г)е81§пег (см. главу 45). Этот механизм реализован в при помощи дирек-
тив препроцессора. Задается свойство при помощи макроса о_ркоректу. Оп- ределение свойства в общем виде выглядит следующим образом: 0_РКОРЕНТУ(Суре паше КЕАЛ деЬРипсЫоп [ИН1ТЕ зеЬЕипсЫоп] [ВЕЗЕТ гезеЦипсЬтоп] [ЛЕЗТСЫАВЬЕ Ьоо1] [ЗСНТРТАВЬЕ Ьоо1] [ЗТОКЕЛ Ьоо1] ) Первыми задаются тип и имя свойства, вторым — имя метода чтения (кеал). Определение остальных параметров не является обязательным. Третий пара- метр задает имя метода записи (иктте), четвертый — имя метода сброса зна- чения (везет), пятый (пезкзыавье) является логическим (булевым) значени- ем, говорящим о том, должно ли свойство появляться в инспекторе свойств РсОеац^пег. Шестой параметр (зсктртавье)— также логическое значе- ние, которое управляет тем, будет ли свойство доступно для языка сценариев (^1 8спр1. Последний, седьмой параметр (зтовел) управляет сериализацией, то есть тем, будет ли свойство запоминаться во время сохранения объекта. Итак, теперь, когда вы познакомились с понятием "свойство", давайте в каче- стве простого примера определим в классе свойство для управления режи- мом только чтения (листинг 2.2). .. Юния реяммом;1^ькожения жм ..................... с1азз МуС1азз : риЬИс ООЬэесС { 0_ОВОЕСТ 0_РНОРЕНТУ(Ьоо! геас!0п1у НЕАЕ 1зКеад0п1у МК1ТЕ зеЪЯеасЮп1у) ргздгаЁе: Ьэо1 т_ЬКеас10п1у; риЬИс: МуС1азз(ООЬзесС* роЬ} =0) : ОСЬ^есЪ(роЪд) , т_ЬКеадОп1у (Са1зе) { } риЬИс: \го1(1 зе«<еадОп1у (Ьоо1 ЪКеадОпХу)
{ ш_ЬКеа<Я0п1у = ЬКеадОп1у; ) ЬооХ ХзКеасЮпХуО сопвЪ ( гееит пЦэКеадОпХу; } Класс мусхазз, показанный в листинге 2.2, наследуется от ©оъ^есп. Мы опре- деляем атрибут т_ькеааоп1у, в котором будут запоминаться значения состоя- ния. Этот атрибут инициализируется в конструкторе значением СаХзе. Для получения и изменения значения атрибута в классе Мус1аза определены ме- тоды 1зкеааоп1у() и зеекеайоп1у (). Эти методы регистрируются в макросе 0_ркоректу. Метод 1звеа<зоп1у() служит для получения значения, поэтому указывается в секции кеао, а метод зеснеааоп1у() — для изменения значе- ния, поэтому пишется в секции интте. Из программы мы можем изменить значение нашего свойства следующим образом: роЬд ->зе^РгорегЬу (” геасЮпХу", Ьгие); А так можно получить текущее значение: ЬооХ ЬНеасЮпХу = роЬ}->ргорегЪу ("геасЗОпХу”) . ^оВооХ (); Механизм сигналов и слотов Элементы графического интерфейса определенным образом реагируют на действия пользователя и посылают сообщения. Существует несколько вари- антов такого решения. Старая концепция функций обратного вызова (саПЬаск Гипсйопа), лежащая в основе X УУ1пс1о\у, основана на использовании обычных функций, которые должны вызваться в результате действий пользователя. Применение такой концепции значительно усложняет исходный код программы, делая его ме- нее понятным. Кроме того, отсутствует возможность производить проверку типов возвращаемых значений, потому что во всех случаях возвращается указатель на уо!д. Например, для того чтобы сопоставить код с кнопкой, не- обходимо передать в функцию указатель на кнопку. Если пользователь на- жимает на кнопку, функция будет вызвана. Сами библиотеки не проверяют, были ли аргументы, переданные в функцию, требуемого типа, а это часто
является причиной сбоев. Другой недостаток функций обратного вызова за- ключается в том, что элементы графического интерфейса пользователя тесно связаны с функциональными частями программы и это, в свою очередь, за- метно усложняет разработку классов независимо друг от друга. Одним из ярких представителей этой концепции является Мой?. Важно помнить, что Мой? и ХУтсклук АР1 предназначены для процедурного программирования, и с реализацией объектно-ориентированных проектов, наверняка, появятся трудности. Для программирования в ОС ХУшсклуз существуют специальные библиотеки классов С++, облегчающие программирование для этой операционной сис- темы. Самой популярной библиотекой является МкгокоЙ Роипйайоп С1а$$е$ (МРС). Ее можно, с большой натяжкой, назвать объектно-ориентированной, так как она создавалась людьми, не подозревающими о существовании са- мых элементарных принципов объектно-ориентированного подхода. Одна из самых фундаментальных заповедей объектно-ориентированного подхода — это инкапсуляция, которая запрещает оставлять атрибуты классов незащи- щенными (ведь тогда объекты могут читать и изменять данные без ведома объекта-владельца), но, несмотря на это, во многих МРС-классах это требо- вание не соблюдено. Сама библиотека МРС является надстройкой, пред- оставляющей доступ к функциям ХУшсклуз, реализованным на языке С, что заставляет разработчиков время от времени использовать устаревшие струк- туры, не вписывающиеся в рамки концепции объектно-ориентированного подхода. Интересно также отметить, что сама М1сго&ой для реализации ши- роко известной программы Мкгозой ХУогй не использует МГС вообще. При использовании МРС для обеспечения связей сообщения и методов обра- ботки используются специальные макросы — так называемые карты сооб- щений (листинг 2.3). Они очень сильно загромождают исходный код про- граммы, заметно снижая ее читаемость. с1азз СРйоЪоЗЬу1егА₽р : риЫхс СИхпАрр { риЫхс: СРИоЬоЗ(:у1егАрр() ; риЫхс: ухгСиа! ВООЬ 1пхЫпзРапсе() ; аЕх^зпзд уохс! ОпАррАЬоиЬ ();
аЬ^_дмзд уотд ОпРПёНемО; ОЕС1ЛКЕ_ЛЕ83АЕЕ_МАР () ); ВЕС1М_МЕЗЗАСЕ_МАР(СРЪоГоЗЬу1ег^р, СИхпАрр) ОЫ_СОММАМВ(Ю_,АРР_АВООТ, ОпАррАЬоиГ) ОН_СОММАМП(1О_П.ЬЕ_ЫЕН, ОпЕПеКеи) ОН_СОММА№>(1Р_Е1ЬЕ_МЕН, СИхпАрр::0пГ11еИем) ' ОЫ_СОММАМО(ГО_Е1ЬЕ_ОРЕМ, СИхпАрр: :ОпЕИеОреп) ОЫ_СОМММПЭ(1С_Р1ЬЕ_РК1ИТ„ЗЕ,ГЦР/ СИтпАрр: :ОпРИеРгхпеЗеЪир) ЕЫР_МЕ53АСЕ_МАР() Конструкции, подобные показанной в листинге 2.3, очень неудобны для че- ловеческого восприятия и приводят в замешательство при проведении анали- за кода программы. Пусть многие рассказывают об удобстве использования средств для автоматического создания подобного кода, но созданы они были не от хорошей жизни. Так, непродуманность самой библиотеки вынуждает разработчика при внесении незначительных изменений модифицировать код самой программы сразу в нескольких местах. Например, для того чтобы до- бавить в диалоговое окно текстовое поле, необходимо провести целый ряд операций. Во-первых, нужно создать в классе диалога атрибут, предназна- ченный для хранения значений, вводимых в текстовом поле. Во-вторых, надо задать идентификатор ресурса текстового поля. В-третьих, поставить иден- тификатор ресурса и атрибут в методе оооасаЕхсЪапде () в соответствие друг с другом при помощи метода оох_техе (), после чего будет осуществляться обмен данными между текстовым полем и атрибутом. В-четвертых, этим об- меном необходимо управлять, передавая в методе црбагеОаъа () значения булевого типа егие или СаХзе. И лишь с помощью средств автоматического создания кода можно, частично, избавиться от этой проблемы, заставив про- извести эти изменения за вас и получив взамен другие недостатки, например, дополнительное засорение кода программы ненужной информацией и воз- можное несовпадение созданного кода с утвержденными для проекта требо- ваниями для форматирования и нотации (если не используется венгерская нотация). Я не противник обоснованного применения подобного рода средств, но, по моему мнению, они не должны создаваться как средство уст- ранения изъянов плохого дизайна самой библиотеки. В данной ситуации часть вины скрыта в самом языке С++. Дело в том, что С-Н- не создавался как средство для написания пользовательского интерфей- са, и поэтому не предоставляет соответствующей поддержки, делающей про- граммирование в этой области более удобным. Например, если бы работа
по передаче событий реализовывалась средствами самого языка, тогда отпа- дала бы необходимость в использовании подобного рода макросов. До на- стоящего времени не удавалось сделать ничего подобного, именно поэтому библиотека явилась "как гром среди ясного неба". В отличие от большин- ства других библиотек программирования, 01 расширяет язык С++ дополни- тельными ключевыми словами. Проблема расширения языка С++ решена в 01 с помощью специального препроцессора МОС (Ме1а 0Ь]ес1 СотрПег, метаобъектный компилятор). Он анализирует классы на наличие специального макроса о_овдест в их определении и внедряет в отдельный файл всю необходимую дополнитель- ную информацию. Это происходит автоматически, без непосредственного участия разрабртчика. Подобная операция автоматического создания кода не противоречит привычному процессу программирования на С++, ведь стан- дартный препроцессор перед компиляцией самой программы тоже создает промежуточный код, содержащий исполненные команды препроцессора. Подобным образом действует и МОС, записывая всю необходимую дополни- тельную информацию в отдельный файл, содержимое которого может быть проигнорировано. Макрос о_овдест должен располагаться сразу на следую- щей строке после ключевого слова с1азз с определением имени класса. Очень важно помнить, что после макроса не должно стоять точки с запятой. Внедрять макрос в определение класса имеет смысл в тех случаях, когда соз- данный класс использует механизм сигналов и слотов, или если ему необхо- дима информация о свойствах. Механизм сигналов и слотов полностью замещает старую модель функций обратного вызова, он очень гибок й полностью объектно-ориентирован. Сиг- налы и слоты — это краеугольный концепт программирования с использова- нием (^1, позволяющий соединить вместе несвязанные друг с другом объек- ты. Каждый унаследованный от класс способен высылать и получать сигналы. Эта особенность идеально вписывается в концепцию объектной ориентации и не противоречит человеческому восприятию. Представьте себе ситуацию: у вас звонит телефон, и вы реагируете на это снятием трубки. На языке сигналов и слотов подобную ситуацию можно описать следующим образом: объект "телефон" выслал сигнал "звонок", на который объект "че- ловек" отреагировал слотом "снятия трубки". Использование механизма сигналов и слотов дает программисту следующие преимущества: □ каждый класс, унаследованный от ооьэесс, может иметь любое количест- во сигналов и слотов; □ сообщения, посылаемые посредством сигналов, могут иметь множестве аргументов любого типа;
□ сигнал можно соединять с различным количеством слотов. Высылаемый сигнал поступит ко всем подсоединенным слотам; □ слот может принимать сообщения от многих сигналов, принадлежащих разным объектам; □ соединение сигналов и слотов можно производить в любой точке прило- жения; □ сигналы и слоты являются механизмами, обеспечивающими связь между объектами. Более того, эта связь может выполняться между объектами, которые находятся в различных потоках (см. главу 39)’, □ при уничтожении объекта происходит автоматическое разъединение всех сигнально-слотовых связей. Это гарантирует, что сигналы не будут высы- латься к несуществующим объектам. Нельзя не упомянуть и о недостатках, связанных с применением сигналов и слотов: □ сигналы и слоты не являются частью языка С++, поэтому требуется запуск дополнительного препроцессора перед компиляцией программы; □ отсылка сигналов происходит немного медленнее, чем обычный вызов функции, который производится при использовании механизма функций обратного вызова; □ существует необходимость в наследовании класса оокдесе; □ в процессе компиляции не производится никаких проверок: имеется ли сигнал или слот в соответствующих классах или нет; совместимы ли сиг- нал и слот друг с другом и могут ли они быть соединены вместе. Об ошибке можно будет узнать лишь тогда, когда приложение будет запуще- но. Вся эта информация выводится на консоль, поэтому для того чтобы увидеть ее в в проектном файле (см. главу 3) необходимо доба- вить в секции сонею опцию сопзо1е (для Глпих никаких дополнительных изменений проектного файла не требуется). Сигналы Сигналы (81§па18) окружают нас в повседневной жизни везде: звонок будиль- ника, жест регулировщика, и не в повседневной, например индейский сиг- нальный костер и т. д. В программировании с использованием <31 под этим понятием подразумеваются методы, которые в состоянии производить вы- сылку сообщений. Причиной для появления сигнала может быть сообщение об изменении состояния управляющего элемента, например, перемещение ползунка. На подобные изменения присоединенный объект, отслеживающий
эти сигналы, может соответственно отреагировать, хотя это и не обязательно. Этот очень важный момент говорит о том, что соединяемые объекты могут быть абсолютно независимы и реализованы отдельно друг от друга. Это по- зволяет объекту, высылающему сигналы, не беспокоиться о том, что впо- следствии будет происходить с этими сигналами. Объект, высылающий сиг- налы, может даже и не догадываться, что их принимают и обрабатывают другие объекты. Благодаря такому разделению, можно разбить большой про- ект на компоненты, которые будут разрабатываться разными программи- стами в отдельности, а потом соединяться при помощи сигналов и слотов вместе. Это делает код очень гибким и легко расширяемым; если один из компонентов устареет или должен будет реализован иначе, то все другие компоненты, участвующие в коммуникации с этим компонентом, и сам про- ект в целом, не изменятся. Новый компонент после разработки встанет на место старого и будет подключен к основной программе при помощи тех же самых сигналов и слотов. Это делает библиотеку особенно привлекатель- ной для реализации компонентно-ориентированных приложений. Однако не забывайте, что большое количество взаимосвязей приводит к возникновению сильно связанных систем, в которых даже незначительные изменения могут привести к непредсказуемым последствиям. Сигналы определяются в классе, как и обычные методы, только без реализа- ции. С точки зрения программиста они являются только прототипами мето- дов, содержащихся в заголовочном файле определения класса. Всю дальней- шую заботу о реализации кода для этих методов берет на себя МОС. Методы сигналов не должны возвращать каких-либо значений, и поэтому перед име- нем метода всегда должно стоять Vо^а. Сигнал не обязательно соединять со слотом. Если соединения не произошло, то он просто не будет обрабатываться. Подобное разделение высылающих и получающих объектов исключает возможность того, что один из подсоеди- ненных слотов каким-то образом сможет помешать объекту, выславшему сигналы. Библиотека предоставляет большое количество уже готовых сигналов для существующих элементов управления. В основном, для решения поставлен- ных задач хватает этих сигналов, но иногда возникает необходимость реали- зации новых сигналов в своих классах. Пример приведен в листинге 2.4. .. с1азз МуЗтдпа! { 0_ОВСГЕСТ
31дпа1з: <ЗоХС () ; Обратите внимание на метод сигнала аохь (). Он не имеет реализации, эту работу принимает на себя МОС, обеспечивая примерно такую реализацию: Му31дпа1: :<ЗоХС() { ОМеСаОЬэесЬ:: асЬзлгаСе (ЪЫз, &зеаМСМееаОЬзес(:, 0, 0); ) Из сказанного становится ясно, что не имеет смысла определять сигналы как рггуаье, ргоеесгей или риЪИс, так как они играют роль вызывающих методов. Выслать сигнал можно при помощи ключевого слова епйх. Ввиду того, что сигналы играют роль вызывающих методов, конструкция высылки сигнала етК: аохъ () приведет к обычному вызову метода аохь (). Сигналы могут вы- сылаться из классов, которые их содержат. Например, в листинге 2.4 сигнал аохео может отсылаться только объектами класса музхдпа! и никакими другими. Чтобы иметь возможность отослать сигнал программно из объекта этого класса, следует добавить метод зепазхдпа! о, вызов которого заставит объект класса му81дпа1 высылать сигнал аохе (), как показано в листинге 2.5. с1азз МуЗгдпа! { 0_ОВСГЕСТ риЬИс: ллохс! зепс151дпа1 {) { ели. I: до1Ё () ; } з1дпа15: той! доТЪ(); Сигналы также имеют возможность высылать информацию, передаваемую в параметре. Например, если возникла необходимость передать в сигнале строку текста, то можно реализовать это как в листинге 2.6.
с1азз Му31дпа1 : рйЬИс ООВдесЪ { 0_ОВОЕСТ риЬИс: той! зепдЗхдпа!() { епйъ зепйЗЪгхпд (" ТпЬэгтаНоп") ; } з1дпа1з: зепдЗЁгхпд(сопзё 05Ьг1пд&); }; Слоты Слоты (81о1з) — это методы, которые присоединяются к сигналам. По сути, они являются обычными методами. Самое большое их отличие состоит в возможности принимать сигналы. Как и обычные методы, они определяют- ся в классе как риЬИс, ргХуаье или ргоеесьеа. Соответственно, перед каж- дой группой СЛОТОВ ДОЛЖНО стоять: рггуаСе зХоЬз:, ргоЬесЪей зХоЬз: ИЛИ риЬИс 81оьз :. Слоты могут быть и виртуальными. Примечание По данным фирмы Ыок1а, соединение сигнала с виртуальным слотом примерно в десять раз медленнее, чем с невиртуальным. Поэтому не стоит делать слоты виртуальными, если нет особой необходимости. Правда, есть небольшие ограничения, отличающие обычные методы от сло- тов. В слотах нельзя использовать параметры по умолчанию, например зХоЬМеСЪсхЗЦп*: п = 8) , или определять слоты как зСаеХс. Классы библиотеки содержат целый ряд уже реализованных слотов. Но оп- ределение слотов для своих классов — это частая процедура. Реализация слота показана в листинге 2.7. с!азз МуЗХоЬ : риЬИс ООЬзесЬ { 0_ОВСЕСТ риЬИс: МуЗХоС(); риЬИс зХоСз:
’У'ОХСЗ. 31ОС () { дЭеЬидО « "1'т а з1оС"; } Внутри слота вызовом метода зепдег () можно узнать, от какого объекта был выслан сигнал. Он возвращает указатель на объект типа ооьдесс. Например, в этом случае на консоль будет выведено имя объекта, выславшего сигнал: хго1с1 з1оЪ() { дПеЬид() « зепдег()->оЬз есСЫате(); } Соединение объектов Соединение объектов осуществляется при помощи статического метода соппесе (), который определен в классе ооьэесе. В общем виде, вызов метода соппесг () выглядит следующим образом: ООЬзесС: : соппесС (сопзС ООЬэесС* зепйег, сопзС скаг* з1дпа1, сопзЪ ООЪ^есС* гесегуег, сопзС ска г* з1оС, <2С: :СоппесС1опТуре Суре = ОС::АиСоСоппесС1оп ) ; Ему передаются пять следующих параметров: □ зепйег — указатель на объект, высылающий сигнал; □ зхдпа! — это сигнал, с которым осуществляется соединение. Прототип (имя и аргументы) метода сигнала должен быть заключен в специальный макрос ЗТСЫАЬ (теСЬоа <) >; □ гесе^ег — указатель на объект, который имеет слот для обработки сиг- нала; □ з1о1: — слот, который вызывается при получении сигнала. Прототип слота должен быть заключен в специальном макросе зьот (тесйоа ()); □ суре — управляет режимом обработки. Имеется три возможных значения: ОС: гЩгесьсоппессхоп — сигнал обрабатывается сразу вызовом соответ- ствующего метода слота, ос: ^иеиебСоппесЫоп — сигнал преобразуется в событие (см. главу 14) и ставится в общую очередь для обработки, ОС: :АиЬоСоппесЫоп — это автоматический режим, который действует
следующим образом: если высылающий сигнал объект находится в одном потоке с принимающим его объектом, то устанавливается режим ОЬ: :ОиеиеаСоппесЫоп, В противном случае— режим ОС::ШгесеСоппесЫоп. Этот режим (оь:: АисоСоппесЫоп) определен в методе «оппесс1оп() по умолчанию. Вам вряд ли придется изменять режимы "вручную", но полез- но знать, что такая возможность есть. Следующий пример демонстрирует то, как может быть произведено соеди- нение объектов в программе. уо1с1 тахпО { ООЬЗесЁ:: соппесГ: {рЗепдег, ЗТСМАЬ (з1дпа1МеЁкос1 ()), рКесед^ег, ЗЬОТ (зХоШеСЬод ()) ); } Если вызов происходит из класса, унаследованного от ооьдесе, тогда ООЬдеси:: можно опустить: МуС1азз::МуС1азз() : ООЬЗесЪО { соппес1з (рЗепбег, 31СЫАЬ {з 1дпа1МеЬИод ()), рКесезлгег, ЗЬОТ (з1оЬМеПкос1 {) ) ) ; } В случае, если слот содержится в классе, из которого производится соедине- ние, то можно воспользоваться сокращенной формой метода соппессо, опустив третий параметр (ркеселллег), указывающий на объект-получатель. Другими словами, если в качестве объекта-получателя должен стоять указа- тель Л1з, его можно просто не указывать: МуС1азз::МуС1азз() : ООЬд еси() { соппесС (рЗепдег, ЗКЗЫАЬ (з1дпа1МеЫю<3 ()) , ЗЬОТ (з1оЪ ())) ; } VО^д МуС1азз:: зХоё. () { дТЭеЬидО « "1'т а зХоС";
Иногда возникают ситуации, когда объект не обрабатывает сигнал, а просто передает его дальше. Для этого необязательно определять слот, который в ответ на получение сигнала (при помощи ет1с) отсылает свой собственный. Можно просто соединить сигналы друг с другом. Высылаемый сигнал дол- жен содержаться в определении класса: МуС1азз::МуС1азз() : ООЬдесЬО { соппесС(рЗепдег, ЗЮЫАЬ(з1дпа1МеЫ1од()), ЗТОЦАЬ(гпуЗхдпа! ())) ; } Высылку сигналов заблокировать можно на некоторое время, вызвав метод Ыоскз1дпа1з () с параметром ыие. Объект будет "молчать", пока блокиров- ка не будет снята тем же методом Ыоск31дпа1з () с параметром Еа1зе. При помощи метода з1дпа1зв1оскеб() можно узнать текущее состояние блокировки сигналов. Два окна программы, показанные на рис. 2.1, демонстрируют механизм сиг- налов и слотов в действии. Создается приложение (листинги 2.8—2.10), в первом окне которого находится кнопка нажатия, а во втором — виджет надписи. При щелчке на кнопке АПО (Добавить) происходит увеличение отображаемого значения на 1. Как только значение будет равно пяти, про- изойдет выход из приложения. ‘ Рис. 2.1. Программа-счетчик. Демонстрация работы механизма сигналов и слотов #1пс1ис1е <0СОи1> #1пс1ис1е "СоипЪег .Ъ" тахп (хпе агдс, сИаг** агду) { ОАррИсаЫоп арр(агдс, агду); ОЬаЪе! 1Ы("0м); ОРиеЪВиЪЬоп стЗ ("МЮ*);
СоипСег соипЪег; 1Ы. зИою () ; стоек зИою() ; ООЬ5 есъ:: соппесХ (&стс1, ЗТСЫАЬ (с1 хскес! ()), йсоипЬег, 5ЬОТ (51оЫпс ()) ); ООЬ} есС: : соппесС (йсоипЁег, 51<ЗЫАЬ (соипкегСкапдес! (1пС)), &1Ы, ЗЬОТ (зеШиш {хпС)) ); ООЬд ес к:: соппес С (йсоипЪег, 310КАБ (доодЬуе ()) , &арр, 5ЬОТ(ои1Ё()) ); геГит арр. ехес () ; } В основной программе приложения (листинг 2.8) создается объект надписи 1Ы, кнопка ста и объект счетчика соипьег (описание которого приведено в листингах 2.9 и 2.10). Далее сигнал сНскеЗО соединяется со слотом а1оыпс(). При каждом нажатии на кнопку вызывается метод г1осхпс(), увеличивая значение счетчика на 1. Он должен быть в состоянии сообщать о подобных изменениях, чтобы элемент надписи отображал всегда только актуальное значение. Для этого сигнал соипсегсьапдеа(1пъ), передающий в параметре актуальное значение счетчика, соединяется со слотом зесншп (1пе), способным принимать это значение. Примечание При соединении сигналов со слотами, передающими значения, важно следить за совпадением их типов. Например, сигнал, передающий в параметре значе- ние 1пъ, не должен соединяться со слотом, принимающим озсгХпд. Наконец, сигнал дооаьуеО, символизирующий конец работы счетчика, соединяется со слотом объекта приложения дихьо, который осуществляет завершение работы приложения, после нажатия кнопки АОО в пятый раз. Наше приложение состоит из двух окон, и после закрытия последнего окна его оабота автоматически завеошится.
! Листинг 2.9. Файл СоиЩегЛи -'.г'*"'.И" 2»'Ъв««вЪ*ч*4&Ь*&ьН*Вавеве««вё* •«•••«**« вв«*вв*вЁвввг*ввв*4<с/**<кМЪ««вО1к«На^1**'ка*««4«^пгк^М'аа.*/&»«**»)^ч^*«^%^ввк**»*'&Й^1аё^а»«^$в^в^|(>1<ё«^*вв*«к'^в*ё^<к>ё*в*ё«« ив ^Гл*'»ЬА*в*еЧа га *«Даа*ава«в*^(««^1а*в1»***< #1Ь1де1: _Соип1:еГ—11_ Ме^хпе _Соип^ег_И_ #1пс1и<3е <0ОЬз’ес11> // = ====================================:====================:============ = с1азз Соип^ег : риЬИс ООЬэесЬ { 0_ОВОЕСТ рг гуа^е: 1П11 ш_пУа1ие; риЫхс: Соипеег(); риЬИс з1о113: Vо^д з1оМпс () ; з1дпа1з: уо!с1 доодЬуе ( ) ; хго1с1 соип1зегСИапде(1(1пП) ; }; #епс11 Е / /_СоипЬег_И_ Как видно из листинга 2.9, в определении класса счетчика содержат- ся два сигнала: дообЬуеО, сообщающий о конце работы счетчика, и соипеегсьапдед (1пе), передающий актуальное значение счетчика, а также слот з1оЫпс (), увеличивающий значение счетчика на единицу. 3^1“...................................... —........ ГЛистинг 2.10. Файл соип1ёг.орр я ; . и....V.;............................................ #1пс1ис1е "СоипЪег.Н” И------------------------------------------------------------------------- СоипЁег::Соип^ег() : ООЬд есЪ() , щ_пУа1ие(0) { } 3 Зак. 3983
уо!д СоипЁег::з1оЪ1пс() { епиХ соип1:егСкапде(Я(++т_пУа1ие) ; (т_пУа1ие == 5) { епиХ досхЗЬуе () ; } } В листинге 2.10 метод слота 51оЫпс() высылает два сигнала: соипсегсъапдеа () и дообЬуе (). Сигнал доехЗЬуе () высылается при значении атрибута т_гл/а1ие, равном 5. Слот, не имеющий параметров, можно соединить с сигналом, имеющим па- раметры. Это удобно, когда сигналы поставляют больше информации, чем требуется для объекта, получающего сигнал. В этом случае, в слоте можно не указывать параметры: МуС1азз::МуС1азз() : ООЬзесЪ() { соппесЬ (рЗепйег, ЗЮЫАЬ (зхдпаХМеСЪос! (1пЬ)), ЗЮЫАЬ (ту81дпа1 ())) ; } Если вы не уверены, пригодится ли параметр сигнала в будущем, то лучше определить слот с параметром и проигнорировать его внутри слота. Зато по- том, когда возникнет необходимость, вам не потребуется менять прототип слота. Разъединение объектов Если есть возможность соединения объектов, то должна существовать и воз- можность их разъединения. В (X при уничтожении объекта, все связанные с ним соединения уничтожаются автоматически, но в редких случаях может возникнуть необходимость в уничтожении этих соединений "вручную". Для этого существует статический метод аХзсоппесе (), параметры которого аналогичны параметрам статического метода соппесе (). В общем виде этот метод выглядит следующим образом: ООЬэесЪ: :сНзсоппес(:(8епаег, зхдпа!, гесезлгег, з1оЬ) ; Следующий пример демонстрирует, как может быть произведено разъедине- ние объектов в программе: тахп ()
ООЬэ есЪ:: сИзсоппесП (рЗепдег, ЗКЗЫАЬ (з 1дпа1Ме^ИоЦ ()), рКесегуег, ЗЬОТ (з1оШееЪод ()) ); } Существуют два сокращенных, не статических варианта: ахзсоп- песЪ (зхдпа!, гесегуег, з1оё) И сНзсоппесЪ (гесе^ег, з!оЪ). Организация объектных иерархий Организация объектов в иерархии снимает с разработчика необходимость самому заботиться об освобождении памяти от созданных объектов. Конструктор класса ооьз-есс выглядит следующим образом: 0ОЬэесЬ(0ОЬ]ес1:* роЬз = 0) ; В его параметре передается указатель на другой объект класса ооьзесь или унаследованного от него класса. Благодаря этому параметру существует воз- можность создания объектов-иерархий. Он представляет собой указатель на объект-предок. Если в первом параметре передается значение равное нулю или ничего не передается, то это значит, что у создаваемого объекта нет предка, и он будет являться объектом верхнего уровня и находиться на вер- шине объектной иерархии. Объект-предок задается в конструкторе при соз- дании объекта, но впоследствии его можно в любой момент исполнения про- граммы изменить на другой при помощи метода зеСРагепь (). Созданные объекты по умолчанию не имеют имени. При помощи метода зесоьзесспате() можно присвоить объекту имя. Имя объекта не имеет осо- бого значения, но может быть полезно при отладке программы. Для того что- бы узнать имя объекта, можно вызвать метод оЬЗесЫ4ате(). Г,,.....-- Листинг 2.,11.Прим.ерСрзданияобъектной иерархий /'V / * г ООкдесе* роЪэ1 = пею ООЬ^еск; ООЬЗесЬ* роЬ]2 = пего 0ОЬэесЬ(роЬэ1) ; ООЬзесП* роЬ}4 = пею ООЬзеск (роЬ}2) ; ООЬэесЪ* роЬдЗ = пею 0ОЬэесЬ(роЬэ1); роЬэ2->зее0ЬдесеЫате("Ле ^хгзк сЫ1а роЪ}!"); роЬэЗ->зееоЬэесЁМате("кИе зесопд сЫ1а о С роЬ}!"); роЬэ4->зеЪОЬэесЪНате (" Ъке Нгзе сМ1а роЬэ2“); В первой строке листинга 2.11 создается объект верхнего уровня (объект без предка). При создании объекта роЬд2 в его конструктор передается, в качестве
предка, указатель на объект роЬз1. Объект роЬ^З имеет в качестве предка роЬ]1, а объект роЬз4 имеет предка роЬ]2. Полученная объектная иерархия показана на рис. 2.2. Рис. 22. Схема получившейся объектной иерархии При уничтожении созданного объекта (при вызове его деструктора) все при- соединенные к нему объекты-потомки уничтожаются автоматически. Эта особенность рекурсивного уничтожения объектов значительно упрощает программирование, так как не нужно заботиться об освобождении ресурсов памяти. Именно поэтому необходимо создавать объекты, а особенно объекты не верхнего уровня, динамически, при помощи оператора пеш, иначе удале- ние объекта приведет к ошибке при исполнении программы. Предупреждение Одна из самых распространенных ошибок программистов на С++ при программи- ровании с использованием СМ — это самостоятельный контроль процесса выде- ления/освобождения памяти для Ьбъекта и нединамическое создание элементов управления. При программировании с СМ важно помнить, что все объекты должны создаваться в памяти динамически, с помощью оператора пе«. Исключение из этого правила могут составлять только объекты, не имеющие предков. Для получения информации об объектной иерархии существуют два метода рагепс () и сниагеп (). С помощью метода рагепс () можно определить объ- ект-предок. Согласно рис. 2.2, вызов роЬз2->рагепс() вернет указатель на объект оЬэ1. Для объектов верхнего уровня этот метод вернет значение 0. Чтобы вывести на консоль всю цепь имен объектов-предков какого-либо из объектов, можно поступить так, как показано в листинге 2.12 (проделаем это для объекта роЬз 4 из листинга 2.11). вывод Ьэг (ООЬдесГ:* роЪ^ « роЪ^4; роЬд; роЬд = роЬэ ->рагепЁ ()) { дОеЬид () « роЬд ->оЬд есШаше ();
И на вашем экране вы увидите: СЬе Е1гзЬ сЫ1с1 оС роЬ]2 СЪе ЛгзЬ сЫ1<3 оГ роЬд1 Метод сЪИйгепО возвращает константный указатель на список объектов- потомков. Для приведенного ранее примера (листинг 2.12, рис. 2.2), метод роЬд 1->сЫ1агеп () возвратит указатель на список ооЬэессЫзе, содержащий два элемента: указатели роь^г и роЬдЗ. Можно осуществлять поиск нужного объекта-потомка при помощи метода Лпасинао. В параметре этого метода необходимо передать имя искомого объекта. Например, следующий вызов возвратит указатель на объект роЬ^4: ООЬзесС* роЬ] = роЬэ1->{1пасЫ1а<00ЬзесС*>("Ы1е Е1гзЬ сЫ1а оЕ р<Лд2"); Для расширенного поиска существует метод Ипасъиагеп (), возвращающий список указателей на объекты. Все параметры метода не обязательны, может передаваться либо строка имени, либо регулярное выражение (см. главу 4), а вызов метода без параметров приведет к тому, что он вернет список указате- лей на все объекты-потомки. Поиск производится рекурсивно. Следующий вызов возвратит список указателей на все объекты, имя которых начинается с букв Й1, в нашем случае их три: 0Ызе<0ОЬ5есЁ*> р!1зь = роЬо1->Ипс1СИ11<Згеп<ООЬзесс*>(ОНедЕхр("ЬИ*"));. Для отладки программы полезен метод ашпроьзесыпГоО, который показы- вает следующую информацию, относящуюся к объекту: □ имя объекта; □ класс, от которого был произведен объект; □ сигнально-слотовые соединения. Вся эта информация поступает в стандартный поток вывода геаоиъ. При отладке можно воспользоваться и методом ашпрОЬэесетгееО, предназна- ченным для отображения объектов-потомков в виде иерархии. Например, вы- зов аилфОЬэесстгее () для нашего объекта роЬЗ 1 из листинга 2.12 покажет: ООЬэесС:: ООЬпесС: :СЪе ЛгзС сЫ1а оЕ роЬэ1 ООЬзесЬ: :Ы1е Е1гзЕ сЫ1а оЕ роЬ]2 ООЬз есС.:: ЬИе зесопа сЫ1а оЕ роЬд! Метаобъектная информация Каждый объект, произведенный от класса собесе или от унаследованного от него класса, располагает структурой данных, называемой метаобъектной информацией (класс омегаоьэесь). В ней хранится информация о сигналах,
слотах (включая указатели на них), о самом классе и о наследовании. Получить доступ к этой информации можно посредством метода ооьзесь: :тебаоЬзесб (). Таким образом, для того чтобы узнать, например, имя класса объекта, от ко- торого он был произведен, можно поступить следующим образом: дЭеЬид () « роЬд 1->теЬаОЬэес11 () ->с1аззЫате (); А для того чтобы сравнить имя класса с известным, можно поступить так: И (роЬз1->теЬаОЬзесЬ()->с1аззЦа1пе() == "МуС1азз") { // Выполнить какие-либо действия ' } Для получения информации о наследовании классов существует метод хпЪегхсз (сопзс сЬаг*), который определен непосредственно в классе ООЬзесг и возвращает значение ьгие, если класс объекта унаследован от ука- занного в этом методе класса либо произведен от данного класса, иначе ме- тод возвращает значение Еа1зе. Например: И(роЬз->1пЬег1бз("ОИхйдеС")) { ОНхддеб* рюдС = зЬаС1с_саз1:<0Н1с1де1:*> (роЬз) ; //Выполнить какие-либо действия с ридб' } Метаобъектную информацию использует и операция приведения типов доЬзесб_сазб<т>. Таким образом, при помощи метода хпЬегЛьзО пример можно изменить: ОНйЯдеЬ* рмдб = доЬзесб_сазб<0И1<1деЬ*>(роЬз); 1Е(ридЕ) { //Выполнить какие-либо действия с ридб } К мета-объектной информации относится также и метод ы (), предназначен- ный для интернационализации программ. Подробнее интернационализация описана в главе 31. Резюме В этой главе мы узнали о сущности сигналов и слотов. Это достойная аль- тернатива используемым на сегодняшний день средствам для реализации связей между компонентами графического интерфейса пользователя, позво- ляющая значительно повысить читабельность программного кода. Сигналы и слоты могут быть соединены друг с другом, причем сигнал может быть соединен с большим количеством слотов. Слот, в свою очередь, тоже может быть соединен со многими сигналами. В случае, когда слот не делает
ничего, кроме отправки полученного сигнала дальше, то можно вообще обойтись без него, а просто соединить сигналы друг с другом. Методы сиг- налов должны быть обозначены в определении класса специальным словом з1дпа1з, а слоты — з1оьз. При этом слоты являются обыкновенными мето- дами С++ и в их определении могут присутствовать модификаторы риЫхс, ргосессед, ргхуаее. Реализацию кода для сигналов берет на себя МОС. Вы- сылка сигнала производится при помощи ключевого слова епйс. Класс, со- держащий сигналы и слоты, должен быть унаследован от класса собесе или от класса, унаследованного от этого класса. Сигнально-слотовые соединения всегда можно удалить (отсоединить), воспользовавшись методом Лзсоппесс (), но это нужно крайне редко, так как при удалении объекта ав- томатически уничтожаются все его соединения. Соединение объектов произ- водится при помощи статического метода ооьзесь:: соппесс (). ООЬзесс — класс, по сути, являющийся основным классом при программиро- вании с использованием 01. Конструктор класса ооьзесс имеет два парамет- ра: первый используется для создания объектных иерархий, а второй — для присвоения объекту имени. Свойства объектов важны, так как позволяют по- лучать информацию о классе и об объекте в процессе исполнения програм- мы. Все объекты класса ооьзесс или унаследованных от него классов долж- ны создаваться динамически оператором пем, а об освобождении памяти созданной объектной иерархии программист может не беспокоиться. Так как концепт сигналов и слотов, а также информацию о наследственности невозможно было реализовать средствами самого языка С++, был создан специальный препроцессор, называемый МОС (метаобъектный компилятор), задачей которого является создавать для заголовочных файлов дополнитель- ные срр-файлы, подлежащие компиляции и присоединению их объектного кода к исполняемому коду программы. Для того чтобы МОС мог распознать классы, нуждающиеся в подобной переработке, такой класс должен содер- жать макрос о_овдест.
Глава 3 Работа с 01 "Чтобы узнать истину, нужно узнать причины". Фрэнсис Бэкон Прежде чем начать изучение и перейти к следующим главам, вам необходи- мо ознакомиться с инструментами и средствами, необходимыми для даль- нейшей работы с рс Интегрированная среда разработки ЮЕ Существует много различных ГОЕ, которые можно очень эффективно ис- пользовать при разработке (^-проектов. К ним относятся Мгсгозой У18иа1 8шд10,1ВМ ЕсНрзе, р!)еуе1ор и другие. Но, пожалуй, самая яркая среда раз- работки — это Ыо1аа 01 СгеаЮг (рис. 3.1). Эта ГОЕ включена в пакет поставки 8ЭК. Ей посвящена в книге отдельная глава, поэтому не буду забегать вперед, а всех заинтересованных читателей, которым не терпится поскорее узнать о ней, отсылаю к главе 48. 01 Аз5181ап1 Документация — это то, чем чаще всего пользуется разработчик. В 0( она со- держит более 1500 НТМЬ-страниц, и необходимо средство, обеспечивающее быстрый поиск нужной информации. Таким средством и является <3( Азз^зим, который по принципу работы похож на веб-браузер. СИ Азз1з(ап( предостав- ляет возможность для поиска текста во всех доступных документах о (X Можно воспользоваться параметром -аосРаеь для того, чтобы установить путь к местоположению другой документации. На рис. 3.2 показано окно этой программы. Разработчики могут, с помощью класса ОАзахзеапесНепь, интегрировать 91 Азз181ап1 в свои программы.
Рис. 3.1. Окно интегрированной среды разработки 01 СгеаЮг ч 2» 4.5: иейр еив (Сил .е глмшмшмн..» ф.| М^тый- Шт3«1 М«и. СИЛ. Мда«» Ц1 □ос1итбглв1к?п •19Л1Ж» бэеИший ЧТ‘.‘ '.• П.«СА--Р‘. /«: и*.к ^*-с- м. 1..^ 01 ЯеГегепсе Ооситегйайоп (Орел Зоигсе ЕсГШоп) НОШ: 1Ш5 ссмюп г, пг ж ае-л<ртет ог * пс-с апй орел Бете ягг«ж- агЛ все • -ни. •_} Шпатв Нгл т СИ 4.6 Ц’^а И. , гЛй О! Тх<>м? ЕМП.Г.Г"»апй АЯ1Н.Г.™*, С««Р.^гМ К.УЭ^ов«« 1в1И|И!^ИИЯЯ1иИ11!в А ^лгги?ч,эзг»-*: 1г1к< !ал ♦'* -‘.ё-гачГ* Гйж С[нт :> ‘,«_ 5'.’%’' 1 -’хги!’:- ' (-ихе^у 'Н-Я’-'ЧЧЛ-ЛТи-Ц * /‘е&к.пгп Рис. 3.2. Окно поогоаммы СИА88181ап1
Работа с дтаке Ни один программист для компиляции своей программы не будет каждый раз задавать параметры для компоновки и передавать пути к библиотекам "вруч- ную". Гораздо удобнее создать таке-файл (такеГПе), который возьмет на се- бя всю работу по настройке компилятора и компоновщика. Создание таке-файлов вручную требует от их создателя опыта и понимания процессов компоновки приложения, причем в зависимости от платформы вид этих файлов будет различаться. Раньше техника создания подобных файлов являлась неотъемлемой частью программирования, но теперь многое измс; пилось. И дело совсем не в том, что структура таке-файлов стала проще, скорее наоборот — она стала сложнее. Просто появились специальные ути- литы — генераторы, которые выполнят эту работу за вас. Утилита цтаке вошла в поставку начиная с версии 3.0. Примечательно, что эта утилита так же хорошо переносима, как и сам 01. Программа цтаке интерпретирует файлы проектов, которые имеют расширение рго и содержат различные параметры, для создания таке-файлов. Самое удивительное, что цшаке способен создавать не только таке-файлы, но и сами рго-файлы. Допустим, вы указали, что в каталоге есть исходные файлы С++, выполнив следующую команду: дтаке -ргозесЕ. Будет произведена попытка автоматического создания рго-файла. Это удоб- но, так как на первых порах вам не понадобится вникать во все тонкости соз- дания рго-файлов. Также это может оказаться полезным в том случае, если вы обладаете большим количеством файлов, к которым требуется создать рго-файл — тогда у вас отпадет необходимость вносить их имена вручную. Создать из рго-файла таке-файл совсем нетрудно, для этого нужно просто выполнить команду: дтаке Ше.рго -о Маке^Ие Как нетрудно догадаться, сне.рго — это имя рго-файла, а макесие — имя для создаваемого платформозависимого таке-файла. Если бы мы выполнили команду без параметров, то дтаке попытался бы найти в текущей директории рго-файл и, в случае успеха, автоматически соз- дал бы таке-файл. Таким образом, имея в распоряжении только' исходные файлы на С++, можно создать исполняемую программу, выполнив всего лишь три команды: дтаке -ргодесС дтаке таке
Конечно, для более серьезной работы, нам понадобится изменять содержи- мое рго-файлов, что позволит нам осуществлять более тонкую настройку для наших проектов. Таблица 3.1 содержит некоторые опции рго-файла, полный список которых можно получить в официальной документации 01, постав- ляемой вместе с самой библиотекой (для этого вы можете просто запустить программу 01 А88181ап<). Таблица 3.1. Некоторые опции для файла проекта Опция Назначение НЕАПЕКЗ Список созданных заголовочных файлов 5ОЦКСЕЗ Слисок созданных файлов реализации (с расширением срр) ГОКМ5 Список файлов с расширением ш. Эти файлы создаются программой 01 0ез!дпег и содержат описание интерфейса пользователя в формате ХМ1_ (см. главы 41 и 45) ТАКСЕТ Имя приложения. Если данное поле не заполнено, то на- звание программы будет соответствовать имени проектно- го файла Ъ1ВЗ Задает список библиотек, которые должны быть подклю- чены для создания исполняемого модуля СОЫР1С Задает опции, которые должен использовать компилятор ПЕЗТШН Задает путь, куда будет помещен готовый исполняемый модуль ПЕР1ЫЕЗ Здесь можно передать опции для компилятора. Например, это может быть опция помещения отладочной информа- ции для беЬидег в исполняемый модуль 1ЫСЫГОЕРАТН Путь к директории, где содержатся заголовочные файлы. Этой опцией можно воспользоваться в случае, если уже есть готовые заголовочные файлы и вы хотите использо- вать их (подключить) в текущем проекте ОЕРЕЫОРАТН В данном разделе указываются зависимости, необходи- мые для компиляции 5ПВВ1КЗ Задает имена поддиректорий, которые содержат рго- файлы ТЕМРЬАТЕ Задает разновидность проекта. Например: арр — приложение, ИЬ — библиотека, зиЬдхгз — поддиректо- рии ТКА№ЬАТЮ№ Задает файлы переводов, используемые в проекте (см. главу 31)
Давайте немного разберем "анатомию" проектных файлов. Итак, рго-файл может выглядеть следующим образом: ТЕМРЬАТЕ = арр НЕАЛЕК5 += €Пе1.И \ Ше2 .Ъ 500КСЕЗ += таХп.срр \ ЕНеХ.срр \ ЕИе2.срр ТАРСЕТ = ЕПе СОЛГКЗ += дС магп_оп геХеазе В первой строке задается тип программы. В данном случае это приложение, поэтому темрьате = арр (если бы нам нужно было создать библиотеку, то темрьате следовало бы присвоить значение иь). Во второй строке, в неабекз перечисляются все заголовочные файлы, принадлежащие проекту. В опции 5ОПКСЕ5 перечисляются все файлы реализации проекта, тарсет определяет имя программы, соыпс определяет опции, которые должен использовать компилятор в соответствии с подсоединяемыми библиотеками. Например, в нашем случае: □ дс указывает, что это (^-приложение и используется библиотека □ «агп_оп означает, что компилятор должен выдавать как можно больше предупреждающих сообщений; □ геХеазе указывает, что приложение должно быть откомпилировано в окон- чательном варианте, без отладочной информации. Как видно из примера, программе фпаке не требуется много информации, так как она опирается на файл локальной конфигурации, который определен сис- темной конфигурацией. Такой файл очень важен еще и потому, что один и тот же вызов дтаке приведет к созданию различных таке-файлов в зависи- мости от того, на какой платформе он был вызван. Это один из очень важных шагов в сторону платформонезависимости самих проектных файлов. Проектный файл может быть использован для компиляции проектов, распо- ложенных в разных директориях. Наглядным примером может служить рго- файл каталога примеров прилагаемого к этой книге ВУВ-диска. Этот файл выглядит примерно так: ТЕМРЬАТЕ = зиЬсИгз 5ЦВБ1К5 = ЕхашрЬе! Ехатр1е2 .... Ехапдэ1еЫ Примечание Для удаления объектных файлов проекта служит опция с1еап, а для удаления объектных файлов, созданных проектом исполняемых модулей и созданы* таке-файлов, существует опция аХзЬсХеап. Например: таке аХэСсХеап.
Рекомендации для проекта с 01 При реализации файлы классов лучше всего разбивать на две отдельные час- ти. Часть определения класса помещается в заголовочный файл с расширени- ем Ь, а реализация класса — в файл с расширением срр. Важно помнить, что в заголовочном файле с определением класса должна содержаться директива препроцессора #Кпае€. Смысл этой директивы состоит в том, чтобы избе- жать конфликтов в том случае, когда один и тот же заголовочный файл будет включаться в исходные файлы более одного раза. #1^пс1е€ _МуС1а55_И._ #йе^1пе _МуС1азз_Ь_ с1азз МуС1азз { }; #епс11€ //_МуС1а53_И_ По традиции, заголовочный файл, как правило, носит имя содержащегося в нем класса. В заголовочных файлах, в целях более быстрой компиляции, для указателей на типы данных используется предварительное объявление для типа данных, а не прямое включение посредством директивы #1пс1иае. В начале определения класса содержится макрос о_овавст для МОС; это не- обходимо, если ваш класс использует сигналы и слоты, а в других случаях, если у вас нет нужды в метаинформации, то этим макросом можно пре- небречь. с1а88 МуС1азз : риЬИс ООЬэесС { 0_ОВ7ЕСТ риЬИс: МуС1азз(); ); Основная программа должна быть реализована в отдельном файле, который является "стартовой площадкой" приложения. Этому файлу принято давать имя таш.срр. Это удобно еще и потому, что проект может состоять из сотен файлов и если следовать такому правилу, найти отправную точку всего про- екта не составит труда. Соблюдение этих рекомендаций может сослужить хорошую службу. Проектам свойственно со временем расширяться, поэтому неплохо с самого начала придерживаться определенной структуры, чтобы потом чувствовать себя в своих и чужих проектах, придерживающихся принятой в структуры, как оыба в воде.
Метаобъектный компилятор МОС < Метаобъектный компилятор (МОС, Ме1а ОЬ]ес1 СотрПег), по сути дела, является не компилятором, а препроцессором, который исполняется в ходе компиляции приложения, создавая, в соответствии с определением класса, дополнительный код на С++. Это происходит из-за того, что определение сигналов и слотов в исходном коде программы недостаточно для компиля- ции. Сигнально-слотовый код должен быть преобразован в код, понятный для компилятора С++. Код сохраняется в файле с прототипом имени тос_<Г11епате>.срр. Внимание! Созданные тос-файлы не стоит включать с помощью команды препроцессора #1пс1ибе "тахп.тос" в конец основного файла. Например: #1пс1иде <ОЪ(Эи1> 1ПЬ та1п(1пЬ агдс, сЪаг** агду) { ОАррИсаЬхоп арр(агдс, агду); геЬигп арр.ехес(); } #1пс1иде "тахп.тос" Лучше, если они будут отдельно откомпилированы и подсоединены компонов- щиком к основной программе. Хотя при написании демонстрационных про- грамм этим правилом можно пренебречь, чтобы разместить весь код в одном файле таю.срр. Если вы работаете с файлами проекта, то о существовании МОС можете и не догадываться, ведь в этом случае управление МОС автоматизировано. Для создания тос-файла "вручную" можно воспользоваться следующей ко- мандой: тос -о ргос.тос ргос.Ъ После ее исполнения, МОС создаст дополнительный файл ргос.тос. Для каждого класса, унаследованного от ооьзесе, МОС предоставляет объект класса, унаследованного от омесаоьзесь. Объект этого класса содержит ин- формацию о структуре объекта, например, сигнально-слотовые соединения, имя класса и структуру наследования.
Компилятор ресурсов ЯСС Почти любая программа так или иначе обращается к сторонним ресурсам, таким как растровые изображения, файлы перевода и т. д. Это не является дос- таточно надежным и эффективным способом, так как эти ресурсы могут быть удалены или недоступны по каким-либо другим причинам. Это, несомненно, может отразиться на правильной работе программы, ее внешнем облике и работоспособности. Компилятор ресурсов предоставляет возможность вне- дрения таких файлов в исполняемые модули, для того чтобы приложение по- лучало доступ к требуемым ресурсам в процессе его исполнения. Существу- ют специальные соглашения об именовании, благодаря которым можно однозначно обращаться к таким ресурсам. Все необходимые для использова- ния файлы (ресурсы)' должны быть описаны в специальном файле с расширением цгс (р1 Кекоигсе СоПесбоп, коллекция ресурсов (ЗД вместе с их путями. Это описание производится в нотации ХМЬ. Например: <!ПОСТУРЕ КССхКСС Vе^з^оп="1.0"> <дгёзоигсе> <ГИе>11падез/ореп.рпд</111е> < {. 11е> йпадез / ди! С. рпд< / !11е> </дгезоигсе> </КСС> Файл нашего примера будут подвергнут анализу компилятором ресурсов — утилитой тсс, для создания из файлов ореп.рп§ и циП.рп§ одного исходного файла С++, содержащего все их данные, которые будут компилироваться и компоноваться вместе с остальными файлами проекта. Все данные ресурса хранятся в файле С++ в виде одного большого массива. Такой подход дает уверенность в том, что необходимые ресурсы всегда дос- тупны, что поможет избежать проблем неправильной установки необходи- мых для исполняемой программы файлов. Сам же цгс-файл должен быть ука- зан в рго-файле в секции кезоцксез, для того чтобы цшаке учел информацию из файла ресурса. Например: КЕЗОЦКСЕЗ = ыпадез.дгс Для того чтобы воспользоваться файлом ореп.рп§, а точнее, представленным в нем растровым изображением, можно поступить следующим образом: р1Ъ1->зеЪР1хшар(0Р1хтар(":/хтадез/ореп.рпд"));
Структура 01-проекта Мы рассмотрели утилиты для (^-проекта по отдельности. Теперь давайте соберем их все вместе, чтобы понять, как они взаимодействуют. Структура проекта ()1 очень проста, помимо файлов исходного кода на С++ обычно имеется файл проекта с расширением рго. Из этого файла вызовом цтаке создается таке-файл. Этот таке-файл содержит в себе все необходимые ин- струкции для создания готового исполняемого модуля (рис. 3.3). Проектный файл Исходные файлы Файлы ресурсов Компиляция 2рос_Я1е.оЬ| Л1е.оЬ] | | дгс_Н1е.оЬ] к Объектный код —Г- 1-------------т- Компоновка ШГ4К Исполняемый модуль Л1е.ехе Рис. 3.3. Схема создания исполняемого модуля В таке-файле содержится вызов МОС для создания дополнительного кода С++ и необходимых заголовочных, файлов. Если проект содержит цгс-файл, то будет также создан файл С++, содержащий данные ресурсов. После этого все исходные файлы компилируются С++ компилятором в файлы объектного кода, которые объединяются компоновщиком Нпк в готовый исполняемый МОДУЛЬ.
Методы отладки Нет программ без ошибок и дефектов (Ьи§, баг). В процессе разработки про- грамм часто возникают проблемы с их обнаружением. Разработчикам прихо- дится тратить немалую часть рабочего времени на то, чтобы найти и устра- нить их. К средствам, помогающим снизить их количество, можно отнести: □ предоставление исходного кода для просмотра другим разработчиками (соде геу»е\у); □ создание классов для автоматизированных тестов, подробно описанных в главе 46. Ошибки можно минимизировать, но, в любом случае, полностью их избежать не удастся, и если в вашу программу вдруг закрался коварный баг, то самым первым средством, помогающим в нелегком труде его поиска, будет отлад- чик. Роль отладчика заключается в предоставлении оболочки, в которой можно отслеживать изменение данных во время выполнения программы, благодаря чему можно узнать, почему созданная вами программа ведет себя не так, как вы это задумывали. Благодаря платформонезависимости 01 разра- ботчик может для отладки своих программ использовать любой из полюбив- шихся ему отладчиков, например, СЭВ или отладчик, встроенный в М1сго8оГ1 У1$иа1 8Шс1ю. Если вы еще серьезно не сталкивались с процессом отладки программ, то рекомендую начать с отладчика без графического пользова- тельского интерфейса, поскольку подача команд в диалоговом, режиме по- может вам понять работу отладчика как такового и в будущем по достоинст- ву оценить отладчики, обладающие графическим интерфейсом. Поэтому мы подробнее разберем отладчик ОВВ, доступный как для ^Упккнуя, так и для Ьтих и Мас 08 X. Примечание Инсталляционный файл 60В для М!п6УУ (УУ|Пс1оууз) прилагается на компакт- диске, идущем вместе с книгой, см. приложение С. Отладчик СЮВ (6Ш ОеЬиддег) ОВВ — это самое привычное средство для отладки программ в ОС Пшх. Ра- бота с этим отладчиком обычно производится из командной строки, хотя можно воспользоваться и оболочками, предоставляющими возможность ра- боты с этим отладчиком в интерактивном режиме, вот некоторые из них: ХХОЭВ, ВВП и КВВО. Пожалуй, самой идеальной средой для работы с от- ладчиком в интерактивном режиме является ЕВЕ, в этом случае все необхо- димое находится "под рукой". Если вы не собираетесь работать с отладчиком
напрямую и предпочтете использовать ЮЕ, то дальнейшее описание ОВВ можете пропустить. Но если вы хотите разобраться в процессе отладки, то давайте создадим программу, заведомо содержащую проблемный код. ИКЙЙЭЩиЖ Ьид() { 1ПЪ п = 3; 1ПЁ* рп = &п; // Ошибка! де!еСе рп; хпЪ тахпО { Ьид(); геЪигп 0; } В листинге 3.1 из функции шахпо производится вызов функции ЬидО, в которой создается и инициализируется переменная п, а после присвоения указателю рп ее адреса, вызовом оператора де1есе производится попытка освобождения памяти, используемой переменной п. Так как память не была выделена динамически, эта операция неизбежно приведет к ошибке. Откомпилируем программу с параметром -д, чтобы в исполняемый файл бы- ла включена информация, необходимая для отладчика: д++ -д Ьид.срр -о Ьид Примечание В данном случае я не стал создавать традиционного для 01 рго-файла и огра- ничился вызовом компилятора напрямую. Здесь это проще. В случаях же с рго- файлами 01, для того чтобы получить исполняемый файл с включенной отла- дочной информацией, переменная рго-файла соыркз должна содержать зна- чения (ЗеЬид или (ЯеЬид_апс1—геХеазе. Отладчик можно запустить следующим образом, указав имя программы, предназначенной для отладки: дс!Ь Ьид.ехе
Примечание Кроме имени программы, в 60В можно дополнительно передавать и соге- файл, сгенерированный операционной системой после аварийного завершения программы. Это очень удобно, так как можно не загружать программу на вы- полнение в отладчик, а найти проблемное место при помощи соге-файла. К со- жалению, ОС \Л/1пс1о\л/8 не генерирует подобных файлов, поэтому в дальнейшем будут описаны приемы работы с отладчиком 60В, применимые на обёих ОС 0ЛЛп<1оуу8 и Шлих). После этого отладчик отобразит строку приглашения следующего вида: (дбЪ) Итак, давайте запустим саму программу под отладчиком. Для этого нужно ввести команду гип: (дйЬ) гип В результате мы получим сообщения, сигнализирующие о ненормальном за- вершении программы. Для того чтобы разобраться в проблеме, нужно про- смотреть стек программы. Это делается при помощи команды ^Иеге: (дйЬ) юЪеге Вывод отладчика будет примерно следующим: # 0 0x77 Г7 67се 1п □ЬхЬпзтсгГ:—а_дпате () # 6 0x00401305 1п орегаСог де1еСе() () # 7 0х004012ае хп Ьид{) () аГ Ьид.срр:5 # 8 0x0040123^ хп шахп () аЪ Ьид.срр:10 Заметьте, что функция тахп() вызвала в десятой строке функцию Ьид (), а вызов пятой строки этой функции создал проблему. С помощью команды ир можно подняться по стеку программы на определен- ное количество уровней. Давайте поднимемся на один уровень, это будет со- ответствовать функции Ьид (): (дсОэ) ир 1 Отладчик покажет следующее: # 7 0х004012ае хп Ьид() () ае Ьид.срр:5 5 де1еЁе рп; Для того чтобы узнать значение какой-либо локальной переменной функции, нужно подать команду ргхпи. Давайте проделаем это для переменной п: (дсЗЬ) ргхпЪ п В ответ отладчик покажет ее значение: $1 = 3
Установка контрольных точек (Ьгеак роти) производится в отладчике при помощи команды Ьгеак. Установим нашу точку в функции Ьид () и переза- пустим нашу программу: (д<ЗЬ) Ьгеак Ьид (дсЗЬ) гип Отладчик остановится на заданной нами контрольной точке и покажет сле- дующее: Вгеакро1пЪ 1, Ьид() () аЬ Ьид.срр:3 3 1пЬ п = 3; Для того чтобы перейти на следующую строку, воспользуемся командой пехс. Эта команда выполняет код построчно без перехода внутрь тела функ- ции: (дс1Ь) пехС 4 1П11* рп = &п; Из этого видно, что отладчик перешел с третьей строки на четвертую. Если понадобится выполнить строки кода, включая строку внутри тела функции, то для этого нужно было бы воспользоваться командой зсер. Например, мы могли бы установить контрольную точку в функции шахпО и при помощи команды зеер войти внутрь функции Ьид (). В табл. 3.2 сведены наиболее часто используемые команды отладчика. Таблица 3.2. Некоторые команды отладчика СЮВ Команда Описание ди1С Выход из отладчика Ье1р Вывод справочной информации. Если дополнительным параметром указана какая-либо команда, то выводится полная справочная ин- формация по этой команде гип Запуск программы аЪ^аск Присоединение отладчика к запущенному процессу с указанным идентификатором де^асЬ. Отсоединение отладчика от присоединенного процесса Ьгеак Установка контрольной точки. Вызов команды без параметра уста- новит точку на следующей исполняемой инструкции. В качестве па- раметра можно передавать имя функции, номер строки и смещение. Если требуется, можно указать имя конкретного исходного файла в виде <имя файла>:<номер строки> или <имя файла>:<имя функции> ЪЬгеак Аналогична команде Ьгеак с той лишь разницей, что контрольная точка будет удалена после ее достижения
Таблица 3.2 (окончание) Команда Описание с1еаг Удаление контрольной точки. Вызов команды без параметра удалит контрольную точку на следующей исполняемой инструкции. В каче- стве параметра можно передавать имя функции, номер строки и смещение. Если требуется, то можно указать имя конкретного исход- ного файла в виде: <имя файла>:<номер строки> или <имя файла>:<имя функции> де1еЁ.е Удаление всех контрольных точек сИзаЫе Отключение всех контрольных точек епаЫе Включение всех контрольных точек сопЫпие Продолжение выполнения программы. Дополнительным параметром можно указать количество игнорирований контрольной точки пех11 Выполнение следующей строки исходного кода программы. Допол- нительным параметром можно задать количество выполняемых строк зЁер Выполнение следующей строки исходного кода программы. Допол- нительным параметром можно задать количество выполняемых строк. В отличие от пехе, при вызове функции происходит вход в нее и остановка ипЪИ Продолжение выполнения программы до выхода из функции Прочие методы отладки Одним из стандартных приемов отладки является вставка в исходный код операторов вывода, что позволяет увидеть значения переменных и сравнить их с ожидаемыми значениями. Такой способ отладки часто используется разработчиками, поскольку ничего не стоит поместить эти операторы или оформить их в виде отдельного дамп-метода. В 01 примером такого подхода является метод ооьзесъ: :аишроьзесъ1п^о(), который выводит на экран ме- таинформацию объекта. 01 предоставляет макросы и функции для отладки, при помощи которых можно встраивать в саму программу различного рода проверки и вывод тес- товых сообщений. В заголовочном файле ООоЬа! содержатся определения двух макросов 0_АЗЗЕКТ() И О_СНЕСК_РТН (): □ 0_аззект () принимает в качестве аргумента значение булевого типа и вы- водит предупреждающее сообщение, если это значение не равно ъгие;
□ о_снеск_ртко принимает указатель и выводит предупреждающее сооб- щение, если переданный указатель равен 0, а это означает, что либо указа- тель не был инициализирован, либо операция по выделению памяти про- шла неудачно. Ос предоставляет глобальные функции дЦеЬид (), диагпхпд () и дкаеа1 (), ко- торые также определены в заголовочном файле ООоЬа!. Их применение по- хоже на функцию ргхпъго. Как и в ргхпего, в эти функции передаются форматированная строка и различные параметры. В Мссгозой Усаиа! §Ш<1ю вывод этих функций производится в окно отладчика, а в ОС Ыпих — в стан- дартный поток вывода ошибок. Примечание Вызов функции драъа1 () после вывода сообщения сразу завершает работу всего приложения. Если потребуется перенаправить поток вывода сообщения, нужно создать и установить свою собственную функцию для управления выводом. Устанав- ливается она при помощи функции дХпзсаИмздНапШег (). Этой функции в качестве аргумента передается адрес на функцию, управляющую сообще- ниями и имеющую следующий прототип: ^сЦрЁМздТуре Ъуре, сопзЪ сЪаг *шзд); На месте Гее должно стоять имя функции. Первый аргумент представляет собой тип сообщения, принимающее одно из значений перечисления ОСМздТуре: ОСОеЬидМзд, ОГИагпхпдМзд ИЛИ 0екаЪа1Мзд. Второй аргумент — это указатель на само сообщение. Для облегчения процесса отладки рекомендуется присваивать всем объектам имена. Таким образом, эти объекты можно будет всегда найти, вызвав метод ООЬэесе: :оЬзесънаше(). Это позволит в процессе работы программы вос- пользоваться методом ооъзесс: :аишроьзесып1о(), который позволяет ото- бразить внутреннюю информацию объекта. Также для отладки можно воспользоваться установкой фильтра событий для объекта класса осогеАррИсаЫоп, в этом случае данный фильтр будет яв- ляться наипервейшим объектом, получающим и обрабатывающим события всех объектов приложения (см. главу 15). Самый простой способ операции вывода в — это использование объекта класса ооеЬид. Этот объект очень напоминает стандартный объекта потока вывода в С++ соие. Например, вывести сообщение в отладчике или на консо- ли с помощью функции доеЬид () можно следующим образом: дОеЪидО « "ТезЬ";
Эта функция создает объект класса потока опеЬид, передавая в его конструк- тор упомянутый выше аргумент ©ыэеЬидМзд. Можно было бы, конечно, по- ступить и так: ОБеЬид {0 СВеЬидМзд) « " Тез ; Но, как вы видите, предыдущая строка выглядит более компактно, поэтому рекомендую пользоваться именно ей. Глобальные определения 01 01 содержит в заголовочном файле ООоЬа1 некоторые макросы и функции, которые могут быть очень полезны при написании программ. Шаблонные функции дмах(а, Ь) идм1п(а, Ь) используются для определе- ния максимального и минимального из двух переданных значений. 1пЬ п = дМах<1п1:>(3, 5); // п = 5 хпе п = дМ1п<1пЬ>(3, 5); // п = 3 Функция дАЬг (а) возвращает абсолютное значение: хпЬ п = дАЬз(-5); // п = 5 Функция дкоипа () округляет передаваемое число до целого: 1пС п = дКоипа(5.2); // п = 5 1ПЬ п = дКоипд(-5.2); // п = -5 В табл. 3.3 приведен список типов 01, которые можно использовать при про- граммировании. Таблица 3.3. Таблица целых типов О( Тип СП Эквивалент С++ Размер дхпЪ8 зхдпес! сЪаг 8 бит дихп!:8 ипзхдпес! сИаг 8 бит дхпС1б зНогЪ 16 бит дихпС16 ипзхдпес! зЪогЪ 16 бит дхп!:32 хп1: 32 бит дихпС32 ипзхдпес! хпЪ 32 бит дхп!:64 1опд, хп!:б4 ИЛИ 1опд 1опд 64 бит дихпСб4 ипзхдпес! 1опд, ипзхдпес! хпСб4 или ипзхдпес! 1опд 1опд 64 бит д1опд1опд То же самое, что и дхпЬ64 64 бит ди1опд1опд То же самое, что и дихп^б4 64 бит
Как видно из табл. 3.3, самым спорным является тип д!пеб4 и ди!пеб4, да- вайте проверим правильность указанных для них в таблице значений бит, а заодно их минимальные и максимальные значения: дЭеЬид() « •’ЫшпЬег ЫЪз Ьэг д1п^64 =" << (зхгео^(д!пЁб4) * 8); дОеЬид() « *М1п1шит о!: д1п1:б4 = -” « ~(~ди!пСб4(0) » 1); дЭеЬид() « "Маххшиш о€ д!п1:64 = " « (~ди!пЬб4(0) » 1) ; дПеЬид() « "ЫитЬег о€ ЫЪз Еог ди!п1:64 =" « (812ео1: (ди1пЬ64) * 8) д!)еЬид() « "М1п1тит ди!п!:64 = " « 0; дОеЬид() « "Маххтит ди1п€б4 =" « ~ди1п^64(0); Результат выполнения: ИшпЬег оЕ ЫЬз 1ог д!пЪ64 = 64 Мйпжлп д!пСб4 = -9223372036854775808 Махйпиш о! д!пеб4 = 9223372036854775807 ЫшпЬег оЕ ЫЬз Еог ди1пЬ64 = 64 Мхпйпшп оЕ ди1пС64 = 0 Махйпиш о€ дихпЬ64 = 18446744073709551615 Резюме В этой главе мы узнали, как выглядит типичный проект с использованием 0( и какие процессы протекают "за кулисами" при создании готового исполняе- мого программного модуля. Мы ознакомились с утилитой цгпаке, берущей на себя всю работу по созда- нию из файлов проекта таке-файлов для любой платформы. Мы узнали под- робнее о препроцессоре МОС, который при запуске создает дополнительный код поддержки сигналов и слотов, рассмотрели возможности для отладки 01- программ, а также специальные макросы, предназначенные для этих целей. Также мы провели небольшой экскурс в глобальные определения 01 и рас- смотрели методы отладки 01-программ и возможность применения СОВ.
Глава 4 Библиотека контейнеров "Ты Дерево, Животное или Минерал?" Льюис Кэрролл. "Алиса в Зазеркалье" Одна из самых распространенных задач в программировании заключается в организации обработки групп элементов. Реализация и отладка программ с использованием подобного рода структур отнимает у разработчиков много времени, так как они вынуждены решать одни и те же задачи в каждый раз. Для решения этой проблемы библиотека контейнеров предоставляет набор часто используемых классов, на правильную работоспособность которых можно положиться. Это позволяет разработчику сконцентрироваться на реа- лизации самого приложения и не вникать в детали реализации используемых контейнерных классов. <21 предоставляет библиотеку контейнеров, именуемую ТиПр, являющуюся составной частью ядра <21, поэтому ее понимание очень важно для дальней- шего изучения. Данная библиотека не только очень похожа на 8ТЬ (81апс1агс1 Тетр1а1е ЫЬгагу, стандартная библиотека шаблонов), но и совместима с ней. В данном случае, разработчик может свободно выбирать, чем ему лучше вос- пользоваться— ТиПр или 8ТЬ. Предпочтительнее выбрать первую, так как ТиПр является частью (Д и активно используется в ней. Второй аргумент в пользу применения ТиПр — эта библиотека оптимизирована по производи- тельности и расходу памяти в соответствии со спецификой классов 01. Нелишне будет отметить, что использование в программах шаблонных клас- сов, на основе которых построены контейнеры, заметно увеличивает размер исполняемых программ. Это связано с тем, что в каждом объектном файле реализации класса, использующего контейнеры, находится созданный ком- пилятором код контейнеров с нужным типами, и этот код может повторяться. ТиПр создавалась с учетом этих обстоятельств и оптимизирована для замет- ного уменьшения размера объектного кода.
Реализация классов ТиПр расположена в модуле Р^Соге. В основе ТиНр (как и в 8ТЬ) лежат три понятия — контейнерные классы (контейнеры), алгоритмы и итераторы. Их взаимосвязь показана на рис. 4.1. Рис. 4.1. Взаимосвязь контейнеров, итераторов и алгоритмов Контейнерные классы Контейнерные классы — это классы, которые в состоянии хранить в себе элементы различных типов данных. Почти все контейнерные классы в (^1 реализованы как шаблонные, и, таким образом, они могут хранить данные любого типа. Основная идея шаблона состоит в создании родового класса, который определяется при создании объекта этого класса. Классы контейне- ров могут включать целые серии других объектов, которые, в свою очередь, тоже могут являться контейнерами. Очень важно правильно понимать отличия разновидностей контейнеров, для того чтобы правильно подобрать контейнер для конкретного случая. От этого в значительной степени зависит скорость работы кода и эффективность ис- пользования памяти. 01 предоставляет две категории разновидностей классов контейнеров: последовательные ($едиепсе соп(ашег8) и ассоциативные (азвоаайуе сопЫшегк). Последовательные контейнеры — это упорядоченные коллекции, где каждый элемент занимает определенную позицию. Позиция зависит от места его вставки. К последовательным контейнерам относятся: вектор (уесЮг), список (1181). стек ($1аск) и очередь (диеие). Соответственно, содержит пять клас- сов этой категории: □ оуесеог<т> — вектор; □ оЫзс<т> — список;
□ оыпкеаыас<т> —двусвязный список; □ озеаск<т> — стек; □ 00иеие<Т> — очередь. Ассоциативные контейнеры — это коллекции, в которых позиция элемента зависит от его значения, то есть после занесения элементов в коллекцию по- рядок их следования будет задаваться их значением. К ассоциативным кон- тейнерам относятся: множество ($еС). словарь (тар) и хэш (ЬакЬ). Классы этой категории: □ 05ес<т> — множество; □ ОМар<к,т> — словарь; □ 0Ии1Ымар<к, т> — мультисловарь; □ ОНааИ<К,Т>—хэш; □ 0Ми1ЫНаз11<К, Т> — МуЛЬТИХЭШ. Во всех контейнерах этих двух групп доступны операции, перечисленные в табл. 4.1. Обратите, пожалуйста, внимание на некоторые исключения для класса озеь<т>. Таблица 4.1. Операторы и методы, определенные во всех контейнерных классах Оператор, метод Описание == И ! = Операторы сравнения, равно и не равно Оператор присвоения [] Оператор индексации. Исключение составляет только класс озес<т>, в нем этот оператор не определен Ьед1п() и сопБЁВедхпС) Методы, возвращающие итераторы, установленные на начало последовательности элементов контейне- ра. Для класса озеъ<т> возвращаются только кон- стантные итераторы епс1() И сопзЪЕпсЦ) Методы, возвращающие константные итераторы, ус- тановленные на конец последовательности элемен- тов контейнера с1еаг() Удаление всех элементов контейнера хпзегЪ() Операция вставки элементов в контейнер гетере () Операция удаления элементов из контейнера зхге() И соипС() Оба метода идентичны — возвращают количество элементов контейнера, но применение первого пред- почтительно, так как соответствует 8Т1_
Таблица 4.1 (окончание) Оператор, метод Описание Vа1ие() Возвращает значение элемента контейнера. В 05еъ<т> этот метод не определен етрСу () И хзЕтрЁу () Возвращают Ёгие, если контейнер не содержит ни одного элемента. Оба метода идентичны, но применение первого предпочтительно, так как со- ответствует ЗТЬ Примечание Важно не забывать о том, что классы, унаследованные от ооЬдесс, не имеют доступного конструктора копирования и оператора присваивания, так как они находятся в секции рггуаЬе. Следовательно, их объекты не могут храниться в контейнерах, поэтому нужно сохранять в контейнерах не сами объекты, насле- дуемые от ооьзесъ, а указатели на них. Осторожно! Если вам нужно проверить, содержит ли контейнер элементы или нет, то не ис- пользуйте для этого метод вхаеО, а используйте метод ешрЪуО. Причина проста: метод етр&у () для всех контейнеров выполняется с постоянной слож- ностью, а зхге () может потребовать для контейнера списка линейных затрат, что может существенно снизить скорость вашего алгоритма. Итераторы Наверняка вам потребуется перемещаться по элементам контейнера. Для этих целей предназначен итератор. Итераторы позволяют абстрагироваться от структуры данных контейнеров, то есть если в какой-либо момент вы ре- шите, что применение другого типа контейнера было бы гораздо эффектив- нее, то все, что вам нужно будет сделать, — это просто заменить тип контей- нера нужным. На остальном коде, использующем итераторы, это никак не отразится. предоставляет два стиля итераторов: □ итераторы в стиле 1ауа; □ итераторы в стиле 5ТЬ. В качестве альтернативы существует вариант обхода элементов при помощи ключевого слова ГогеасЪ.
Итераторы в стиле Зауа Итераторы в стиле 1ауа очень просты в использовании. Они были разработа- ны специально для программистов, не имеющих опыта с контейнерами 8ТЬ. Основным их отличием от последних является то обстоятельство, что они указывают не на сам элемент, а на его двух соседей. То есть в начале итера- тор будет указывать на положение перед первым элементом контейнера и, с каждым вызовом метода пехь () (см. табл. 4.2), будет перемещать указатель на одну позицию вперед. Но на самом деле итераторы в стиле )ауа представ- ляют собой объекты, а не указатели. Их использование в большинстве случа- ев делает код более компактным, чем при использовании итераторов в стиле 8ТЬ: 0ЫаЬ<05Ьг1пд> ИзС; ИзС « ’ШЬЫл ТевпрЬаЫоп* « "АпиЬхз" « ’МапЪиз*; <21.1зЫЬегаЬ.ог<'Э5Ьг1пд> 1С(ИзС); мЫ1е (И:. ЬазЫехЬ ()) { дОеЬидО « "Е1етепС:" « 1С.пехЬ(); } В табл. 4.2 указаны методы класса оыаыьегаъог. Эти методы также приме- нимы И ДЛЯ 0Ь1пкес1Ь1зЫЬегаСог, ОУесСогТЬегаЬог, ОНазЫЬегаЬог, 0Мар1- гегаьог. Эти итераторы являются константными, а это значит, что изменение значений элементов, их вставка и удаление не возможны. Таблица 4.2. Методы оЫзыьегаъог Метод Описание ЁОГГОПЪ() Перемещает итератор на начало списка ЁоВаск() Перемещает итератор на конец списка ЪазЫехЪ() Возвращает значение 1гие, если итератор не нахо- дится в конце списка пехЪ() Возвращает значение следующего элемента списка и перемещает итератор на следующую позицию реекЫехЪ() Просто возвращает следующее значение без изме- нения позиции итератора 11азР^еV^ои5 () Возвращает значение (те, если итератор не нахо- дится в начале списка р^еV^оиз() Возвращает значение предыдущего элемента списка и перемещает итератор на предыдущую по- зицию
Таблица 4.2 (окончание) Метод Описание реекР^еV^оиз{) Просто возвращает предыдущее значения без из- менения позиции итератора ЁхпдЫехС(сопз е Т&) Поиск заданного элемента в прямом направлении ^^пс^Р^еV^ои8 (сопзЪ& Т) Поиск заданного элемента в обратном направлении Если необходимо производить изменения в процессе прохождения ите- ратором элементов, то для этого необходимо воспользоваться изменяющи- мися (тЩаЫе) итераторами. Их классы называются аналогично, но с до- бавлением "Ми1аЫе": ОМиЬаЫеЫзЫЪегаъог, ОМиЬаЫеНазЫЪегаЪог, ОМиЬаЫеЫпкедЫзЫЪегаЪог, ОМиЬаЫеМарТСегаЬог И ОМиЪаЫеУесСоПСегаЬог. Метод гешоуеО удаляет текущий элемент, а 1пзегь() производит вставку элемента на текущую позицию. При помощи метода зеьУа1ие () можно при- своить элементу другое значение. Давайте присвоим элементу списка "ВопеуМ" значение "Ко1Пп§ Зюпез": 2Ь13Ь<08Сг1пд> ИзС.; ИзС « "ВеаС.1ез" « "АВВА" « "ВопеуМ"; 0МиЬаЫеЫзЫС.егаСог<03С.г1пд> 1Ъ(11зС); здЬИе (1Г .ИазМехЬ ()) { (зХ.пехЬО == "ВопеуМ") { 1Ъ.зеЬУа1ие("КоШпд ЗЕопез"); } дЭеЬид() « .реекргеухоиз(); } Основным недостатком итераторов в стиле 1ауа является то, что их использо- вание, как правило, заметно увеличивает объем созданного объектного моду- ля, в сравнении с использованием итераторов стиля 8ТЬ, которых мы сейчас и разберем. Итераторы в стиле 8Т1_ Итераторы ₽ стиле 8ТЬ немного эффективнее итераторов 1ауа-стиля и могут быть использованы совместно с алгоритмами 8ТЬ. Пожалуй, для разработчи- ков на С++ это самый привычный тип итераторов. Подобный итератор мож- но представить как некоторый обобщенный указатель, ссылающийся на эле- менты контейнеоа.
Вызов метода ЬедхпО из объекта контейнера возвращает итератор, указы- вающий на первый его элемент, а вызов метода епао возвращает итератор, указывающий на конец контейнера. Обратите внимание: именно на конец контейнера, а не на последний элемент, то есть на позицию, на которой мог бы быть помещен следующий элемент. Другими словами, этот итератор не указывает на элемент, а служит только для обозначения достижения конца контейнера (рис. 4.2). ( Ьед1п() | Г текущая позиция } [ епд() | Рис. 4.2. Методы ЬедХп (), епД () и текущая позиция Операторы ++ и — объекта итератора производят перемещения на следую- щий или предыдущий элемент, соответственно. Доступ к элементу, на кото- рый указывает итератор, можно получить при помощи операции разымено- вания *. Например: 0УееХог<03Ьг1пд> уес; уес « *1п ЕхЬгето" « "В1аскпюге'з ИхдЪс" « "СиЗХиз Регох"; 0УесЬог<05Ъг1пд>:: дХегаЬог = уес.ЬедхпО; Гог (; != уес.еп<5(); ++1С) { дПеЬидО « "ЕХетепЬ:" « *1Г; ) На экране будет отображено: Е1етепЬ: "1п ЕхЬгето" ЕХетепЪ: "ВХаскшоге'з ЫхдЪЬ" ЕХетегХ: "СиХСиз Гегох" Обратите внимание, что для увеличения итератора дх в цикле-используется операция преинкрементации, то есть ++дх. Это позволяет избежать, при каж- дом витке цикла, сохранения старого значения, как скрытно делается в ин- крементации, что делает цикл более эффективным. При прохождении элементов в обратном порядке при помощи оператора — необходимо помнить, что он не симметричен с прохождением при помощи оператора++. Поэтому цикл должен в этом случае выглядеть следующим образом: 2УесЪог<08Ы1пд>: :1ХегаЬэг = Vес.епс^(); Сог (;1С != Vес.Ьедхп();) {
—лХ; дОеЬидО « "Е1еп>епЬ:" « *1Ь; } На экране будет отображено: ЕХетепЬ: "СиЗХиз Регох” ЕХетегХ: " ВХасктоге ‘ з МдЫ: ” ЕХетепЬ: "Хп Ех^гето” Если вы собираетесь только получать значения элементов, не изменяя их, то го- раздо эффективнее будет использовать константный итератор сопзи_1иегаиог. При этом нам нужно будет пользоваться (вместо Ьед1п() и епдо) методами сопзьведхп() и сопзЪЕпдО. Таким образом, наш пример примет следую- щий вид: 0УесЁог<03ЁГ1пд> Vес; уес « "Хп ЕхЪгешо" « "ВХасктоге'з Ы1дЫ:" « "СиХЬиз Регох”; 0\7есеог<03ег1пд> : : сопзЁ_И:ега1:ог 11: = Vес. сопзЁВедхп () ; Хог (; Vес .сопзЁЕпд() ; ++11:) { дЭеЬидО « "ЕХетепС:” << *1ё; } Примечательно также то, что эти итераторы можно использовать со стан- дартными алгоритмами 8ТЬ, определенными в заголовочном файле а!§оп1Ьт. Например, для сортировки вектора, посредством 8ТЬ алгоритма зогс (), можно поступить следующим образом: 0УесСог<03Ёг1пд> Vес; Vес « "Хп Ехегето" « "ВХасктоге’з ЫхдЫ:" « "СиХЬиз Регох"; з116: : зогё ^ес.Ьед1п() , Vес. епс1 () ) ; дВеЬид(} « Vес; На экране будет отображено: ОУесСог("ВХасктоге’з ЫхдЫ:", "СиХ1:из Регох", "1п Ех^гето") предоставляет и свои собственные алгоритмы, которые мы разберем в этой главе позже. Ключевое слово ТогеасЬ Разумеется, в С++ нет такого ключевого слова, оно было создано искусст- венно, посредством препроцессора, и представляет собой разновидность цикла, предназначенную для перебора всех элементов контейнера. Этот спо- соб является альтернативой константному итератору. Например: 0ЫзЬ<03Гг1пд> ИзЬ; ИзЪ « "ЗиЬ»ау Ъо за11у" « *Каптзъе1п* « "АСЪег Гогеуег";
^огеасИ(231:г1пд з^г, Изё) { дЭеЬидО « ”Е1ешепС:" « зёг; } В ^огеаси, как и в циклах, можно использовать ключевые слова Ьгеак, сопихпие, а также вкладывать циклы друг в друга. Внимание! 01 делает копию контейнера при входе в цикл ^огеасИ, поэтому если вы буде- те менять значение элементов в цикле, то на оригинальном контейнере это ни- как не отразится. Последовательные контейнеры Последовательные контейнеры представляют собой упорядоченные коллек- ции, где каждый элемент занимает определенную позицию. Операции, дос- тупные для всех последовательных контейнеров, перечислены в табл. 4.3. Таблица 4.3. Общие методы последовательных контейнеров Оператор/метод Описание + Объединяет элементы двух контейнеров += Добавляет элемент в контейнер (то же, что и «) « Добавляет элемент в контейнер аъ() Возвращает указанный элемент Ьаск() и1азЬ() Возвращают ссылку на последний элемент. Эти мето- ды предполагают, что контейнер не пуст. Оба метода Ьаск () и 1азе () идентичны, но применение первого предпочтительнее, так как соответствует 8Т1_ сопСад-пз () Проверяет, содержится ли переданный в качестве параметра элемент в контейнере егазе() Удаляет элемент, расположенный на позиции итера- тора, передаваемого в качестве параметра ЬгопЪ() И С1Г8Ё() Возвращают ссылку на первый элемент контейнера. Методы предполагают, что контейнер не пуст. Оба метода Сгопъ () и С1гзъ () идентичны, но примене- ние первого более предпочтительно, так как соот- ветствует 8Т1_ хпдехОС() Возвращает позицию первого совпадения найденно- го в контейнере элемента, в соответствии с пере- данным в метод значением. Внимание: в контейнере 0Ь1пкеаЫзе этот метод отсутствует
Таблица 4.3 (окончание) Оператор/метод Описание 1азЫп6ехО1 () Возвращает позицию последнего совпадения най- денного в контейнере элемента, в соответствии с переданным в метод значением. Внимание: в кон- тейнере оЫпкеаЫзЁ этот метод отсутствует т1(5() Возвращает контейнер, содержащий копии элемен- тов, задаваемых начальной позицией и количеством рор_Ьаск() Удаляет последний элемент контейнера рор_СгопЬ() Удаляет первый элемент контейнера ризк—Ьаск() и аррепД() Добавляют один элемент в конец контейнера. Оба метода идентичны, но применение первого предпоч- тительно, так как соответствует 8Т1_ ризЪ_СгопИ () и рге- репсЯ () Добавляют один элемент в начало контейнера. Оба метода идентичны, но применение первого предпоч- тительно, так как соответствует 8Т1_ герЯасе() Заменяет элемент, находящийся на заданной пози- ции, значением, переданным как второй параметр Пример: 0УесИог<С5ЁГ1пд> уес; Vес.аррепд.("1п Ех^гето"); уес. аррепсЯ ("В1асктоге' з ЫхдкЬ") ; уес.аррепд(“СиЗЛиз Регох•); дРеЬидО « уес; На экране вы увидите: ОУес Ьог (•• 1п ЕхСгешо ”, “ В1асктоге ‘ з Ытдкс •', м Си1 еиз Регох") Вектор ОУес1ог<Т> Вектор — это структура данных, очень похожая на обычный массив (см. рис. 4.3). Однако использование класса вектора предоставляет некото- рые преимущества по сравнению с обычным массивом, например, можно по- лучить количество содержащихся в нем элементов или динамически изме- нить его размер. Еще этот контейнер наиболее экономично расходует память, по сравнению с другими. Рис. 4.3. Структура вектора
Для добавления элементов в конец последовательного контейнера (см. табл. 4.3) можно воспользоваться методом ризъ_ь>аск (). К элементам вектора можно обратиться как посредством оператора индексации [ ] (см. табл. 4.1), так и при помощи итератора. Например: ОУесЬогсгпО Vес; уес .риз1с_Ъаск. (10); уес.ризй_Ьаск(20); чес.ризН_Ьаск(30); дОеЬидО « чес; На консоли будет отображено следующее: 0иесЬог(10, 20, 30) Размер вектора можно задать в конструкторе при его создании. По умолча- нию только что созданный вектор будет иметь размер равный 0, так как не содержит ни одного элемента. Изменить его размер можно, либо добавив к нему элементы, либо вызвав метод гезхзе () (табл. 4.4). Таблица 4.4. Некоторые методы контейнера 0Уессог<т> Метод Описание йаЪа() Возвращает указатель на данные вектора (то есть на обычный массив) Н11() Присваивает одно и то же значение всем элементам вектора гезегуе() Резервирует память для количества элементов, в соответствии с переданным значением гез!ге{) Устанавливает размер вектора в соответствии с переданным значением СоЫзе () Возвращает объект оЫзё с элементами, содержащимися в век- торе ЁоЗСаУесСог() Возвращает объект зъб:: Vес^о^ с элементами, содержащими- ся в векторе 1 Массив байтов ОВу1еАггау Этот класс очень похож на 0Уессог<т>, но разница заключается в том, что это не шаблонный класс, и в нем допускается хранение только элементов, имеющих размер один байт. Объекты типа овуьеАггау можно использовать везде, где требуется промежуточное хранение данных. Количество элементов
массива можно задать в конструкторе, а доступ к ним получать при помощи оператора []: ОВуСеАггау агг(З); агг[О] = агг[1] = ОхГР; агг[2] = 0x0; К данным объектов класса овуседггау можно также применять операцию сжатия и обратное преобразование. Это достигается при помощи двух гло- бальных функций дСотргезз () и дипсотргезз (). Просто сожмем и разожмем данные: СВуЬеАггау а = "Тезе ОаЬа"; ОВуЬеАггау аСотргеззед = дСогпргезз(а); дЭеЬидО « дПпсотргезз (аСощргеззес!); На экране появится "Тезе Ва(а". Случается, что нужно преобразовывать бинарные данные в текстовые. На- пример, вам нужно записать растровое изображение в текст ХМЬ-файла (см. главу 41). Класс овуЪеАггау предоставляет для этих целей два метода Ъовазеб4 () и Еготвазеб4 (). Из названий методов видно, что бинарные дан- ные будут преобразовываться в формат Ва$е64. Этот формат был специально разработан для передачи бинарных данных в текстовой форме. Приведем не- большой пример, а для того чтобы проводимые преобразования были понят- ны, мы применим их к обычной строке текста: ОВуЬеАггау а = "ТезЪ БаЬа"; ОВуЁеАггау аВазе64 = а.ЪоВазе64(); дГ>еЬид() « аВазе64; На экране мы увидим "УОУхдСВЕУХКИ". Теперь проведем обратное преоб- разование при помощи статического метода Гготвазеб4 (): дОеЬидО « ОВуЬеАггау;:СготВазеб4(аВазе64); На экране появится "Тее! Эа1а". Чтобы бинарные данные занимали меньше места в текстовом файле, их можно перед кодированием в Вазе64 сжать. Массив битов ОВПАггау Этот класс управляет битовым (или булевым) массивом. Каждое из сохра- няемых значений занимает только один бит, не расходуя лишней памяти. Значения упаковываются в байты с помощью овуъедггау. Этот тип исполь- зуется для хранения большого количества переменных типа Ьоо1. Для операций с битами этот класс предоставляет методы для чтения ъезевхео и записи зеьвхсо. Наряду с этими методами существует также
оператор [ ], с помощью которого можно обращаться к каждому биту в от- дельности: ОВхЬАггау Шз(3); Ыез[0] = ЫСв[1] = Сгие; Ы^з[2] = Га1зе; Списки 0и'з1<Т>, 0ипкесН-18?<Т> Список — это структура данных, представляющая собой упорядоченный на- бор связанных друг с другом элементов. Преимущество списков перед векто- рами и очередями состоит в том, что вставка и удаление элементов в любой позиции происходит эффективнее, так как для выполнения этих операций изменяется только минимальное количество указателей, исключение состав- ляет только вставка элемента в центр списка. Но есть и недостаток — списки плохо приспособлены для поиска определенного элемента по индексу, и для этой цели лучше использовать вектор. Списки реализует класс оЫгь<т>. В общем виде данный класс представляет собой массив указателей на элементы (рис. 4.4). Рис. 4.4. Структура списка Специфические операции для списков приведены в табл. 4.5. Таблица 4.5. Некоторые методы контейнера оЫзс<т> Метод Описание тс^е () Перемещает элемент с одной позиции на другую гетоуеРхгзС() Удаляет первый элемент списка гетсгуеЬазЬ () Удаляет последний элемент списка зюар() Меняет местами два элемента на указанных позициях ЁакеАС() Возвращает элемент на указанной позиции и удаляет его ЬакеЕхгзе() Удаляет первый элемент и возвращает его ЪакеЬазС() Удаляет последний элемент и возвращает его
Таблица 4.5 (окончание) Метод Описание СоЗеС{) Возвращает контейнер озеи<т> с данными, содержащимися в объекте оЫзе<т> созсаыес () Возвращает стандартный список 8ТЬ аса:: 11зе<т> с элемен- тами, содержащимися в объекте оЫзе<т> СоУесСог() Возвращает объект вектора 0УесСог<т> с элементами, содер- жащимися в объекте 0Ызс<т> Если вц не собираетесь изменять значения элементов, то, из соображений эффективности, не рекомендуется использовать оператор индексации []. Вместо этого лучше будет воспользоваться методом ас (), так как этот метод возвращает константную ссылку на элемент. Одна из самых распространенных операций — обход списка для последова- тельного получения значений каждого элемента списка. Например: ОЫзЪсхпО ПзЬ.; Пае « 10 « 20 « 30; 07а1иеЫзе<1пе>::хЬегаеог = ПзС.ЬедхпО ; «Ы1е (1е != ПзС.епдО) { дГЭеЬидО « "Е1еапепС:" « *хЬ; ++1С; } На консоли будет отображено следующее: Е1етепЬ:10 Е1апепЪ:20 Е1етепе:30 Если вы работаете с большими списками и/или вам часто требуется вставлять элементы, то эффективнее будет использовать двусвязные списки 0Ыпкеаызе<т>. Хотя этот контейнер расходует больше памяти, чем 0Ызе<т>, как видно из его структуры (рис. 4.5), зато операции вставки и уда- ления сводятся к переопределению четырех указателей, независимо от пози- ции удаляемого или вставляемого элемента. Рис. 4.5. Структура двусзязного списка
Стек О81аск<Т> Стек реализует структуру данных, работающую по принципу ЫРО (Ьа$11п Нга! Ощ, последним пришел, первым ушел), то есть из стека первым удаля- ется элемент, который был вставлен позже всех остальных (рис. 4.6). прмш*л Рис. 4.6. Принцип работы стека Класс озиаск<т> представляет собой реализацию стековой структуры. Этот класс унаследован от класса 2Уесеог<т>. Процесс помещения элементов в стек обычно называется проталкиванием (ри$Ып§), а извлечение из него верхнего объекта — выталкиванием (рорщд). Каждая операция проталкива- ния увеличивает размер стека на 1, а каждая операция выталкивания — уменьшает на 1. Для этих операций в классе <23Саск<т> определены методы ризь () и рор (). Метод сор () возвращает ссылку на элемент вершины стека. Следующий пример демонстрирует использование класса стека. 03Саск«23Сг1пд> зек; зек.ризк("Ега"); зек.ризЪССогуиз Согах"); зек.ризк("СаЬЪегёпд"); угЪНе (’ . етрЪу ()) { дВеЬидО « "Е1етепС: * « зек.рорО; }
На консоли будет отображено следующее: Е1етепЁ: "ЗаеЪег1пд" Е1етеп11: "Согуцз Согах" Е1етепЁ:"Ега" Очередь СЮиеие<Т> Очередь реализует структуру данных, работающую по принципу ЯРО (Ри8( 1п Рий ОиС, первым пришел, первым ушел), то есть из очереди удаляется не последний вставленный элемент, а тот, который был вставлен в очередь раньше всех остальных (рис. 4.7). Реализована очередь в классе <2<Эиеие<т>. который унаследован от <2Ызг:<т>. Рис. 4.7. Принцип работы очереди Следующий пример демонстрирует принцип использования очереди: 00иеие<05Сг1пд> дие; дие.епдиеие("Ега"); дие. епдиеие ("Согтиз Согах”) ; дие. епдиеие ("СаеНегхпд”) ; теИПе (’ дие. етрСу ()) {
дСёЬид () « ” Е1етепГ: ” «дие. дедиеие (); } На экране должно появиться: Е1ешепГ:"Ега" Е1егпепГ: "Согуиз Согах” Е1ешепГ:" (За Лег 1пд" Ассоциативные контейнеры Задача ассоциативных контейнеров заключается в сохранении соответствий ключа и значения. Это позволяет обращаться к элементам не по индексу, а при помощи ключа. Для всех контейнеров этого типа (за некоторыми ис- ключениями для контейнера <23ее<т>) доступны методы, перечисленные в табл. 4.6. Таблица 4.6. Общие методы ассоциативных контейнеров Метод Описание сопигпз () Возвращает значение ггие, если контейнер содержит элемент с заданным ключом. Иначе возвращается значение Га1зе егазе() Удаляет элемент из контейнера в соответствии с переданным итератором ^1пд() Осуществляет поиск элемента по значению. В случае успеха возвращает итератор, указывающий на этот элемент, а в случае неудачи итератор указывает на епб () 1пзегГМи1Г1() Вставляет в контейнер новый элемент. Если элемент уже при- сутствует в контейнере, то создается новый элемент. Данный метод отсутствует в 05еъ<т> хпзегГ() Вставляет в контейнер новый элемент. Если элемент уже при- сутствует в контейнере, он замещается новым элементом. Дан- ный метод отсутствует в 05ег<т> кеу () Возвращает первый ключ в соответствии с переданным в этот метод значением. Данный метод отсутствует в 03ес<т> кеуз() Возвращает список всех ключей, находящихся в контейнере. Данный метод отсутствует в 05еС<т> Гаке() Удаляет элемент из контейнера в соответствии с переданным ключом и возвращает копию его значения. Данный метод отсут- ствует в 03ес<Т> ипхГе() Добавляет элементы одного контейнера в другой Vа1ие5() Возвращает список всех значений, находящихся в контейнере
Словари ОМар<К,Т>, ОМиШМар<К,Т> Словари, в принципе, похожи на обычные, используемые в повседневной жизни. Они хранят элементы одного и того же типа, индексируемые ключе- выми значениями. Достоинство словаря в том, что он позволяет быстро по- лучать значение, ассоциированное с заданным ключом. Ключи должны быть уникальными (рис. 4.8), за исключением мультисловаря, который допускает дубликаты (рис. 4.9). |Ндду|+496313221871 |Кип1+49631322181 | |еогио|+496313221861 +49631322000 +49631322010 Рис. 4.9. Мультисловарь |Квш*|+49631322181 | | Ндду +49631322187 +49631322999 Рис. 4.8. Словарь В контейнеры этого типа заносятся элементы вместе с ключами, по которому их можно найти, которыми могут выступать значения любого типа. В случае со словарем <2Мар<к,т> необходимо следить за тем, чтобы не было занесено двух разных элементов с одинаковым ключом, ведь в таком случае один из этих элементов невозможно будет отыскать. Каждый ключ словаря омар<к,т> должен быть уникален. При создании объекта класса рмар<к,т> нужно передать его размер в конст- руктор. Этот размер не является, как это принято в других контейнерных классах, размером, ограничивающим максимальное количество элементов, а представляет собой количество позиций. Количество позиций должно быть больше количества элементов, ожидаемых для хранения, иначе поиск элементов в словаре будет проводиться недостаточно эффективно. Жела- тельно, чтобы это значение относилось к разряду простых чисел, так как в этом случае размещение элементов будет более удобным (см. приложение Б). Таблица 4.7 содержит некоторые из методов класса омар<к, т>.
Таблица 4.7. Некоторые методы контейнера $мар<к, т> Метод Описание 1отеегВоипс1 () Возвращает итератор, указывающий на первый элемент с задан- ным ключом еоЗСдМар() Возвращает стандартный словарь 8Т1_ с элементами, находящи- мися в объекте дмар<т> иррегВоипд() Возвращает итератор, указывающий на последний элемент с за- данным ключом Одним из самых частых способов обращения к элементам словаря является использование ключа в операторе []. Но можно обойтись и без него, так как ключ и значение, можно получить с помощью методов итератора кеу () и уа1ие (), например: 2Мар<05Пг 1пд, 05Ьг1пд> шарРЪопеЪоок; тарРИопеЬоок [”Р1дду"] = ”+49 631322187"; трРЪопеЪоок[ "КегшХ"] = "+49 631322181"; тарРкопеЬоок["Ооп2о"] = "+49 631322186"; <ЭМар<05Сг1пд, 251:г1пд>: :1ЪегаСог = шарРЪопеЬоок. Ьедхп {) ; Еог (; 1 (: ! = тарРИопеЬоок. епд (); ++1 (:) { дВеЬид () « "Ыате:" « дх. кеу () « я Ркопе:" « 1Ъ^а1ие(); } На консоли будет отображено следующее: Ыате:0оп2О Ркопе:+49 631322186 Ыате:КеппдХ РЪопе:+49 631322181 Ыате:Р1дду Ркопе:+49 631322187 Особое внимание следует обратить на использование оператора [], который может использоваться как для вставки, так и для получения значения элемента. Но надо быть осторожным, так как задание ключа, для которого элемент не существует,, приведет к тому, что элемент будет создан. Чтобы избежать этого, нужно проверять существование элемента, привязанного к ключу. Подобную проверку можно осуществить при помощи метода сопСахпез (). Например: И(тарРЪопеЬоок.сопЪагпз("Кегли.С")) { дОеЬидО « "РЬопе:" « тарРИопеЬоок[”Кегли.Ь“]; } На практике случается так, что нужно внести сразу несколько телефонных номеров для одного и того же человека, например его домашний, рабочий
и мобильный телефоны. В этом случае обычный словарь омар<к,т> уже не подходит. Для этого нам будет необходимо воспользоваться мультисловарем 0ми1и1мар<к, т>. Подобный пример показан на рис. 4.9, давайте снабдим его программным кодом и узнаем телефонные номера для "Р1§§у": 0Ми11:1Мар<С51:г1пд, 231:г1пд> шарРЪопеЬоок; тарРЬопеЬоок.хпзегС ("КегпиЛ", "+49 631322181"); тарРкопеЬоок.1пзегС("Оопзо”, "+49 631322186"); тарРИопеЬоок. тпзегЬ("Оопзо", "+49 631322000й.) ; тарРкопеЬоок.тпзегЬ("Сопго", "+49 631322010"); тарРЪопеЬоок. 1пзег1: ("Рхдду” , "+49 631322187"); тарРкопеЬоок.хпзегЪ("Рхдду”, "+49 631322999"); 0Ми1Ъ1Мар<С31:г1пд, 05^г1пд>: :1ЬегаЁ.ог зЛ = тарРкопеЬоок. С 1пс1 (" Рхдду") ; Сог (; ! = тарРИопеЬоок.епсК) && зЛ.кеуО == "Рхдду"; ++1Х) { д!)еЪид() « 1е^а1ие() ; ) Хэш и 0На8(1<К,Т>и ОМиШНаз11<К,Т> Функциональность хэшей очень похожа на омар<к,т>, с той лишь разницей, что вместо сортировки по ключу, этот класс использует хэш-таблицу. Это позволяет ему осуществлять поиск ключевых значений гораздо быстрее, чем это делает омар<к, т>. Так же, как и в случае с <2Мар<к, т>, следует соблюдать осторожность при ис- пользовании оператора индексации [], так как задание ключа, для которого элемент не существует, приведет к тому, что элемент будет создан. Поэтому важно проверять существование элемента, привязанного к ключу при помо- щи метода сопса1пъв () контейнера. Если вы намереваетесь разместить в оназъ<к,т> объекты собственных клас- сов, то вам необходимо будет реализовать оператор сравнения == и специа- лизированную функцию дназь () для вашего класса. Вот пример реализации оператора сравнения: 1пНпе Ьоо1 орегаСог== (сопзЬ МуС1азз& тс1, сопз1: МуС1азз& тс2) { ге<1игп (тс1. ^хгзЁЫате () == тс2 . ИгзЫйате () && тс1. зесопдЫате () ~ тс2 . зесопдЫате ()
Функция дНазИ () должна возвращать число, которое должно быть уникаль- ным для каждого находящегося в хэше элементе. Например: 1пИпе ихпс дНазЪ(сопаС МуС1азз& тс) { геЬигп дНазЪ(тс.ЕхгзСНате()) Л дНазИ(тс.зесопсМате()); } Класс оми1Ыназ11<к, т> унаследован от оназь<к,т>. Он позволяет размещать значения с одинаковыми ключами и, в целом, похож на оми1Ымар<к,т>, но учитывает специфику своего родительского класса. Методы, присущие толь- ко для этих контейнеров, указаны в табл. 4.8. Таблица 4.8. Некоторые методы контейнеров оназЪск, г> и 0Ми1е1назЬ<к,Т> Метод Описание сарае!Ьу () Возвращает размер хэш-таблицы гезезгуе () Задает размер хэш-таблицы здиееге() Уменьшает объем внутренней хэш-таблицы для уменьшения ис- пользуемого объема памяти Множество О5е1<Т> Как заметил немецкий математик Георг Кантор, "Множество — это есть многое, мысленно подразумеваемое нами как единое". Это "единое", в кон- тексте ТиНр, есть не что иное, как контейнер <эзее<т>, который записывает элементы в некотором порядке и предоставляет возможность очень быстрого просмотра значений и выполнения с ними операций, характерных для мно- жеств, таких как объединение, пересечение и разность. Необходимым усло- вием является то, что ключи должны быть уникальны. Класс озес<т> базиру- ется на использовании хэш-таблицы оназъ<к,т>, но является вырожденным ее вариантом, так как с ключами не связываются никакие значения. Главная задача этого класса заключается в хранении ключей. Контейнер озеъ<т> можно использовать в качестве неупорядоченного списка для быстрого по- иска данных. Пример множеств показан на рис. 4.10, на кфором изображены два множества, состоящие из трех элементов каждое. Операции, которые можно проводить с множествами, проиллюстрированы на оис. 4.11.
Рис. 4.10. Два множества Рис. 4.11. Некоторые операции над множествами Создадим два множества и запишем в них элементы в соответствии с рис. 4.10. 05еЁ<05Ъг1пд> зеЪ1; 05е€<05Ьг1пд> зеЁ2; зе^Х « "ТНегХоп" « "ЫХдкЬ^ХзЬ" « "ХапсЗгХа"; зеЁ2 « "МапЪиз" « "Надагд" « "ТЬегХоп"; Произведем операцию объединения (см. рис. 4.11) этих двух множеств, а для того чтобы элементы множеств остались неизмененными, введем промежу- точное множество зеСКезиХъ: 05ее<05Ёг1пд> зеСЯезиХС = зеЫ; зеЁЯези11:.ипИ:е(зе^2) ; дПеЬидО « "Объединение = " « зеЬКезиХС. ёоЫзП (); На экране должно быть показано следующее: Объединение = ("ХапдгХа", "НадагсГ, "Мап Сиз", "МХдЪСюХзЪ", "ТНегХоп") Теперь произведем операцию пересечения (см. рис. 4.11): зеСКезиХС = зеЪ1; зеСКезиХС.ХпСегзесС(зеС2); дБеЪидО « "Пересечение зе^Х с зе^2 = " « зеГКееиХ!;. ЁоЫзЪ ();
Поскольку два множества имеют только один одинаковый элемент, то на эк- ране мы увидим: Пересечение зеЪ1 с зее2 = ("ТИегхоп") И последняя операция, которую мы произведем, будет операция разности двух множеств (см. рис. 4.11): зеЁНезиЗХ = зеЫ; зеГКезиЗХ.зиЬЁгасЁ(зе^2); дГ)еЬид() « "Разность зеЁ1 с зеЪ2 = " « зееКези1С.1х>ЫзС() ; Множество зе^1 отличается от множества зее2 двумя элементами, поэтому на экране должно отобразиться: Разность зее1 с зеЪ2 = ("ХапсЗгха”, "ЯНдЫдохвИ") В табл. 4.9 сведены операции для контейнера 05ее<т>. Таблица 4.9. Некоторые методы контейнера ^5е^:<т> Метод Описание хпСегзесЬ() Удаляет элементы множества, не присутствующие в переданном множестве гезегуе() Задает размер хэш-таблицы здиееге{) Уменьшает объем внутренней хэш-таблицы для уменьшения ис- пользуемого объема памяти зиЬСгасЁ() Удаляет все элементы множества, присутствующие в переданном множестве ёоЫзС () Возвращает объект контейнера оЫзс<т>, содержащий элементы из объекта контейнера дзе(:<т> ипаЛе () Объединяет элементы множеств Алгоритмы Алгоритмы определены в заголовочном файле С^А^огпЬтз и предоставляют операции, применяемые к контейнерам, например: сортировка, поиск, преоб- разование данных и т. д. Следует отметить, что алгоритмы реализованы не в виде методов контейнерных классов, а в виде шаблонных функций, что по- зволяет использовать их как для любого контейнерного класса ТиПр, так и для обычных массивов. Например, для копирования элементов из одного массива в другой можно, задействовать алгоритм дсору (): ОЗЪгхпд Vа1ие8[] = {"Хапдгха", "ТЪегхоп", "КхдЫэтхзЪ”, "НаддагсР}; сопзё хпЬ п = зхгео^^аХиез) / зхгео^ (ОЗЪгхпд);
ОЗСгЗ-пд соруОЕ\7а1иез [п] ; дСору(уа1иез, уаХиез + п, соруОСУа1иез); При копировании контейнеров важно убедиться, что целевой контейнер име- ет размер, достаточный для размещения копии. В нашем примере мы позабо- тились о том, чтобы целевой контейнер имел такой же размер, как контей- нер-источник. В табл. 4.10 перечислены все предоставляемые алгоритмы Тийр. 01 предо- ставляет только самые основные алгоритмы, но если их вдруг окажется недостаточно, всегда можно воспользоваться алгоритмами 8ТЬ. Таблица 4.10. Алгоритмы Алгоритм Описание дВхпагуРхпс! () Двоичный поиск заданных значений дСору() Копирование элементов, начиная с первого дСоруВаскадагй() Копирование элементов, начиная с последнего дСоипЁ() Подсчет элементов контейнера дПе1еЁеА11() Удаление всех элементов. Необходимо, чтобы элементы контейнера не были константными указателями дЕдиа1() Сравнение. Необходимо, чтобы для размещенных объектов был определен оператор == дШ1() Присваивает всем элементам контейнера заданное значе- ние дПпсК) Поиск заданных значений дЬоюегВоипб () Нахождение первого элемента со значением, большим либо равным заданному дПррегВоипд() Нахождение первого элемента со значением, строго боль- шим заданного д5ог1: () Сортировка элементов дЗЬаЫеЕогЪ () Сортировка элементов с сохранением порядка следования равных элементов дЗюар() Перемена двух значений местами Сортировка Сортировку осуществляет функция-алгоритм дзогьо (табл. 4.10). Для сор- тировки необходимо, чтобы к типам элементов контейнера можно было при- менить операторы сравнения, так как они необходимы для принятия решения
самим алгоритмом. Например, для дзъгЫд эти операторы доступны. Произ- ведем сортировку для списка с элементами озъгхпд: 0ЫзЪ<05Ъг1пд> ИзЪ; Изё « "Ю1ёЫп ТеплрЁаЁхоп" « "АпиЫз" « "Мапеиз"; дЗогЫНзЁ) ; дБеЬидО « "ЗогЪед 1151=" « ИзЬ; На экране мы увидим следующее: Зог^ед 11511= ("АпиЫз", "МапЪиз", "МИзЫп ТеалпрЁаЫоп") Поиск За поиск элементов отвечает функция-алгоритм дПпао (см. табл. 4.10). Эта функция возвращает итератор, установленный на первый найденный элемент, или на епа (), если элемент не найден. 0ЫзЪ<081:г1пд> Изё; 1131: « мМ1ёЫп ТетрЪаЫоп" « "АпиЫз" « "МапЁиз" ; 0ЫзЪ<05Ъг1пд>:: зХега^ог 1Ъ = дГ1пд(113Ё.Ьед1п() , ИзЁ.епсЦ), "АпиЫз"); 1^ (1Ъ != ИзЪ.епдО) { дБеЬидО « "Еоипс^" « *111; } е1зе { дИеЪидО « "Ыоё Роипд" ; } На экране появится: Еоипс1=АпиЫз Сравнение Иногда возникает необходимость в сравнении содержимого контейнеров различных типов. Это можно осуществить при помощи функции-алгоритма дЕдиаЮ (см. табл. 4.10). Как и в случае сортировки, для элементов контей- нера должны быть применимы операторы сравнения. ОЫзЁ<ОЗ^Г1пд> ИзС; 11зё « "ЮдхЫп ТетрИаЫоп" « "АпиЫз" « "МапСиз"; 0Уес1:ог<0311Г1пд> Vес(3); Vес[0) = "Ю1ЪЫп ТетрЬаЫоп"; Vес С1 ] = "АпиЫз";
уес[2] = "Мап^из”; дПеЬидО « "Едиа1=" « дЕдиа1(11зС.Ьед1п(), ИзЁ.епсЦ), Vес.Ьед^п()); На экране вы увидите: Едиа1=Ггие Если вы измените в одном из контейнеров какую-нибудь из строк, например ”МапШ8" на "МапСих", то функция дЕдиа! () возвратит значение €а1зе. Заполнение значениями В некоторых случаях может понадобиться присвоить значения элементам какой-то части контейнера. Для этих целей существует алгоритм згш () (см. табл. 4.10). Присвоим всем элементам списка значение "ВеаОеа": 0Ыз1:<05Гг1пд> Из*:; НзЪ « “МхЪЫп ТепрСаЫоп" « "АпиЫз" « "МапСиз"; дГИ1 (ИзГ.Ьед1п(), Изе.епвО , "ВеаЫез"); дйеЬидО « ИзЬ; На экране должно появиться: ("ВеаЪ1ез", "ВеаЁ1ез’', "Веае1ез") Строки Практически все приложения оперируют текстовыми данными. В реали- зован класс озсг^пд, объекты которого могут хранить строки, находящиеся в формате Пп1ссх1е, где каждый символ занимает два байта. Принцип хранения данных аналогичен классу ОУесеог, единственное отличие состоит в том, что элементы всегда относятся к символьному типу осЪаг, то есть, можно ска- зать, строка — это контейнер для хранения символов. Класс озегхпд предос- тавляет целую серию методов и операторов, позволяющих проводить со строками разного рода операции, например, соединять строки, осуществлять поиск подстрок, преобразовывать их в верхний или нижний реестр и многое другое. Строки можно сравнивать друг с другом при помощи операторов сравнения ==, ! =, <, >, <= и >=. Результат сравнения зависит от регистра символов, на- пример: 03Сг1пд зСг = "Ьо" ; Ьоо1 Ы = (зСг == "Ьо"); // Ы = Ьгие Ьоо! Ь2 = (аЬг != "ЬО"); // Ь2 = Ьгие
При помощи метода хзЕтрсуО можно узнать, не пуста ли строка. Того же результата можно добиться, проверив длину строки методом 1епдпь(). В классе озсгхпд имеются различия между пустыми и нулевыми строками, таким образом, строка, созданная при помощи конструктора по умолчанию, является нулевой строкой. Например: ОЗЁгхпд зСг1 = ОЗЁгхпд з!:г2; зЬг1.1зЫи11(); // Са1зе зёг2.1зЫи11(); // Пгие Объединение строк является одной из самых распространенных операций. Провести его можно разными способами, скажем, при помощи операторов += и + или вызовом метода аррепа (). Например: ОЗПгхпд зГг1 = "Ьо"; ОЗЪгхпд зЪг2 = "зСгхз"; 03Г:г1пд зЪгЗ = 51г1 + зЫ2; // зСгЗ = "ЬозЪгхз" зЪг1.аррепа(зЪг2); //зёг! = "БозЬгхз" Для замены определенной части строки другой класс озигхпд предоставляет метод гер1асе (). Например: ОЗСпьпд зСг = "ЬозЪгхз"; з!аг.гер1асе( "зЪгхз", "д1с"); // зег = "Бодхс" Для преобразования данных строки в верхний или нижний регистр исполь- зуются методы еоьоюег () или соОррег (). Например: ОЗ^гхид з1:г1 = “Ео5ГК13и; ОЗЬгхпд зГ:г2 = зЪг1.^оЬо«ег (); // з!:г2 = "1оз^г1з" ОЗегхпд зёгЗ - зЪг1.ЪоПррег(); // зЪгЗ = "БОЗТЕТЗ” При помощи метода зееышпО можно конвертировать числовые значения в строковые. Того же результата можно добиться вызовом статического ме- тода пишЬег (). Например: ОЗЪгхпд зЪг = ОЗСгтпд::питЬег(35.123); Аналогичного результата можно добиться также и при помощи текстового потока р1. Например: 031:г1пд зСг; 0Техе311геат(&з1:г) « 35.123; Преобразование из строкового в числовое значение производится методами, содержащими в своем имени название типа. В этих методах вторым пар^мет-
ром можно передавать ссылку на переменную булевого типа для получения информации о том, успешно ли была проведена операция. Например: Ьоо1 ок; ОБЪгхпд зСг = "234"; <ЗоиЫе <3 = зСг.ЪоБоиЫе(&ок) ; 1ПЬ и = зЪг.Ъо1пе(&ок); Строка может быть разбита на массив строк при помощи метода зрИсо класса овьгЗпдЫзс. Следующий пример создаст список из двух строк "Клп- %о" и "81аг": 05Ьг1пд зСг = "Кхпдо БЬаг"; ОЗЬгхпдЬхзЪ ИзЬ = зЬг.зрИЦ" Операция объединения списка строк в одну строку производится при помощи метода зо1п(). Например, объединить список из двух элементов ("К1П§о" и "8(аг1") в одну строку, разделив их знаком пробела, можно следующим образом. зЬг = ИзС.зо1п(" "); Регулярные выражения Для работы с регулярными выражениями (^1 предоставляет класс окедЕхр. Регулярные выражения — это мощное средство анализа и обработки строк. Они содержат в себе шаблон, предназначенный для поиска в строке. Это по- зволяет быстро и гибко извлекать совпавший с шаблоном текст. Но следует заметить, что работа с регулярными выражениями производится медленнее методов, определенных в классе озсгхпд, и поэтому их применение должно быть обоснованным. Таблица 4.11 содержит основные шаблонные символы, поддерживаемые классом окедЕхр. Таблица 4.11. Шаблоны регулярных выражений Символ Описание Пример Любой символ а.Ь $ Должен быть конец строки АЬс$ [] Любой символ из заданного набора [аЬс] - Определяет диапазон символов в группе [ ] [0-9А-2а-а] Л В начале набора символов означает любой сим- вол, не вошедший в набор (лае{] ★ Символ должен встретиться в строке ни разу или несколько раз А*Ь
Таблицами (окончание) Символ Описание Пример + Символ должен встретиться в строке минимум 1 раз А+Ь ? Символ должен встретиться в строке 1 раз или не встретиться вообще А?Ь {П) Символ должен встретиться в строке указанное число раз А{3}Ь {П,} Допускается минимум п совпадений а{3,)Ь {<п} Допускается до п совпадений а{,3}Ь {п,т} Допускается от п до ш совпадений а{2,3}Ь I Ищет один из двух символов ас | Ьс \ь В этом месте присутствует граница слова а\Ь \в Границы слова нет в этом месте а\ва ( ) Ищет и сохраняет в памяти группу найденных символов (аЬ|ас)ад \а Любое число \о Все,кроме числа \з Любой тип пробелов \5 Все, кроме пробелов Любая буква, цифра или знак подчеркивания Все, кроме букв \А Начало строки \ъ Целое слово \в Не слово \2 Конец строки (совпадает с символом конца строки или перед символом перевода каретки) \2 Конец строки (совпадает только с концом строки) Для того чтобы найти один из нескольких символов, нужно поместить их в квадратные скобки. Например [аЬ] будет совпадать с а или ь. Чтобы не пи- сать все символы подряд, можно указать диапазон, например [а-2] совпадает с любой буквой в верхнем регистре, [а-г] — с любой буквой в нижнем реги- стре, а [0-9] — с любой цифрой. Можно совмещать такие записи, например, [а-г? ] будет совпадать с любой буквой в нижнем регистре и с цифрой 7.
Также можно исключать символы, поставив перед ними знак Л. Например [ Ло-9] будет соответствовать всем символам, кроме цифр. Указанные в табл. 4.11 величины в фигурных скобках называются предела- ми. Пределы позволяют точно Задать количество раз, которое символ должен повторяться в тексте. Например, а{4,5} будет совпадать с текстом, если бук- ва а встретится в нем не менее 4, но не более 5 раз подряд. Например, в сле- дующем отрывке заданно регулярное выражение для 1р-адреса, им можно воспользоваться, например, для того, чтобы проверить строку на содержание в ней 1р-адреса: ОКедЕхр гед("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"); ОЗЫхпд зЬгССЫз 1з ап 1р-аск1гезз 123.222.63.1 1еЬз сЬеск 1Ъ") ; дЭеЬидО « (зЬг.сопЬагпз(гед) > 0); // Ьгие Обратите внимание, что для указания символа точки в регулярном выраже- нии перед ним стоит обратная косая черта (\), а в соответствии с правилами языка С++ для ее задания в строке она должна удваиваться. Если бы косой черты не было, то точка имела бы в соответствии с табл. 4.11 значение "лю- бой символ", и регулярное выражение распознавало бы, например, строку "1г2уЗх4", как 1р-адрес, что, разумеется, не правильно. Шаблоны можно комбинировать при помощи символа |, задавая ветвления в регулярном выражении. Регулярное выражение с двумя ветвями совпадает с подстрокой, если совпадает одна из ветвей. Например: ОКедЕхр гхр("(.сош|.ги)"); хпЬ п1 = гхр.1пдех1п("««».Ыте’.ги"); // п1 = 7 (совпадение на 7-й позиции) 1пС п2 = гхр.1пдех1п("*лм.Ыту.де’); // п2 = -1 (совпадений не найдено) Указанные в табл. 4.11 символы с обратной косой чертой (обратным слэшем) позволяют значительно упростить регулярные выражения. Например, регу- лярное выражение [а-гА-го-9_] идентично выражению \зд. Произвольный тип ОУапап1 Объекты класса оуагхапь могут содержать данные разного типа, включая контейнеры. К этим типам относятся: 1пь, ипзхдпей 1пь, аоиЫе, Ьоо1, фЗЬгхпд, <Э5Ьг1пдЫзЬ, ОТшаде, ОРххшар, ОВгивЬ, 0Со1ог, ОКедЕхр И Др. Важно учитывать то обстоятельство, что частое использование этого типа может отрицательно отразиться на скорости программы и эффективности использо- вании памяти, а также может заметно снизить читабельность самой програм- мы. Поэтому объекты класса ОУагхапе не следует использовать без особой на то необходимости.
Для создания объектов класса ОУаггапь необходимо передать в конструктор переменную нужного типа. Например: ОУагхапС VI(34); ОУагхапе ч2(Сгие); ОУагхапЬ ч2("ЬозСгтз"); Метод суре () позволяет узнать тип записанных в объекте ОУагхапс данных. Этот метод возвращает целочисленный идентификатор типа. Чтобы преобра- зовать его в строку, следует передать его в статический метод суретоыате (). Того же результата можно добиться и вызовом метода суреыате (), который возвращает информацию о Типе в виде строки: ОУагхапС V(5.О); дОеЬидО « ОУагхапС:: СуреТоЫаше (V. Суре ()); // =><ЗоиЫе Чтобы получить данные из объекта 0Уаг1апс нужного типа, существует се- рия специальных методов сот {), где т — это имя типа. Метод сот () создает новый объект типа т и копирует данные из объекта ОУагхапс в нужный объ- ект. Например: ОУагхапЪ V2(23); хпП а = V2.ЪоIпС() + 5; // а = 28 Примечание Ввиду того, что ОУаггапс реализован в СИСоге, соответствующих методов СОТ () ДЛЯ классов 0Со1ог, ОТгпаде и <2Р1хтар и др., находящихся в модуле О1би1, не предоставляется. Вместо методов сото для приведения нужному типу можно использовать также шаблонный метод Vа1ие<т> (). Наш пример с преобразованием объек- та ОУагхапс к целому типу можно преобразовать следующим образом: ОУагхапЪ V2(23); 1п1: а = V2.Vа1ие<^пе>() + 5; //а = 28 Модель общего использования данных Из соображений эффективности во многих классах (^1 избегается копирова- ние данных, вместо этого используется ссылка на эти данные (см. рис. 4.12). Этот принцип получил название общее использование данных (зЬагей дай). В 0( используется модель неявных общих данных. В этой модели вызов кон- структора копирования или оператора присваивания не приведет к копирова- нию данных, а только увеличит счетчик ссылок на эти данные на 1. Соответ- ственно, при удалении элемента счетчик ссылок уменьшится на 1. Если значение счетчика ссылок становится равным 0, то данные уничтожаются.
Копирование данных происходит только при изменениях, соответственно значение счетчика ссылок при этом уменьшается. На рис. 4.12 на первом шаге создаются два объекта, и так как данные им не были присвоены, то они оба указывают на 8кагеа_пи11 (общий ноль). На втором шаге первому объекту присваиваются данные, счетчик ссылок становится равным единице. На третьем шаге второму объекту присваивается первый объект, и они теперь оба указывают на одни и те же данные, счетчик ссылок при этом увеличивается на единицу. На четвертом шаге производится изменение данных первого объекта, что приводит к созданию для него от- дельной копии, а счетчик ссылок старых данных уменьшается на один, так как на один объект, использующий эти данные, стало меньше. Если бы мы пятым шагом изменили данные второго объекта, после создания копии для новых данных, счетчик ссылок старых данных уменьшился бы до значения О, и это привело бы к освобождению памяти и уничтожению старых данных. Шаг 1 $Ьагес1_пи11 Шаг 2 Объект 2 I $Г>агес1_пи11 1 Счетчик ссылок ШагЗ Объект 1 Объект 2 Смотчик ссылок 2 Й00111010010011001111001 Шаг4 Объект 1 I _________, 1 аороооооооооооооооооосоо | Объект2^|| 1 100111010010011001111001 ссылок Рис. 4.12. Четыре шага использования общих данных
Проиллюстрируем изображенную на рис. 4.12 ситуацию программным кодом: з^г1; //Ссылается на зИагес1_пи11 ОЗЬгхпд з!:г2; //Ссылается на зИаге<Я_пи11 зёг1 = "Новая строка” //ссылается на данные, счетчик ссылок = 1 зЬг2 = зЪг1; // зёг1 и зёг2 указывают на одни и те же данные // счетчик ссылок = 2 зёг1 += ” добавление”; //Производится копирование данных для з^г1 Резюме В этой главе мы узнали, что контейнер — это объект, предназначенный для хранения и управления содержащимися в нем элементами. Он заботится с выделении и освобождении памяти, а также отвечает за добавление и удале- ние элементов. Контейнерные классы подразделяются на последовательные и ассоциативные. К последовательным контейнерам относятся вектор, список, стек и очередь, к ассоциативным — множество, словарь и хэш. Для прохождения по элементам контейнера используются итераторы. 01 пре- доставляет итераторы в стилях 1ауа и 8ТЪ. В качестве альтернативы для про- хождения по элементам контейнера можно также воспользоваться макросом ГогеасЪ. При помощи алгоритмов можно проводить такие операции над содержимым контейнеров, как сортировка, поиск и многое другое. Класс сзьгхпд представляет собой реализацию строк и содержит целый ряд методов для проведения различного рода операций с ними. Регулярные выражения представляют собой мощный механизм для проверки строк на соответствие шаблону. Объекты класса ОУагхапе могут содержать данные разного типа, включая также и контейнеоы.
цж »иаи Часть II Элементы управления
Глава 5 С чего начинаются элементы управления "Кто скажет, где кончается одно и начинается другое?" Сунь-Цзы Практически любая программа имеет графический интерфейс пользователя (О1Л). Виджеты (\У1д§е18) — это "строительный материал" для его создания. Виджет — это не просто область, отображаемая на экране, а компонент, спо- собный выполнять различные действия, например, реагировать на посту- пающие сигналы и события или посылать сигналы другим виджетам. Рис. 5.1. Иерархия классов виджетов
рг предоставляет полный арсенал виджетов: от кнопок меню до диалоговых окон, необходимых для создания профессиональных приложений. Если вам окажется недостаточно этих виджетов, то можно создать свои собственные, наследуя классы уже существующих. Иерархия, показанная на рис. 5.1, содержит классы виджетов. В этой части находится описание большинства из них. Классы, не описанные в этой части, можно найти в других главах книги: омепи (см. главу 32), осиладеь (см. главу 23), омахпихпаом (см. главу 35), осгарк1сзУ1е\< (см. главу 21). Класс ОУУк1де1 Класс оихадес является фундаментальным для всех классов виджетов. Его интерфейс содержит. 254 метода, 53 свойства и массу определений, необхо- димых каждому из виджетов, например, для изменения размеров, местопо- ложения, обработки событий и др. Сам класс он1адее, как видно из рис. 5.1, унаследован от ооьдесс, а значит, может использовать механизм сигна- лов/слотов и механизм объектной иерархии. Благодаря этому виджеты могут иметь потомков, которые отображаются внутри предка. Это очень важно, так как каждый виджет может служить контейнером для других виджетов, то есть в (^1 нет разделения между элементами управления и контейнерами. Виджеты в контейнерах могут выступать в роли контейнеров для других виджетов, и так до бесконечности. Например, диалоговое окно содержит кнопки ОК и Сапсе! (Отмена), следовательно, является контейнером. Это удобно еще и потому, что если виджет-предок станет недоступным или неви- димым, то виджеты-потомки автоматически примут его состояние. Виджеты без предка называются виджетами верхнего уровня (1ор-1еуе1 \У1(1§е<8) и имеют свое собственное окно. Все виджеты без исключения могут быть виджетами верхнего уровня. Позиция виджетов-потомков внутри вид- жета-предка может изменяться методом зеьсеотеЬгуО "вручную" или авто- матически, с помощью специальных классов компоновки (1ауоЩ$) (см. главу 6). Для отображения виджета на экране вызывается метод зЪою(), а для скрытия — метод ыае (). Примечание Не забывайте, что после создания виджета верхнего уровня, чтобы показать его на экране, нужно вызвать метод зНоад(), иначе и его окно, и виджеты- потомки будут невидимыми. Класс омхйдеъ и большинство унаследованных от него классов имеют конст- руктор с двумя параметрами: ОйИадеЪ (ОШддеЁ* рмдС = О, ОС: :М1пйо™г1адз = 0)
Из определения видно, что не обязательно передавать параметры в конструк- тор, так как они равны нулю по умолчанию. А это значит, что если конструк- тор вызывается без аргументов, то созданный виджет станет виджетом верх- него уровня. Второй параметр ос: :И1пдо«Р1адз используется для задания свойств окна, и с его помощью можно управлять внешним видом окна и ре- жимом отображения (чтобы окно не перекрывалось другими окнами и т. д.). Чтобы изменить внешний вид окна, необходимо во втором параметре конст- руктора передать значения модификаторов, объединенные с типом окна (рис. 5.2) побитовой операцией | (ИЛИ). Аналогичного результата можно добиться вызовом метода зеси1пао«г1адз (). Например: «дЪ.зеС.И1пдамГ1адз (ОЬ: :Н1пйои | ОС: :НгпдоиТ1ЫеН1п1: | ОЪ:хИхпдомЗЬаузОпТорНхпЪ); Примечание Значение ос: :И1паоггёеауг0пторН1пс не изменяет внешний вид окна, а лишь рекомендует, чтобы окно всегда находилось на переднем плане и не перекры- валось другими окнами. На рис. 5.2 изображены некоторые из вариантов применения этих значений. Рис. 5.2. Вид окон виджетов верхнего уровня
При помощи слот-метода зеыЛпаоэдПЫеО устанавливается надпись заго- ловка окна. Но это имеет смысл только для виджетов верхнего уровня. Например: «дЬ. зеСИ1паоиТ1Ые ("Му Ихпдои" ) ; Слот зегЕпаЫеао устанавливает виджет в доступное (епаЫей) или недо- ступное (<Я$аЫес1) состояние. Параметр сгие соответствует доступному, а ^а1зе — недоступному состоянию. Чтобы узнать, в каком состоянии нахо- дится виджет, вызовите метод хвЕпаЫеа (). При создании собственных классов виджетов важно, чтобы виджет был в состоянии обрабатывать события (см. главу 14). Например, для обра- ботки событий мыши необходимо перезаписать хотя бы один из следующих методов: тоизеРгеззЕХгепЪ(), тоизеМоуеЕуепС(), тоизеКе1еазеЕуепЬ() ИЛИ тоизеВоиЫеСИск&уепЕ. (). Размеры и координаты виджета Виджет представляет собой прямоугольную область (рис. 5.3). Рис. 5.3. Виджет в области экрана (или предка) Существует целый ряд методов, с помощью которых можно узнать местона- хождение виджета и его размеры. Методы зхгеО, ИехдЪсо и здхапио воз- вращают размеры виджета. При этом если вызовы ИегдИс () и ихаьы) вернут значения высоты и ширины целого типа, соответственно, то вызов метода г!хе() вернет объект класса 051хе (см. главу 17), хранящий ширину и высо- ту виджета. Методы х (), у () и роз () служат для определения координат виджета. Первые два метода возвращают целые значения координат по осям X и У, а метод роз () — объект класса орохпс (см. главу 17), хранящий обе координаты.
Метод деотеъгуО возвращает объект класса окесъ (см. главу 17)„ описы- вающий положение и размеры виджета. Положение виджета можно изменить методом тсггеО, а его размеры — методом гезхге (). Например: рзддЁ->пкууе (5, 5); рзддЪ->гез12е(260, 330); Одновременно изменить и положение, и размеры виджета можно, вызвав ме- тод зессеотесгу (). Первый параметр этого метода задает координату левого верхнего угла виджета по оси X, второй — по оси У, третий задает ширину, а четвертый — высоту. Например, следующий вызов эквивалентен двум вы- шеприведенным вызовам тоуе () И гезхге (): рюд^->зеЪСеатеЪгу (5, 5, 260, 330); Механизм закулисного хранения Техника закулисного хранения (Васкт§ 81оге) заключается в запоминании растровых изображений для всех виджетов окна в любое время в памяти компьютера, что позволяет очень быстро помещать нужную часть хранимой области без вызова системой событий рисования (Рат1Еуеп1). При этом не играет роли, насколько сложно рисование самого виджета. Вызов события рисования для виджета производится только в тех случаях, когда это дейст- вительно необходимо, например, для изменения фона. Эта техника позволяет значительно повысить производительность. Виджет имеет новое свойство аиъокшваскдгоипа, которое по умолчанию равно Га1зе. То есть все потомки виджета не заполняются фоном, значит, они невидимы. Установив это свойство равным ъгие, вы тем самым заставите виджет заполнять фон автоматически, что сделает его видимым. Например: юдЪ. зеЁАиЁоГШВаскдгоипб (Ъгие) ; Установка фона виджета Виджету можно задать фон, причем это может быть цвет или растровое изо- бражение. Для заполнения сплошным цветом или растровым изображением необходимо сначала создать объект палитры (см. главу 13), а затем вызовом метода зеьРаХессе () установить его в виджете. В примере, показанном на рис. 5.4, один виджет заполнен сплошным цветом, а другой — растровым изображением. 5 Зах. 3983
Рис. 5.4. Виджёты с фоном .................. ^Листинг 5.1. тайъсрр #1пс1ис1е <01:Ои1> 1ПС гоа1п(1пП агдс, сЪаг** агду) { ОАррИсаИхоп арр(агдс, агду); ОМхйдеЁ зддС ; ОЮхсЗдеЪ* рмдС1 = пею 0И13де1:(&мд1:) ; 0Ра1е<:Ёе ра11; ра11. зеЁСо1ог (р^дЪ1->ЬаскдгоипсЗКо1е (), ОЪ::Ыие) ; ридП1->зе1:Ра1ееСе(ра11); рмдЪ1->гез1ге(100, 100); рмдЁ1->тоуе(25, 25); раддС1 ->зеЁАиЪоГ111Васкдгоип(1 (1:гие); ОММдеС* рюдЬ2 = пеш дейЗдеС (&шд11) ; 0Ра1еЪ1:е ра12; ра12. зеЪВгизЪ (рюдС2 ->ЬаскдгоипдКо1е (), ОВтзИ (ОРгхтар (’’ з Сопе. з рд "))) ; рмд^2->зеЪРа1еЪ^е(ра12); рл?дЪ2->гез1хе (100, 100) ; р«дС2->тоуе(75, 75); ридС2 ->зе1АиГ1оР111Васкдгоипд (Игие) ; мде.гез12е(200, 200);
«дС.зИошО ; геСигп арр.ехес(); } В листинге 5.1 создается виджет верхнего уровня юдс, который передается двум другим виджетам (указатели р«ды и рмдег) в качестве предка. Первому виджету методом зеьРаХессе () передается объект палитры, кото- рый устанавливает в нем сплошной цвет фона (голубой). После изменения его размеров методом гез1хе() и перемещения в области виджета-предка (метод тоуеО) с ПОМОЩЬЮ метода зеСАиЬхоГШВаскгоипсН) свойству аисоршваскдгоипа присваивается значение сгие, чтобы отобразить виджет. Со вторым виджетом-потомком (указатель ршдсг) производятся те же опера- ции, что и с первым. Разница заключается в том, что во втором виджете в ка- честве фона устанавливается растровое изображение из файла 8(опе.)р§ при помощи объекта палитры ра12. Изменение указателя мыши Класс указателя (курсора) мыши дсигзог определен в файле (^Сигзог. Указа- тель представляет собой небольшую растровую картинку, информирующую пользователя о позиции мыши на экране. В зависимости от местоположения, внешний вид указателя может меняться. В большинстве случаев это стрелка, но, скажем, при попадании на границу окна он может превратиться в двуна- правленную стрелку, информируя пользователя, что размеры окна могут быть изменены. Установить изображение указателя можно методом зеьсигзого, передав ему одно из указанных в табл. 5.1 значений. Таблица 5.1. Значения сигзогзкаре пространства имен рс Значение Вид Описание АггоюСигзог Стандартный указатель стрелки, появляется поверх большинства виджетов. Служит для ука- зания, выбора или перемещения мыши ПрАггоюСиг5 ог Т Стрелка, показывающая вверх. Применение этого указателя зависит от конкретной ситуации СгоззСигзог Крестообразный указатель используется для выделения прямоугольных областей. Может появиться над любым виджетом, допускающим эту операцию
Таблица 5.1 (продолжение) Значение Вид Описание МахЁСигзог 2 Указатель ожидания, появляется над любым виджетом или позицией при выполнении опера- ции в фоновом режиме ХЬеашСигзог I 1-образный текстовый указатель (1-Ьеат сигвог) - представляет собой вертикальную линию. По- является над текстом для изменения, выбора и перемещения РохпСхпдНапдСигзог ъ Указатель в виде руки появляется над гипер- текстовыми ссылками РогЫааепСигеог О Указатель невозможности входа, появляется над объектом-приемником при проведении опе- раций перетаскивания, сигнализируя о том, что принимающая сторона не в состоянии принять перетаскиваемый объект Шта^зТЫзСигзог Указатель с вопросом, появляется поверх большинства виджетов для получения контек- стно-зависимой помощи ЗхгеУегСигзог I Указатель изменения вертикального размера окна. Появляется поверх регулируемой границы окна Зхг еНогСиг з ог ◄—► Указатель изменения горизонтального размера окна. Появляется поверх регулируемой границы окна ЗххеВВгадСигзог Указатель изменения размеров окна по диаго- нали. Появляется поверх регулируемой границы окна 312еРБ1адСигзог Указатель изменения размеров окна по диаго- нали. Появляется поверх регулируемой границы окна ЗхгеАНСигзог Указатель для изменения местоположения ок- на, сигнализирует о готовности окна быть пере- мещенным ЗрИПУСигзог Указатель изменения высоты для разделенных виджетов, появляется над границей между дву- мя разделенными виджетами. Разделение вид- жетов описано в главе 6 ЗрИЁНСигзог НН Указатель изменения ширины для разделенных виджетов, появляется над границей между дву- мя разделенными виджетами. Разделение вид- жетов описано в главе 6
Таблица 5.1 (окончание) Значение Вид Описание ОрепНапдСигзог л Указатель в виде разжатой руки сигнализирует о возможности перемещения частей изобра- жения в видимой области С1озебНапсЮигзог о Указатель в виде сжатой руки сигнализирует о готовности перемещения частей изображе- ния в видимой области В1апкСигзог Пустой указатель Пустой указатель, говорит о невозможности использования мыши Примечание Вызов статического метода ОАррИса^хоп: : зеШ^еггхдеСигзог () устанав- ливает изображение указателя для всего приложения. Это может понадобить- ся, например, для информирования пользователя о том, что приложение вы- полняет интенсивную, продолжительную по времени операцию и не в состоянии реагировать на команды. В этот момент все виджеты должны ото- бражать указатель мыши в виде песочных часов, для чего вызывается метод ОАррИсаСхоп: : зеЩуегг1аеСигзог (ОП: :Юа1Г:Сигзог). Когда приложение снова будет в состоянии выполнять команды пользователя, вызовом статиче- ского метода рАррИсаЪгоп: хгезСогеОуеггхдеСигзог () указателю мыши возвращается его прежний вид. В классе осигзог содержится метод роз (), который возвращает текущую по- зицию указателя мыши относительно левого верхнего угла экрана. При по- мощи метода зесроз () можно перемещать указатель мыши. Чтобы создать собственное изображение указателя мыши, нужны два растро- вых изображения типа ов1ыпар. Эти изображения должны иметь одинаковые размеры, а одно из них будет представлять собой битовую маску. В тех мес- тах, на которых маска будет иметь цвет со1ог1, будет нарисовано само изо- бражение указателя, а в местах, где маска будет иметь цвет со1ог0 — изображение будет прозрачно. Рис. 5.5. Использование собственного изображения для указателя мыши
Более простой способ — это использование объекта класса ОРЬапар. Про- грамма, показанная на рис. 5.5, демонстрирует эту возможность. 1пС та!п(1пЪ агдс, сИаг** аг^) ( ОАррИсаЪхоп арр(агдс, а^дV) ; ОМхбдеП «дЪ; ОРххтар рхх (" с1оск. рпд") ; ОСигзог сиг(р!х); «де.зеесигзог(сиг); «дЪ.гезхге(180, 100); «дЪ.экою(); гееигп арр. ехес (); В листинге 5.2 сначала создается виджет «де, затем— объект растрового изображения р1х, в конструктор которого передается имя рп§-файла, пред- ставляющего собой растровое изображение и битовую маску (подробное описание этого формата и возможностей класса ОРххтар можно найти в главе 19). Для создания указателя мыши объект растрового изображения передается в конструктор класса осигзог и с помощью метода зеесигзог () устанавливается в виджете. Стек виджетов Класс озеаскедНтйдее унаследован от класса оргаше и представляет собой виджет, который показывает, в отдельно взятый промежуток времени, только один из своих потомков. Виджеты добавляются в стек при помощи метода аааихадеь (). Он принима- ет указатель на виджеты и возвращает присвоенный виджету идентификаци- онный номер. Удаление виджетов из стека производится вызовом метода гепюуешадеъ (), в который передается указатель на виджет. Передавая указатель на виджет в слот зеесиггепежадеь () или идентифика- ционный номер виджета в слот зеесиггепыпаех (), вы делаете его видимым.
Идентификационный номер виджета можно узнать вызовом метода 1пйехог (). В этот метод передается указатель на виджет. Рамки Класс ОРгате унаследован от класса оихадее и расширяет его возможностью отображения рамки. Этот класс является базовым для большого числа клас- сов виджетов (см. рис. 5.1). Стиль рамки может быть разным, и устанавлива- ется он с помощью метода зеьРгатезсуХе (), которому передаются флаги формы и флаги теней рамки. Значения соединяются друг с другом побитовой операцией | (ИЛИ). Существуют три флага теней (табл. 5.2): ОРгате: :На1зей, ОРгате: : Р1ахп и оггаше: :зипкеп. С их помощью достигается эффект вогнутости или выпук- лости рамки. Для задания внешнего вида рамки можно воспользоваться одной из пяти основ- ных форм (табл. 5.2): ОРгате: :Вох, ОРгате: :Рапе1, ОРгате: :И1пРапе1, ОРгате: :НЫпе, ОРгате: :УЬ1пе. ЕСЛИ нужно, Чтобы рамка вообще не ОТОбража- лась, то тогда в метод эеЪРгатезъуХе () передается значение ОРгате: :КоРгате. Таблица 5.2. Примеры рамок Флаги Вид Флаги Вид Вох | Р1Э1П 1' цц| НЫпе | Р1а1п НИМ* Вох | Кахзед Г НЪхпе | На1вед —** Вох ' | Зипкеп □ НЬхпе | Зипкеп Рапе! | Р1ахп у*™"! . УЫпе | Р1а1п Рапе! | Нахзей 1 УЫпе | Кахзед 1 Рапе1 | Зипкеп УЬхпе | Зипкеп И1пРапе1 | Р1аз.п ЗСу1едРапе1 | Р1а1п И1пРапе1 | На13ед ЗПу1едРапе1 | Нахзед - МхпРапе! | Зипкеп ЗЪуХедРапе! | Зипкеп Г \ , 3
Методом зеьсопсепьзмагд1п() класса оихадеь устанавливается расстояние от рамки до содержимого виджета, а методами зесЫпеИхасъ () и зеьмхаьхпеихасы) можно изменять толщину самой рамки. ОРгате р!пп = пей ОРгате; р^гт->зеЬРгатеЗСу1е(ОЕгате::Вох | ОРгате::3ипкеп); р^гт->зеЬЫпеК1ас11(3) ; В этом примере создается виджет рамки, в котором методом зеергашезьу1е () устанавливается нужный стиль рамки, а методом зеьыпешаьъ () — ее толщина. Виджет видовой прокрутки Базовый класс для вйдовой прокрутки 0АЬзегассзсго11Агеа унаследован от класса ОРгате и предоставляет собой окно для просмотра только части ин- формации. Сам виджет видовой прокрутки реализует класс озсгоИАгеа. Этот виджет может размешать виджеты потомков, а если хотя бы один из них вылезет за границы окна просмотра, то автоматически появляются вер- тикальная и/или горизонтальная полосы прокрутки. С их помощью можно перемещать части виджета в область просмотра. Если вы хотите, чтобы по- лосы прокрутки были видны всегда, то нужно передать в методы управления поведением полос значение <2е= :Зсго11вагА1шаузОп. Например: 05сго11Агеа за; за.зеШог12оп1:а13сго11ВагРо11су (ОЪ: :5сго11ВагА1юаузОп); за.зеЁУегЁ1са15сго11ВагРо11су(0^::5сго11ВагА1адаузОп) ; Как видно из рис. 5.6, виджет видовой прокрутки является совокупностью сразу нескольких виджетов, работающих вместе. Указатели на эти виджеты можно полу- чить обозначенными на рисунке методами. Получить доступ к виджету области просмотра можно посредством метода ОАЬзегассзсгоИАгеа: ^хездрогь (). Ме- тоды Vе^^^са1зс^о11ва^ о и Иог12опьа1зсго11ваг () возвращают указатели на виджеты вертикальной и горизонтальной полосы прокрутки (класса 03сго11ваг), соответственно. Метод согаегихадеъ () возвращает указатель на виджет, находящийся в правом нижнем углу. Установить виджет в виджете видовой прокрутки можно при помощи метода зеснхадес (), передав в качестве параметра указатель на устанавливаемый виджет. Эта операция автоматически сделает переданный виджет потомком виджета области просмотра. Указатель установленного виджета всегда мож- но получить методом «хадес (). Удаление виджета из озсгоИАгеа произво- дится вызовом метода гетс^есъиа ().
|уегйса18сго11Ваг() ЯИ^^-|согпегУУ|с1де1() ...| ' |Г|оп2оп1а18сго11Ваг() Рис. 5.6. Структура виджета видовой прокрутки Применение видовой прокрутки иллюстрирует программа, показанная на рис. 5.7, позволяющая перемещать части изображения в видимую область окна. Рис. 5.7. Пример виджета видовой прокрутки ..................................?............. Г Ли<п>нг5.31>\Файл,та1пхрр:5г ’ _____________________________________________...........________ #1пс1ис1е <(2еСих> хпЬ та1п(1пе агдс, сИаг** агду) { ОАррИсаЬхоп арр(агдс, агду); 05сго11Агеа за; ОЮХДдеЪ* рюдЪ = пею ОЮхддеЪ; рРтхтар р1х (" 1тд. дрд ”} ; 0Ра1еИ:е ра1; ра1. зе^Вгизк (р«де->Ьаскдгоип<ЗКо1е (), ОВгизИ (р!х));
рюдЪ->зеЁРа1еЁЁе(ра1); рзддС->зе1АиЁоР111Васкдгоип6 (Ъгие) ; р«де->зеСР1хе6512е (рхх. шх&Ъ (), рхх. Иехдк!: ()) ; за. зеШхдде!: (р«дЪ) ; за.гез1ге(350, 150); за.зИою(); геЪигп арр.ехес(); } В листинге 5.3 создается виджет видовой прокрутки за. Затем создается обычный виджет (указатель р«де) и объект растрового изображения рхх. Объект растрового изображения инициализируется файлом который затем устанавливается вызовом метода зесра1ессе() в качестве заднего фона виджета. Для того чтобы виджет был виден, метод зееАиеоРШваскдгоипа)) включает режим автоматического заполнения фона. Размеры виджета (указа* тель рмде) приводятся в соответствие с размерами растрового изображения вызовом метода зепкххеазхге (). Затем виджет видовой прокрутки за вызо- вом метода зесихадес () добавляет в свое окно созданный нами виджет. Резюме В центре создания пользовательского интерфейса стоит понятие виджет (элемент управления), оихадес является базовым для всех элементов управ- ления. Все, из чего в основном состоит интерфейс пользователя в приложе- ниях Ои — это объекты класса оихадеь и унаследованных от него классов. Разделение между виджетами-контейнерами и просто виджетами отсутству- ет: любой виджет может использоваться в качестве контейнера для других виджетов. К основным операциям с виджетами, помимо показа (зЬо\у) и скрытия (ЬИе), относятся методы, позволяющие изменять их размеры и рас- положение. Позиция и размеры виджетов внутри виджета-предка могут при использова- нии классов компоновки устанавливаться автоматически. Виджеты верхнего уровня имеют свое собственное окно, которое можно по- разному декорировать, например, изменять рамку окна. В каждом виджете можно устанавливать указатели мыши. предоставляем ряд предопределенных изображений, которые можно использовать для уста-
новки. Также предоставляется возможность создавать свои собственные ука- затели из растровых изображений. Класс ОРгате унаследован от омЫдее и представляет собой прямоугольник с рамкой, стиль которой можно менять. Класс огьаскеанШдеь показывает, в отдельно взятый промежуток времени, только одного из потомков. Этим свойством пользуются тогда, когда есть много виджетов, и нужно, чтобы только один из них в определенное время был видимым. Виджет видовой прокрутки предоставляет окно для просмотра только части информации. Этот виджет применяется для отображения информации, раз- меры которой превышают выделенную для нее область.
Глава 6 Управление автоматическим размещением элементов "Порядок и беспорядок зависят от ор- ганизации". Сунь-цзы Классы компоновки виджетов (ЬауоШз) являются одной из сильных сторон 91 (не надо путать компоновку виджетов и компоновку приложения — это совсем разные вещи). По сути, это контейнеры, которые после изменения размеров окна автоматически приводят в соответствие размеры и координаты виджетов, находящихся в нем. Хотя они ничего не добавляют к функцио- нальной части самой программы, тем не менее, они очень важны для внешне- го вида окон приложения. Компоновка определяет расположение различных виджетов относительно друг друга. Конечно, можно "вручную" размещать виджеты в окнах приложения, но это существенно усложняет разработку. Ведь в этом случае нужно отлавливать и обрабатывать изменение размеров окна приложения, чтобы заново упорядо- чить элементы. Подобные ситуации хорошо известны программистам на языке У18иа1 Вазгс, которые вынуждены писать для этого сложные методы. Еще один из недостатков размещения "вручную" состоит в том, что если приложение поддерживает несколько языков, то, поскольку слова в разных языках имеют разную длину, необходим механизм, который мог бы в про- цессе работы программы динамически поправлять и изменять размеры и ко- ординаты виджетов, иначе части текста на другом языке могут оказаться "от- резанными". Классы компоновки библиотеки От выполняют эту непростую работу за вас. Более того, классы компоновки могут инвертировать направ- ление размещения элементов, что может быть полезно для пишущих справа налево, например, в Японии. <3( предоставляет так называемые менеджеры компоновки, позволяющие ор- ганизовать размещение виджетов на поверхности другого виджета. Их работа
базируется на том, что каждый виджет может сообщить о том, сколько ему необходимо места, может ли он быть растянут по вертикали и/или горизон- тали и т. д. Менеджеры компоновки (1ауои1 тападегз) Менеджеры компоновки предоставляют возможности для горизонтального, вертикального и табличного размещения. Они способны управлять размеще- нием не только виджетов, но и встроенных компоновок. Это позволяет кон- струировать довольно сложные размещения. Фундаментом для всей группы менеджеров компоновки является класс ОЬауоин. Это абстрактный класс, унаследованный сразу от двух классов роьдеси и оьауоиШЁет (рис. 6.1). Этот класс определен в заголовочном фай- ле рЬауоиС. (дНВохкауощ ! Рис. 6.1. Иерархия классов менеджеров компоновки Примечание Создание своего собственного класса компоновки — явление очень редкое, так как практически все задачи размещения можно решить стандартными клас- сами размещения, предоставляемыми О!. Но если вам понадобится создать свой собственный менеджер компоновки, то можно унаследовать класс ОЬауоиЪ, реализовав методы аскИСет(), соипЪ (), зеССеотеСгу (), ЪакеА*: (} И 1ЁетАЁ, (). От оьауоин унаследованы классы оогхаьауоин и двохьауоиь (рис. 6.1). Класс ОСгхаьауоиЁ управляет табличным размещением, а от овохьауоис унаследо- ваны два класса онвохъауоич и дувохьауоис для горизонтального и верти- кального оазмешения.
По умолчанию, между виджетами остается небольшое расстояние. Это рас- стояние необходимо для их визуального разделения. Задать его можно с по- мощью метода зесзрасхпдО, передав в него нужное значение в пикселах. Методом зеемагдхп () можно установить отступ виджетов от границы ком- поновки, обычно типичными значениями могут быть 5 или 10 пикселов. Ри- сунок 6.2 иллюстрирует смысл этих методов на примере горизонтального размещения. 11 А в | с 8е1Магд}п() зе!5раС1пд() Рис. 6.2. Размещение виджетов по горизонтали При помощи метода ааашадее () производится добавление виджетов в ком- поновку, а с помощью метода аааьауоиь () можно добавлять встроенные ме- неджеры компоновки. Если понадобится удалить какой-либо виджет из ком- поновки, то следует воспользоваться методом гетоуеихадес (), передав ему указатель на этот виджет. Горизонтальное и вертикальное размещения Для горизонтального или вертикального размещения можно воспользоваться классом овохЬауоис или унаследованными от него классами онвохьауоис И рУВохЬауоиЪ. Класс ОВохЬауои! Объект класса ОВохЬауоШ может управлять как горизонтальным, так и вер- тикальным размещением. Для того чтобы задать способ размещения, первым параметром конструктора должно быть одно из следующих значений: □ Ье^ьтоНхдЬс— горизонтальное размещение, заполнение производится слева направо; □ кхдъьтоье^с — горизонтальное размещение, заполнение производится справа налево; □ тортовосъот — вертикальное размещение, заполнение производится свер- ху вниз; □ восьоттотор — вертикальное размещение, заполнение производится снизу вверх.
Этот класс расширяет класс оьауоис методами вставки на заданную позицию: виджета — хпзегшхадес (), встроенной компоновки — хпзегЪЕауоиЪ (), расстояния между виджетами — хпзегъзрасхпдо и фактора растяжения — хпзегЪЗЪгеЁск(). К компоновке при помощи метода аааврасхпд о можно добавить заданное расстояние между двумя виджетами. Класс овохьауоиъ определяет свой собственный метод ааамхадес () для до- бавления виджетов в компоновку с возможностью указания, в дополнитель- ном параметре, фактора растяжения (по умолчанию этот параметр равен ну- лю). Программа, изображенная на рис. 6.3, демонстрирует эту возможность. Рис. 6.3. Кнопки с факторами растяжений #1пс1иае <ОЁСгих> хпё шахп(хп^ агдс, сЪаг** агду) { 0Лрр1хсаНхоп арр(агдс, агду); ОМхадеЁ эддЬ; ОРизЪВиЪЪоп* решал = пем ОРизЪВиСИоп ("А”) ; ОРизИВиЬеоп* решав = пезд ОРизЪВЩХоп ("В") ; ОРизЬВиСЪоп* решас = пею ОРизЪВиЪСоп("С") ; //Ьауои^ зеЪир 2ВохЬауои1:* рЪхЬауоиГ: = пею ОВохЬауоиЪ(ОВохЬауоиЁ::Ье^ЁТоКхдШ:); рЬхЬауоиС, ->ааам1адеЁ. (решал, 1) ; рЬхЬауоие->ааам±адеГ: (решав, 2); рЬхЬауоиС->ааам1адеП (решас, 3); >?дЪ. зе^ЕауоиС (рЬхЬауоиЪ); «д!:. гезхге (450, 40);
«дС..зЬом(); геЬигп арр.ехес(); } В листинге 6.1 создаются три кнопки А, В и С, которые помещаются в компо- новку с помощью метода двохьауоие:: аааишдее (), причем во втором пара- метре этого метода указывается параметр растяжения. При создании объекта компоновки в конструктор передается параметр овохьауоие: :Ье€сток1дье, который задает горизонтальное размещение элементов слева направо. Вызов метода оихбдее:: зесьауоие () устанавливает компоновку в виджете аддс. Возможно, что приведенный в листинге 6.1 пример произведет шокирующий эффект, так как создаваемые кнопки (указатели решал, решав и решас) не имеют объекта предка, а это значит, что некому будет позаботиться об осво- бождении памяти, выделенной для этих виджетов. Так что же все-таки про- исходит? Неужели программа и вправду содержит ошибку, которая может привести к утечке памяти (тешогу 1еак)? На самом деле все в порядке, так как за присвоение виджета-предка отвечает сама компоновка. При вызове метода зесьауоие () всем помещенным в ком- поновку виджетам будет присвоен виджет предка, в нашем случае это «дь. Факторы растяжения можно самостоятельно добавлять в компоновки, для этого существует метод ааазыегси (). В этом случае фактор растяжения об- разно можно сравнить с пружиной, которая находится между виджетами и может иметь различную упругость, в соответствии с задаваемым параметром. На рис. 6.4 показан пример добавления фактора растяжения между двумя виджетами для расположения виджетов по краям окна. Рис. 6.4. Добавление фактора растяжения между виджетами А и В Программа, показанная на рис. 6.5, демонстрирует добавление фактора растяжения и представляет собой небольшую модификацию показанной на рис. 6.3 программы, где вместо одной из кнопок добавляется фактор растя- жения.
Рис. 6.5. Добавление фактора растяжения между кнопками А и В #1пс1ис1е <0ЁСи1> хпЪ та!п(1п1: агдс, сЪаг** аг^) { ОАррИсаЪхоп арр(агдс, а^дV) ; (Ж1с1дее тедЪ; ОРизИВиЪЪоп* рспкЯА = пею СРизИВи^оп ("А") ; ОРизИВиСЪоп* рспкЭВ = пею СРизЪВиСЬоп("В") ; //ЬауоиЬ зеСир ОВохЬауоиЁ* рЬхЬауоиЪ = пею ОВохЪауоиЪ (ОВохЬауоиЬ: -.ЬеСЪТоКтдИЬ) ; рЬхЬауои^->ас1сЗМ:1с1де1: (рстдА) ; рЪхЬауоиС->адс1511геЁс]1 (1) ; рЬхЬауоиС->ас1(ЗМ1адеС (рспкЗВ) ; аддС. зе!:Ьауои11 (рЬхЪауоиЪ) ; юдЬ.гезхзе(350, 40); «д11. зЪоте () ; геЬигп арр.ехес(); В листинге 6.2 создается виджет класса оихадес и две кнопки А и В. После создания объекта компоновки для горизонтального размещения в него вы- зовом метода оьауоиь:: аааихадеь () добавляется первая кнопка (указатель рстад). Затем вызов метода овохьауоис:: ааазъгеьсъ () добавляет фактор растяжения, после чего добавляется вторая кнопка (указатель решав). Классы онвохьауоиь и ОУВохЬауоиг, унаследованные от овохьауоиь, отлича- ются от него тем, что в их конструктор не передается параметр, говорящий
о способе размещения — горизонтальный или вертикальный, так как порядок размещения заложен уже в самом классе. Эти классы сами производят гори- зонтальное или вертикальное размещение, слева направо или сверху вниз. Горизонтальное размещение ОНВохЬауоШ Объекты класса онвохьауоип упорядочивают все виджеты только в горизон- тальном порядке, слева направо. Его использование аналогично использова- нию класса овохьауоис, но передавать в конструктор дополнительный пара- метр, задающий горизонтальный порядок размещения, не нужно. Программа, показанная на рис. 6.6, упорядочивает виджеты при помощи объекта класса ОНВохЬауоиЪ. ! А : ' & : С , Рис. 6.6. Размещение кнопок по горизонтали #1пс1иде <011Си1> юа1п(1пЪ агдс, сйаг** агду) { ОАррИсаЪхоп арр(агдс, агду); ОМхддеС >?дЁ; 0Риз11ВиЁЁоп* рспкЗА = пет/ ОРизИВиЪЪоп ("А") ; ОРизЪВиЪЪоп* рсгпдВ = пею ОРизИВиССоп ("В”) ; □РизЪВиЁПоп* рспкЯС = лею ОРизИВиССоп("С"); //ЬауоиЪ зеЬир СНВохЪауоиЬ* рЪЬхЬауоиЪ = пею ОНВохЬауоиС; рЪЬхЬауоиЪ->зеШагд1п (10) ; рИЬхЬауои11->зе115рас1пд (20) ; рИЬхЬауоиЁ->адсЗИ1дде1: (рспкЗА); рЪЬхЬауои^->ас!с1Ю1ддеЪ (рспкЗВ) ;
рИЬхЬауоиЬ->аск1М1с1деЪ (рсшйС) ; аддЪ. зе^ЬауоиС (рНЬхЬауоиС) ; «д<:. зИозд () ; геЪит арр. ехес () ; } В листинге 6.3 создаются три кнопки А, В и С (указатели решал, решав и решас). Затем создается объект класса днвохьауоир для горизонтального размещения дочерних виджетов. Метод оьауоие:: зеъмагд1п () устанавливает толщину рамки 10 пикселов. Метод оъауоис: :зегзрас1пд() устанавливает расстояние между виджетами равным 20 пикселам. Три вызова метода оьауоиъ:: ааамхбдеъ () добавляют виджеты кнопок в компоновку. Вертикальное размещение ОУВохЬауоиТ Компоновка отвохьауоие унаследована от овохьауоис и упорядочивает все виджеты только по вертикали, сверху вниз. В остальном она ничем не отли- чается от классов овохьауоис и онвохЪауоиь. Если заменить в листинге 6.3 имя класса онвохьауоис на ОУВохЬауоие, то в результате получится програм- ма, окно которой показано на рис. 6.7. Рис. 6.7. Размещение кнопок по вертикали Вложенные размещения Размещая одну компоновку в другой, можно создавать размещения практи- чески любой сложности. Для организации вложенных размещений существу- ет метод асМЬауоие (), в который вторым параметром передается фактор рас- тяжения для добавляемой компоновки.
На рис. 6.8 показан пример вложенного размещения двух менеджеров ком- поновки. В компоновку ОУВохЬауоин помещается компоновка онвохьауоиъ. Рис. 6.8. Вложенное размещение #1пс1иде <0ЪОи1> хпЪ гпа1п(1п1: агдс, скаг** а^дV) { 0Арр1хсаСхоп арр(агдс, агсрл); ОММдеЬ юдЪ ; ОРизИВиЁЪоп* рспкЗА = пею ОРизЪВиЪЪоп ("А”) ; ОРизИВиШзоп* рспкЗВ = пею ОРизЪВиЪЁоп ("В") ; ОРизИВиЬЪоп* рстйС = пего ОРизИВиЪ^оп(”СИ) ; ОРизИВиСХоп* рспкЗО = пем ОРизИВи^Хоп ("О") ; ОУВохЬауои!:* р^хЬауогХ = пею (ЭУВохЬауоиЬ ; ОНВохЬауоъХ* рНЬхЪауоиЪ = пе^ ОНВохЬауоиГ; рЪЬхЪауоие->зеЬМагд!п(5) ; рЪЬхЬауоиЪ->зеЁ5рас1пд(15); рИЬхЬауоиЁ->аддМ1с1деЪ (рстдС) ; рИЬхЬауоиЪ->а(1с1Ю1с1деЪ (рстсЮ) ; р^хЬауоиЪ->зеШагд±п (5); руЬхЬауои11->зе1:Зрас1пд (15) ; рVЬx^ауои^->ад<а^^<5де1: (рспкЗА) ;
рVЬx^ауои^->ас^<ам^ддее (рсзпдВ); рVЬx^ауои^->ад<2^ауои^ (р!!ЬхЬауоиЪ); юдС. зеСБауоиЁ (р^хЬауои!:) ; тедЁ:. зкоте () ; геЁ:игп арр. ехес (); } В листинге 6.4 приводится фрагмент программы, показанной на рис. 6.8. Программа будет читаться гораздо лучше, если сначала создать все виджеты, а затем объекты менеджеров компоновки, что мы и делаем. Используя метод для установки толщины рамки геъмагд1п(), мы устанавливаем ее равной пя- ти для обеих компоновок, а с помощью метода зеезрасхпд () устанавливаем расстояние между виджетами 15 пикселов, также для обеих компоновок. В горизонтальную компоновку мы добавляем виджеты кнопок решас и решал. Затем виджеты кнопок решал и решав по очереди передаются в метод ОЬауоиС: :аааи1адеЬ() вертикальной КОМПОНОВКИ р^хЬауоиЬ, после чего при помощи метода овохьауоие:: аааьауоие () в нее передается объект горизон- тальной компоновки рЪЬхЬауоие. Вызов метода оихадеЬ:: зесьауоие () уста- навливает вертикальную компоновку р^Ьхьауоие в виджете иди. Табличное размещение ООпс1Ьауои1 Для табличного размещения используется класс дегхаьауоие, с помощью которого можно быстро создавать сложные по структуре размещения. Таб- лица состоит из ячеек, позиции которых задаются строками и столбцами. До- бавить виджет в таблицу можно с помощью метода ааашадес (), передав ему позицию ячейки, в которую помещается виджет. Иногда необходимо, чтобы виджет занимал сразу несколько позиций, и этого можно достичь тем же ме- тодом ааамхадес (), указав в дополнительных параметрах количество сток и столбцов, которые будет занимать виджет. В последнем параметре можно задать выравнивание (см. табл. 7.1 в главе 7), например, по центру: р1ауои^->аддМ1с1деС (угхддеЪ, 17, 1, ОС::А1 хдпСепеег) ; Фактор растяжения устанавливается методами веькоздзыеъсь () и зессо1ишпзггепсИ (), но не для каждого виджета в отдельности, а для строки или столбца. Расстояние между виджетами также устанавливается для столб- цов ИЛИ строк методом зеСЗрасхпд (). Пример, показанный на рис. 6.9, размещает четыре кнопки А, В, С и И в таб- лице оазмеоом 2 на 2 ячейки.
Рис. 6.9. Размещение кнопок в табличном порядке #хпс1ис1е <0«5их> хпЬ тахп(хп1: агдс, сЪаг** агду) { САррИса^хоп арр(агдс, агду) ; ОЮхддеГ год!: ; ОРизЪВиГЪоп* рспкЗА = пею ОРизЪВиПГоп("А”) ; □РизкВиесоп* рстйВ = пете ОРизЪВиЁХоп {"В") ; ОРизИВиП^оп* рспкЯС = п&л СРизЪВиЬЪопС "С") ; ОРизЪВи^Ъоп* рспкЮ = пею ОРизЪВиЪСоп ("Э") ; ОСгхдЬауоиС* рдгсЗЬауоиЪ = пем ОСгхдЬауоиП; рдгсИ|ауоиЬ->зеШагд1п (5) ; рдгбЬауоиЪ->зеСЗрасхпд(15); рдг<1ЬауоиЬ->ад(ЭД1ддеЬ(рстйА, 0, 0); рдгсЗЬауоиЪ->а<1с1Мхс1деЪ (рспкЗВ, 0, 1) ; рдгсЗЬауоиЪ->асИЮхс1деП(рстйС, 1, 0); рдгдЬауоиС - >ад<ЗМх<5деС (рспкЗП, 1, 1); юдЪ.зеЪЬауоиЬ(рдгдЬауоиЬ); «дГ.зко^О ; геЪигп арр.ехес{); В листинге 6.5 создается компоновка табличного размещения рдгдьауоие. Метод зесмагдхпО устанавливает отступ от границы в 5 пикселов. Вызов метода зеьзрасхпд () установит расстояние в 15 пикселов между виджетами.
Виджеты кнопок добавляются в компоновку вызовом метода айдиМдеь (), последние два параметра которого указывают ту строку и столбец, в которой должен быть расположен виджет. Приложение, показанное на рис. 6.10, демонстрирует применение табличного размещения на примере калькулятора. В примере задействованы классы сте- ка 0Уа1ие5Саск и регулярного выражения окедЕхр, описанные в главе 4. #1пс1иде <0АррИсаС1оп> #1пс1иде "Са1си1аГог.Ъ" 1П11 ша1п(1п1: агдс, сИаг** а^дV) { ОАррИсаЁхоп арр(агдс, агд^) ; Са1си1аЪог са1си!аГюг; са1си1а1:ог. зеСМ1пс1оадТ1С1е ("Са1си1аЪог’') ; са1си1аЁог.гез12е(230, 200); са1си1аЬог.зЪою(); геСигп арр.ехес();
В программе, приведенной в листинге 6.6, создается виджет калькулятора са1си1асог (см. листинги 6.7—6.11). После изменения размера методом гезхге () вызов метода зИо™ () отображает калькулятор на экране. _Са1си1а1:ог_к_ МеСхпе _Са1си1аЬог_11_. #1пс1ис1е <0М1дде1:> #1пс1иде <03Гаск> с1азз ОЪСВЫшпЬег; с!азз ОРизкВиСГоп; // =======================================:=============================== с1азз Са1си1а1:ог : риЬИс <2М1с1де^ { 0_ОВСГЕСТ рг^а^е: ОЬСВМитпЬег * щ_р1 с<5; 251:аск<03Ъг1пд> 1Ц_зГк; 051:г1пд т_зСгР1зр1ау; риЬИс: Са1си1аСог(ОИгддеС* рюдЪ = 0); □РизЪВиССоп* сгеаГеВи^ЪогЦсопзГ 05егз.пд& зСг) ; тохс1 са1си1аЪе ( ) ; риЬИс з1оёз: Vо^с^ з1оСВШ:ЬопС11скес1() ; }; #епс11 С / /_Са1си1а1зог_к_ В определении класса Са1си1а^ог, приведенном в листинге 6.7, описываются три атрибута: т_р1са — указатель на виджет электронного индикатора, т_зск — стек для проведения операций вычисления и т_зегп1зр1ау — строка, в которую мы будем записывать символы нажатых пользователем кнопок. Метод сгеагеви1:1:оп() предназначен для создания кнопок калькуля-
тора, а метод са1си1аъе() —для вычисления выражений, находящихся в стеке т_зик. Слот з1оевиИюпС11скед() вызывается при нажатии на любую из кнопок калькулятора. ....................................................................... Са1си1аЪог: :Са1си1аЪог (СМхддеЪ* рздде/*= 0*/) : О&ЯддеЬ (ртедЬ) { т_р1сс1 = пеу; ОЬСОЫитЬег (12) ; т_р1сс1->зе113едтепЁ:31:у1е (ОЮТЫшлЬег: : Р1аЪ)»; т^1сй->зеШ1п11тлп512е (150, 50); ОСИаг аВиЪЪопз[4][4] = {{‘7’, 18’, ’9', ' {'4', '5', ’б', ’2', ’З', {'О’, ' + ’} }; //ЬауоиГ: зе11ир ОСгхдЬауоиС* рЪорЬауоиЪ = пем 0Сг1с1ЬауоиЪ; рЬорЬауои^->а6сМ1с1дее (т_р1с6, 0, 0, 1, 4) ; рЬорЬауои^->адЗМ1с1деС (сгеаЬеВиСХоп (” СЕ") , 1, 3) ; 5эг (1пГ 1 = 0; 1 < 4; ++1) { ^ог (1ПЪ 3 = 0; з < 4; ++з) { р1:орЬауоиЬ->ад(ЗМ1(1де12(сгеа!:еВи11Соп(аВиПСопз[1] [з]) , 1 + 2, з); ) } зе^Ьауои^(рЪорЬауоиЬ); При создании электронного индикатора (листинг 6.8) в его конструктор пе- редается количество сегментов, равное 12. Флаг оьспышпЬег: :г1ас, передан- ный в метод зеьзедтепсзьуХе(), задает сегментам индикатора плоский стиль. Метод зесмхпХшитзХгеО переустанавливает минимально возможные размеры виджета индикатора. В массиве ависьопз определяются надписи для кнопок калькулятора. Виджет электронного индикатора помещается в ком- поновку вызовом метода аа<аи1<3деь (), первые два параметра которого, зада- ют его расположение, а последние два — количество занимаемых им строк и столбцов табличной компоновки.
Кнопка СЕ, после своего создания методом сгеапевисгоп (), помещается в компоновку методом ааамШдеъ () на позиции (1,3) (то есть на пересечении второй строки и четвертого столбца — они нумеруются с нуля). Все осталь- ные виджеты кнопок создаются и помещаются в компоновку в цикле, с по- мощью методов сгеаЬеВиЬСоп () И аадНШдеС (). «9.'ФаМЙ&1а*огЖ>р;.Мвтод ОРизЬВи^Соп* Са1си1аЪог::сгеаееВи1:^оп(сопзЪ ОВСгхпдй зГг) ( ОРизкВиСеоп* рота = пею 0РизЪВиЫ:оп(з1хг); рстсЯ->зеШ1п1ти1п512е(40, 40); соппесЁ (решал 51(ЗЫАЬ(с11скед()) , 5Ь0Т(з1оЁВи1:ЁопС11скеа())) ; геЪигп решб; Метод сгеаъевиьъоп (), приведенный в листинге 6.9, получает строку с над- писью и создает нажимающуюся кнопку. После этого вызовом метода зеемхпхтитЗхге () для кнопки устанавливаются минимально возможные раз- меры, а сигнал сИскеао соединяется со слотом з1освиъеопсискеа() вызо- вом соппесе(). Са1си1аЁог::са1си!аЪе() ( доиЫе аорегап62 = т_з11к. рор () . ЪоОоиЫе (); ОЗЪгхпд зЪгОрегаСхоп = т_зСк.рор(); аойЫе аорегапд! = т_зСк. рор () . СоЭоиЫе () ; аойЫе аКезиИ: = 0; (зЁгОрегаЬхоп ==•’ + ") { аКези1Ъ = ЗОрегапсИ + <1Орегапс12; } (зСгОрега1:1оп =="-") { дКези1е = аорегапд! - аорегапа2; } (зСгОрегаЬхоп =="/") { с!Вези1С = аорегапд! / аорегапд2;
} (зЪгОрегаЫоп ==**") { <ЗКези1С = сЮрегапсН * <30регапс12; } щ_р1с<1->с11зр1ау (йНезиИ:) ; Назначение метода са1си1аье () (листинг 6.10) состоит в вычислении выра- жения, содержащегося в стеке щ_зек. Переменная аорегапаг получает снятое с вершины стека значение, преобразованное к типу аоиЫе. Строковая пере- менная зьгОрегаЫоп получает символ операции. Переменная аорегапаг по- лучает последнее значение из стека, которое также преобразуется к типу аоиЫе. В операторах г г символ операции сравнивается с четырьмя допусти- мыми и, в случае совпадения, производится требуемая операция, результат которой сохраняется в переменной акези1е. После этого вызовом метода а1з1ау() значение переменной акезиге отображается на электронном инди- каторе (указатель ш_р1са). тоМ Са1си1а1:ог:: з1оЬВиЁЁопС11ске6 () { 05Сг1пд зЬг = ((дРизЪВиССоп*)зепбег())->еехе(); (зег == "СЕ") { т__зек.с1еаг () ; щ_5^гВ1зр1ау = ""; т_р1са->а15р1ау("0 *); ге^игп; ) (з^г.сопЪахпз(2КедЕхр("[0-9]"))) { т_5ЁгП1зр1ау += зег; т__р1сд->сИзр1ау (т_з11гС1зр1ау. ЪоГЮиЫе ()); ) е1зе (зЪг == 1л_зЪгВ1зр1ау += з^г; т„р1сд->а1зр1ау (т_зСгО1зр1ау) ; } е!зе { 1Г (щ_з^к.соип!:() >= 2) {
т_зЁк .ризк (ОЗЬгхпд () . зеЪЫит (т_р1сс1->лга1ие () )) ; са!си1аЪе(); т_зЬк.с1еаг(); щ_зСк.ризк(рЗЪг!пд() .зе^ит(т_р1с<3-^а1ие())) 1Е (зег != "=") { т^зЬк.ризк(зЪг); } ) е1зе { т_з1:к.ризк (ОЗСгхпд() . зеШит(щ_р1сс1->лла1ие ())) ; т_зСгВ1зр1ау = ””; т_р1сс1->а1зр1ау ( " 0") ; ) } } В слоте зТогвиесопсИскеас) (листинг 6.11) производится преобразование виджета, выславшего сигнал, к типу дризквиесоп, после чего переменной зсг присваивается текст надписи на кнопке. Если надпись равна "СЕ", то произ- водится операция сброса — очистка стека и установка значения индикатора в 0. Если была нажата цифра или точка, то она добавляется в конец строки т_зСгП1зр1ау и отображается индикатором, с последующей актуализацией. При нажатии любой другой кнопки мы считаем, что была нажата кнопка операции. Если в стеке находится менее двух элементов, то отображаемое число и операция заносятся в стек. Иначе, в стек заносится отображаемое значение и вызывается метод са1си1аее() для вычисления находящегося в стеке выражения. После этого стек очищается с помощью метода с1еаг(), и в него записывается значение результата, отображаемое индикатором, и следующая операция. Если выполняется операция "=", то она не будет до- бавляться в стек. Порядок следования табулятора Пользователь может взаимодействовать с виджетами при помощи мыши и клавиатуры. В последнем случае для выбора нужного виджета использует- ся клавиша табулятора—<ТаЬ>, при нажатии которой происходит переход фокуса, согласно установленному порядку, от одного виджета к другому. Иногда возникает необходимость в изменении этого порядка, который по умолчанию соответствует очередности установки дочерних виджетов в вид- жете предка. На рис. 6.11 цифрами изображен порядок смены фокуса с по-
мощью табулятора. При появлении диалогового окна с тремя кнопками фо- кус будет установлен на кнопке С, и после нажатия на клавишу табулятора он перейдет на кнопку В, а затем — на кнопку А. 1 зЬ Огс1ё! - мп। а .и в а Рис. 6.11. Порядок смены фокуса Изменить порядок можно с помощью статического метода (Жгадес:: зестаЬОгдег (), получающего, в качестве параметров, два указателя на виджеты. Следующие вызовы изменят порядок следования табулятора, по- казанный на рис. 6.11, на более логичный порядок, показанный на рис. 6.12: ОМхбде!::: зеЪТаЬОгдег (А, В) ; 0М1ддеС::зеСТаЬОгдег(В, С) ; Рис. 6.12. Измененный порядок Разделители ОЗрНПег Разделители придуманы для возможности одновременного просмотра раз- личных частей текстовых или графических объектов. В некоторых случаях применение разделителя более предпочтительно, чем размещение с помощью классов компоновки, так как появляется возможность изменения размеров виджетов. Конкретный тому пример — это всем известная программа ОС "\У1пдо№ "Проводник" (\У1пдо'л'8 Ехр1огег). Разделители реализованы в клас- се озрИссег, определение которого находится в заголовочном файле ОЗрНПег. С помощью виджета разделителя можно располагать виджеты как горизонтально, так и вертикально. Между виджетами отображается черта разделителя, которую можно двигать с помощью мыши, тем самым изменяя размеры виджетов.
Если необходимо, чтобы виджеты разделителя были проинформированы об изменении размеров, то тогда нужно вызывать метод зесорадиекезхзео, передав ему значение ъгие. Пример, показанный на рис. 6.13, разделяет два виджета класса отехСЕахе. #1пс1иае <2Ъ0и1> та1п(1пЪ агдс, скаг** аг^) { ОАррИсаЪхоп арр(агдс, аг^); 05р11^Ъег зр1(0Ъ::УегХ1са1); ОТехЪЕаН:* рЪхС1 = пеад ОТехЬЕадХ; ОТехЪЕдИ:* рЪхЬ2 = лею ОТех^ЕдтЬ; зр1 .аааШддеЪ (рехС1) ; зр1. аааМхддеЪ (рСхЪ2) ; рЁхП1->зееР1а1пТехЪ ("Ыпе1\пи ”Ыпе2\п” "ЬхпеЗХп*' "Ыпе4\п" "Ыпе5\пм
. "Ыпе6\пи "Ь1пе7\пи ) ; рЪхе2->зееР1а!пТехЁ (рехЁ1->еоР1ц1пТехЪ ()); вр1.гез12е(200, 220); зр1.зИою(); геЁигп арр.ехес(); } В листинге 6.12 в конструктор класса озиъьег передается флаг <2Ь: :УегЫса1 и, тем самым, создается виджет вертикального разделителя. Примечание Для создания горизонтального разделителя в конструктор нужно передать зна- чение озрИееег: :Ног12опеа1. Того же эффекта можно добиться, если пе- редавать флаги ОЗрПЪЬег: :Ног1гопЪа1 и ОЗрИъсег: :УегЫса1 в метод зеЬОгхепЬаСхоп(). После создания виджетов класса отехсваи:, они добавляются в виджет разделителя, для чего их указатели передаются в метод ОЗрЫсьег:: а<3<зи1<3дес (). Текст в виджетах отехсвадх устанавливается мето- дом зеСР1а1пТехЬ(). Резюме Объекты компоновки виджетов при изменении размеров окна автоматически производят размещение виджетов в нем. 01 предоставляет менеджеры ком- поновки, которые обладают возможностью горизонтального, вертикального и табличного размещения. Эти способы можно комбинировать для создания сложных размещений. Классы менеджеров компоновки базируются сразу на двух классах: ось^есс и оьауоиыъет. Объекты компоновки предоставляют возможность установки фактора растяжения для управления соотношением размеров между виджетами. Реализованы методы для установки расстояний между виджетами и настройки размера отступа виджетов от границы компо- новки. Виджет разделителя предоставляет возможность одновременного просмотра содержимого виджетов. При перетаскивании разделителя пользователем происходит изменение размеров виджетов, помещенных в нем.
Глава 7 Элементы отображения "Видеть— значит верить, но только чувства отражают истину". Томас Фуллер Элементы отображения не принимают активного участия в действиях поль- зователя и используются для информирования о происходящем. Эта инфор- мация может носить как текстовый, так и графический характер (картинки, графика). Надписи Виджет надписи используется для показа состояния приложения или пояс- няющего текста и представляет собой текстовое поле, текст которого не под- лежит изменению со стороны пользователя. Информация, отображаемая этим виджетом, может изменяться только самим приложением. То есть приложе- ние может сообщить пользователю о своем изменившемся состоянии, но пользователь не может изменить эту информацию в самом виджете. Класс виджета надписи оьаЬе1 определен в заголовочном файле 0ЬаЬе1. Виджет надписи унаследован от класса ОРгате и может иметь рамку. Ото- бражаемая им информация может быть текстового, графического или анима- ционного характера, для передачи которой используются слоты зеьтехе (), зеЬРххтар () И зеСМоуте (). Расположением текста можно управлять при помощи метода зеСАИдшепсо. Метод использует большое количество флагов, и некоторые из них указаны в табл. 7.1. Обратите внимание, что значения не пересекаются, и это позволя- ет комбинировать их друг с другом с помощью логической операции | (ИЛИ). Наглядным примером служит значение АИдпСепЬег, составленное из Значений АНдпУСепСег И АИдпНСепСег.
Таблица 7.1. Значения флагов АИдптепсПад пространства имение Константа Значение Описание АИдпЬеЕЪ 0x0001 Расположение текста слева АИдпКхдкЬ 0x0002 Расположение текста справа АПдпНСеп^ег 0x0004 Центровка текста по горизонтали АЫдпСГизЫСу 0x0008 Растягивание текста по всей ши- рине АИдпТор 0x0010 Расположение текста вверху АИдпВоССош 0x0020 Расположение текста внизу АИдпУСепСег 0x0040 Центровка текста по вертикали АЫдпСепЁег АИдпУСепЪег | А1хдпНСепЪег Центровка теста по вертикали и горизонтали Как видно из рис. 7.1, виджет надписи может отображать не только обычный текст, но и текстовую информацию в формате НТМЬ (Нурег Тех! Магкир Ьап§иа§е, язык гипертекстовой разметки). В этом примере (листинг 7.1) ис- пользовался НТМЬ для вывода текста, таблицы и растрового изображения. О1_аЬе1 - НТМ1 Оето 1таде 1. Опе 2. Т^о 3, ГЬгее ЬогМ 81у!е Рис. 7.1. Отображение информации виджетом надписи в формате НТМЬ 6 Зак. 3983
*> 1“ ’* * »’ ‘ *%•*. * #1пс1иЗе «2Ши1> 1пъ ша1п(1п1: агдс, сЪаг** а^дV) { 0Арр11саЪгоп арр(агдс, агду); ОЬаЪе! 1Ы (”<Н1><СЕЫТЕК>ОЬаЬе1 - НТМЬ Пето</СЕКТЕК></Н1>" " <Н2 ><СЕОТЕК>1таде</СЕ№ГЕВ><Н2 >" "<СЕЫТЕКх1Ме ВОКВЕК=\"0\" 5ВС=\иипд.зрд\их/СЕНТЕК>" " <Н2 ><СЕЫТЕК>Ь1зё< /СЕЫТЕКхН2> " " <ОЬ><Ы>Опе< /Ы> ” " <Ы>Т^О</Ъ1>’' '• <Ы>ТЬгее</Ы>" "</ОЬ>" " <Н2 ><СЕМТЕК>РопЬ 5Гу1е</СЕЫТЕНхН2> " "<СЕЫТЕКхРООТ СОЬОК=ВЕО>" " <В>Во1<3</В>, <1>1Ёа11с</1>, <Ц>ипдегИпе</и>” и </ВОЬГГх/СЕЫТЕВ> ” " <Н2 ><СЕдаЕВ>ТаЫе< / СЕОТЕВХ /Н2 >" "<СЕИТЕК> <ТАВЬЕ>” 4 " <тк восоьов=#«оо^>" " <ТО>1,1</ТО><ТЛ>1,2</ТОхТЛ>1,3</ТБ><ТВ>1,4</ТО>" " </ТК>” " <ТК ВОСОЬОК=УЕЬЪОМ>” " <ТЛ>2,1</Т0хТО>2,2</ТОхТО>2,3</ТОхТО>2,4</ТЕ»" ’’ «/ТГО" " <тк восоьок=#оо^ооо>и ” <Т0>3,1</Т0хТП>3,2</ТОхТО>3,3</ТО><ТО>3,4</ТО>” - </ТЮ>" "</ТАВЬЕ> </СЕМТЕК>н ); 1Ы.8кою() ; геШгп арр. ехес () ; } В листинге 7.1, при создании виджета надписи 1Ы, первым параметром в конструктор передается текст в формате НТМЬ. Его можно передать и по- сле создания этого виджета при помощи метода-слота зеетехъ (). Второй па-
раметр конструктора опущен, а так как по умолчанию он равен 0, это делает его виджетом верхнего уровня. Следующий пример (листинг 7.2), показанный на рис. 7.2, демонстрирует возможность отображения информации графического характера в виджете надписи без использования формата НТМЬ. Рис. 7.2. Отображение графической информации виджетом надписи #1пс1ис1е <0Г:Ои1> 1пЪ 1па1п(1п(1 агдс, сЪаг** а^дV) { О^ррИсаСгоп арр(агдс, агд^); ОРххшар рхх; р1х. 1оа<1 ("тхгаЗрд”) ; 0ЬаЬе1 1Ы; ХЫ.гезхгеСрхх.зхгеО ) ; 1Ы. зеЪРххтар (рхх); 1Ы.зТхж(); геЁигп арр.ехес();
Как видно из листинга 7.2, сначала создается объект растрового изображе- ния <2Р1хтар. После этого вызовом метода 1оаао в него загружается файл пйга.]р§. Следующим шагом является создание самого виджета надписи — объекта 1Ь1 класса оъаЬе1. Затем вызовом метода гез1ге() его размеры при- водятся в соответствие с размерами растрового изображения. И, наконец, вы- зов метода зеьрххтарО устанавливает в виджете само растровое изображе- ние. При помощи метода зесвиаауО виджет надписи может ассоциироваться с любым другим виджетом. Если текст надписи содержит знак &, то символ, перед которым он стоит, будет подчеркнутым. При совместном нажатии это- го символа с клавишей <А11> фокус перейдет к виджету, установленному ме- тодом зесвиаау (). На рис. 7.3 показаны такие виджеты, а в листинге 7.3 при- веден соответствующий текст программы. ___ 1аЬе1Виаа7 ; Шп-'в: ; Аде: Рис. 7.3. Использование знака & #1пс1иДе <0ГСи1> 1пЬ ша1п(1пЪ агдс, сИаг** а^дV) { 2Арр11са1:1оп арр(агдс, а^дV) ; ОЮйЗдеС <2ЬаЪе1* ОГЛпеЕсНП* ътдС; р1ЫЫате = пею ОЬаЪе! (" &Ыате:") ; рЬхСЫате = пею рЫпеЕсИП; р1ЫЫате->зеЁВискЯу (р^хЪЫате) ; ОЬаЬе! р!ЫАде = пею ОЬаЬе! ("&Аде:");
С5р1пВох* рзрЬАде = лею 05р1пВдх; р1ЫАде->зеЬВи<1ду (рзрЬАде); //ЬауоиЪ зеЪир ОТВохЬауоие* риЪхЬауоиЪ = пею ОУВохЬауоиГ; р^хЬауоиГ->ад(М1аде1:(р1ЫЫате) ; р^хЬауои1:->ас1сМ:1(Зде1: (р^хПЫаше); рVЬx^ауоиС - >адс1М1аде е (р1ЫАде) ; рVЬx^ауоиЬ ->асМДО1 ддеЬ (рзрЬАде) ; аддП. зеЁЬауоиЪ (р^хЬауоие) ; . зкоад () ; геЪит арр. ехес () ; } В листинге 7.3 виджет «де класса онхадеъ является виджетом верхнего уров- ня, так как по умолчанию его конструктор присваивает указателю на виджет- предок значение 0. Виджеты не обладают способностью самостоятельного размещения виджетов-потомков, поэтому позже в нем производится уста- новка компоновки для вертикального размещения отвохьауоие. В виджете надписи Нате (Имя) в тексте символ И определен как символ для быстрого доступа. Затем создается виджет однострочного текстового поля. Далее, вы- зов метода зееви<аау() связывает виджет надписи с созданным текстовым полем, используя указатель на поле в качестве аргумента. Аналогично проис- ходит создание виджета надписи А$е (Возраст), поля для ввода возраста (класса 05р1пВох) и их связывание. Во всех виджетах есть возможность обработки событий клавиатуры и мыши. Этим можно воспользоваться, например, для создания гипертекстовой ссыл- ки, которая при нажатии вызовет определенную НТМЬ-страницу. Подроб- ную информацию о событиях можно получить в части III. Но можно поступить и проще. Дело в том, что класс оьаЬе! предоставляет поддержку для гипертекстовых ссылок и при нажатии на ссылку высылает сигнал ИпкАсъ^аееас), который можно соединить со слотом, из которого произойдет вызов страницы. Но есть и другой, более простой способ, он заключается в том, чтобы пере- вести виджет оьаЬеХ в состояние, когда он сам будет в состоянии открывать ссылки в \УеЬ-браузере (что будет достигаться неявным вызовом статическо-
го метода опезкъорЗе^хсез:: ореппг1 ()). Для этого нужно просто вызвать метод зеъорепЕх^егпа1Ыпкз () с параметром сгие. Например: ОЬаЬе!* р!Ы = лею 0ЬаЬе1("<А НКЕГ=\’’ЬШК\м>к11<:р://улл7.Ькнл.ги</А>*) ; 1Ы. зеЪОрепЕхЪегпаХЫпкз (Ъгие); Индикатор прогресса Индикатор прогресса (рго§ге$8 Ьаг) — это виджет, показывающий процесс выполнения операции и заполняющийся слева направо. Полное заполнение индикатора информирует о завершении операции. Этот виджет необходим в том случае, когда программа выполняет продолжительные действия. Он дает пользователю понять, что программа не зависла, а находится в работе. Также он показывает, сколько уже проделано и сколько еще предстоит сде- лать. Класс ОРгодеззВаг виджета индикатора прогресса определен в заголо- вочном файле ОРго^геззВаг. Обычно индикаторы прогресса располагаются в горизонтальном положении, но это можно изменить, передав в слот зе(:0г1епьаЫоп() значение ос: :7егЫса1— после этого он будет располо- жен вертикально. Следующий пример, приведенный на рис. 7.4, демонстрирует использование индикатора прогресса. При нажатии кнопки 81ер (Шаг) производится увели- чение значения индикатора на один шаг. Нажатие кнопки КеяеС (Сбросить) сбрасывает значение индикатора. __Ргсу‘1еэ$ --1----- Рис. 7.4. Пример индикатора прогресса #5.пс1ис1е <0АррИса111оп> #1пс1иде "Ргодгезз .И" тахп (1пЪ агдс, скат** агду) ОАррИса^хоп арр(агдс, агду);
Ргодгезз ргодгезз; ргодгезз.зЪозд(); ге!:игп арр. ехес (); В основной программе, приведенной в листинге 7.4, создается виджет, пока* занный на рис. 7.4. #1€пс1еГ _Ргодгезз_11_ Ме^хпе _Ргодгезз_И_ #1пс1ис1е <0Ю1<1деЁ> с1азз ОРгодгеззВаг; с1азз Ргодгезз : риЫхс О^хбде^ { О^ОЫЕСТ ргдлгайе: ОРгодгеззВаг* пцрргЬ; хпЪ щ_пЗЁер; риЫхс: Ргодгезз (ОМхддеЁ* роЬ} = 0) ; риЫхс з1оЬз: уо1с! зХоЪЭДер () ; тохс! з1о«<езе^ (); }; #епсИ Е / /_Ргодгезз__И_ В листинге 7.5 приведен файл Рго§ге$$.Ь, который содержит определение класса Ргодгезз, унаследованного от о«1адее.. Класс содержит два атрибута: указатель на виджет индикатора прогресса и целое значение, представляю-
щее номер шага. В классе определены два слота зТоъзсерО и зТоекезег(). Первый предназначен для наращивания шага на единицу, а второй — для ус- тановки индикатора прогресса в нулевое положение. #1пс1и<1е <0ГОи1> #1пс1ис1е "Ргодгезз.И” И------------------------------------------------------------------------------- Ргодгезз:: Ргодгезз (О^хсЯдеЁ * раддЬ / * = 0 * /) : 0М1<Зде1:(рюдГ) , т_пЗЪер(О) { т_рргЬ = пей? фРгодгеззВаг ; т_рргЬ’>зеЬКапде(0, 5) ; т^ргЬ->зеШ1п1титЮ1с1Ь11 (200) ; т_рргЬ->зе^А11дптеп^(0е: :А1хдпСепЬег) ; ОРизИВиЬЁоп* рстйзеер = пею ОРизкВи^Ъоп("&3еер") ; ОРизЪВи^Ёоп* рстбКезе^ = пею ОРизЪВи^Гоп (”&НезеЁ") ; ООЬд есЬ:: соппес!: (рстк131:ер, ЗТОЫАЬ (сИскес! ()), ЗЪОТ (з1о!:8Гер ())) ; ООЬд ес11:: соппесЬ (рсзпсЗКезеГ, ЗХОЫАЬ (сИскесЯ () ), ЗЬОТ (з1о!ЖезеС () ) ) ; //ЬауоиЁ. зеЬир ОНВохЪауоиЁ* рЪЬхЬауои^ = пем ОНВохЬауои^; ркЬхЬауои1:-->а(1<ЗМ1(ДдеГ (т_рргЬ) ; рЪЪхГ|ауоиС->ас1<2М1(5деЁ (рстсЯЗГер) ; ркЪхЬауоиС->асИМ1с1де<: (рстдКезеЪ) ; зеЬЬауоиЬ(ркЬхЬауоиЪ); } //------------------------------------------------------------------------------ тохд Ргодгезз:: з1оГ31:ер () { 1л_рргЪ->зеЁУа1ие(++т_пЗСер);
И----------------------------------------------------------------- уод-й Ргодгезз:: зХоСКезеС () { 1Ц_пЗЬер = 0; щ_рргЬ->гезеС(); } В конструкторе класса (листинг 7.6) атрибуту т_пзеер присваивается значе- ние 0. После создания объекта индикатора т_рргЬ вызовом метода зеснапде () задается количество шагов, равное 5, и метод зеьмХпХтитШдеи () устанавливает минимальную длину виджета индикации прогресса, в нашем случае мы запрещаем ему иметь длину менее двухсот пикселов. Вызов метода зеьАНдпшепь () с параметром ое:: АИдпСепсег переводит индикатор в ре- жим отображения процентов в центре (см. табл. 7.1). Затем создаются две кнопки 81ер (Шаг) и Кезе! (Сбросить), которые соединяются со слотами з1оъзьер() и з1оенезес (). В слоте зХоьзъеро значение атрибута ш_пзьер увеличивается на 1 и передается в слот ОРгодгеззВас: -.зеЬУаХиеО объекта индикатора прогресса. Слот зХо«гезес() устанавливает значение атрибута ш_пзьер равным 0 и, вызвав СЛОТ ОРгодгеззВаС: : гезеЦ), возвращает инди- катор в исходное состояние. Для размещения виджетов-потомков горизон- тально, слева направо, необходимо установить в виджете Ргодгезз объект класса компоновки ОУВохьауоис, предварительно добавив в него, в нужной очередности, виджеты-потомки. Электронный индикатор Класс оьсоыишЬег виджета электронного индикатора определен в заголовоч- ном файле рЬСОМитЬег. По внешнему виду этот виджет представляет собой набор сегментных указателей как, например, на электронных часах. С помо- щью этого виджета отображаются целые числа. Допускается использование точки, которую можно отображать между позициями сегментов или как от- дельный символ, вызывая метод зесзтаХХпесХтаХРоХпь () и передавая в него сгие или 1а1зе, соответственно. Количество отображаемых сегментов можно задать в конструкторе или с помощью метода зесцшпШдКхз (). В том случае, когда для отображения числа не хватает сегментов индикатора, отсылается сигнал оуегСХоад (). По умолчанию стиль электронного индикатора соответствует стилю Оисххпе, но его можно изменить, передав методу зесзедтепезьуХе() одно из следующих значений: ОБСВЫшпЬег: :ОиСХХпе, ОЬСгаЛлпЬег: :Р1ХХеа ИЛИ ОЬСгаЛдйЬег: :РХаС.
В табл. 7.2 приводится внешний вид виджета для каждого из перечисленных стилей. Таблица 7.2. Стили электронного индикатора Константа Внешний вид ОиЫхпе Пас . 31 РШей ПЛ □ 1 Электронный индикатор можно включать в режиме отображения двоичной, восьмеричной, десятеричной или шестнадцатеричной систем счисления. Режим отображения изменяется с помощью метода зегмо<3е(), в кото- рый передается одно из следующих значений: оьспиитЬег: :в1п (двоич- ная), оьсоншпЬег:: ос с (восьмеричная), оьажшпЬег: :Оес (десятичная) или ОьспыитЬег: :Нех (шестнадцатеричная). Также для смены режима отображе- ния МОЖНО воспользоваться слотами зеСВхпМоде (), зеЬОсСМобе (), эеЬПесМойе () и зеьнехМойе (), соответственно. Пример, показанный на рис. 7.5, демонстри- рует электронный индикатор, отображающий шестнадцатеричные значения. Рис. 7.5. Пример электронного индикатора #1пс1иде <0ЬОи1> 1ПС махп (1пЬ агдс, сЪаг** агду)
□АррИсаЪхоп арр(агдс, агду-) ; ДОМде е «дЬ; ОЬСОЫигпЬег* р1сс! = лете СЬСЖитЬег; ОЗрхпВох* рзрЬ = лею <25р1пВох; р1сД->зеЪЗедтепЪ5Ъу1е(0ЬСШшпЬег: :РИ1ед) ; р1сд->зеШо<1е(СЬСВЫи1пЬег: :Нех) ; рзрЬ->зеЪГ1хес1Не1д11Ъ (30); ООЬэ есЪ:: соппесЪ (рзрЬ, ЗЮЫАЬ (Vа1иеСЬалде<^ (1Л1:)), р1сд, 5ЬОТ(а1зр1ау (хпе)) ); //ЬауоиЁ зеЬир ОУВохЪауои^* рVЬx^ауои^ = лете ОУВохЬауоиЪ; р^хЬауоиЬ->аск1№1с1деЬ (р1сс1) ; руЬхЪауоие->аскЗ&Яс1де1:(р8рЬ) ; юдЪ. зееЬауоиЬ (р^хЬауои!:) ; У1дС.гез1ге(250, 150); аддЪ.зНою(); ге^ит арр. ехес () ; } В листинге 7.7 создаются виджеты электронного индикатора (указатель р1са) и счетчика (указатель рзрЬ). Затем вызовом метода зеьзедшепсзьуХе () изме- няется стиль сегментных указателей. Электронный индикатор вызовом мето- да зеьмодеО и с параметром оьсонитЬег: :нех переключается в режим шест- надцатеричного отображения. У элемента счетчика методом зесрххеанехдъъ () устанавливается неизменная высота, равная 30. После этого сигнал Vа1иесЬапдеа () виджета счетчика соединяется со слотом а1зр1ауо элек- тоонного индикатооа.
Резюме В этой главе мы ознакомились с элементами отображения. Виджет надписи содержит информацию, предназначенную только для просмотра и не подле- жащую изменению со стороны пользователя. Этот виджет способен отобра- жать не только простой текст, но и текст в формате НТМЬ. Кроме текстовой, может отображаться и графическая информация. Виджет индикатора прогресса — прекрасный элемент для оповещения поль- зователя о процессе работы приложения. Этот виджет незаменим для иллю- страции хода выполнения продолжительных операций. Виджет электронного индикатора по внешнему виду представляет собой на- бор сегментных указателей, как на электронных часах. Стиль виджета можно изменять и использовать различные режимы отображения чисел — в двоич- ной, десятеричной, шестнадцатеричной и восьмеричной системах счисления.
Глава 8 Кнопки "Нажми на кнопку, получишь резуль- тат, и твоя мечта осуществится". Группа “Технология' Кнопки — это один из важнейших и наиболее часто встречаемых элементов пользовательского интерфейса. Даже если программистом в коде программы не создается ни одной, все равно в правом верхнем углу присутствуют кноп- ки, управляющие окном приложения. Кнопка может быть нажата (оп) или отжата (оП)- С чего начинаются кнопки. Класс ОАЬз{гас1ВиПоп Класс ОАЬзегасевиесоп — базовый для всех кнопок. В приложениях приме- няется три основных вида кнопок: нажимающиеся кнопки (оризьвиъьоп), которые обычно называют просто кнопками, флажки (осиесквох) и пере- ключатели (овасИовиьсоп). В классе ользсгасьвиссоп реализованы методы и возможности, присущие всем кнопкам. Сперва мы обсудим основные из этих возможностей, а потом поговорим о каждом виде в отдельности. Установка текста и изображения Все кнопки могут содержать текст, который можно передать как в конструк- торе первым параметром, так и установить с помощью метода зеътехьо. Для получения текста в классе ОАЬзегасьвисьоп определен метод ъехе (). Растровое изображение устанавливается на кнопке при помощи метода зеысопО. После установки изображения вызовом метода зеЫсопЗтгеО можно изменить его максимальный размер, который занимает изображение на кнопке (изображения меньшего размера не растягиваются). Для получения
текущего максимального размера изображения определен метод З.сопз1ге(у. И, наконец, для того чтобы кнопка возвратила установленное в ней изобра- жение, нужно вызвать метод 1соп (). Взаимодействие с пользователем Для взаимодействия с пользователем класс ОАЬзЫасъвиъъоп предоставляет следующие сигналы: □ сИскедо —отправляется при щелчке кнопкой мыши; □ ргеззей () — отправляется при нажатии на кнопку мыши; □ ге1еазей() — отправляется при отпускании кнопки мыши; □ соддХеао —отправляется при изменении состояния кнопки, имеющей статус выключателя. Опрос состояния Для опроса текущего состояния кнопок в классе ользегасьвисгоп определе- ны три метода: □ 1гОоип() возвращает значение ыие, если кнопка находится в нажатом состоянии. Изменить текущее состояние может либо пользователь, нажав на кнопку, либо вызов метода зеы>о«п (); □ хгсъескеа () возвращает значение ыие, когда кнопка находится во вклю- ченном состоянии. Изменить текущее состояние может либо пользова- тель, нажав на кнопку, либо вызов метода зессиескеа (); □ кнопка доступна, то есть реагирует на действия пользователя, если хзЕпаЫеао возвращает значение сгие. Изменить текущее состояние можно вызовом метода зеьЕпаЫеа (). Кнопки Виджет кнопки можно встретить в любом приложении, например, почти все- гда имеются кнопки ОК или Сапсе! (Отмена) — без них не обходится ни од- но диалоговое окно. Иногда такой виджет называют "командной кнопкой". Он представляет собой прямоугольный элемент управления и используется, как правило, для выполнения определенной операции при нажатии на нее. Класс ОРизйвисьоп виджета нажимающейся кнопки определен в заголовоч- ном файле ОРизйВиНоп. Создать нажимающуюся кнопку можно следующим образом: ОРизИВиППоп* рспкД = пею ОРизЪВиЪЪоп ("Му ВиЪЪоп");
Первый параметр (типа строка) задает надпись кнопки; как обычно, в конст- руктор можно передавать также виджет предка, но это делать не стоит, так как менеджер сделает это за нас. Как мы уже знаем, при щелчке по кнопке высылается сигнал'сНскеао. Кнопка считается нажатой, если пользователь щелкнул на нее кнопкой мыши или нажал на клавишу <Ешег>, при условии, что кнопка была текущей (была в фокусе). Для того чтобы кнопку сделать таковой, можно воспользоваться МеТОДОМ зеЫЭеСаиХе (). Пример, показанный на рис. 8.1, демонстрирует различные варианты нажи- мающихся кнопок: □ Иоппа! ВиИоп (Обычная кнопка) соответствует обычной кнопке, которую мы привыкли видеть в большинстве случаев. После отпускания кнопка всегда возвращается в свое исходное положение; □ То$>81е ВиПоп (Выключатель) может прибывать в двух состояниях: нажа- том или не нажатом, которые соответствуют положениям включено или выключено. Логика действия этой кнопки идентична, например, логике обычного комнатного выключателя; □ Е1а1 ВиПоп (Плоская кнопка) по своим функциональным особенностям идентична обычной кнопке. Разница лишь во внешнем виде. Например, благодаря тому, что контуры этой кнопки не видны, ею можно воспользо- ваться для размещения "секретной кнопки" диалогового окна; □ наконец, последняя кнопка Ркхтар ВиИоп (Кнопка с изображением) представляет собой кнопку, содержащую растровое изображение. Рис. 8.1. Поимеоы нажимающихся кнопок
#1пс1иде <0еСи1> хпЪ тахп (хпе агдс, скейт** агду) { ОАррИсаехоп арр(агдс, агду) ; ОМхддее «де ; ОРизЪВиееоп* рспкШоппа! - пе« ОРизИВиееоп( "&Мопоа1 Виееоп"); рРизЪВиееоп* рстс1Тодд1е - пе« ОРизкВиееоп("&Тодд1е Виееоп"); рсшйТодд1е->зееС11ескаЫе (егие) ; рсгс(ЗТодд1е->зееС11ескес1(егие); СРизЬВиееоп* рспиЗПае = пей? СРизкВиееоп("&Е1ае Виееоп"); рстбГ1ае->зееГ1ае(егие) ; ОРххтар рхх (’’ а1оепа. э рд "); ОРизкВиееоп* рспи1Рхх = пе« ОРизкВиееоп(”&Р1хтар ВиЁСоп"); рспкЯР1х->зеЁ1соп (рхх) ; рстс1Р1х->зе1:1соп312е(р1х.з12е() ) ; //ЬауоиЬ зеСир ОХТВохЬауоиС* р^хЬауои!: = пет/ ОУВохЪауоиЪ; рVЬx^ауоиЁ->а(^дМ^<^де^(рспк3^о^та1) ; р^хЬауоиП->а(ИЮ1адее (рахк1Тодд1е) ; р^хЪауои1:->а(ИйШ}деС (рспк1К1ае) ; руЬхЪауои!:->аааю1с1дее (рстсЯРхх) ; «де. зееЪауоиЁ (р^хЪауоиЪ); «дЪ.зкою(); геСигп арр.ехес(); } В листинге 8.1 приводится текст приложения, окно которого показано на рис. 8.1. Сперва создается виджет обычной кнопки (указатель рсшс1ыогта1). Кнопка-выключатель (указатель рсшатодд1е) создается так же, как и обычная
кнопка, но для нее вызовом метода зеьсЪескаЫе () с параметром ггие уста- навливается режим выключателя. Вызов метода зеьсьескеа () с параметром ыие приводит эту кнопку во включенное состояние. Затем создается виджет плоской кнопки (указатель рстдПаъ) как обыч- ной кнопки. Вызовом метода зеьБЧае () с параметром сгие ей придается пло- ский вид. Для создания кнопки с растровым изображением сначала создается объект растрового изображения р!х, в который загружается файл а1оепа.)р§, указан- ный в конструкторе. Затем создается кнопка (указатель рстархх), после чего объект растрового изображения передается в метод аеысоп() .для установки на кнопке. Слот геЫсопзхгеО задает размеры растрового изображения, в данном случае они соответствуют оригинальным размерам изображения, ко- торые возвращает метод з1ге () объекта ОРххтар. Существует возможность использования в нажимающихся кнопках всплы- вающего меню (рис. 8.2). Подобные кнопки можно встретить, например, в обозревателе Мгсгозой 1п1егое< Ехр1огег. Кнопка ЗДаН (Пуск) панели задач ОС АМпскмуз также представляет собой такую кнопку. Добавить меню мож- но, вызвав метод аеьмепиО и передав указатель на объект всплывающего меню. Подобные кнопки могут использоваться в качестве альтернативы для выпадающего списка (см. главу 11). __ВсКспРсрср . Йет2 Рис. 8.2. Нажимающаяся кнопка с всплывающим меню #1пс1ис1е <0ЁОи1> 1пС тпа1п (хпё агдс, сЪаг** агду) { ОАррИсаЪхоп арр(агдс, агду) ; ОРизИВиееоп ста (“Мепи ”);
фМепи* ртпи = пею 0Мепи(&спк1); ртпи->а<ЗдАсЪ1оп (" 1Ъет1") ; ртпи->ад<1АсГ11оп (" 1Ъет2 ") ; рпши->ас1сЗАс111оп (" &ОизХ", &арр, 2Б0Т (дизХ ())); сзпд.зеПМепи(ртпи) ; сгпскзЪоюС); геЪигп арр. ехес () ; } В листинге 8.2 создаются виджеты кнопки ста и всплывающего меню ртпи. Вызовом метода ааадсЫоп () добавляется элемент меню (см. главу 32). По- следняя команда Оий (Выход) соединяется со слотом ди1с () объекта прило- жения, что позволяет пользователю завершить приложение, выбрав эту команду из меню. Установка созданного меню в нажимающейся кнопке про- изводится вызовом метода веъмепи (). Флажки Большинство программ предоставляют целый ряд настроек, позволяющих изменять поведение программы. Для этих целей может быть полезным вид- жет флажка, который позволяет пользователю выбирать сразу несколько оп- ций. Класс оскесквох виджета кнопки флажка определен в заголовочном файле ОСЬескВох. Примечание Если же опций больше пяти, то лучше использовать виджет списка ОЫвскШдее (см. главы 11 и 12). Флажок состоит из маленького прямоугольника и может содержать пояс- няющий текст или картинку. При щелчке на виджете в прямоугольнике поя- вится отметка. Этого же можно добиться нажатием клавиши <Пробел>, ко- гда виджет находится в фокусе. Этот виджет устанавливается в положение "включено" или "выключено" и является, по логике действия, кнопкой- выключателем (1о§§1е ЬиПоп). Но, в отличие от последней, флажок может иметь еще и третье состояние — неопределенное (рис. 8.3). Пример исполь- зования такого состояния можно увидеть в диалоговом окне Ргоргебе» (Свойства) Проводника в ОС АУшбом'з при выборе нескольких файлов, имеющих разные атрибуты.
,__ СКескВсх I 1/1 Н^тла! СЬеск Бох ,Л; ТлеШе Скеск Вох Рис. 8.3. Примеры флажков #1пс1иде <0ЪСи1> хпЪ та!п(1пЪ агдс, скаг** а^дV) { 0Арр11саЁ1оп арр(агдс, агду) ; ОМхддеЪ **дЪ; ОСкескВох* рсккЫогта! = пею ОСкескВох ( п&Ыоппа1 Скеск Вох"); рсккЫоппа!->зеЪСкескес1 (1:гие); ОСкескВох* рсккТгхзкаСе = лею ОСкескВох ("йТтхэкаке Скеск Вох"); рсккТг 1зкаЪе->зекТг1зкаке (Ъгие); рсккТг1зЪа1:е->зееСкеск5еа1:е (Ок:: РагЫаЫуСкескеЗ); //ЬауоиЬ зеЬир дтВохЬауоиЪ* р^ЬхЬауои!: = пею ОУВохЬауоиЪ ; р^хЬауои11“>асЯс1М±с1де{1 (рсккЫогта!) ; рVЬx^ауои^“>а<5дМ^с^де1: (рсккТгхзЬаЪе); аддЪ. зеЪЬауоиЬ (ртЪхЬауои!:) ; ^дЪ.зкошО ; геЪигп арр. ехес (); } В листинге 8.3 создаются два флажка—указатели рсышоппа1 и рсЪктгхгьаье. Флажок №>ппа1 СЬеск Вох (Обычный флажок) помечается вызовом мето- да зессьескеао с параметром сгие. Флажок ТпвШе СЬеск Вох (Флажок
с неопределенным состоянием) переводится в режим поддержки третьего, неопределенного состояния передачей значения сгие в метод зеьтг1зьаее(). Затем вызовом метода зеьсйескзъасе () и передачей в него значения ОЬ:: РагЫаНуспескед устанавливается третье состояние. Переключатели Свое английское название — гасНо ЬиПоп — виджет переключателя получил благодаря своей схожести с кнопками радиоприемника, на панели которого может быть нажата только одна из кнопок. Нажатие на другую кнопку ра- диоприемника приводит к тому, что в группе автоматически отключается кнопка, нажатая до этого. Переключатель представляет собой виджет (рис. 8.4), который может нахо- диться в одном из двух состояний: включено (оп) или выключено (ой). Эти состояния пользователь может устанавливать с помощью мыши или клавиши <Пробел>, когда кнопка находится в фокусе. Класс окасЦоВиьсоп виджета переключателя определен в заголовочном файле рКасйоВиПоп. Этот виджет должен предоставлять пользователю, по меньшей мере, выбор одной из двух альтернатив. Виджеты переключателей не могут использо- ваться в отдельности и должны быть сгруппированы вместе. Их группировку можно произвести, например, при помощи класса осгоирвох. Поясняющие надписи должны быть определены для каждого используемого в группе переключателя, а также желательно задать и сочетания клавиш для быстрого доступа к каждому из переключателей. Это достигается включени- ем в надпись символа & перед нужной буквой. Примечание Преимущество переключателей состоит в том, что все опции видны сразу, но они занимают много места. Поэтому если количество переключателей больше пяти, то лучше воспользоваться виджетом выпадающего списка осотЬоВох (см. главу 11). Рис. 8.4. Пеоеключатели
#1пс1иде <0Ъ(5и1> 1ПЁ ша1п(1п1: агдс, скат** агду) < ОАррИсаСхоп арр(агдс, агду) ; ОСгоирВох дЬх("&Со1огз"); ОВадхоВиССоп* ргадКес! = пем 0Ка<а1оВикЁоп("&Ке(1"); 0КасИоВи1:еоп* ргасЮгееп = пем ОКабхоВиекоп ("&Сгееп") ; ОКадхоВиЪкоп* рга<ЗВ1ие = пею ОКасИоВиккоп("&В1ие") ; ргааСгееп->зе^Скескед. {егие) ; //Ьауоие зеСир ОТВохЬауоие* руЬхЬауоиЪ = пем ОХТВохЬауоиЪ; руЪхЬауоиЪ->адсЗМ1йдее (ргасЭЯед); руЬхЬауоиЪ->аскЗМ1<5дее (ргаскЗгееп); руЪхЪауоие->аскЗМ1с1дее (ргадВХие) ; дЬх.зееЬауоие (руЬхЪауоиЪ); дЬх. зкою () ; геЁигп арр.ехес(); } В листинге 8.4 создается виджет для группы переключателей дЬх. После соз- дания переключателей: ргайкед, ргайСгееп и ргадв1ие, один из них (ргасЮгееп) устанавливается во включенное состояние вызовом метода геьсъескев () с параметром егие. Переключатели размещаются на поверхно- сти виджета группы дЬх, а объект класса отвохьауоие. автоматически вы- страивает их в вертикальном порядке (см. рис. 8.4). Группировка кнопок Виджеты группировки кнопок, в основном, не предназначены для взаимо- действия с пользователем. Их основная задача — повысить удобство исполь- зования и упростить понимание программы. Для этого важно, чтобы элементы
интерфейса были объединены в отдельные логические группы. Кроме того, нужно помнить, что кнопки переключателей скадховиьоп не могут использо- ваться отдельно друг от друга и должны быть объединены вместе. Класс осгоирвох является классом для такой группировки и представляет собой контейнер, содержащий в себе различные элементы управления. Он может иметь поясняющую надпись в верхней области, и эта надпись может содержать клавишу быстрого доступа, при нажатии на которую фокус пере- водится на саму группу, а также можно снабдить его дополнительным флаж- ком, который будет управлять доступностью сгруппированных элементов (см. рис. 8.5). Этот класс определен в заголовочном файле ООгоирВох. Приложение, показанное на рис. 8.5, представляет собой группу переключа- телей, отвечающих за цвет фона. Например, выбор переключателя Вей (Красный) приведет к тому, что фон виджета верхнего уровня станет крас- ным. Флажок Ы^ЬС (Яркость) управляет яркостью цвета, а кнопка ЕхВ (Вы- ход) осуществляет выход из программы. Текст соответствующего файла тат.срр приведен в листинге 8.5. „ гиКспбгсир ОбширВом о Ве4 Вив Рис. 8.5. Пример группировки кнопок #1пс1ийе <0АррНса1:1оп> #1Пс1исЯе "ВиЬЪопз.Ъ" 1пЪ тахп (хпЪ агдс, скат** а^дV) { САррИсаЪтоп арр(агдс, аг^) ; Ви^Ъопз ЬиЪЪопз:
ЬиЬЬопз.экою(); геСигп арр.ехес(); Двйиж*§!йй®йай ТО #1й1с1е^ _ВиЫ:опз_к_ Ме^хпе _Виееопз_к_ #1пс1иде <ОСгоирВох> с1азз ОСкескВох; с1азз О&иНоВиНЪсп; // =============^======================================================== с1азз ВиСеопз : риЫхс ОСгоирВох { О^ОВОЕСТ ргЗлгаЪе: ОСкескВох* щ_рскк; ОКасНоВиеЪоп * т_рга<ЗКед ; ОКасНоВиЪЪоп* щ__ргадСгееп; ОВасИоВиЬПоп* т_ргадВ1ие; риЬИс: ВиЁЁопз(ОМхддеС* рюдЪ = 0); риЬИс з!оЪз: То1с1 з1ойВиЬЬопС11скес1() ; }; #епЛ^ / /_Вие^опз_к_ Как видно из листинга 8.6, в котором определяется класс виььопз, он насле- дуется от класса ОСгоирВох. В классе определяются атрибуты ш_рскк для флажка и т_ргайкеа, т_ргаасзгееп, т_ргайв1ие для переключателей. Это необ- ходимо, чтобы вышеперечисленные атрибуты были доступны из слота з!оевиьеопС11скес1 ().
|ЗДЗ|й^7'4М-Зи1|м^^ *м^ЛПка1««А4< «|^}Л^КЙкв><иааК Л |Й4*м*^1^т^1^«1Л'| /**<< ВиСЬопз: : ВиЬеопз (О&ЯддеЪ * рмдЪ/ * = 0 * /) : ООгоирВох (" ОСгоирВох", рмдЪ) { гезхзеСЮО, 150); зеЁСкескаЫе (Ъгие); зеескескед(^гие); т^ргабКесЗ. = пею фКасНоВи^оп (” &КесГ) ; т_ргадСгееп = пего ОКасНоВи^оп (”&Огееп") ; т_ргас!В1ие = пеу; рНасИоВиГХоп ("&В1ие"); т_ргайСгееп->зеЁС11ескей (Сгие) ; соппесЬ (щ_ргадКей, ЗЮЫАЬ (с1 хскес! ()) , ЗЬОТ (з 1оСВи1ХопС11ске<1 ())) ; соппес(: (т_ргас1Сгееп, ЗКЗЫАЬ (сИскед ()) , ЗЬОТ (з1о€ВиЪЪопС11скес1 ()) ) ; соппесГ: (т_ргадВ1ие, 51СЫАЪ (сИскед ()) , ЗЬОТ (зХоЬВиЬГюпСИскед ())) ; щ_рс11к = пеу/ 0СИескВох( "йЫдке'’) ; пирсИк->зе1:Скескес1 (Ыие) ; соппесС (т^рскк, 51(ЗЫАЬ (сИскей ()) , ЗЬОТ (зТо^иЪЬопСИскед ())); ОРизкВиЪЁоп* рспкЗ. = пеу; ОРизИВиЫюп ("&Ех1Ь") ; соппесС (рапа, ЗПЗМАЬ (сИскей ()) , дАрр, ЗЬОТ (дихС ())) ; //ЬауоиЪ зеЁир ОУВохЬауоиЁ* р^хЬауои!: = пеу/ ртВохЬауоиГ; рVЬx^ауоиI:->ас^с^М^с^де^ (щ_ргас!Веа) ; РVЬx^ауоиС->ааа^^<Зде^ (т_ргас1Сгееп) ; рVЬx^ауоие->аасй^^^с^де^ (т_ргас1В1ие); рVЬx^ауоиЁ->а(^а^^адеС (т_рсГ1к); рVЬx^ауоиЁ->адс^^^аде^ (рспк!) ; зеЬЬауоиП (р^хЬауоиЪ) ; 51о^ВиЪЁопСИске<1 () ; } В конструкторе класса виьеопг (листинг 8.7) вызовом метода геьсьескаЫе () с параметром ыие устанавливается флажок, который "включается" методом зеЪСкескесН).
После создания трех переключателей (указатели ргаавеа, ргайсгееп и ргаав1ие) первый из них выделяется вызовом метода зеьскескеао с параметром ыие. Сигнал сИскеао для каждого переключателя соединяется со слотом з1оЪВи(:СопС11скеа(). Флажок (Яркость) (указатель пцрсЪк) включается сразу после своего создания при помощи метода зессьескеас). Последней из кнопок создается кнопка ЕхЛ (Выход) (указатель рста). Для выхода из приложения при нажатии на эту кнопку, сигнал сИскеао соеди- няется со слотом дин () объекта приложения. Затем созданные виджеты размещаются в вертикальном порядке при помощи объекта класса ОУВохЬауоие. В последней строке вызывается слот з1освиеьопсискеа() для инициализации. То1с1 ВиССопз:: зХоСВиССопСХХскед () { ОРаХеССе раХ = раХеССе(); 1пС пЫдкС = пирсИк~>1зСИескед () ? Х50 : 80; 11 (гц_рга<ЗКед- >1зСкеске<1 ()) { раХ. зеССоХог (ЬаскдгоипЖоХе () , ОСоХог (ОС: гесХ) . ХХдЪС (пЫдИС) ) ; У еХзе (псргас!<Згееп->1зСХ1ескев()) { раХ.зеССоХог(ЬаскдгоипдКоХе(), ОСоХог(ОС::дгееп).ХхдИС(пЬХдЪС)); } еХзе { раХ. зеССоХог (Ъаскдгоипс1КоХе () , ОСоХог (ОС:: ЬХие) . ХХдЪС (пЫдЪс) ) ; } зеСРаХеССе(раХ); В листинге 8.8 приводится реализация метода зХоъвиъьопсИскеао, в кото- ром при каждом вызове создается объект палитры и проверяется состояние флажка (Яркость) для установки переменной пЫдЬс, управляющей ярко- стью цвета. В операторах к производится анализ состояния кнопок- переключателей при помощи метода 1зсьескеа<). В зависимости от помеченной кнопки переключателя, цвет фона палитры ои1адеь: :Ъаскдгоипако1еО изменя- ется вызовом метода о₽а1есее: :зепсо1ог-() и устанавливается в виджете при помощи метода оихДдее:: зее₽а1еъъе (). Подробнее о палитре можно прочи- тать в главе 13.
Резюме Существует три основных виджета, унаследованных от оАЪзьгассвиьеоп: кнопки, флажки и переключатели. Кнопки используются для выполнения определенных действий. При нажатии на кнопку высылается сигнал ргеззеао, после отпускания высылается сиг- нал геХеазейО. Чаще всего используется сигнал сИскеао, который высы- лается, если пользователь нажал и отпустил кнопку. Флажки часто используется в диалоговых окнах, содержащих опции. Группа флажков используется для одновременного выбора нескольких опций. Воз- можен вариант, когда не будет выбран ни один из них. Переключатели используются только в группе, в которой одновременно можно выбрать только один из переключателей. Тем самым, при помощи этой группы моделируется отношение, "один ко многим". Основная задача виджета группировки — облегчить восприятие и работу с программой. С его помощью элементы интерфейса объединяются, по при- надлежности, в отдельные логические группы.
Глава 9 Элементы настройки "Происходящим необходимо управлять — иначе оно будет управлять вами". Бак Роджерс, вице-президент 1ВМ Группа виджетов, относимых к элементам настройки, используется, как пра- вило, для установки значений, не требующих большой точности, напри- мер— настройка громкости звука, скорости движения курсора (указателя) мыши, скроллинг содержимого окна и другие подобные действия. Класс 0АЬз1гас18Идег Этот класс является базовым для всех виджетов настройки: ползунка (озиаег), полосы прокрутки (озсгоПВаг) и установщика (оо1а1) (см. рис. 5.1). Все перечисленные далее возможности также доступны и во всех унаследованных от него классах. Его определение содержится в заголо- вочном файле (}АЬ$1гас1$11с1ег. Если требуется создать свой собственный виджет, то можно унаследовать этот класс и реализовать метод гиаегсъапдеС), который вызывается всякий раз при изменении значения. Изменение положения Классы виджетов, унаследованные от ользъгасезпзег, могут быть как гори- зонтальными, так и вертикальными. Для изменения расположения исполь- зуется слот 8еъог1епьаЫоп (), в который для задания горизонтального расположения передается значение ос: :Ног12опса1, а для вертикального — ОЪ: :УегЫса1.
Установка диапазона Для установки диапазона значений используется метод зеькапде (). В этот метод первым параметром передается минимально возможное значение (его нижняя граница), а вторым — задается его максимально возможное значение (верхняя граница). Также можно воспользоваться методами геыИп1тшп() и зесмах1тит(), соответственно. Например, для того чтобы задать диапазон от 1 до 10, можно поступить следующим образом: рз1<а->зеСКапде (1, 10); или рБ1<3->8еЫИп1пиип(1) ; рз!с1->зеЬМах11Пшп(10) ; Установка шага При помощи метода зеьз1пд1езьер() можно задать шаг, то есть значение, на которое, например, ползунок сдвинется при нажатии на стрелки полосы про- крутки или на клавиши курсора клавиатуры. Метод зееРадеЗЬер () задает шаг Для страницы. Перемещение страниц вы- полняется, например, для элемента ползунка при нажатии на область, нахо- дящуюся межу стрелкам и головкой ползунка или клавишами <Ра§е 1}р>, <Ра§е Воу/п>. Установка и получение значений Для того чтобы установить какое-либо значение, необходимо воспользовать- ся слотом зеьуа1ие (). Для получения текущего значения можно вызвать ме- ТОД уа1ие (). Сигнал зЫаегмстуед (1пъ) передает актуальное значение положения и посы- лается при изменении пользователем указателя текущего положения. Сигнал уа1иесиапдеа () посылается одновременно с сигналом з1Хаегм<лгеа(1пъ) сразу после изменения положения ползунка и также передает измененное зна- чение полосы прокрутки. Поведение сигнала изменяется вызовом метода зестгаскХпд (). Если передать ему значение €а1зе, это приведет к тому, что сигнал уаХиесъапдеа () будет высылаться только при отпускании указателя текущего положения. Чтобы узнать, отпустил ли пользователь указатель текущего положения пол- зунка или все еще удерживает его, можно присоединиться к сигналам зИйегРгеззесН) ИЛИ зИс1егКе1еазеа().
Ползунок Ползунок позволяет довольно комфортно выполнять настройки некоторых параметров приложения. Класс озиаег ползунка определен в заголовочном файле (281к1ег. Класс озИсЭег содержит метод, управляющий размещением рисок (шкалы) ползунка. Риски очень важны при отображении ползунка. Они дают пользо- вателю визуально более четкое представление о местонахождении и показы- вают шаг. Возможные значения, которые можно передать в метод зеът1ск- Роз1Ыоп (), приведены в табл. 9.1. Таблица 9.1. Значения перечисления т1скРоз1ъ1оп класса оянаег Константа Описание Вид ЫоТхскз Ползунок без рисок НН Т1скзАЬсгуе Отображение рисок на верхней стороне ползунка 1 » » 1 » 1 ——- V—— Т1скзВе1о^ Отображение рисок на нижней стороне ползунка | 1 1 * ‘ 1 * Т1скзВоЁ1151ае5 Отображение рисок на верхней и нижней сторонах ползунка — |> '! Метод зестхскхпсе^а! () задает шаг рисования рисок. Не следует задавать большое количество рисок, так как это приведет к появлению сплошной се- рой линии и не принесет никакой пользы. Приложение, изображенное на рис. 9.1, содержит виджеты ползунка и надписи, причем текст надписи изменяется в зависимости от положения ползунка. Рис. 9.1. Приложение, демонстрирующее работу ползунка
#1лс1и<1е <0ЪОи1> 1пЬ шахл (Зле агдс, скаг** агду) { ОАррНсаЫол арр(ахдс, агду); ОМхсЯдеЪ аддЪ; 031х4ег* рз1д = лею 031хс1ег (0^: :НогхзолЪа1); ОЬаЪе!* р1Ы = лею 0ЬаЬе1 (" 3 ") ; рз1д->зеЪКалде(0, 9); рзМ->зеЪРаде5Сер(2); рз1а->зеЪУа1ие(3); рз1а->зеЪТ1ск1пГ:егуа1(2) ; рз1а->зеЪТхскРозхехол (031хс1ег::ТхскзВе1ою); ООЬэ есЪ:: соллесЪ (рз1с1, ЗШЫАЬ (уа1иеСЪапдес1 (хпЪ)), р1Ы, ЗЬОТ (зеЪИит (хлС)) ); //ЬауоиЪ зеЪир ОНВохЬауои!:* рЪЬхЬауоиС = лею ОНВохЬауои!: ; ркЬхЬауоиЪ->аа<ан1аде1: (рзМ); рЪЬхЬауои!:->аасЗМ±адее (р1Ы) ; «дЬ. зеЪЬауоиГ: (рЪЬхЬауоиЪ); «дЪ.зкоюО ; геЪигл арр.ехес(); } В листинге 9.1 создаются виджеты ползунка (указатель рз1а) и надписи (ука- затель р1ы). После этого вызовом метода зеькапде () производится установ- ка диапазона значений ползунка от 0 до 9. Шаг страницы устанавливается равным 2 методом зеПРадегСер (). При помощи метода зеста1ие () можно задавать стартовое значение, исполь- зуемое для синхронизации с другими элементами управления, работающими вместе с ползунком. С его помощью можно сделать так, чтобы величины
виджетов совпадали друг с другом, а также он может служить просто для за- дания начального значения при первом показе элемента. Таким образом, в метод ползунка зесуа1ие () передается значение 3, синхронизирующее его со значением, отображаемым при создании надписи. Шаг для рисования рисок устанавливается равный двум, для чего в метод ползунка ее&Т1ск1псегуа1 () передается значение 2. Вызов метода зеСИсктагкз () ПРОИЗВОДИТ установку рисок снизу. Методом соппесС () сиг- нал ползунка уа1иеСЪапде<1 (1пС) соединяется со СЛОТОМ надписи зеСИшп (1пС). В завершение производится размещение элементов на поверхности виджета «где при помощи горизонтальной компоновки. Полоса прокрутки Полоса прокрутки — это важная составляющая практически любого пользо- вательского интерфейса. Она интуитивно воспринимается пользователем, и с ее помощью отображаются текстовые или графические данные, по размерам превышающие отведенную для них область. Используя указатель текущего положения полосы прокрутки, можно перемещать данные в видимую область. Он показывает относительную позицию видимой части объекта, благодаря которой можно получить представление о размере самих данных. Класс ОЗсгоИВаг является реализацией виджета полосы прокрутки. Он определен в заголовочном файле О^сгоПВаг и не содержит никаких дополнительных методов и сигналов, расширяющих определения класса ОАЬзСгассзХхдег. Отдельно полосы прокрутки используются очень редко. Они встроены в виджет фАЬзЕгасезсгоЫАгеа. Поэтому если вы намерены воспользоваться классом полосы прокрутки озсгоиваг, то не исключено, что лучшим вари- антом может оказаться использование одного из виджетов, наследующих ОАЬзЫасСЗсгоИАгеа. У объектов, унаследованных от озсгоиваг, можно вызвать контекстное ме- ню с параметрами навигации по умолчанию (рис. 9.2). Виджет полосы прокрутки имеет минимальное и максимальное значение, текущее значение и ориентацию. Перемещение указателя текущего положе- ния осуществляется с помощью левой кнопки мыши. В качестве альтернати- вы можно просто нажать на кнопки стрелок, расположенных на концах поло- сы прокрутки. Приложение, показанное на рис. 9.3, состоит из электронного индикатора и полосы прокрутки. Значение, отображаемое электронным индикатором, изменяется в зависимости от положения указателя текущего положения.
А Л л... й 5сгоП Неге 1еЙ ес!де ’ РидЫ ес!де Раде1еЛ Раде пдН ; 5сгс111еП 5сгсП пдЫ | рЭ* МЮ**»-* Рис. 9.2. Контекстное меню полосы прокрутки Рис. 9.3. Приложение, демонстрирующее работу полосы прокрутки #1пс1ис1е <ОСС5и1> 1пГ: ша1п (1пС агдс, скаг** агду) { рАррИсаЪз-оп арр(агдс, агду); ОЮлЛдеЁ одС; ОЪСШшпЬег* р1сб = пего ОЪСШшпЬег (4) ; ОЗсгоИВаг* ркзЪ = пею ОЗсгоИВаг (0^: гНогхгопСа!); ООЬд есЬ:: соппесЪ (рИзЬ, 31(ЖАЬ (уаХиеСЪапдеб (хпГ:)), р1сс1, ЗЬОТ (сНзрХау (1пС)) ); //ЬауоиЁ зеСир ОТВохЬауоиС* руЬхЬауоиС = пезд ОТВохЪауоиС; руЬхЬауоиП->а(ЯсШс1де1: (р1с<1); руЬхЬауоиЬ->аа<ЗМ1ддеЪ (рИзЬ) ; аддП. зеЪЬауоиЪ (руЬхЬауоиЬ) ; аддЪ.гез12е(250, 150); адд^.экою(); гееигп арр.ехес();
В листинге 9.2 создаются виджеты электронного индикатора (указатель р1са) и полосы прокрутки (указатель рИзЬ). После этого сигнал уа1иесЪапдеа () полосы прокрутки соединяется со слотом а1зр1ау () электронного индикато- ра, служащего для отображения значений целого типа, при помощи метода соппесь (). В завершение, виджеты электронного индикатора и полосы про- крутки размещаются вертикально, на поверхности виджета «де, при помощи объекта класса оувохьауоие. Установщик Класс ор1а1 виджета установщика определен в заголовочном файле рВ1а1. Этот виджет очень похож на крутящийся регулятор громкости радиоприем- ника, которым можно манипулировать при помощи мыши или. клавиш кур- сора. По своим функциональным возможностям он похож на ползунок. Раз- ница в том, что круглая форма этого виджета позволяет пользователю после достижения максимального значения сразу перейти к минимальному, и наобо- рот. Для разрешения или запрета прокручивания служит слот зееигаррхпд (). За отображение рисок отвечают метод зеемоьсътагдеь (), который устанав- ливает их количество, и слот зесыоьсЬеаУхзхЫеО, который управляет их видимостью. Приложение, представленное на рис. 9.4, содержит виджеты установщика и индикатора прогресса. Состояние последнего зависит от местоположения стрелки установщика. Рис. 9.4. Приложение, демонстрирующее работу установщика 7 Зак. 3983
.. !ЯистмнК»^ ' ». #1пс1ис1е «2ПСи1> 1пС гаахп (1пП агдс, сЬаг** агд^) { САррИсаПхоп арр(агдс, агду) ; ОЮхддеН >гдН; 0П1а1* рсНа = пею фЬха!; РРгодгеззВаг* рргЬ = пете ОРгодгеззВаг; рсИа->зеНКапде (0, 100); рд1а->зеНПоНсЬТагдеН(5); рд1а->зеНКоИсЪезУ131Ые (Нгие) ; ООЬ} есЬ:: соппесН (рсИа, ЗЮТАЬ (уаХиеСИапдед {1пС)), рргЬ, ЗЬОТ(зеНУаХие(1пП)) ); //ЬауоиП зеНир ОУВохЬауоиЪ* руЬхЬауоиС = пем ОУВохЬауоиН; рVЬx^ауоиЪ->ас^<3^^дде^ (рсНа) ; руЬхЬауоиН - >адсЗМ1ддеН (рргЬ); аддН. зеНЬауоиН (руЬхЬауоиС) ; утдЁ.гезхге(180, 200); «дС. зИоте () ; генит арр. ехес {) ; } В листинге 9.3 создаются виджеты установщика (указатель р<Иа) и индика- тора прогресса (указатель рргЬ). Вызовом метода зескапде () виджета уста- новщика задается диапазон значений от 0 до 100. Метод зеьыоссМагдее () устанавливает шаг рисования рисок равным 5, а слот зесноьсЬезУ1з1Ые () делает их видимыми, получив значение ыие. Сигнал виджета установщика Vа1иес11апдеа(^пе) соединяется при помощи метода соппесс () со слотом индикатора прогресса зесргодгезз (1пе). В завершение виджеты установщика и индикатора прогресса размещаются вертикально при помощи объекта класса дувохъауоиъ.
Резюме Виджеты настроек применяются в тех случаях, когда не требуется точная установка значений. Базовым классом для всех виджетов установки является класс ользсгассзиаег, который содержит основные методы для определе- ния диапазона, шага и текущего значения. Ползунок позволяет осуществлять настройки параметров приложения, на- пример, громкость звука, скорость движения указателя мыши и т. д. Этот виджет содержит риски, дающие пользователю визуально более четкое пред- ставление о шаге и месте нахождения. Полоса прокрутки позволяет отображать текстовые или графические данные, превышающие по размерам {Отведенную для них область. При помощи указа- теля текущего положения можно перемещать данные из невидимой области в видимую. Установщик очень похож на виджет ползунка, разница состоит во внешнем виде, в префиксах высылаемых сигналов и в возможности разрешения про- кручивания при переходе от максимального значения к минимальному.
Глава 10 Элементы ввода "Человек и компьютер — разные, и, увы, многие считают, что легче приспособить человека к компьютеру, чем наоборот". Чарльз Петцольд Группа виджетов элементов ввода представляет собой основу для ввода и редактирования данных — текста и чисел — пользователем. Большая часть элементов ввода может работать с буфером обмена и поддерживает техноло- гию перетаскивания (дга§ & скор), что избавляет разработчика от дополни- тельной реализации. Текст можно выделять с помощью мыши, клавиатуры и контекстного меню. Однострочное текстовое поле Этот виджет является самым простым элементом ввода. Класс ^ьхпеЕаК: од- нострочного текстового поля определен в заголовочном файле.(ДлпеЕсШ. Текстовое поле состоит из прямоугольной области для ввода строки текста, поэтому не следует использовать этот виджет в тех случаях, когда требуется вводить более одной строки текста. Для ввода многострочного текста имеет- ся класс отехьЕсИс. Текст, находящийся в виджете, возвращает метод сехь (). Если содержимое вид- жета изменилось, то высылается сигнал еехьсиапдед!). Сигнал гехигпРгеззеа() уведомляет о нажатии пользователем клавиши <Еп1ег>. Вызов метода зеснеадОпХу () с параметром сгие устанавливает режим "только для чтения", в котором пользователь может только просматривать текст, но не редактировать. Текст для инициализации виджета можно передать в слот зеетехъ (). Для однострочного текстового поля можно включить режим ввода пароля, который устанавливается вызовом метода зееЕсЬомоаеО с флагом раггиога.
В результате этого вводимые символы не отображаются, а заменяются сим- волом *. Программа, показанная на рис. 10.1, имеет два однострочных поля, одно из которых установлено в режим ввода пароля. Вводимый в это поле текст ото- бражается в виджете надписи. Рис. 10.1. Программа с однострочными полями ввода #1пс1ийе <0ЬОи1> шахп (1пЪ агдс, скаг** агд^г) { 0Арр11саЁ1оп арр(агдс, агду); 0Ю1с1деЁ. ’идЁ; 0ЬаЬе1* р1ЫП1зр1ау = пею 0ЬаЬе1; р1ЫР1зр1ау->зе1:Гга1пе31:у1е (ОРгате:: Вох | ОРгате:: Кахзей) ; р1ЫП1зр1ау->зеЪЬ1пеЮ1<М1 (2) ; р1ЫВ1зр1ау->зе!:Г1хес1Не1д11(1(50) ; 0ЬаЪе1* р1ЫТехЪ = пезд ОЬаЬе! ("&ТехЪ: ”) ; ОЫпеЕсНЪ* рСхС = пего ОЫпеЕсИС; р1ЫТех1:->5еЪВисЦу (рехЪ) ; ООЬд ес1:::соппесЪ(рехС, 51СЫАБ(еехССИапдеб(сопзе ОЗСгхпд&)), р1ЫС13р1ау, 5ЬОТ (зеСТехЬ (сопзС фЗег 1пд&))
); 0ЬаЬе1* р1ЫРаззюог<3 = пем <2ЬаЬе1 ("&Раззюог<Я:и) ; ОЫпеЕсИе* рЪх^Раззюогд = пею ОЫпеЕсЗлХ; р1ЫРаззюога->зе(:Вис1с1у (р^х^Разз^огй) ; р!:х1:РаззУ7огд->8еГ:Ес]1оМоде (ОЬхпеЕсНЪ: :Раззшогс1) ; ООЬэ есг:: соппесИ (рЁхГРаззюогсЬ 51СЫАЬ (Сех^СИапдед (сопзё. 05(:г1пд&)), р1ЫС1зр1ау, ЗЬОТ(зеСТехС (сопз!: 03ег1пд&)) ); //ЬауоиЪ зейир ОУВохЬауоиЪ* р^хЬауои!: = лею ОТВохЬауоиС; р^хЬауои1:->адс1И:1с1де11 (р1ЬЮ1зр1ау); р^ЪхЬауоиС~>а<1сЗМ1дде11 (р1ЫТехЪ); рVЬx^ауоие->ад<^^^дде^:(р^x^:) ; р^хЬауоиЬ->асМИ1с1де1: (рТЫРазз^огд); р^ЬхЬауоиЪ->адсПлГ1дде11 (р^х^Раззмэгд) ; адде. зе^ЬауоиЪ (руЬхЬауоие); адд^:. экою (); гееигп арр.ехес(); } В листинге 10.1 создается виджет надписи (указатель р1ЬЮ1зр1ау). Метод зесргашео устанавливает стиль рамки, а зееЫпеИ1аеъ()—ее толщину. Высота виджета надписи фиксируется с помощью метода зеЬРххеднехдИг (). Еще две надписи, ртытехс и р1ЫРаззиога, связываются с виджетами одно- строчного текстового поля методом зесвиаауо. Затем сигналы ьехссиапдеа() соединяются со слотами зеетехе () виджета надписи р1ЬН>1зр1ау для ото- бражения вводимого текста. И, в завершение, виджеты размещаются верти- кально при помощи объекта класса отвохьауоиъ. Количество вводимых символов можно ограничить методом зесмахьепдси (), передав в него целое значение, ограничивающее максимальную длину стро- ки. Для получения текущего максимального значения длины существует ме- ТОД тахЬепдек (}. Класс оыпеЕахе предоставляет следующие слоты для работы с буфером обмена: □ сору () — копирует выделенный текст; □ си с () — копирует выделенный текст и удаляет его из поля ввода;
□ раз ее о —вставляет текст (начиная с позиции курсора), стирая выделен- ный текст. Метод ипао () отменяет последнюю проделанную операцию, а метод геао () повторяет последнюю отмененную. Узнать, можно ли воспользоваться опе- рациями отмены и повтора, можно с помощью методов 1зппас^аИаЫе() и хзКеаоАуаИаЫе (), возвращающих булевы значения. Правильность ввода гарантируется при помощи специального объекта- контроллера (уаПдаСог), пример реализации которого рассмотрен в этой главе вразд. "Проверка ввода". Редактор текста Класс отехЬЕсШ: позволяет осуществлять просмотр и редактирование как простого текста, так и текста в формате НТМЬ. Он унаследован от класса ОАЬзсгасевсгоИАгеа, что дает возможность автоматически отображать по- лосы прокрутки, если текст не может быть полностью отображен в отведен- ной для него области. Класс отехсЕ<ие содержит следующие методы: □ зескеабоп1у () устанавливает или снимает режим блокировки изменения текста; □ ьехь () возвращает текущий текст. А вот и некоторые его слоты: □ зесмаХптехс () — установка обычного текста; □ зеснип1 () — установка текста в формате НТМЬ; □ соруО, сисо и раз ее о —работа с буфером обмена (копировать, выре- зать и вставить, соответственно); □ зе1ессди () или аезеХесс () — выделение или снятие выделения всего текста; О с!еаг () — очистка поля ввода. И сигналы: О пехесиапдеа () —высылается при изменении текста; □ зе1есс1опсъапдеа() — высылается при изменениях выделения текста. Для работы с выделенным текстом служит класс отехьсигзог, и объект этого класса содержится в классе отехеиаи:. Класс отехьсигзог предоставляет ме- тоды для создания участков выделения текста, получения содержимого вы- деленного текста и его удаление. Указатель на объект класса отехъсигзох МОЖНО ПОЛУЧИТЬ ВЫЗОВОМ метода ОТехСЕахЬ: : СехЬСигзог ().
Виджеты класса отехЪЕйдх также содержат в себе объект отехьпоситепс, указатель на который можно получить посредством метода ОтехьЕйхЬ: :йоситепь (). Можно также присвоить другой документ, при помощи метода отехЪЕсНъ:: зеыэоситепь (). Класс отехъоосшпепс предостав- ляет слот ипдо () (для отмены) или гедо () (для повтора действий). При вы- зове СЛОТОВ ипйоО И гебо() посылаются сигналы ипйоАуа11аЫе(Ьоо1) и гедоАуа11аЫе(Ьоо1), сообщающие об успешном (или безуспешном) про- ведении операции. Эти сигналы высылаются как из отехъпоситепъ, так и из отехсваи:. В большинстве случаев удобнее будет использовать сигналы класса отехеЕахъ. Большинство методов класса отехевадх являются делегирующими для клас- са отехьоосишепь. Например, как уже было сказано ранее, класс отехСЕеНс способен отображать файлы с кодом на языке НТМЬ, содержащие таблицы и растровые изображения. Для его размещения и показа можно воспользо- ваться методом зеснып1 (), в которой передается строка, содержащая в себе текст в формате НТМЬ, или воспользоваться слотом 1пзегснт1 (). Эти ме- тоды определены в обоих классах, и их вызов из объекта класса (уГехЙЕсШ приведет к тому, что будет вызван аналогичный метод из объекта класса (ЗТехЮосшпет. Для помещения обычного текста в область виджета можно воспользоваться методом зеер1а1птехь() или слотом 1пзегеР1а1пТехъ (). При помощи слота аррепао производится добавление текста, причем добавленный текст не вносится в список операций, действие которых можно вернуть с помощью слота ипбоо, что делает этот слот быстрым и не-требующим дополнитель- ных затрат памяти. Метод Ипао может быть использован для поиска и вы- деления заданной строки в тексте. Примечание Класс отехЬЕйхЪог можно использовать совместно с классом ОЗупСахНхдИИдксег для подсветки синтаксиса. Слоты 2оош1п () и гоотОиь () предназначены для увеличения или уменьше- ния размера шрифта, и их действие не распространяется на растровые изо- бражения. Примечание Если вам требуется только отобразить текст в формате НТМЦ то, возможно, лучше воспользоваться классом оьаЬе1 (см. главу 7}. Приведенный на рис. 10.2 пример отображает ЬппЬдокумент. Текст докумен- та можно редактировать.
ТНЕ МУ1ЫЫЕП ТАКЕ8 ГГ А1,Ь (В.Алс1ег85оп/В.СН*аеи8) АЬснМ&е 1№кд$ &е\е доле &гоид/* Т&а иг Ж /г Ч Л///Лл/7 /ш* Рис. 10.2. Программа, отображающая ГПт1-документ #1пс1ис1е «ЭС<ЗиЛ> 1пё таЛп(1пГ. агдс, скат** агду) { 0Арр1хсаЪЛоп арр(агдс, агду); • 0Тех11Е<Ш1 СхЪ; СхЬ. зеСНетЛ ( “ <НТМЬ> " ”<ВОПУ ВОСОЬОК=МАСЕЫТА>" "<СЕЫТЕК><1№ ЗНС=\"адпеЪЪа.зрд\"></СЕОТЕК>” "<Н2><СЕЫТЕК>ТНЕ ШИНЕН ТАКЕЗ 1Т АЬЬ</СЕМТЕКх/Н2>” "<НЗ><СЕНТЕК> (В. Апс1егззоп/В.П1уаеиз) </СЕКТЕК><НЗ>" "<ЕОЫТ С0Ь0К=ВШЕ>" "<Р АЫОЫ=\мсепеег\и>” ’ "<1>" ”1 доп’Ъ юаппа Ъа1к<ВК>" "АЬоиЪ Пке сЫпдз юе’уе допе Ы-1ГоидЪ<ВВ>" "ТЬоидк Ле ’з ИигеЛпд ше<ВК>п "Ыоте Ле'з кЛзеогу<вк>" ”1’уе р1ауед а!1 пгу сагаз<ВЯ>"
"АпсЗ екаС'з юкаЪ уои^е Допе Ъоо<ВК>" "Ыо^Ыпд тоге Со зау<ВК>" нЫо тоге асе Со р1ау<ВН><ВН>м "ТЬе ю!ппег Сакез 1С а11<ВК>” "ТЪе 1озег зЪапсИпд зта11<ВВ>" "Везхде СЪе ^сПогу<ВВ>" "ТкаС'з Ъег с1езС1пу<ВН>" ”</ГО1ЯТ>“ "</ВООУ>" "</НТМЬ>" >; СхС.гез12е(320, 250); СхС.зкотеО ; геСигп арр.ехес(); В листинге 10.2 создается виджет верхнего уровня схе. Метод вепнип! () ус- танавливает в виджете отехСЕШс текст в формате НТМЬ. Расцветка синтаксиса (зугйах ЫдЫ1дМ1пд) Расцветка и форматирование способствует более удобному восприятию структуры текста программы. Добавить расцветку синтаксиса в отехсЕШе очень просто, для этого нужно унаследовать класс озупеахнхдЫхдьеег, и реализовать в унаследованном классе метод ЫдЫ1дНев1оск(). Следую- щий простой пример выделяет цифры в тексте красным цветом: /^1ГЁиа1*/ тохс! МуНхдЪИдкСег: :ЫдЫ1д11СВ1оск(сопзП 25Ъг1пд& зег) { 1ог (1пЪ 1 = 0; 1 < зегЛепдихО; ++1) { (зЪг.аШ) ЛзЫитЬег()) { зеЪРогтаЬ(1, 1, :гес!) ; } } } Как правило, в метод ЫдИИдЪев1оск() передается одна строка текста. В первом параметре метода зесРоппаъо мы передаем стартовое значение,
второй параметр задает количество символов (в нашем случае 1), а в послед- нем параметре мы передаем цвет для расцветки (в нашем случае ос:: гей). Вместо цвета в последнем параметре можно также передавать и шрифт (ОГопе, см. главу 20). Для того чтобы применить объект класса расцветки к объекту отехсвахь, нужно при создании передать ему указатель на объект отехгоосшпепе. Например: МуНхдЪНдЪСег* рНхдЫхдкеег = п&я МуНхдИИдИСег (рСхЬ-хЪстлпепС ()) ; Как видно из примера, для применения расцветки синтаксиса нужен указа- тель на объект класса отехьпосишепс, а это значит, что применение расцветки не ограничивается только классом отехсвахе, и ее можно применять ко всем классам, имеющим в своем распоряжении объект класса отехыэосшпепс. В ЧИСЛО таких классов входят, например, ОТехСВгошзег, ОТехИРгате, ОтехстаЫе, класс элемента текста осгарЪхезтехътсет графического пред- ставления (см. главу 21) и другие классы. Теперь, когда нам стал ясен принцип применения класса огупеахнхдЪИдьеег, перейдем к более сложному примеру и реализуем виджет, который делает расцветку программ на С++ в стиле Вог1ап(1 (см. рис. 10.3). / Спи г. Зе * л г«*. 1 иНе "*.•>пГ .-1 ।лЮ тн’чт е»'. 5уп1ахН1дЬ11дИ1ег: :5угП<м(Н1дН!1{|ЬГег((}Тех10осигпеп1* рагеп! ** /) : Ц'ЬупкахНчдМтдЫег(рагеп!) { 1 с. > / ШПСЛ идНВИснк(соп$1 51г) { чТаГе -« ргеи1ои5В1оскМа1еО; 1П1 О; Рог 1 » 0; 1 < 51г. НепдИЦ); { 1Т (гГгЛс Тл;1дг<5Гу1еСсхптепГ) { ?) == { 51а1е = Ыигпл! 5-Га1е; 5е1Рогта1(ь1аг I» » - $1аг1 ♦ 2, (Ц *. :йаг1.бгау); } > еНе тР (>ТаГе = Хл'ийеОГНг.д) ( И , I) « "V’" II 51Г.Й1 (1 ( 1 , 1) == "V ’) ( = Нагта151а1е; ье1РогтаЬ(51аг1, т - хСаг! + 1, (Н::суап); Рис. 10.3. Расцветка синтаксиса
#1пс1ис1е <0ЁОи1> # 1пс1иде " ЗупЁахНхдЫ ХдЫхег. И" 1пС шахп (1пЪ агдс, скаг** а^дV) { ОАррИсаЁхоп арр (агдс, аг^); ОТехШНЁ ехГ; ОРопГ ^пЬСЬисхда Сопзо1е”, 9, ОРопё: :Ыота1) ; СхГ:. скхялпепЪ () ->зеГЯ)е^аи1ЪРопЁ (СпЬ); пего 5упГахН1дЪИд111:ег (ГхГ:. с1оситепЁ ()) ; 0Ра1еГХе ра1 = ГхЪ.ра1еСЪе() ; ра1. зеЪСо1ог (2Ра1еГХе: :Вазе, ОГ:: :<ЗагкВ1ие) ; ра1 .зеПСо1ог(0Ра1еГХе: :ТехЪ, 01:: :уе11ош) ; ехГ.зе^Ра1еЫ:е(ра1); СхГ:. зИою () ; ЪхГ:. гезхге (640, 480); ОРИе Ше (": /ВупеахНхдИИдкеег .срр"); ^Пе. ореп (ОРИе::Кеас1Оп1у); ЪхП. зе^РХахпТехГ: (0Ьа1:1п15Ёг1пд (СИе. геа<ДА11 ())) ; гееигп арр.ехес(); } В основной программе (листинг 10.3) мы создаем виджет ехе класса ОТехЪЕсНъ — это и будет наш редактор. Затем мы создаем объект шрифта Епс и устанавливаем его в нашем редакторе вызовом метода зееое^аиХеРопс () из объекта документа редактора. Далее создаем объект расцветки синтаксиса созданного нами и описанного чуть позже класса гупсахнХдЪИдЬСег и пере- даем ему в качестве предка указатель на объект документа нашего редактора; этот объект позаботится о его уничтожении при своем разрушении. Создаем объект палитры ра1, он нам нужен, чтобы установить цвет шрифта (желтый)
и фона (темно-синий) по умолчанию. Отображаем наш редактор вызовом метода зкои() и задаем его размеры методом гезхге (). Исходный файл 8уп- (ахН1§Ы1§Ь1ег.срр включен нами в ресурс, поэтому при его открытии методом ореп () нам не нужно проверять успешность этой операции. Его текст считы- вается методом геадди () и устанавливается в нашем редакторе вызовом ме- ТОДа зе^ПахпТехС (). # 1 _5уп^ахН1дИ11дИ{1ег_Т1_ #с1е^1пе _ЗупеахН1дИ1хдЪЪег_11__ #хпс1ис1е <05уп1:ахН1д1111дк^ег> с1азз ОТехиЗосшпепЬ; // ====================================================================== с1азз ЗупЪахНхдЪИдИЪег: риЬИс рЗупЬахНхдкЫдИЬег { О_ОВСГЕСТ ргоЪесЬед: епиш { Ыогтпа15Ёаее = -1, 1пз1сЯеС5Ъу1еСаттепЁ, ТпзхдеСЗЁгхпд }; V^^I:иа1 тоМ ЫдЫ1дЫ1В1оск (сопзЬ 05Ъг1пд&) ; ОЗЁгхпд деЪКеуюогс! (1, сопз11 031:г1пд& зЪг) сопзЪ; риЬИс: ЗупЬахНхдИИдЪЬег(ОТехЬВоситепЬ* рагеп11 = 0); }; #епдх€ //_ЗупЪахНхдМ 1дЫ:ег_И_ В заголовочном файле (листинг 10.4) наш класс зупъахНхдЫхдЬеег наследу- ется от озупьахНхдЪИдИЪег. В нем определены перечисления моппатзгале, 1пз1десзьу1есопш1епъ и 1пзхаесзегхпд, они нам понадобятся далее для опре- деления текущего состояния фрагмента. Мы так же перегружаем метод ЬхдЫ1дЫ:в1оск(), который нам необходим для реализации собственной расцветки синтаксиса. Метод дескеуадога () — это наш эксперт, который нам будет давать ответ на вопрос, является ли строка на указанной позиции клю- чевым словом языка С++ или определением 01.
ЗупСахНхдМхдЬЪег::ЗупЪахНхдЫхдЫег (ОТехЪВоситепЪ* рагепе/*= 0*/) : ОЗупеахНхдЫхдЬЬег (рагепЪ) { } В конструкторе не происходит ничего особенного, мы просто делегируем переданный в наш класс указатель передка дальше к унаследованному классу (листинг 10.5). /^хгЁиа1*/ тохс1 ЗутЛахНхдЫхдЬЪег: :Н1дк11дЪ1:В1оск(сопзе рЗЁгхпд& зСг) { хпЬ пЗЪаЪе = ргетз.оизВ1оскЗГ:а(:е () ; 1ПЁ пЗЬагЪ = 0; Сог (хпЬ х = 0; х < зег.1епд!21(); ++х) { х^ (пЗЁаЪе == 1пзхдеС5Ьу1еСсотпепЬ) { х^ (зЫ.шхаСх, 2) == "*/-) { пЗЪаСе = Ыогта15ЪаСе; зе!:ГоппаП(пЗЁагЁг х - пЗГагЁ + 2, ОЬ: :6агк0гау); Х++; } } е1зе хС (пЗСаЪе == ТпзхЗеСЗСгхпд) { х! (зЪг.тхаСх, 1) == "\*" || зёг.шхсЦх, 1) == { х! (зСг.01x3(1 - 1, 2) != "\\\"" && зСг.тхсЦх - 1, 2) != "\\\*" . ) { пЗ^аЪе = Ыогта13ЪаСе; зеЁГогтаНпЗЁагЪ, х - пЗИаге + 1, ое: :суап) ; } } } е1зе { х€ (зЬг.тхсЦх, 2) == "//") { зеЪГоппаЪ(1, зЪг.1епд^Ъ() - х, ре::ЗагкОгау); Ьгеак:
е1зе (зГг.тхсКх, 1) == "#") { зеГРоппаГ (1, зГг < 1епдГЪ () - 1, рг:: дгееп); Ьгеак; } е1зе 1С (зГг.аГ(1) ЛзЫшпЬег()) { зеГРоппаГ (1, 1, ©Г: :суап) ; } е1зе 1С (зГг.пи. (3.(1, 2) == ’’/*") { пЗГагГ = 1; пЗГаГе = 1пз1деС8Гу1еСоттепГ; } е1зе 1С (зГг.пйсЩ, 1) == "V" || зГг.пйсЩ, == ”\’”) { пЗГагГ = х; пЗГа’Ге = 1пз1с1еС8Гг1пд; } е1зе { ОЗГгхпд зГгКеуюогсЗ = деГКеуотогд (1, зГг); ЦЕ (! з ГгКеуиюгЗ. 1зЕпрГу ()) { зеГРоппаГ (1, зГгКеуиюиЗ.. 1епдГ11 (), ОГ:: юЫГе) ; 1 += зГгКеутеогсЗ. 1епдГк() - 1; } } } } И (пЗГаГе == 1пз1(1еСЗГу1еСоп«пепГ) { зеГРогтаГ(пЗГагГ, в Гг.1епдГИ() - пЗЪагГ, ОГ::дагкСгау); } 1^ (пЗГаГе == 1пз1с1еСЗГг1пд) { зеГРогтаГ(пЗГагГ, зГг.1епдГк() - пЗГагГ, 0Г::суап); } зеГСиггепГВ1оск5ГаГе(пЗГаГе); } С принципом реализации метода ЫдЫ1дЬев1оск () мы уже успели позна- комиться, поэтому остановимся только на основных моментах. В метод ЫдЪИдЪсв1оск() передается только одна строка текста, но не все концепции синтаксиса ограничиваются одной строкой. Поэтому мы ввели для этих слу- чаев три состояния: □ могтоаХБЬаье — нормальное состояние, при котором должна использо- ваться расцветка, задаваемая нашей палитрой (см. листинг 10.3);
□ 1пз1десзъг1пд — состояние, в котором текущая позиция находится внут- ри строки, в этом случае цвет текста должен быть бирюзовым ое:: суап; □ 1пз1<Зес$еу1еСоттепс— состояние, когда текущая позиция находится в комментарии вида /*„*/, в этом случае цвет текста должен быть темно- серым ОС::дагкСгау. Текущее состояние мы устанавливаем в переменной пзсаее (см. лис- тинг 10.6), а получаем его вызовом метода ргеухоизвтоскзгаке (). Самым первым в цикле мы контролируем моменты завершения состояний 1пз1аесгсу1есо1шпепъ и 1пз1аеС5ъг1пд. Для завершения первого мы проверя- ем на текущей позиции строку "*/• и, если находим ее, присваиваем пере- менной пзьасе состояние Могта1зъасе и докрашиваем строку в темно-серый цвет, после чего увеличиваем переменную 1 на единицу, так как следующий символ мы уже обработали. В отслеживании наступления состояния конца строки шзхаесзегхпд мы сна- чала проверяем наличие символа кавычек (" либо •), а потом следим за тем, чтобы этому символу не предшествовал символ \, так как иначе, встретив строку вида \*, расцветка синтаксиса неправильно интерпретирует ситуацию и раскрасит косую черту как элемент строки, а кавычку поймет как конец строки. Если последние контрольное условие выполняется, то строка завер- шилась, изменяем статус на нормальный и докрашиваем ее последний сим- вол в бирюзовый цвет. В третьей секции основного мы контролируем наступления состояний, сравнивая текущую позицию в строке с различными символами, которые должны менять форматирование. Первые три сравнения, как и последнее, не производят смену состояния, так как не могут выходить за пределы одной строки. Это однострочный комментарий вида //, директивы препроцессора, начинающиеся с символа #, цифры (определяются с помощью метода ОзегХпд:: хзНимЬег ()) и ключевые слова языка С++ и определения (X Клю- чевые слова определяются при помощи реализованного нами (листинг 10.7) метода деъкеуадога (). Этот метод возвращает само ключевое слово, которое он нашел на заданной позиции. Это слово мы раскрашиваем в белый цвет и увеличиваем переменную 1 на его длину, так все символы слова были отра- ботаны. Дня смены состояния на 1пзхаесзеу1есоттепь или хпз1аесзсг1пд достаточно нахождения на текущей позиции строки /* либо символа " соответственно. В случае если цикл завершился, и мы находимся в текущем состоянии 1пз1аесзсу1есоштепь либо тпзхаесзыхпд, мы закрашиваем наш блок с на- чала позиции смены состояния и до самого конца. В завершение мы устанав- ливаем текущее состояние методом зеъсиггепъзсаее ().
ОЗСгхпд ЗупЬахНхдЫхдЪеег:: деПКеуздогд (хпС пРоз, сопзЪ 28ег±пд& зЪг) сопзе ( ОЗСгхпд зСгТеюпр = ОЗЪгхпдЬх з е 1з 1:Кеу^ог6з; 1з11Кеуюог(1з « "^огеасЬ" « "Ъоо1" « ИХПМ" « "тохд" « '’боиЫе” « ”ПоаЪ" « исИаг" « ”с1е1еЁе" « "с1азз" « "сопзС" « ^хгГиа!'' « "ти^аЫе" « " ЬИхзи « "зегисЪ" « "ипхоп” « "ГИгою" « "^ог" « "И" « "е1зе" « И^а1зе" « "пашезрасе" « "зупХсЬ” « "сазе” « "риЫхс" « "ргдлтаЁе" « "рго^ес^ей" « "пею" « "геЁигп" « "изхпд” « ”11гие” « •• _>« « И» II « « « И< II « и рг « « П { " « И } « « II [ Н « « И 4- п « и _ н « И * « « И /II « II „ п « И | « « П II « И п г « г « и . и « « "етхЪ" « "соппесЪ" « "ЗВЗКАЬ" « II | и « "ЗЬОТ” « ”510^3" « ”зхдпа1зИ 7 Согеаск (рЗЪгхпд з^гКеумогсЗ, 1зСКеу^огдз) { хЕ (зег.тхб {пРоз, зЪгКеуюогсЗ.. 1епдЪ11 ()) == зЪгКеуюогй) { зЪгТеюпр = зЪгКеуюогд; Ьгеак; ) геШгп зЁгТетпр; Назначение метода деъкеукох-а () заключается в нахождении ключевых слов языка С++ и определений 01. Для этого мы их все вносим в объект списка 1гскеу»ога и всякий раз при вызове метода пытаемся найти в цикле гогеасй совпадение с элементами списка согласно текущей позиции и размера клю- чевого слова. При удаче ключевое слово присваивается промежуточной строковой переменной зьгтетр, цикл прерывается (Ьгеак), и значение этой переменной возвращается в качестве результата. Надо отметить, что приведенный алгоритм несовершенен и неправильно об- рабатывает целый ряд ситуаций, например, не отличает цифр в идентифика-
торах от численных значений. В редакторах, поддерживающих подсветку синтаксиса, применяются, как правило, более сложные алгоритмы, рассмат- ривать которые здесь не представляется возможным; этот пример лишь ил- люстрирует работу С классом ОЗупЪахНхдйИдЪГег. С чего начинаются виджеты счетчиков Виджет абстрактного счетчика — класс олЬзыассзрхпВох предоставляет всем унаследованным от него классам небольшое текстовое поле и две стрелки для уменьшения или увеличения числовых значений. От этого вид- жета унаследованы следующие классы (см. рис. 5.1): □ 08р1пвох — счетчик; □ 0Оасет1теЕа1ь — элемент для ввода даты и времени; □ 0поиЫезр1.пвох — элемент для ввода значений, имеющих тип аойЫе. Можно установить циклический режим, когда за максимально возможным значением будет следовать минимально возможное, и наоборот. Этот режим устанавливается вызовом метода зеенгарргпд () с параметром ъгие. Реализованы два метода пошагового изменения значений зсерпр () и зсерпоюп (), которые симулируют нажатие на кнопки стрелок. С помощью метода ееезрес1а1Уа1иетехс () устанавливается текст, незави- симо от числового значения, например: рзрЬ->зеСЗрес1а1Уа1иеТехЬ("деЕаи!^"); Счетчик Виджет озрхпвох предоставляет пользователю доступ к ограниченному диа- пазону чисел. Все вводимые значения проверяются, для предотвращения вы- хода за пределы установленного диапазона, который устанавливается мето- дом геькапдеО. Значения можно устанавливать с помощью метода зеРУа1ие()', а получать — методом уа1ие(). При изменении значений посы- лаются сразу два сигнала Vа1иес11апдеа(): один с параметром типа 1пь, а Другой — С сопзР 05Ьг1пд&. Можно изменить способ отображения с помощью методов зееРгеЯхО и геезиШхО. Например, вызов следующих методов приведет к тому, что число будет отображаться в скобках: рзрЬ->зее₽геЕ1х("(">; рзрЬ->зеЕЗиЕЕ1х(")");
Можно изменить изображение стрелок на символы + (плюс) или - (минус), передав методу зеЁВиЬ^опЗушЬоХз () флаг Р1изМ1пиз. На рис. 10.4 изображен пример, позволяющий выбирать и устанавливать числа в диапазоне от 1 до 100. Рис. 10.4. Пример счетчика #1пс1ийе <0ЁСи1> 1пЪ тахп (1пё агдс, скаг** а^дV) { ОАррИсаЁхоп арр(агдс, агду) ; 05р1пВох зрЬ; зрЬ. зеСКапде(1, 100); зрЬ. зеГЗи^хх(” МВ"); зрЬ.зеЁВиЁЪоп5утЬо1з (05р1пВох:: Р1изМ1пиз); зрЬ. зеШгаррхпд (Ъгие); зрЬ.зЪошО ; зрЬ.гезхге(50, 30); ОАррИсаСа-оп:: зеЁ5еу1е (пею 0Ю1паоюз5Ёу1е) ; геСигп арр.ехес(); В листинге 10.8 создается виджет счетчика зрЬ. После его создания произво- дится установка диапазона при помощи метода зегкапдео. Вызов метода зесзи^ЛхО добавляет строку " мв" после отображаемой счетчиком величи- ны, а метод зеьвиьеоп3ушьо1з (), которому передается флаг Р1изм1пиз, заме- няет стрелки счетчика знаками +/-. Метод зеенгаррхпд() устанавливает циклический лежим.
Элемент ввода даты и времени Этот виджет состоит из нескольких секций, предназначенных для показа и изменения даты и времени. При изменении даты или времени посылается сигнал ааьетЧтесъапдеа (). Для класса ооасеТхтеЕсНе этот сигнал передает константную ссылку на объ- ект типа ООаСеТхше. Пример, изображенный на рис. 10.5, отображает актуальную дату и время запуска программы, которые можно модифицировать. Рис. 10.5. Дата и время #1пс1ис1е <0ЪОи±> 1п11 тпахп (хпС агдс, сЬаг** агду) { 0Арр1хсаЁ1оп арр(агдс, агду); РВаЪеТттеЕЛь йаееТ1теЕд1С (ОПаЪеТте:: сиггеп^Ва^еТтте ()) ; с1аЪеТ11пеЕ(1111. зкою () ; геСигп арр.ехес{); } Как видно из листинга 10.9, при создании виджета опаСаТэ-теЕДдх в его кон- структор передается текущая дата и время, которая возвращается вызовом статического метода опасетЫе:: сиггепсоаьетхше (). Виджет отобразится на экране после вызова метода гкои (). Проверка ввода Объект класса ОУаИДаЬог (далее контролёр) гарантирует правильность ввода пользователя. Для установки объекта класса <2УаИДаЪог необходимо передать его в метод зееУаИДасог (), который содержится в классах ОСотЬовох и <2ЫпеЕД1Г. Для проверки ввода чисел пользуются готовыми
классами отпеуаиаасог и овоиЫеУаИаапог. Создавая свой класс проверки ввода, нужно унаследовать класс от ОУаИаасог и перезаписать метод Vа1^аа&е (), в который передается вводимая строка и позиция курсора. Ме- тод должен возвращать следующие значения: О ОУаиааьог ••: 1тгаПа — если строка не может быть принята; □ ОУаиаасог: :АссерпаЫе — если строка может быть принята без изме- нений. В примере, изображенном на рис. 10.6, пользователю предлагается ввести свое имя, и цифры в этом случае недопустимы. Это отслеживается классом контролера, который не допускает того, чтобы имя содержало цифры. Рис. 10.6. Программа, контролирующая данные, вводимые пользователем с1азз ЦатеУаНйаеог : риЫхс ОУаИйаСог { риЪЫс: ЫатеУаИдаЬог:: ЫашеУаИдаЪог (ООЬ^ есЪ* рагепЪ) : ОУаИда^ог (рагепС) { } V^^Ьиа1 ЗЪаее уаИааСе(03Ъх1пд& зег, 1пС& роз) сопзё { фКедЕхр гхр = фКедЕхр(”[0-9]"); (зиг.сопЪахпз(гхр)) { геЪигп 1пуаИй; } геСига Ассео^аЫе;
Как видно из листинга 10.10, класс ыатеУаИйаьог унаследован от класса <2УаИдачог. В этом классе производится перезапись метода уаиааеео, ко- торый получает вводимый текст и позицию курсора. Внутри метода создает- ся объект регулярного выражения гхр (см. главу 4), в конструктор которого передается шаблон, представляющий собой диапазон цифр от 0 до 9. В опе- раторе вызовом метода озыхпд: .-сопеахпзО производится проверка строки на содержание в ней заданного шаблона. В случае, если совпадение найдено, метод возвращает значение тпуаиа, сообщая, что ввод не удовле- творяет заданным критериям, в противном случае возвращается значение ДссерЪаЫе и ввод принимается. хпС шахп (хпе агдс, сИаг** агд^) { ОАррИсаехоп арр(агдс, агду) ; ОИхйдеЪ зддЁ; 0ЬаЬе1* р1ЫТехе = пеад рЬаЬе! (”&Мате (ТЬе дхдхез «х11 по С Ье ассерЪесН ) ОЫпеЕсЦЪ* рЪхе = пею ОЬхпеЕЛе; ЫатеУаИааЪог* рпатеУаИдаеог = пею ЫатеУаИЗаеог (рЬхЪ) ; • рехЪ->зееУа1хсЗаеог (рпатеУаШаСог У ; р1ЫТехе->зеСВидс!у (рехС); //ЬауоиЁ зееир ОУВохЬауоие* риЪхЬауоиЁ = пею ОТВохЬауоие; руЬхЬауоиЪ->адсЗМхс1деЪ(р1ЫТехе) ; р^хЬауоие->аа<ЭЮхддее (рГхП) ; «де. зееЬауоие (руЬхЬауоие); «дЪ.зЪо«(); геЪигп арр.ехес(); } В листинге 10.11 создается виджет надписи (указатель р1Ытехс), виджет од- нострочного текстового поля (указатель рехе) и объект контролера (указа- тель рпатеУаИдаЬог). После создания, вызовом метода зеьУаИдаьог о кон- тролер устанавливается в виджете однострочного текстового поля (указатель ръхс), а последний связывается с надписью вызовом метода зеъвиаау ().
Резюме В этой главе мы познакомились с группой виджетов, позволяющих пользова- телю осуществлять ввод текста и числовых значений. Большая часть этих виджетов обладает всеми необходимыми методами для работы с буфером обмена, а также поддерживает технологию перетаскивания (<1га§ & дгор). Класс оыпеЕЛь предназначен для ввода одной текстовой строки. Для мно- гострочного ввода следует использовать класс отехьвазх. Этот класс унасле- дован от класса ОАЬзъгасьзсгоПАгеа, что позволяет им отображать текст частями, если для отображения всего текста недостаточно места. Виджет счетчика (озрхпвох) применяется для ввода числовых значений. Он осуществляет контроль, чтобы вводимые значения не выходили за пределы установленного диапазона. Для проверки правильности ввода данных в виджетах классов осошЬовох (см. главу 11) и оЫпеЕсНъ можно устанавливать объект контролера с по- мощью метода зеЬУаНОаСог ().
Глава 11 Элементы выбора "Если вам предстоит сделать выбор, а вы его не делаете — это тоже выбор". У. Джеймс Элементы выбора представляют собой стандартные элементы графического интерфейса пользователя для отображения, модификации й выбора данных. Нужно сразу сказать, что если вы собираетесь использовать классы для про- смотра списков и таблиц в простых ситуациях, то описанных далее классов будет вполне достаточно. Эти классы базируются на архитектуре "модель- представление", описанной в главе 12. Непосредственное использование же классов этой архитектуры дает, в сравнении с описанными в этой главе клас- сами, некоторые преимущества, и поэтому в большинстве случаев являет- ся более предпочтительным. Для того чтобы ознакомиться с архитектурой "модель-представление", просто откройте следующую главу. Но лучше, если вы сделаете это после прочтения этой главы, тем более что ее материал не ограничивается только описанием таблиц и списков. Итак... Простой список Класс оЫзьихадес — это виджет списка, предоставляющий, пользователю возможность выбора одного или нескольких элементов. Элементы списка могут содержать текст и растровые изображения. Чтобы добавить элемент в список, нужно вызвать метод ааахеето. Реализовано два варианта этого метода: для текста и объекта класса дызш1адеыеет. Если необходимо уда- лить все элементы из списка, то для этого нужно вызвать слот с1еаг (). ОЫзьихадеехееш — класс для элементов списка. Объекты этих классов могут создаваться неявно, например, при передаче текста в метод 0Ызш1адеЪ: :ааа1Ъет(). Следует отметить, что класс оызыяадестсет пре- доставляет конструктор копирования, что позволяет создавать копии элемен- тов. Также для этой цели можно воспользоваться методом с1опе ().
Вставка элементов В список можно добавить сразу несколько текстовых элементов, передав объект класса озсгхпдЫзе, содержащий список строк (см. главу 4), в метод хпзегысешз (). Для создания текстового элемента можно воспользоваться методом тпзегстието, в который нужно передать только строку текста. Объект оЫзьихадесхьет также можно вставить в список при помощи метода З-пзегысепН). Отличие от метода ааатсетС) в том, что можно явно указать по- зицию добавляемого элемента. Созданному элементу можно присвоить растровое изображение, что выполняется с помощью метода оызшхадеысет:: зеысоп () объекта элемента списка. Примечательно также и то, что в элементах списка можно устанавливать не только растровые изображения и текст, но и виджеты. Для этого в классе ОЫзМЯадеЬ определены методы зеЫЬепШадеСО И 1ГепОТ1адеЬ (). Первым параметром метода зеыъетмхадеь () нужно передать указатель на объект элемента списка, а вторым — указатель на виджет. Для того чтобы получить указатель на виджет, расположенный в элементе списка, необходимо пере- дать в метод тЪешНтйдес () указатель на объект элемента списка. На рис. 11.1 показан пример использования простого списка, в котором пере- числяются операционные системы. Рис. 11.1. Пример простого списка #1пс1иде <01:Ои1>
ОАррИсаЁхоп арр(агдс, гп^) ; ОЗЪгхпдЫзЁ 1зЪ ; 2ЫзШ16деЁ 1«д; 2Ъ18ЁМ1ддеЁ1Ёет* р1Ёет = 0; 1адд.зеЪ1соп81хе(081хе(48, 48)); 15Ё « "Ыпих" « "Шпскэюз" « "МасО5" « "052"; Ёогеаск(25Ёг1пд зёг, 1зё) { рхЁет = пею 0ЫзЁЮ1ддеЁ1Ёет(зЁг, &1юд); р1Ёет->зеЁ1соп(0Р1хтар(зЁг + ".дрд")); } 1ид.гез1хе(125, 175); 1здд.зЪом() ; геЁигп арр.ехес(); } В листинге 11.1 создается виджет простого списка 1«д. Методом виджета списка зеысопЗхзе () задается размер для растровых изображений элемен- тов. Затем список строк 1зс заполняется надписями для элементов. Пройдя по этому списку при помощи ^огеасЪ, мы создаем и добавляем элементы в список. Вызов метода зеЫсоп () устанавливает растровое изображение для каждого элемента. Сортировка элементов Элементы списка можно упорядочить вызовом метода зогысетз (). При пе- редаче в этот метод значения ос: :Азсепс1хпд0гаег, сортировка элементов бу- дет выполнена в возрастающем порядке, а при ое: :Везсепахпдогаег— в убывающем. Выбор элементов пользователем Узнать, какой элемент выбрал пользователь, можно с помощью метода аызьнхадеь: :сиггепыьет(), который возвращает указатель на выбранный элемент. Если ж;е выбранных элементов несколько, то нужно вызвать метод зеХессеатеетз (}, который вернет список выбранных элементов. Чтобы разрешить режим множественного выделения, необходимо установить, при помощи метода зеьзе1есг.1опмоае (), реализованного в базовом классе ОАЬзЬгасЫЬепГУхек, значение ОАЬзСгасЫЬетУхеы: :Ми1Ь13е1есЬ1оп. В ЭТОТ ме-
год можно передавать и другие значения, например, для того чтобы запретил» выделение вовсе, в него нужно передать ОАЬзегасысетУхе»: :ЫоЗе1есс1оп, а для возможности выделения только одного из элементов — ОАЬзСгасЫЬет:: 51пд1еЗе1есЫоп. После щелчка на элементе списка высылается сигнал хьетсИскеао. При двойном щелчке мыши на элементе высылается сигнал хсетооиЫесИскеао с параметром оЫзсихадестсет*. После каждого изменения выделения эле- ментов высылается сигнал хСетЗе1есСхопС11апдеа(). Изменение элементов пользователем Чтобы предоставить пользователю возможность изменения текста элемента, необходимо вызвать из нужного объекта элемента метод ОЬхзЬИхадеСХСет: :зеСЕ1адз() И передать В него значение ОС:: ТСетТзЕсНсаЫе и другие требуемые значения. Например: рхСет->зеСР1ад8(0С:: ХСетТзЕахСаЫе | ОС:: ХСетпХзЕпаЬ^еа) ; Переименование производится двойным щелчком мыши на элементе списка либо нажатием клавиши <Г2>. По завершении переименования виджет ОЬхзЫПадеС высылает сигнал хСетСЬапдеасОЬхвСИхадеЫСет*) и хЬетКепатев (ОЫзШхадеЪХЪет*). Режим пиктограмм Виджет списка можно перевести в режим пиктограмм (иконного представле- ния), который позволяет выбирать элементы, проводить над ними операции перемещения и перетаскивания (<1га^ & дгор). Пример, показанный на рис. 11.2, предоставляет пользователю выбор иконки одной из четырех операционных систем. Рис. 11 Л. Режим пикгопэамм
#1пс1и(3е <0ЁСи1> хпЪ гоа1п(1пе агдс, сЪаг** агду) 0АррИсаЁ1оп арр(агдс, агду) ; ОЬ13 Ш1с1деЪ 1ид; ОЫзетхддеЁТеет* рхЁегп = 0;, ОЗЪгхпдЫзЪ 1з1:; Ъл/д.зеЁТсопЗХге(0312е(48, 48)); 1у?д. зеЪЗе1ес^1ОпМо'ае (ОАЬзЪгасЁ1СетУ1ею: :Ми1^15е1есЁ1оп) ; 1адд.зе<:У1еиМоае(дЫзСТхею: :ХсопМос1е); 1б(: « "Ыпих" « "Шпскэтлге" « "МасОЗ" « "052”; 1огеас11(05Ёг±пд зёг, 1зё) { рзХет = пего дЫзШ1адеС11:ет(5Ёг, &1юд); р1Ёет->8еЁ1соп(0Р1хтар{5ег + ".дрд")); р1Ёет->5еЁР1адз (ОЪ:: ТЁетХзЕпаЫед | Оё :: Т^етТзЗеХесСаЫе | ОЪ:: ЗХетТзЕсНеаЫе | 0^: : 1Ёет1бОгадЕпаЫес1) ; } Хюд.гезхзе(135, 135); Хадд.зИсюО ; геЁигп арр.ехес(); > В листинге 11 <2 создается виджет списка 1ид. Вызов метода 8еезе1есЁ1опмоае () производит установку режима выбора нескольких элемен- тов. Далее ВЫЗОВОМ метода зетеюМодеС) С параметром ОЫзСУХею: :1сопМоДе устанавливается режим пиктограмм. Затем создается список строк 1зе, кото- рый заполняется именами файлов иконок. Сами элементы списка 1мд созда- ются в цикле СогеасЪ. В метод зеиПадзО каждого элемента передается комбинация флагов, которые делают его доступным (оё: :1Ёет1зЕпаЫеа), разрешают выделение (дс: :1Ёет1зЗе1есЪаЫе), делают его редактируемым (0Ё: : 1Ёет1зЕд1ЬаЫе) И перетаскиваемым (0Ё: : 1еет1зПгадЕпаЫеа). По умолчанию, все помещаемые элементы будут заполнять виджет ОЫзшхадес слева направо и сверху вниз. Это можно изменить с помощью
метода зеьг1оте(), который определен в базовом классе Для этого нужно передать ему значение оЫзеУхею::тортовоеъот, что приведет к за- полнению опций сверху вниз и слева направо. Иерархические списки Виджет отгееихйдее отображает элементы списка в иерархической' форме и поддерживает возможность выбора пользователем одного или нескольких из них. Его часто применяют для показа содержимого дисков и каталогов. В случае, когда область отображения не в состоянии разместить все элемен- ты, ПОЯВЛЯЮТСЯ ПОЛОСЫ Прокрутки. С ПОМОЩЬЮ Метода зеЫЬетИхадеЬО в иерархическом списке можно размещать виджеты. Элементы списка явля- ются объектами класса отгеешадеыеет и предоставляют возможность ото- бражать несколько столбцов с данными. Класс отгееихадеысеш содержит конструктор копирования и метод с1опе () для создания копий элементов. При помощи методов ааасьпагепо и хпзегьсъиагепо можно добавлять и вставлять сразу несколько элементов. Если необходимо снабдить элементы небольшими растровыми изображения- ми, ТО ОНИ устанавливаются С ПОМОЩЬЮ метода ОТгееИхадеЫЬеш: : зеЫсоп (), а текст элемента — с помощью отгееихадеысет: :зеътехьо. Первый пара- метр обоих методов соответствует номеру столбца. На рис. 11.3 список содержит три элемента. Двойной щелчок мыши на элемен- те Ьоса! О1вк(С) сворачивает и разворачивает вложенные элементы (папки). т , I !Г’; •*! __ Тгеелг.' юде! ----------—— ] гойеге 1Не-з Зразе * Н мв* коса1 ь,) О'ге ГЛ В г I ' кх/) к’-’ 6 : : Е'тестслуг г’.’В < .... ..........................................;...... Рис. 11.3. Иерархический список #1пс1ис1е <0С(5и1> 1пС тахпСхпС агдс, сЪаг** агду)
ОАррИсаСхоп арр(агдс, аг^) ; 0ТгеёМ1дде(: Сздд; ОЗЁгхпдЫзЪ 181;; 1зё « "РоМегз" « "Пзеа Зрасе"; Сид.зеСНеадегЬаЬе1з(1зЪ); Ьттд. зе(15ог1:1пдЕпаЫед (1:гие); СТгееЮ1сЭдеЪ1Ъет* рЪидХЪет = пею ОТгееЛ1<Зде^1^ет(&Ъ5л?д); р^д1Пет->зеЁТехС(0, "Боса! 01вк(С)"); рЬ«д!Ъет->зе€:1соп (0, ОРххтар ("аг злге. Ьпр")); ОТгееИхддеЫЁет* р^у^дХЁетПхг = 0; Ьзг (1пе 1=1; 1 < 20; ++1) { рЬядТЪетШг = пею ОТгееМхсЗдеЫСетСр^аддХЁет) ; рЪ^д1ееяйЬ1г->зе1;ТехП(0, "ШгесЪогу" + 03Ъг1пд: :гшхпЬег (1)); р1^д1ЁепЮ1г->зеСТехС (1, ОЗёг1пд:: пигпЬег (1) + "МВ") ; рЁ^д!1;епЮ1г->зеЁ1соп (0, ОРххшар (" ^о1аег. Ьпр")); } . зеЪХЬетЕхрапйес! (рС«д1Сет, Ъгие) ; Ёмд.гезхге(250, 110); Ьлтд.зЫжО ; геЬит арр. ехес () ; Для отображения структуры каталогов диска (например, как в Проводнике ОС ^пк1ом/8) необходимо построить объекты отгеемхддеЁтеет в нужном иерархическом порядке. В листинге 11.3 создается виджет списка ъ^д. Надписи для столбцов вносятся в список 1зё при помощи оператора «. Ме- тод зеьнеабегЬаЬеХз () задает заголовки столбцов РоШегв (Папки) и (1зе<1 Брасе (Занимаемый объем). В конструктор объекта первого элемента (указа- тель р^дтеет) передается Ъдрес виджета списка с«д. Этот элемент является предком для создаваемых в дальнейшем элементов и представляет собой вершину иерархии. Далее в цикле создается 20 потомков для вершины спи- ска. При помощи метода зеътехео задается текст столбцов, а метод зесюопО добавляет к первому столбцу растровое изображение. Метод зеетъетЕхрапаеао разворачивает элемент вершины р^дтеет и показывает иеоаохию элементов.
Обратите внимание на столбцы, они предоставляют намного больше воз- можностей, чем просто указание заголовка. При нажатии на заголовок столб- ца проводится сортировка элементов в убывающем или возрастающем по- рядке. Подобную сортировку можно разрешить или запретить, вызвав метод 8еъ5огЫпдЕпаЫеа(). Для разрешения нужно передать в этот метод значение ьгие, а для запрещения — Са1зе. По умолчанию, пользователь может одновременно выбирать из списка толь- ко один из элементов. Если этого недостаточно, то нужно вызвать метод 8еьзе1есЫопмоае () базового класса ОАЬзЬгасысетУхею с параметром <2АЬзегасыеетУ1е«: :Ми1Ь13е1есг1оп, что устанавливает режим множествен- ного выделения. Для того чтобы "пройтись" по всем элементам виджета иерархического списка отгееи1Ддеп, можно воспользоваться итератором, Например: ОТгееКхйдеЫСетКхегаПог И:(&Ьид, ОТгееИхддеЫЬетТСегаСог: :А11) ; мЫ1е (*(++хЬ)) { дОеЬидО « (*1С)->ЪехЪ(0); } Обратите внимание, что в первом параметре в конструктор итератора был передан адрес самого виджета иерархического списка. Вторым параметром передается флаг, указывающий, какие виджеты должны приниматься во внимание при обходе. В нашем конкретном случае мы передали значение Отгеенхадеысетхьегаьог: :А11, что произведет обход всех элементов иерар- хического списка. Для обхода, например, только выделенных элементов, В конструктор нужно передать значение ОТгееИхадеЫЬетИхегаЬог:: Зе1есЬеа. Есть и другие флаги, их полный список можно посмотреть в документации. Класс отгееихбдес содержит следующие сигналы: □ 1Ьет5е1есЫопскапдеа () сообщает об изменении выбранных элементов; □ 1ьеп1СНскеа(отгеен1адее1гет*> хпъ) высылается после щелчка на элементе; □ 1СетСоиЫеС11скеа(0ТгееН1адеЫеет*, хп€) высылается При ДВОЙНОМ щелчке мыши; □ 1СетАсЫуасеа(отгееихаде1:1еет*, хпс) высылается при двойном щелчке мыши, а также при нажатии клавиши <ЕпСег> на элементе. В случаях, ко- гда двойной щелчок мыши и нажатие клавиши <Еп1ег> должны вызвать одно и то же действие, код можно реализовать в одном слоте и соединить его с этим сигналом. Второй параметр последних трех сигналов содержит номер столбца, на кото- вом пооизошел щелчок.
Класс отгеешадеь поддерживает технологию перетаскивания (дга^ & дгор). Для ее реализациии необходимо вызвать метод зеСР1адз() тех элементов, для которых нужно включить поддержку перетаскивания, с параметром Ос::тьеттзпгадЕпаЫеа, возможно, скомбинированным с другими требуе- мыми значениями. Например: р1Сет->8е1:Р1адз(0Ь: :1Ьет1аОгадЕпаЫеа | ОС: : ХСетХзЕпаЫеа) ; По умолчанию, режим переименования элементов отключен. Включить его можно, передав в метод зеспадзО значение ос: гхьешхзЕахеаЫе (но не за- будьте указать и другие необходимые значения!). Таблицы Класс отаЫевд1адес представляет собой таблицу. Объект ячейки реализован в классе отаЫеихадеьхьет. Созданную ячейку можно вставить в таблицу вызо- вом метода отаЫеипадес: :зеС1Сет(). Первым параметром метода зеысето является номер строки, а вторым — номер столбца. Таким образом, эти парамет- ры задают местоположение ячейки в таблице. Установить текст в са- мой ячейке МОЖНО с ПОМОЩЬЮ метода ОТаЫеИХадеЫЬет: :зеСТехС(), а для размещения растрового изображения можно воспользоваться методом ОТаЫеиШдеысет: :зеысоп(). Если в ячейке установлены как текст, так и растровое изображение, то растровое изображение займет место слева от текста. Класс ячейки отаЫеихадеысет предоставляет конструктор копиро- вания, что позволяет создавать копии элементов. Также для этой цели можно воспользоваться методом стопе (). В таблицу допускается, помимо текста и иконок, помещать и виджеты. Для этого используется метод зеьсенихадес (). При двойном щелчке кнопкой мыши на поле ячейки она переходит в режим редактирования, при этом используя виджет оЫпеЕахе. Приложение, показанное на рис. 11.4, использует таблицу, состоящую из трех столбцов и двух строк. Содержимое ячеек можно изменять. Зесопа ТЬга Рис. 11.4. Таблица
#хпс1и<1е <0С(5их> хпС тахп(хпЁ агдс, сИаг** агду) { сопзЬ хпЪ п = 3; ОАррИсаЪхоп арр(агдс, аг^); ОТаЫе^хддеЪ ЪЫ(п, п); ОТаЫеЮхддеЪТЪет* рГмх = 0; □ЗЪгхпдЬхзЪ 1зё} 131: « "Рхгзё" « "Зесопс!" « "ТЫгд"; ЬЫ. зеСНогх2оп1:а1НеадегЬаЬе1з (1зЪ) ; ЬЫ. зеСУегЪхса1Неас1егЬаЪе15 (1зЪ) ; ^ог (хп1: х = 0; х < п; ++х) { €ог (хпС 1 = 0; ] < и; ++э) { рЪюх » пею ОТаЫеЮхсЭдеЪХееп! (ОЗЪгхпд (" %1, %2") . агд (х). агд (□)); ЪЫ.зееТЪепЦх, 5, рЪюх) ; } } 11Ы.гезх2е(350, 125); СЬ1. зИою () ; геЬигп арр.ехес(); } В листинге 11.4 создается виджет таблицы пы. Первый и второй параметры, передаваемые в конструктор, задают количество строк и столбцов. Наша таб- лица будет иметь размерность 3 на 3. Чтобы задать заголовки строк и столб- цов таблицы (которые у нас совпадают), мы сначала формируем их список, а затем передаем его в метод зеъногхзопЁа1НеадегьаЬе1 о для горизонталь- ных и в зеЁУегехса1НеайегьаЬе1 () для вертикальных заголовков. Создание объектов ячеек производится в цикле (указатель рёюх). В качестве текста в конструктор передаются номера строки и столбца ячейки. Вызовом метода зегтьешО созданная ячейка таблицы устанавливается в позицию, указанную в первом и втором параметрах. 8 Зак. 3983
Выпадающий список Класс осошЬовох предоставляет пользователю возможность выбора одного элемента из нескольких. Его функциональное назначение совпадает с видже- том простого списка оызъихддес. Основное преимущество выпадающего списка состоит в отображении только одного (выбранного) элемента, благо- даря чему для его размещения не требуется много места. Отображение всего списка (раскрытие) происходит только на некоторый промежуток времени, чтобы пользователь мог сделать выбор, а затем список возвращается в свое исходное положение (сворачивается). В качестве элемента можно добавить текст и/или картинку. Для этого служит метод ааатъешО. Можно добавить сразу несколько текстовых элементов, передав указатель на объект класса озеггпдЫзь в метод ааахсетз (). Вызвав метод аееоир11саъезЕпаЫед(Са18е), можно включить режим, исключаю- щий повторяющиеся элементы и? списка. Если необходимо удалить все эле- менты выпадающего списка, тогда вызывается слот с1еаг (). Чтобы узнать, какой из элементов является текущим, нужно вызывать метод сиггепыпаех (), который возвратит его порядковый номер. Можно сделать так, чтобы пользователь мог сам добавлять элементы в список. Типичным примером этого является адресная строка Проводника ОС \У1п4оху8, содержа- щая в себе список просмотренных адресов (ссылок). Для установки виджета в этот режим вызывается метод зеЬЕаШаЫеО с параметром ьгие. После того как пользователь изменил текст выбранного элемента, высылается сигнал еа1етехесъапдеа(сопзе <23ьг1пд&), и новый элемент добавляется в список. После выбора элемента высылаются сразу два сигнала асЫлгаьеао —один с параметром типа 1пе (индексом выбранного элемента), а другой с парамет- ром типа сопзе 08&г1пд& (его значением). Эти сигналы высылаются, даже если пользователь выбрал ранее выбранный элемент; для информирования о реальном изменении служат два сигнала сиггепыпаехсьапдеа (), также высылаемые с параметрами хпс и сопзе озегхпдь каждый. Пример, приведенный на рис. 11.5, демонстрирует виджет выпадающего списка. При изменении элемента список дополнится новым. Рис. 11.5. Пример выпадающего списка
А 1лИ Й Т&Л * Л *Ч> «ЙН #1пс1иде <фЬСи!> 1ПС та1п(1пС агдс, сЪаг** а^дV) ОАррИсаЪхоп арр(агдс, аг^) ; ОСотЬоВох сЬо; ОЗЪгхпдЬхзЪ 1зЪ; 1з1: « "«Тойп" « "Раи1" « "Сеогде* « "Кхпдо"; сЬо.айсНЪетз (1зЪ); сЬо. зе!:ЕЛ ЪаЫе (Сгие); сЪо.зИом(); геЪигп арр.ехес(); } В программе, приведенной в листинге 11.5, создается виджет выпадающего списка сЬо. Затем в список 1зс добавляются четыре строки. Эти четыре стро- ки устанавливаются вызовом метода ааахьетзо в виджете выпадающего списка. Вызов метода веЬЕазЛаЫеО с параметром ыие переводит список в режим редактирования. Примечание Если при редактировании текста требуется проверять правильность ввода ин- формации, в виджете осошЬовох следует установить объект класса ОУаИбаСог (см. главу 10). Закладки При чтении книг вы, возможно, используете закладки, которые помогают вам быстро открыть книгу в нужном месте. Ситуация с компьютерными заклад- ками аналогична, только с той лишь разницей, что они используются для бы- строй смены диалоговых окон. При выборе закладки в окне будет отображен закрепленный за ней виджет. Закладки могут содержать как текст, так и рас- тровое изображение. Основное назначение закладок — разгрузить сложное диалоговое окно, имеющее большое количество опций, разделив его на серию логически скомпонованных диалоговых подокон.
Страницы можно делать доступными и недоступными. Чтобы сделать стра- ницу недоступной, нужно вызвать метод зеетаЬЕпаЫеао и передать ему значение ^а1зе. Вызовом слота зеисиггепстпдех () можно сделать закладку текущей. Пример, показанный на рис. 11.6, содержит четыре закладки: Ьших, М/шдолуз, МасО8 и 082. При выборе каждой из закладок происходит отображение виджета, закрепленного за ней. #1пс1ис1е <0ЪСи1> 1пЪ ша1п(1пЪ агдс, скат** агду) » { ОАррИса^хоп арр(агдс, агду) ; 0ТаЖ1с1деЪ ЪаЬ; ОЗЬгхпдЫзЪ 1зЬ; 1зЪ « "Ыпих" « "Ю1пс1слл75" « "МасОЗ" « "032"; ^огеаск(03ЁГ1пд зЪг, 1зЪ) { ЁаЪ.адс1ТаЬ(пею $ЬаЬе1 (зПг, &ЬаЬ), 0Р1хтар(зЪг + " .дрд"), зЪг); } ЪаЬ.гезхгеСЗОО, 100); ЪаЬ.зкоюО ; геЬигп арр.ехес();
В листинге 11.6 создается виджет закладок саЬ. Вызовом метода адбтаЬО в цикле добавляются новые закладки. В первом параметре этого метода пе- редается указатель на виджет, который должен отображаться при выборе за- кладки, во втором — растровое изображение, а в третьем — текст закладки. Виджет панели инструментов Класс отоохвох представляет собой закладки, расположенные вертикально. Связанные (с закладками) виджеты отображаются непосредственно под ни- ми. Текст закладки добавляется вместе с виджетом при вызове метода ааахсешо. Если требуется вставить закладку на определенную позицию, то вызывается метод 1пзегыеет(). Количество закладок можно узнать, вызвав метод соипъ (}. Для удаления закладок реализован метод гетслгеххетС). Вызвав метод сиггепьихадее (), можно получить указатель на закрепленный за текущей закладкой виджет. Виджет панели инструментов располагает только одним сигналом сиггепьсьапдей(1пе), высылаемым при выборе одной из закладок. В примере, приведенном на рис. 11.7, реализована панель инструментов, со- держащая четыре закладки с закрепленными за ними виджетами надписей. Рис. 11.7. Панель инструментов ет®?:'""' #1пс1ис1е <0ССи1> хпЁ та1п(1пЪ агдс, сТъаг** а^дV)
{ ОАррИсаЫоп арр(агдс, агду); 0Тоо1Вох СЬх; ОЗ^гхпдЫзЪ 1гЪ; 1зё « "Ыпих" « "Ихпаога" « "МасОЗ* « "032"; ^огеаскСфЗСгхпд зЫ, 1зЬ) { СЪх.ас1с11Ъет(пеу7 фЬаЬеКзСг, &еЬх), 0Р1хтар(зСг + ".дрд"), зСг) ; } Шк.гез 126(100, 80); ЪЬх.зИомО ; геЪига арр. ехес () ; } В листинге 11.7 создается виджет панели инструментов. После этого в него в цикле добавляются закладки с помощью метода ааатсетО. Первым пара- метром передается указатель на виджет, который отображается при выборе закладки. Вторым — растровое изображение. Третьим параметром передает- ся текст закладки. Резюме Элементы выбора могут содержать как текст, так и растровые изображения. Сами элементы можно выбирать с помощью левой кнопки мыши или клавиш курсора. Базой для классов ОЫзЗДЛадеС, отгее№адес И ОТаЫеИЫдее являются классы архитектуры "модель-представление", с которыми мы познакомимся в следующей главе. Вкратце, рассмотренные в этой главе классы можно охарактеризовать сле- дующим образом: □ класс оызыяадес удобен для выбора от одного до нескольких элементов. Этот класс можно переключать в режим пиктограмм (иконного представ- ления); □ класс отгееи1адеъ предназначен для отображения элементов в иерархиче- ском виде*.
□ класс отаЫеНтйдег: предоставляет собой таблицу, в ячейки которой мож- но помещать текст и растровые изображения; □ класс осошЬовох позволяет выбрать только один элемент. Основное его преимущество состоит в том, что он не требует много места; □ класс отаьнхадес позволяет разбить сложное диалоговое окно на серию простых диалоговых окон, благодаря чему приложение становится более понятным; □ класс отоо1вох по сути очень похож на класс отаыяадее. Разница состоит в вертикальном расположении закладок.
Глава 12 Интервью или модель-представление "Вкусовых ощущений только пять, но вкусовых сочетаний так много, что ни- кому не суждено познать их все". Сунь-цзы Применение элементно-базированного подхода, представленного в главе 11, не всегда является подходящим и оптимальным. Этот подход идеально под- ходит для простых ситуаций, когда йужно отобразить небольшой объем дан- ных. Но в более сложных ситуациях, таких как, например, работа с базами данных, файловой системой и т. д., его использовать, из соображений эффек- тивности и расхода памяти, не рекомендуется. Представьте себе, что для по- лучения результатов 8рЬ-запросов вам придется записывать их в элементы и тем самым дублировать данные. А при использовании трех виджетов, пока- зывающих эти данные, объем данных утроится, и, кроме того, вам нужно бу- дет решать проблему синхронизации при отображении этих данных. предоставляет технологию, называемую "интервью", или, иначе, "модель- представление". Наверняка, многие из читателей уже знакомы с шаблоном проектирования "модель-представление". Очень важно понимать то, что ар- хитектура "модель-представление", реализованная в 01, не является прямой реализацией этого шаблона, а использует только основные его идеи, такие как, например, отделение данных от их представления. Использование тех- нологии "интервью" дает следующие преимущества: □ возможность показа данных в нескольких представлениях без дублирова- ния. Если вы работаете с элемёнтно-базированным подходом и необходи- мо добавить новые элементы, то, при синхронизации отображения с дан- ными, происходит дублирование самих данных. В подходе "модель- представление" мы работаем с моделью данных, поэтому дублирования не происходит;
□ возможность внесения изменений с минимумом временных затрат. На- пример, представим себе, что в программе полностью изменился способ сохранения данных. Это не будет помехой, так как связь с данными про- исходит с помощью интерфейса, и до тех пор, пока сам интерфейс оста- нется нетронутым, это не повлечет за собой больших изменений в коде программы; □ удобство программного кода. Так как производится разделение на данные и представление, то, если появится необходимость что-то дополнить или исправить, эти изменения коснутся лишь одной из частей. Остальные час- ти вашего приложения останутся без изменений; □ удобство тестирования кода. Как только интерфейс задан, можно написать тест, который может быть использован для любой модели, реализующей этот интерфейс. Ос предоставляет специальную библиотеку для проведе- ния тестов модулей (см. главу 4бу, □ эта же модель используется Ос для 80Ь, чтобы сделать интеграцию баз данных проще для программистов, не связанных с разработкой баз дан- ных (см. главу 42). Концепция Все части технологии "интервью" могут взаимодействовать друг с другом в соответствии с показанными на рис. 12.1 направлениями стрелок. Рис. 12.1. Взаимодействие компонентов “интеовью"
Давайте сначала разберемся в назначениях частей этой технологии, а затем перейдем к их подробному рассмотрению. Вот ее основные составляющие: □ модель — отвечает за управление данными и предоставляет интерфейс для чтения и записи данных; □ представление — отвечает за представление данных пользователю и за их расположение; □ выделение элемента — специальная модель, отвечающая за централизо- ванное использование выделений элементов; □ делегат — отвечает за рисование каждого элемента в отдельности, а так- же за его редактирование. Модель Модель — это оболочка вокруг исходных данных, предоставляющая стан- дартный интерфейс для доступа к ним. Так как именно интерфейс модели является основной единицей, обеспечивающей связь между моделью и пред- ставлением, это дает дополнительные преимущества, а именно модели можно разрабатывать отдельно друг от друга и при необходимости заменять одну на другую. Интерфейс любой (^-модели базируется на классе сАЬзьгасшеетМобе! (рис. 12.2). Для того чтобы создать свою собственную модель, вам придется унаследовать либо этот класс, либо один из его потомков. Сам класс 0АЬзегасыъепмоае1 представляет собой обобщенную таблицу, за каждой ячейкой которой можно закрепить 'подтаблицу. Благодаря этому свойству можно создавать модели для сложных структур данных. Например, для древовидной структуры, описывающей содержимое каталога, некоторая ячейка, находящаяся в строке и представляющая каталог, будет иметь под- таблицу, строки которой будут соответствовать файлам и подкаталогам. Подкаталоги, в свою очередь, могут также иметь подтаблицы со строками, в которых будут появляться файлы и подкаталоги и т. д. Рассмотрим некоторые унаследованные от 0АЬзегасыеетмоае1 классы, по- казанные на рис. 12.2. Класс 0АЬзъгасьЫземоае1 представляет собой одно- мерный список, а класс 0АЬзыасетаЫеМоае1 — двумерную таблицу. Класс озьапдагбхь^шмоде! позволяет напрямую сохранять данные в модели. Хоть это и немного противоречит основной идее модель-представление, но в некоторых приложениях, которые манипулируют незначительным количе- ством данных, является довольно удобным и практичным подходом. Класс озег^пдЫзьмоде! — это реализация олЬзсгасеЫзьмоае!, которая предоставляет одномерную модель, предназначенную для работы со списком
строк. Источник данных — это список строк (озыхпдЫзь). Эта модель пре- доставляет возможность редактирования, то есть если пользователь изменит, с помощью представления, одну из записей, то старая запись будет замещена новой. Каждая запись соответствует одной строке. Например: СЗЪгхпдЫзШойе! пкхЯе1 ; хподе1.8ее8Ъг1пдЪ1зе(03Ъг1пдЬ1з1:() « "Первая строка" « "Вторая строка" « "Третья строка" Рис. 12.2. Иерархия классов модели Основная идея класса ользъгассргохумоае! состоит в извлечении данных из модели, проведении некоторых манипуляций с ними и возвращении их в ка- честве новой модели. Таким образом можно производить выборку и сортировку данных. Для проведения этих операций можно воспользоваться унаследованным классом озогЪЕИсегРгохумойе!. Данный подход будет подробно рассмотрен далее в этой главе. Класс оэхгмоаех представляет собой готовый класс иерархии файловой системы.
Данные, предоставляемые моделями, могут совместно использоваться, посред- ством интерфейса, различными представлениями (виджетами, унаследованны- ми от ОАЬзЪагсыьетУхеи) (рис. 12.3). Для того чтобы модель и представление могли понимать друг друга, модель информирована об основных свойствах представления: каждая запись занимает в ней одну строку и столбец, а также может иметь индекс, который играет важную роль во вложенных структурах. Рис. 12.3. Связь модели с различными представлениями Представление Как видно из рис. 12.4, базовым классом для всех представлений является ОАЬзьассвсгоИАгеа, что позволяет, в тех случаях, когда отображаемая ин- формация занимает больше места, чем область показа, воспользоваться поло- сами прокрутки. Все классы представлений наследуются от класса дАЬзЪгасетсешУгек, который дает такие базовые возможности для всех пред- ставлений; как, например, установка моделей в представлении, методы для прокрутки изображения и многие другие. Этот класс содержит метод зееЕШстгхддегз (), который задает параметры переименования элементов. В этот метод можно передать следующие значения: □ ыоЕа1етг1ддегз — переименование невозможно; □ поиЫесИскей — переименовать, если на элементе был произведен двой- ной щелчок мышью; □ 5е1есьеасИскеа — переименовать, если произошел щелчок мышью по выбранному элементу. Для представления данных в 01 используются, в основном, три класса: □ оЫзсутем представляет собой одномерный список. Этот класс также рас- полагает режимом пиктограмм (иконного отображения); □ 0ТгееУ1е« отображает иерархические списки. Этот класс также способен отображать столбцы; □ 0ТаЫеУ1ем отображает данные в виде таблицы.
|ООЬ)вс< } |0Ра>пЦ)еу4се | ^ОУУШдеД " ' <№гатв. |дцб<теия»> 1 |0ТаЫеЩМи«* 1 |ОТгееУУ1дде1 ~'| Рис. 12.4. Классы представлений (выделены черным цветом) Заметьте, что класс онеадегУхеи унаследован непосредственно от ОАЬвсгасысеиУхеи. Но он не предназначен для самостоятельного отображе- ния данных, а используется совместно с классами отаЫеУ1е« и отгееУхеи для отображения заголовков столбцов и строк. Выделение элемента Обычно в представлениях имеется механизм, управляющий выделением эле- ментов, то есть каждое представление реализует свое собственное выделение элементов, в отдельной части кода. Это неудобно, так как для большого ко- личества представлений этот код может быть разбросан по разным частям программы. Описанный далее механизм позволяет реализовать выделение элемента централизованно, в одном месте. Таким образом, мы получаем воз- можность разделения между различными представлениями, работающими с одной моделью данных, не только собственно ее данных, но и механизма выделения. Управление выделением осуществляется при помощи специальной модели, реализованной в классе охсетзеХесЫопмоае! (рис. 12.5). Для получения
модели выделения элементов, установленной в представлении, нужно вы- звать метод ользъгасетьетУхеуп : зе1есЫопМоае1 (), а установить новую мо- дель можно С помощью метода ОАЬзСгасЫСетУхезд: :зеС5е1есС1опМоае1 (). Рис. 12.5. Класс выделения $1Сет5е1есС1опМоае1 Программа, окно которой показано на рис. 12.6, выполняет разделение выде- ления элементов между тремя представлениями. Выделение элемента в од- ном из представлений приведет к выделению этого же элемента и в осталь- ных представлениях. Рис. 12.5. Демонстрация разделения выделения элементов между представлениями #1пс1иае <ОС(Зих> 1пС гоахпСхпС агдс, сЪаг** агд^) { ОАррИсаСхоп арр(агдс, агду); ОИхадеС «дС; ОЗСгхпдЫзСМоае! шоае!; тоае!. зеСЗСгхпдЫзЪ (ОЗСгЛпдЫзС () « « "1Сет2" « "ТСетЗ"); ОТгееУхею* рТгееУхеот = пею 0ТгееУ1еш;
рТгееУхею->зеЕМоаеХ (&то<Яе1) ; ОЬхзСУхею* рЬхзЪУхею = пею рЬхз^Ухе»; рЬ1зПУ1ем->5еШоде1 (&тоаеХ) ; ОТаЬХеУхею* рТаЬХеУхею = лею ОТаЬХе^7хею; рТаЫеУ1ею->зеШоде1 (йлюдеХ); ОХПетЗеХесЫопМоаеХ зе1есП1оп(&тоде1) ; рТгееУхе^->зе1:Зе1есЪхопМоаеХ (&зе1есС1оп) ; рЬхзПУхем->зеЪ5еХес1:хопМоаеХ (йзеХесСхоп). ; рТаЬХеУхею->зеЪ5еХес1:хопМоаеХ (&зеХес!:хоп); //Ьауоие зеЪир ОНВохЬауоиЬ* рЪЬхЬауоиЪ = лею ОНВохЪауоий; рЪЬхЪауоиЬ->ад<ЯМ1<1деЪ (рТгееУхеад); рЪЬхЬауоиЪ->ааа^хадеЬ (рЬхзЪУхею) ; рИЬхЬауои!:->аскЖ1сЯде1: (рТаЬХеУхею); аддЬ. зеЪЬауоиЬ (рИЬхЬауои^); аддЬ.зкоиг() ; геПигл арр.ехес(); ) В листинге 12.1 мы создаем модель списка строк (объект тоаех), которую инициализируем тремя элементами. Каждый элемент является строкой. Затем мы создаем три разных представления (указатели рТгееУхек, рЫзеУхею и ртаЫеУхе^) и устанавливаем в них нашу модель, вызывая метод зеемоаеХ (). Самый важный момент — создание модели выделения (объекта класса 01петзе1ес1:холмоае1). При создании этот объект инициализируется ориги- нальной моделью (объект тоаех). После этого модель выделения устанавли- вается вызовом метода зе^ЗеХес^хопМоаеХ () во всех трех объектах пред- ставления. Индексы текущих выделенных позиций можно получить вызовом метода 01ЪешЗе1ес1:холМоае1: :зеХесСеаХпаехез(). А ВЫДвЛЯТЬ элементы программ- но можно с помощью метода 211:ет5е1есЪхолМоае1: :зе1есе(). При изме- нениях выделения модель выделений высылает сигнгщы сиггепесъапдеа (), зеХес^хопСИапдеа (), сиггепЪСоХитпСЪапдеа () И сиггеп^ВоздСкапдеа ().
Делегат Для стандартных представлений списков и таблиц отображение элементов производится посредством делегирования. Это позволяет очень просто соз- давать представления для любых нужд без написания большого количества нового кода. Делегат отвечает за рисование каждого элемента и за его редак- тирование (изменение пользователем), которое производится при помощи элемента однострочного текстового поля. В (^1 есть готовый класс делегата 01СетПе1едаее (рис. 12.7), который предоставляет методы редактирования каждой записи, и для большинства случаев его вполне достаточно. Но если вам потребуется осуществлять особый контроль над отображением и ре- дактированием данных, то для этого понадобится создать свой собст- венный делегат. Для этого необходимо унаследовать свой класс либо от ОАЪгСгасЫЬетоеХеда-Се, либо от 01СешВе1едаСе. Давайте реализуем простой пример делегата, который выделяет элемент, как только пользователь проведет над ним курсор (указатель) мыши (рис. 12.8). Рис. 12.8. Демонстрация делегата Рис. 12.7. Классы делегатов #1пс1ис1е <0ЪОи1> // ==================================:============^===================:==== сХазз ЗХтрХеВеХедаЬе : риЬХХс ОХЪетВеХеда^е { риЬХхс: ЗХтрХеГЭеХедаЪе (ООЬэесЁ* роЬд =0) : ОТ^епЮеХеда^е (рокд ) { } раХпЪ(ОРаХпСег* рРаХп^ег, сопзЪ 05ЪуХеОрЁ1оп71ею11:е1п& орСХоп, сопзП ОМскЗеХ 1пдех& Хпдех
) СОПЗС { (орСхоп.еСаСе & 03Пу1е::5СаСе_МоизеОуег) { ОКесС гесС = орСхоп.гесС; ОЫпеагбгасНепС дгас&епС (0, 0, гесС., гесС. кехдЪС ()); дгаДхепС. зеССо1огАС (0, ОС:: шЫСе) ; дгайхепС. зеССо1отАС (0.5, ОС:: Ыие) ; дгайхепС. зеССо1огАС (1, ОС:: дгееп) ; рРа1пСег->зеСВгизк(дгасИепС); рРа1пСег-х9гаиЖесС(гесС); } 01СегаОе1едаСе: :ра1пС(рРа!пСег, орСхоп, хпдех) ; } }; ‘ //--------------------------------------------------------------------- 1пС тпахп (хпС агдс, сЪаг *агду [ ]) { ОАррИсаСхоп арр(агдс, агду-); ОЗСгхпдЫзСМойе! тоде1; тоде1.зеС5Сг1пдЫзС(05СГ1пдЫзС() « "1Сет1" « "ГСет2" « "1СетЗ"); ОЫзСУхею ИзСУхеш; 11зСУ1ею. зеСМоде! (&тос1е1) ; 11з СУ1е». зе С1С егпОе! еда Се (пею 51тр1еПе1едаСе (&НзСУ1ею)) ; ИзСУхею.V^еиро^С () ->зеСАССг±ЪиСе (ОС: :МА_Нслгег) ; ИзСУхею.зйоюО ; арр. ехес () ; ) В функции та1п(), приведенной в листинге 12.2, вызов метода зес1сешое1едасе () устанавливает в представлении объект созданного нами делегата (класс 81шр1еПе1едаСе). Для того чтобы представление реагировало на перемещения курсора мыши над ним, необходимо установить в окне просмотра флаг ос: :ИА_нсгуег при ПОМОЩИ метода зеСАССг1ЬиСе ().
В классе 21тр1еое1едаъе (листинг 12.2) в методе для рисования рахпео мы получаем три аргумента. Первый аргумент — это указатель на объект класса 0₽а1пьег. Второй — это ссылка на структуру озсу1еорЫопУ1е»1Ъет, опреде- ленную в классе озсу1е. Третий — модельный индекс, который будет рас- смотрен в следующем разделе. Для перерисовки элемента каждый раз, когда пользователь проведет над ним мышь, мы просто проверяем флаги состояния установленных битов объекта структуры орЫоп, чтобы определить, находит- ся ли мышь на элементе или нет. Если биты флага озсу1е:: зьасе_моизесллег установлены, это значит, что курсор мыши находится над элементом, и, в этом случае, его фон рисуется при помощи линейного градиента. Рисование градиентом рассмотрено в главе 18. Если бы мы захотели изменить стандартный способ редактирования, то нам пришлось бы реализовать в унаследованном классе методы сгеаъеМНюг (), зеЪЕдхЬогОаЬа () и зеЪМодеЮаСа (). Метод сгеаСеВсНСог () создает виджет для редактирования. Метод зеСЕдхеогоасаО устанавливает данные в видже- те редактирования. Метод зеьмодеЮаса () извлекает данные из виджета ре- дактирования и передает их модели. Индексы модели Итак, мы определили структуру данных, представляющую собой таблицу. Теперь нам нужен способ для получения доступа к каждому ее элементу. На- пример, для двумерной таблицы без иерархии это означает, что мы могли бы использовать строку и столбец, чего было бы достаточно. Но при использо- вании иерархии нам понадобится другое решение. В этом случае нам нужно знать дополнительную информацию относительно строки и столбца. Эта ин- формация и будет индексом модели. Индекс модели — это небольшой список объектов, который используется для адресации ячейки в таблице, имеющей иерархию. Индекс модели представ- ляет собой информацию, состоящую из трех частей: строку, столбец и внут- ренний идентификатор. Внутренний идентификатор зависит от реализации; это может быть указатель или, например, целочисленный индекс. Каждая ячейка таблицы имеет уникальный индекс, который представлен классом омоаеНпаех. Индексы класса омойеНпаех не имеет смысла запоми- нать в программе, так как они могут измениться, например, после сортиров- ки. Однако ими удобно пользоваться для получения текущих значений ячеек таблицы С ПОМОЩЬЮ метода ОАЬзСгасЫСетМоае!: :ааГа(). Получить индекс модели ДЛЯ любой ячейки МОЖНО методом ОАЬзЫасЫСешМоае!: :1пвех(), при помощи которого можно двигаться по всей структуре данных. Например,
для того чтобы узнать значение ячейки с координатами (2, 5), нужно проде- лать следующее: 0Мос1е11п<Зех 1п<3ех = рМоде1->1п<аех(2, 5, ОМодеИпдех ()) ; ОУагхапС уа1ие = рМоде1->даЬа(1пс1ех); Может получиться так, что подтаблица не располагает элементом с заданны- ми нами координатами (2, 5). В этом случае метод хпаех () возвратит пустой индекс (туаКс! тёех). Является ли индекс действительно валидным или пус- тым, можно проверить вызовом метода омоаеНпбех:: хзУаиа(). Иерархические данные Каждая ячейка таблицы может иметь дочерние таблицы. И, думая об иерар- хиях, мы должны думать об иерархиях таблиц. Давайте воспользуемся ин- терфейсом модели озг:апаага1Ъетмоае1 для создания нашей иерархии и ото- бразим ее (рис. 12.9). Рис. 12.9. Отображение иерархических данных #1пс1иае <ОС(Зи1> 1пС та1п(1п<: агдс, скат** агду) { ОАррНса^хоп . арр (агдс, а^дV); ОЗЪапаагаХЁетМоае! пюае!(5, 3) ; 11ОГ (хпЬ пТорКОУГ = 0; пТорЯот/ < 5’; ++пТорКом) {
ОМодеИпйех хпдех = гподе1. хпдех(пТорКоад, 0); тоде1.зеЪОаЕа(1п<Зех, "1±ет" + ОЗСгхпд::пшпЬег(пТорНозд + 1)); люде!.хпзегЬКоздз(0, 4, хпдех); тоде1.!пзегССо1итпз (0, 3, 1пс1ех) ; Еог (1пЪ пКоад = 0; пКо» <4; ++пКои) { Еог (1пЬ пСо! = 0; пСо1 < 3; ++пСо1) { ОЗЫЗлд зСгРоз = 03Ьг1пд (" %1, %2"). агд (пНои). агд (пСо!); тоде1.зеЬОаЬа(тоде1.1пс1ех(пК<лл пСо1, хпдех), зЬгРоз); } } } ОТгееУхем СгееУхеи; ЪгееУхем.зеЁМобе!(&тоде!); СгееУхем.зЪом(); геЬигп арр.ехесО; } Схема использования класса озъап<3агаи:етмс>ае1 очень проста. Сначала необходимо создать его объект, а потом методом зееоаъа () установить дан- ные для каждого элемента. Впоследствии эти данные можно будет получать методом даЬа (). В примере, приведенном в листинге 12.3, мы создаем модель, представляю- щую собой таблицу из пяти строк и трех столбцов. В цикле мы получаем те- кущий индекс с помощью метода хпдехО и задаем данные для элемента. Затем мы вставляем, по текущему индексу, подтаблицу с четырьмя строками и тремя столбцами при помощи методов хпзегскомз () и 1пзегесо1шппз (). Во вложенных циклах вызовом метода зеспаса() ячейки подтаблицы одна за другой заполняются данными. Подобный пример не является хорошим образцом манипуляции с данными, но дает представление о том, как производить заполнение данными модели 03Сап<1аг<11ЪетМос1е1. Теперь приведем пример, успевший стать с момента появления СМ настоя- щей классикой. Он как нельзя лучше показывает простоту и потенциал, связанный ^использованием моделей. В программе, окно которой показано на рис. 12.10, благодаря готовой модели оШгмоае! всего лишь при помощи нескольких строк реализуется обозреватель файловой системы.
1 )ате Эге Туре С‘з1е . А. [>пуе 1О2Г|Йй«|1вв«1Ш^ - ; Я С: С»пуе !.+• М1пЗ'Л‘ Йе гсйег 1 21 ~гг~ 5 22 22 ' Г+! Г’гс-дгат Йе РоИег 1/22-'2К7 5'51 23 РМ |+| Сг- Йе РоНег 1/21/2:2-5.22:45 АМ 1 н;< Мвесв Не РсИег 1/2К22Г 1.35:23 АМ • и? О «пАулв Н1е Ео1дег 1/20.-2г:-5'5-58₽М 1Й аЛоехес.М 24 Ьугес Ьа1 Йе 4/21 2225 3 5125 РМ ; С0|А>д.®у5 12 Ьуъев $у5 йе 4,21. 2-_-€ 8:51:25 РМ р !5П тг'дгип-хМ 472 Ьу1е® ш РПе 5.19 2256 5:0113 АМ Г н 0: Опуе 1/1/ 1570 1::0:0С АМ г ИиВм М1'4 Йе Рокйг 1/21 Ж" Г 11 ШГ И! Макете 240 КВ Рй 1.21. 2007 Е.15А8РМ ’ +" '.;Уг ‘/«Ь- '7 . Рис. 12.10. Показ директорий и файлов #1пс1иде <0ЪОи1> 1п^ та1п(1пЪ агдс, сИаг** агду) { ОАррИсаЫоп арр(агдс, агду); 0П1гМоде1 тоде1; фТгееУхею ЪгееУхею; Пгее71ею. БеЁМос1е1 (&пюс1е1); ЫееУхею. зкоад () ; геЬит арр. ехес () ; } В листинге 12.4 создается объект модели класса <2О1гмойе1 и устанавливается в представлении. И приложение готово! В принципе, эту модель можно уста- навливать в любом представлении, но для отображения иерархических дан- ных и навигации лучше всего подойдет отгееУ1е«г. Для показа определенного пути можно воспользоваться методом 1паех(). Обычно этот метод ожидает три параметра: столбец, строку и индекс предка. Но можно обойтись и без них, достаточно передать в него строку с путем.
Таким образом, мы увидим в нашем представлении содержимое только те- кущей директории, добавив в нашу программу из листинга 12.4, следующие строки: ОМойеНпдех 1пс1ех = тоде1.1пдех(0О1г: :сиггепЪРаЫ1()); ЪгееУ1ем.зеЪКооЫпдех(1пйех) ; Воспользовавшись слотом зеькооетаех () и еще несколькими слотами и сигналами, можно соединить иерархическое представление с табличным представлением и реализовать программу обозревателя (листинг 12.5), пока- занную на рис. 12.11. _ Ехр1огег ? 1 I, » "13 - ь 1 сггаке.сасге ' . ’ м 15ТА1 : В) 1Х=:.5ЕиР1 1 ! ИакеЯе ; оРЕязодагы ;. । ;+; ' ; Й 5г. Ооел5оогсе.1 3 АЕ-.ОМЕ : ГЛ / Мате 1 О М3 ?гоу*ат ’ _1 а: ! ? О -|5егз 5 О ’Л’.пао'Л'8 б Цй алое «ее Ье: ’ с:Н-35у<. 4 , 3<2е Туре ЦЖа е 121.1::-5 2!:' { п!е Р.усе- 1.2121-5 51 =1/ А;е 12112:-51 1 АМ г! гйе Рссег 1'21 2211 35 л А1’ И2”:“5 5‘ н;е ^аае- Р У 24Ьу!е§ Ьа: 2:11=1 ; РУ ЕЬ'дез $у? йе <21'2'226 3 51 : в е Г-'С к." * - | г Рис. 12.11. Окно нашего обозревателя #1пс1ис1е <0ЁОи1> 1пЪ ша1п(1п1: агдс, сЪаг** а^дV) { 0АррИсаГ:1оп арр(агдс, а^дV) ; ОЗрИЪЪег зр1 (ОЬ::НогхаопЬа!); 0Э±гМосЯе1 тоде1 ; ОТгееУхею* рТгееУхею = пею ОТгееУхею; рТгее71ем->зеШх1е1 (&кподе1);
ОТаЫеУхе»* рТаЫеУхеи = пей ОТаЫеУхем; рТаЫеУ1еи->веСМос1е1 (&тос1е1) ; ООЬзесЪ::соппесЪ(рТгееМхеи, 31<ЗЫАЬ(с11ске<1(сопзЪ 0Моде11пдех&)), рТаЫеУхе», ЗЬ0Т(зеЬКооЫп<Зех(соп8к 0Мос1е1Хпс1ех&)) ); ООЬэесЪ: :соппесС(рТаЫе71е«, 31<ЗЫАЬ(ас1:}лгаЬед(сопзЪ 0Мо<Зе11п<1ех&)), рТгее71еи, 5Ь0Т(гекСиггепк1пс1ех(сопзЪ 0Моде11п<Зех&)) ); ООЬЗесЬ: :соппесЬ (рТаЫеУХеи, 31<ЖА1»(асЬгуаЁес1(сопзЬ 0Моде11пс1ех&)), рТаЫёУ1е«, 5ЬОТ(зеЬКоо1:1пйех(сопз<; 0Мос1е11пс1ех&)) ); зр1.а<3<аИ1<2деС(рТгее\^1еад); зр1.адсЭН1<1деС(рТаЫе71еи); 8р1.зкои(); геЪит арр. ехес (); } Первые строки листинга 12.5 идентичны листингу 12.4, с той разницей, что мы дополнительно создаем виджет разделителя (зр1) и табличное представ- ление (указатель ртаЫеУ1еш). Оба представления разделяют одну и ту же модель (тоЗе1). Основа реализации нашей программы заключается в сиг- нально-слотовых соединениях. Первое соединение производит в табличном представлении установку директории, выбранной в иерархическом представ- лении, в качестве узловой. Второе соединение нам нужно для того, чтобы при выборе одной из директорий табличного представления производилось выделение этой директории в иерархическом представлении. Последнее со- единение служит для показа содержимого директорий при работе в таблич- ном представлении. Таким образом, двойной щелчок мыши или нажатие на клавишу <Еп1ег> на директории вышлет из табличного представления сигнал асе^аеедо, который будет отловлен самим табличным представлением, и слот зеекооыпйех () установит данную директорию в качестве базовой. А это значит, что табличное представление позволяет нам входить только внутрь директорий, но не выходить из них. Но это не проблема, например, правая часть Проводника ОС АЛИпдохуз работает аналогичным образом. Вос- пользовавшись левой частью проводника, мы можем выбрать любую интере- сующую нас директорию.
Роли элементов Благодаря индексу (омоаеНпаех) модель может ссылаться на нужные дан- ные и, тем самым, передать эти данные представлению. Для того чтобы дан- ные были правильно показаны на экране, представление обращается, посред- ством объекта индекса (омоаеНпаех), к так называемым ролям. Каждый элемент в модели может содержать различные данные, которые привязаны к различным значениям ролей. Данные заданной роли можно по- лучить с помощью метода САЬзыасысетМоаеХ: :ааеа(), передав в него ин- декс и значение нужной роли, например п1зр1ауко1е. Если для заданной роли не будет найдено соответствующего значения, то метод ааса () возвра- тит объект класса <27агХапе, не содержащий никаких данных. Элементы, помимо текста, могут иметь и растровое изображение, а также и дополнительный текст. Нам нужно каким-либо образом обращаться к этим данным. Разумеется, когда мы рисуем элементы, то используем делегат и можем не пользоваться дополнительной информацией, которая заложена в элементе посредством ролей. Существующие представления и делегаты понимают много ролей. Наиболее часто используемые: □ Э1зр1ауко1е — текст для показа: □ 0есогаС1опко1е — растровое изображение; □ РопскоХе — шрифт для текста; О тоо1т1рко1е — текст для подсказки (ТооПлр); □ кьаьтызко1е — текст для подсказки "Что это?"; □ Техссо1огко1е — цвет текста; О Васкдгоипасо1огно1е — цвет фона элемента. Пример, показанный на рис. 12.12, демонстрирует установку ролей ОС: :П1зр1ауНо1е, ОЬ: :Тоо1Т1рКо1е И <2Ь: :ОесогаЫопКо1е. За0|« . : Ж Ж г опух МасОЗ $32 [ТосППр Гсг УУ|пс1оуу5| г I Рис. 12.12. Демонстоания солей
#1пс1иде <0ЪСи1> 1пГ та1п(1пГ агдс, скаг** агду) { ОАррИсаЫоп арр(агдс, агду) ; ОЗГгХпдЫзЪ 1зГ; « "Ыпих" « "РЯпбсжз" « "МасОЗ" « "032"; ОЗГапдагсИГегоМодеХ тоде! (ХзГ. З12е (), 1) ? Гог (ХпГ 1 = 0; 1 < тобеХ.гоадСоипЪ() ; ++1) { ОМойеХ 1пс1ех 1пс1ех = тодеХ.1пс1ех (1, 0); ОЗГгХпд зёг = 1зе.аС(1); тойеХ. зеЫЭаЬа (Хпдех, зГг, ОГ: : СХзрХауКоХе) ; тойеХ .зеГПаГа(1пс1ех, "Тоо1Т1р Гог " + зСг, рГ: ;ТооХТ1рКоХе) ; пкхЯе1. зеЫЗаЪа (1пс1ех, рХсоп (зГг + " . зрд"), рЬ: : ЭесогаЪ!опКоХе) ; } рЫзГУхею ИзСУХею; ХХз^Ухем. зе^УХемМоде (рЬ1з1:У1еУ7: :1сопМос1е) ; ’ИзЪУХек. зеШосЯе! (&то6е1); 115еУхезд. зкою () ; геСит арр. ехес (); Пример, приведенный в листинге 12.6, демонстрирует, как использовать ро- ли. Для отображения текста мы создаем список строк с использованием роли ОС: :П1зр1ауКо1е, а для отображения всплывающей подсказки мы использу- ем роль ос::тоо1т1рко1е. С ролью декорации ос: :ПесогаЫопко1е добавля- ется растровое изображение, на которое мы ссылаемся посредством конкате- нации строки с ".]р2- В результате получается элемент с текстом, растровым изображением и всплывающей подсказкой. Создание собственных моделей данных Как уже было сказано, для создания собственной модели нужно унаследовать либо класс ОАЬзсгасысетМоде!, либо один из его потомков.
На диаграмме классов моделей (см. рис. 12.2) мы видим класс модели для списка строк, но модели для списка целых чисел нет. Давайте устраним этот недостаток и реализуем эту модель. Программа, показанная на рис. 12.13, производит отображение данных модели целых чисел, которую нам предсто- ит реализовать. Рис. 12.13. Отображение данных модели списка целых чисел #1пс1ис1е <0ЪСи1> #1пс1ийе "1пеЫз1^Иоае1.И” хпС ша1п( 1пС агдс, скат** агду ) { ОАррИсаСхоп арр( агдс, агду ); 1пСЫзШск1е1 пкх1е1(0Ь1БЪ<хпЪ>() « 123 « 2341 « 32 « 5342 « 723); ОЬхзЪУхею ИзС; ИзС. зеШсхЗе! (&тос1е1) ; НвЪ.зЪоюО; ОТаЫеУхею СаЫе; ЪаЫе. зеЪМоде! (&тоде1) ; ЪаЫе.зТюМ);
В листинге 12.7 при создании объекта нашей модели мы передаем в конст- руктор список из пяти чисел. Затем устанавливаем созданную модель, вызы- вая метод зегмоае! (), в двух представлениях. Заголовочный файл самой мо- дели приведен в листинге 12.8. #1Ь1ае^ _шахзШоаеЦи МеНпе _1пЫЛзЁМоае1__11— #1пс1ис1е <0АЬзЬгасГ:Ы5ЁМоае1> // =================,==========================:=========:================== с1азз 1пЪЫзЁМос1е1 : риЬИс 0АЬзСгасГ:ЫзЁМоае1 { 0.ОВ7ЕСТ ргаллаЪе: рЫзЪ<1пЪ> хп_11зЪ; риЬИс: ТпЪЫзЁМодеКсопзГ: СЫзГ<1пС>& НзГ, ООЬэесЪ* роЬ] = 0) ; ОУагхапЪ <ЗаЪа(сопзЪ 0Моде11пс1ех& хпйех, 1пЬ пКо1е) сопзё; Ьоо1 зеЪОаЪаСсопзГ: $Мо<1е11п<5ех& хпдех, сопзЬ 0Уаг1апЪ& Vа1ие, 1пЬ пКо1е ); хпе гоадСоипЪ(сопзГ: 0Мос1е11п<1ех& рагепЪ = Ф<ос1е11пс1ех()) сопзЬ; ОУагхап!: ЪеадегБаЁа (хпЪ пЗесЫоп, ОЪ::Ог1епЪаЁ1оп огхепЪаЪхоп, пЯо1е = 2Г: :О1зр1ауКо1е ) сопзЬ; Оё: :1СетЕ1адз Г1адз(сопзГ: СМодеИпбех ЫпсЯех)' сопзС ; }; #епах^ / /_1пЪЫзЁМоаеЦг_
Для реализации модели на базе класса оАЬзъгасеьхзсмоае! необходимо реа- лизовать методы го«соипс() и йасаО. Метод го«соипе() будет сообщать о количестве строк модели, а метод ааса () отвечает за доставку данных, ко- торые он возвращает в объектах класса ОУагхапе. Также мы переопределяем метод Неайегсаса (). Это нам нужно нам для того, чтобы модель работала с заголовками отаЫеУхе« и отгееУхем. Наша модель допускает изменение данных, и именно поэтому мы переопределяем методы €1адз() И зеЬВаЬа(). 1пЬЬхзШо<Яе1: :1пЬЬхзШоде1 (сопзЬ ОЬхзЬ<хпЬ>& 1хзЬ, ООЬэесЬ* ро^/*=0*/) : САЬзЫасьЫзЪМоде! (роЬз) , 1Ц_11зЬ(11зЬ) { } Конструктор нашего класса модели списка целых чисел (листинг 12.9) слу- жит для инициализации атрибута ю_1хзе списком чисел и передачи указателя на объект предка унаследованному классу. ОУагхапЬ 1пЬЫзШх1е1: :йаЬа(сопзС 0Мсх1е11пс1ех& хпдех, хпЪ пКо1е) сопзЬ ( И (!хпдех.хзУаИй(В { гееит ОУагхапЬ (); } геСит (пНо1е == ОС: :Охзр1ауКо1е || пЯо1е == ОЪ:-.ЕсЦЬКоХе) ? гц_1хз!:.аЪ(хпдех.гом()) : ОУагхапЬО; } Модель не должна поставлять данных, это работа интерфейса модели, кото- рую мы определили для связи со структурой данных, опрашиваемых пред- ставлением. Метод аасао (листинг 12.10) возвращает интересующую пред- ставление информацию об элементе в объекте класса ОУагхапе. Помимо индекса, в этот метод передаются значения ролей. В нашем случае, если они предназначены для отображения (оь: :Пхзр1ауНо1е) или редактирования (вс: :Е<Зхеко1е), мы возвращаем значение, записанное в атрибуте, представ- ляющем собой список целых чисел, на позиции строки. Если роль не предна-
значена для отображения или редактирования, или же представление спросит о данных, которых мы не имеем, то мы возвратим пустой объект оуагхапс и тем самым скажем, что не располагаем этими данными. Ьоо1 ТпСЬхзГМобе!: :зеЪОаСа(сопзЪ 0М<х1е11п<Зех& хпбех, сопвъ 0Уаг1апЪ& 1пЬ Vа1ие, пКо1е хС (хпбех.хзУаИсЦ) && пКо1е == ОГ: :Ес11ЬКо1е) { гц_1хзГ. гер1асе (хпбех. го» (), уа1ие. уа1ие<хпЬ> ()) ; ели. Г даЪаСЪапдесЩпйех, хпдех) ; геЬит Ыие; } геЪит ХаХзе; Для установки значения в методе зеъоаъао требуются три параметра: ин- декс, значение и роль (листинг 12.11). Прежде всего мы проверяем индекс вызовом метода хзУаИао. Если индекс не пуст, а роль предназначена для редактирования, то мы заменяем существующее значение в атрибуте списка (ш_Изе) новым, используя метод гер1асе(). Заметьте, что перед тем как произвести замену, мы должны преобразовать значение к нужному нам типу. В нашем случае мы преобразуем атрибут уаХие класса оуагхапъ к целому типу при помощи шаблонного метода ОУагхапЬ: :уа1ие<т>(), параметризо- вав его типом хпъ (см. главу 4). Эту конструкцию можно заменить методом ОУагхапъ: :ъо1пъ(). После замены мы высылаем сигнал баГаСЪапдеа(), что необходимо для того, чтобы все подсоединенные к модели представления могли незамедлительно обновить свое содержимое. Возвращаемое нами (из метода) значение ъгие сообщает об успешно проведенной операции установ- ки данных, а ГаХзе сообщает представлению об ошибке. хпЬ ХпСЬхзСМобе!: :гоиСоипГ(сопзЪ 0МобеХ1паех& рагепЬ/*=0Моае11паех()*/ ) сопзЬ { геГит щ_ИзС.812е();
Метод гошсогшс (), приведенный в листинге 12.12, должен сообщать о коли- честве строк. Количество строк модели соответствует количеству элементов, содержащихся в атрибуте списка целых чисел (т_Изс). ‘ 1 1 < мН ОУагхапЪ 1пСЬ15ЬМоде1::ЬеайегВаЪа(хпЬ пБесЫоп, ОЪ: :0г1еп^а1:1оп огхепЬаЫоп, 1пе пКо1е/*=Б1зр1ауНо1е*/ ) сопзЬ { 1Е (пКо1е != Оё: :О13р1ауКо1е) { геЁит ОУагхапЪ () ; } геЬигп (огхепЬаЪхоп == ОЪ: :Ног12ОпЬа1) ? 05Ёг1пд( "ШтЬег”) : ОЗегхпд: титЬег (пЗесЬхоп) ; } Метод НеайегОаСаО из листинга 12.13 нужен для того, чтобы модель была в состоянии подписывать горизонтальные и вертикальные секции заголовков, которыми располагают иерархическое и табличное представления. Иерархи- ческое представление имеет только горизонтальные заголовки, табличное представление располагает обоими типами заголовков (горизонтальными и вертикальными). Если представление запрашивает надпись для горизон- тального заголовка, то мы возвращаем строку "МитЬег", а для вертикальных секций заголовков возвращается номер переданной секции. Гфшмнг.-Ж' ОС:: ХСешР1адз ХпСЬ1зСМойе1:: Е1адз (сопзС ОМойеНпдехЬ тпйех) сопзС { ОС:: ХСапР1адз С1адз = 0АЬзСгасСЫзСМойе1:: СХадз (хпйех) ; гесигп 1пдех.1зУа11д() ? (С1адз | ОС: :ХСетХзЕсИСаЫе) : С1адз; } Для того чтобы предоставить возможность редактирования элементов, метод С1адз () возвращает для каждого элемента унаследованное значение с добав- лением флага ос::хсешхзЕсИсаЫе. Если индекс пуст, то возвращается значе- ние без добавлений (листинг 12.14).
Для создания табличной модели на базе класса ОАЬзегасЁТаЬХемодеХ нужно поступить так же, как мы поступили в случае класса одьз^гассЫзъмоаеХ. Дополнительно к этому, в унаследованном от ддЬз^гасптаЬХемоаеХ классе необходимо реализовать метод соХитпСоипъ (), предоставляющий информа- цию о количестве столбцов таблицы. В качестве примера создадим программу, отображающую данные созданной нами табличной модели (рис. 12.14). Тдо1еМсс1€1 за ; €1 61.88 25 61.89 61.52 51 61.51 | ..2-1 €1.92 .^1 > , с: 62.59 €2.53 62.81 62 82 ' 62 8? 38 €3,85 €3,52 52.81 83.62 ! 64 64,35 64,25 64,53 64.51 64 82 65 65 38 I 65.55 65.91 65:52 ! СС РС ор 66.88 86.90 68,91 Ее 52 67 67.33 ] , 67,25 67.98 67,21 С~ 03 ц I Рис. 12.14. Отображение табличной модели данных #1пс1иае <О«ЗиХ> 4 // =======================================================================: сХазз ТаЬХеМодеХ : риЬХхс 0АЬзегас1:ТаЬХёМодеХ { рголгаСе: 1П^ ПХ—ПКОМЗ; 1ПЁ щлСоХглппз ; 0НазИ<0МоаеХ1пдех, ДОагХапО щ_Ьазк; риЫхс: //-------------------------------------------------------------------------------- ТаЬХеМодеХ ( 1пё пКо«з, ХпЬ пСоХитпз, ООЬдесе* роЬз = 0)
: 0АЬз^гасЬТаЫеМоде1 (роЬд} , т_пНо\л73 (пНоздз) , т„пСо1итпз(пСо1шппз) . { } //----------------------------------------------------------------------- ОУагхап!: даЪа(сопз!: 0Моде11пдех& хпдех, 1пЪ пКо1е) сопзЪ { х^ (! хпдех. хзУаИд ()) { геЁигп рУагхапЁ(); } ОЗЪгхпд зЪг- 05Ъгхпд("%1,%2И) .агд(хпдех.гозд() + 1).агд(хпдех.со1шпп() + 1); геГит (пКо1е == ОЪ: :Схзр1ауКо1е || пЯо1е == Оё: :ЕдхЫ<о1е) ? Щ-казИ. Vа1ие (хпдех, ОУагхапЕ: (зЪг)) : ОУагхапе () ; } и---------------------------------------------------------------------- Ьоо1 зеГ:1)аЬа(сопзЪ 0Моде11пдех& хпдех, сопзё 0Уагхап^& Vа1ие, хпъ пКо1е ) {’ хС (хпдех.хзУа1хд() && пКо1е -= 0^: :ЕдхЬКо1е) { ' т_казЬ [ хпдех] » Vа1ие; етхЬ да^аСЪапдед (хпдех, хпдех); геЬигп Ъгие; } геЪигп ^а1зе; } //--------------------------------------------------------------------- хпё гомСоипЪ (сопзЪ 0Моде11пдех&) сопзП { ге^ит п_пКоюз;
И----------------------------------------------------------------------- 1пС со1шппСоипС (сопзС 0Моае11пдех&) сопзС { геШт т_пСо1итпз; ) // --------------------------------------------------------------------- ОС: : 1Сет$1адз ^1адз(сопзС 0Мо<Зе11пс1ех& хпдех) сопзС { ОС: : 1СетГ1адз ^1адз = 0АЬзСгасСТаЪ1еМос1е1: : С1адз (хпЗех) ; геСигп 1пс1ех. хзУаИсЦ) ? (^1адз | ОС: : 1Сет1зЕсИСаЫе) : С1адз; } //----------------------------------------------------------------------- 1пС ша1п(1пС агдс, скаг *агду[]) { 0Арр11саС1оп арр(агдс, агду); ТаЫеМобе! тоде! (200, 200); ОТаЫеУтем СаЫеУхете; СаЫеУхею. зеСМойе! (&тос1е1) ; СаЫеУхею. зИоте () ; геСигп арр.ехес(); } Пример в листинге 12.15 в целом очень похож на предыдущий, приведенный в листингах 12.7—12.14. Разница в том, что мы храним наши данные в хэше и дополнительно перезаписываем метод со1шппсоипс (), В конструкторе мы допускаем инициализацию данными. Данные ячеек устанавливаются автома- тически и представляют собой строку, составленную из номеров строки и столбца ячейки. Но эти данные можно изменять. Для этого мы проверя- ем роль ос: :Ед1ско1е в методе зеспасао, а в методе 11адз() возвращаем значение с добавленным флагом ОС: :1Сет1зЕа1саЫе. В функции та±п() мы создаем нашу модель размером 200 на 200 и устанавливаем ее в табличное представление. 9 Зак. 3983
Если бы мы хотели построить класс нашей модели на классе ОАЬзсгассизетМоае!, то его реализация выглядела бы аналогично табличной модели 0АЬзегасьтаЫемо<1е1. Но, дополнительно, в унаследованном классе потребовалось бы реализовать методы ОАЬзггасстеетмоаеХ: :1пйех() и ОАЬзьгассхсетМойе!: :рагепе(). В этом случае для создания индексов ис- пользовался бы метод ОАЬзСгасЫСетМоае! : : сгеаЪеХпвех (), ПОЭТОМУ его То- же нужно было бы перегрузить. Промежуточная модель данных (Ргоху тоде1) В оригинальной модели может получиться так, что какой-либо из элементов находится первым, а нам нужно поместить его в конец или в середину. Изме- нение расположения данных в оригинальной модели данных вызовет изме- нения во всех присоединенных представлениях, что может быть нежелатель- но. В подобных случаях нам понадобится промежуточная модель. Промежуточная модель — это модель, находящаяся между моделью данных и представлением (рис. 12.15). Такая модель предоставляет возможность вы- полнять манипуляции с данными, при этом не изменяя данные оригинальной модели. МОДЕЛЬ МОДЕЛЬ ПРЕДСТАВЛЕНИЕ ПРОМЕЖУТОЧНАЯ Рис. 12.15. Схема использования промежуточной модели С ее помощью можно произвести сортировку или перестановку данных. Та- ким образом можно сделать два представления: одно из которых показывает измененные данные, а другое — оригинальные. Другая полезная операция, которую можно выполнить с помощью промежу- точной модели, — это отбор элементов данных. Для этого в промежуточ- ной модели необходимо установить критерии, с помощью которых будет осуществляться отбор. Класс озогсРЫеегРгохуМоае! является обобщенной реализацией промежуточной модели, позволяющей производить сортировку и отбор. Для задания критериев отбора может быть использован слот ОЗогьрИьегРгохумо<Зе1: :зеср11сегкедЕхр(), в который передается объект регулярного выражения класса онедЕхр (см. главу 4).
При отборе модель возвращает индексы только тех строк, для которых текст в столбце соответствует указанному критерию. При сортировке порядок рас- положения производится в соответствии со значениями элементов, располо- женных в каждом столбце. Сортировку каждого столбца можно проводить по возрастанию и убыванию. Программа, окно которой показано на рис. 12.16, осуществляет отбор тех элементов, имена которых начинаются на букву "Е". В левой части окна про- граммы представление отображает оригинальную модель, а в правой — промежуточную модель. Рис. 12.16. Отбор элементов #хпс1иде <ОЪ(Зих> 1пС шахп(хпЁ агдс, сЪаг** агду) { ОДррИсаЪхоп арр(агдс, аг^); ОМхддее эдде ; 0511г1пдЫзШоЦе1 пюде1; тоае1.зеЪ5ЬгхпдЬхз1:(05егхпдЬхзе() « "Хапагха" « "Ерхса" « "ТЪегхоп" « "ЕХлапезсепсе" « "ЫхдкЪ^хзк" 03огЪРх1еегРгохуМоае1 ргохуМскЗе!; ргохуМсхЯе!. зеСЗоигсеМобе! (&то6е1);
ргохуМос1е1. зеСШЬегМИбсагсК "Е*") ; ОЫзСУхею* рЬхзПУхею! = лею ОЫзПУхею; рЫзЪУ1ею1->зеШо<}е1 (&тос1е1); □Ь1зе71ею* рЬ1зЪУ1ею2 - лею ОЫзИХЛею; рЪ1зПУ1е>72->зеШоде1 (&ргохуМосйе1) ; //ЬауоиЪ зеПир ОНВохЬауои^* рЪЪхЬауоиС = пезд ОНВохЬауоиГ ; ркЬхЬауоиС->аскЭМ1адеГ(рЬ13^1ею1) ; рИЬхЪауоиС->асаШ1с1деЪ (рЫзЪУ1ею2) ; ътдЪ. зе^ЬауоиС (рИЬхЪауоиС) ; вдС.зкою(); ге^игп арр.ехес(); } В функции ша1п(), приведенной в листинге 12.16, мы создаем модель ОЗЬгхпдЫземойе!, которую инициализируем строковыми значениями. После этого создается промежуточная модель 03огсР11сег₽гохуМоае1, которая с помощью метода зесзоигсемойе () связывается с Оригинальной моделью. Для осуществления отбора элементов, начинающихся на букву "Е", в слоте зеегньегинасагао устанавливается маска пе*п. Примечание Можно было бы поступить и так: создать виджет текстового поля, с помощью которого пользователь сам бы устанавливал критерий для выборки. В этом случае сигнал ьехссЪапдеа () нужно было бы соединить со слотом зеЬР11ьегИИасага() модели ЗогЬЕ11сегРгохуМос1е1, что позволило бы при изменении критерия отбора, находящегося в текстовом поле, сразу же применить его. Создав два представления оЫзЬУХеы, в одном из них мы устанавливаем ори- гинальную модель, а в другом — промежуточную. Модель элементно-базированных классов В предыдущей главе мы ознакомились с классами элементно-базированного подхода, которые работают по принципу: создать элемент с данными и вста- вить его в представление. Заметьте, данные содержатся в самих элементах.
Этих классов три (с постфиксом оызыяадес, отгеешадеь и 0ТаЫеИ1адеЪ. На самом деле, эти три класса тоже базируются на архитектуре "модель- представление" и унаследованы от классов представлений оЫзсухеад, ОТгееУгеи и отаЫеУгеи (см. рис. 12.4). Но, в отличие от этих классов, внутри себя они имеют свою собственную, встроенную модель данных. А это зна- чит, что данные элементно-базированных классов можно разделять с други- ми представлениями (рис. 12.17), для чего нужно лишь получить указатель на эту модель Данных, который возвращает метод ОАЬзЬгасЫЬетУхеи: :то<1е1 (). Подобный механизм разделения моделей данных виджетов элементно- базированного подхода с представлениями не рекомендуется с позиции "мо- дель-представление", но для Простых ситуаций он вполне приемлем и может помочь сэкономить время, если в программе уже имеется реализация эле- ментно-базированных классов. Рис. 12.17. Разделение моделей классов элементно-базированного подхода с представлениями На рис. 12.18 показан пример разделения модели виджета класса оызсшадеь (слева) с представлением класса оЫзСУхе» (справа), другими словами, оба виджета смотрят на одну и ту же модель данных.
Рис. 12.18. Разделение модели данных #1пс1и<3е <0«Зи1> 1пЬ ша1п(1пЪ агдс, сЪаг** а^дV) { ОАррИсаЁхоп арр(агдс, агду); ОЗЁгхпдЫзЪ 18^; ОЫзШхадеС 1адд; ОЪ1зШ1<Яде^11:ет* рК;ет = О; 1юд.зеЪ1соп312е(0512е(48, 48)) ; 1зС « "Ыпих* « "ЮхпсЯоюз" « "МасОЗ" « "032"; СогеасЩОЗЪгхпд зЪг, 1зЪ) { р1Ьет = пею ОЫзЪМхаде^ТЪетСзСг, &1мд) ; р1Гет->зеЪ1соп(фРххтар(з^г + ".эрд")); } 1«д. зеЬМ1пйо^Т1С1е (" ЫаЬКх^деС"); 1мд. зИот/ () ; ОЫзЬУхеш НзЪУхею; ИзГЛЛе^. зеШоде! (1угд .тоде! ()) ; ИзПУхею. зеЪЗе1есЫопМо<1е1 (1адд. зе1есЁ1опМоде1 ());
И8ЪУ1еш.8еИ#1пбо«Т1€1е( "ЫаЬУХеш"); ИзЬЛЛеи.зИсМ); геЬигп арр.ехес(); ) За базу для программы из листинга 12.18 был взят код листинга 11.1, в кото- ром создается виджет элементно-базированного класса оЫзънШдеъ и запол- няется элементами данных. Его мы дополнили созданием представления спи- ска 0ЫгСУ1ет1Г. Для того чтобы быть в состоянии показать модель виджета (класса 0Ызъи1адеъ), мы вызываем из него метод шоае! () и передаем возвращенный им указатель в метод зееиодеК) объекта Изсухем. Также мы разделяем модель выделения при ПОМОЩИ методов зе1есЫопМоде1 () и зеСЗе1есПхопМоае1 (). Резюме Концепция "интервью" является свободной вариацией шаблона разработки "модель-представление", адаптированного специально для элементов дан- ных. Она отделяет данные от их представления. Это делает возможным ото- бражение одних и тех же данных в различных представлениях по-разному, без каких-либо изменений структуры самих данных. Также эта концепция предоставляет расширяемость и гибкость при использовании большого объ- ема данных. Благодаря этому подходу можно разделять между представлениями не толь- ко модель данных, но и выделение самих элементов в представлениях. Модель — это оболочка вокруг данных, связь с которыми происходит с по- мощью интерфейса. Вы можете реализовать свой собственный интерфейс для своей собственной структуры данных. Для обеспечения связи с данными необходимо унаследовать один из классов моделей и реализовать интерфейс вашей модели. При помощи промежуточной модели можно манипулировать с данными, не воздействуя на данные оригинальной модели. Классы элементно-базированного подхода основаны на архитектуре "модель- представление" и имеют встроенную модель данных, которую можно разде- лять с другими представлениями.
Глава 13 □Ж Цветовая палитра элементов управления Просыпается программист, открывает окно, на улице стоит серый ненастный день. "Опять палитра слетела", — раз- досадованно подумал он. Цветовая палитра элементов управления —это таблица, в которой содержат- ся цвета, используемые виджетом при отображении на экране. Дело в том, что цвета виджетов не определены окончательно и в любой момент могут быть изменены передачей соответствующего цвета текста, цвета фона и т. д. Рис. 13.1. Схема цветовой палитры виджета Каждый из виджетов содержит в себе объект палитры, доступ к которому можно получить с помощью метода ра1еъсе () класса оихадеъ. Сама палит- ра— это класс о₽а1еспе, который состоит из трех основных групп объектов,
показанных на рис. 13.1. Эти группы определяют три возможных состояния виджета: активное (Асйуе), неактивное (1пасйуе) и недоступное (ОкаЫей). Каждая из этих групп состоит из различных цветовых ролей (со1ог го1ез). описание которых приведено в табл. 13.1. Каждая роль имеет кисть (овгиаи) и цвет (осо1ог). Примечание Класс <2Ра1еЫ1е использует механизм неявных общих данных, а это означает, что все виджеты используют ссылку на один и тот же объект палитры. Если па* литра виджета подвергается изменению, виджет получает свой собственный объект данных палитры. Таблица 13.1. Цветовые перечисления Со1огко1е класса 0Ра1ессе Флаг Описание ДОхпскжТехЪ Цвет, который используется по умолчанию для рисования пером (см. главу 78), Этот цвет находится на переднем плане. По умолчанию это черный цвет ТехС Цвет текста. По умолчанию — черный Вг1дЫ1Тех1: Яркий цвет текста, отличающийся от цвета И1пао«техь. Обычно совпадает с техъ. По умолчанию — черный ВиЁЬопТехЪ Цвет текста для надписей на кнопках. По умолчанию — черный НхдЪЫдкЁ Цвет фона выделения элементов. По умолчанию — темно- голубой Н1дИ11дИ^ес1Техе Цвет текста выделенных элементов. Представляет собой кон- траст к цвету, заданному значением нхдИъИдкч. По умолча- нию — белый М1пс(озд Основной цвет фона. По умолчанию — светло-серый цвет Ваге Цвет для заднего фона виджета. По умолчанию — светло- серый ВиСЪоп Цвет кнопки, как правило, одного цвета с фоном. По умолча- нию — светло-серый Ыпк Цвет, используемый для непосещенной гипертекстовой ссыл- ки. По умолчанию — голубой Ьд-пкУхззЛесЗ. Цвет, используемый для обозначения посещенной гипертек- стовой ссылки. По умолчанию — розовый ЫдЪЁ Цвет эффекта объемности. Должен быть светлее цвета за- данного значением Виесоп. По умолчанию — белый (см. рис. 13.2)
Таблица 13.1 (окончание) Флаг Описание мхаидъе Цвет эффекта объемности. По умолчанию -г светло-серый (см. рис. 13.2) Оагк Цвет эффекта объемности. Должен быть темнее цвета задан- ного значением Виеьоп. По умолчанию — темно-серый (см. рис. 13.2) М16 Цвет эффекта объемности. По умолчанию — средне-серый Зкайоад Цвет эффекта объемности. По умолчанию — черный (см. рис. 13.2) На рис. 13.2 показано, каким областям виджета счетчика соответствуют не- которые из значений, приведенных в табл. 13.1. Рис. 13.2. Некоторые элементы палитры виджета счетчика Пример изменения палитры приведен в листинге 13.1. На рис. 13.3 показан виджет счетчика с измененной палитрой. Рис. 13 Л. Приложение, демонстрирующее измененную палитру виджета
#гпс1ийе <0Ь6и1> 1ПС та!п(1пЪ агдс, сЪаг** агду) { ОАррХХсаЫоп арр(агдс, лгду) ; ОЗрхпВох зрЬ,- ОРаХеССе раХ = зрЬ.раХеЪСеО; раХ.зеЁВгизЪ(ОРаХеЪЪе::ВиЬСоп, ОВгизЫОС::гед, ОС:хЮепзеЗРаССегп)); ра1.зеЬСоХог(ОРаХеССе::ВиССопТехС., ОС::ЬХие); раХ.зеССоХог(ОРаХеССе::ТехС, ОС::тадепСа); раХ.зеССоХог(ОРаХеССе::АсЫуе, ОРаХеССе::Вазе, ОС::дгееп); зрЬ.зеСРаХеССе(раХ); зрЬ.гезХхе(50, 50); зрЬ.зЬокО ; арр.зеС5СуХе(пеи ОКХпОонзЗСуХе); геСит арр.ехесО; } В листинге 13.1 создается виджет счетчика зрЬ и объект палитры раХ. При создании объекту палитры присваивается значение палитры виджета счетчика, которое извлекается вызовом метода раХессе (). После этого объ- ект палитры подвергается изменениям с помощью метода зесвгизьо и се- рии вызовов метода зессоХогО. В эти методы передаются флаги цветовых ролей, которые нужно поменять (см. табл. 13.1). Обратите внимание на по- следний вызов, в котором указано, что значение роли цвета ОРаХессе: :Вазе предназначено только для активного состояния. Это значит, что если окно сделать неактивным, то базовый цвет будет взят из неактивной группы па- литры (в нашем случае зеленый цвет поменяется на белый). Полученная па- литра устанавливается в виджет методом зеСРаХессе (). Наконец, мы приме- няем стиль ХУ1пс1о\У8 для того, чтобы наш виджет не использовал стиль платформы и выглядел везде одинаково. Более подробная информация о сти- лях приведена в главе 26.
Как мы уже знаем из листинга 13.1, при помощи метода зесвгизъо можно применять не только цвет, но и образец заполнения. Правда, это имеет смысл только для заполнения площадей, так как при рисовании линий образец бу- дет проигнорирован. Применить образец заполнения для кнопки-можно при помощи метода зеьвгизъ (). Листинг 13.1 демонстрирует изменение палитры только одного виджета. На практике это нежелательно: представьте себе приложение, все элементы которого имеют разные цвета! Поэтому если необходимо изменить палитру для виджетов, то лучше делать это для всех виджетов сразу, централизован- но. Для этого необходимо передать объект палитры в статический метод ОАррПсаЫоп: :зеСРа1еьсе(). Желательно создавать такую палитру, чтобы в приложении использовалось не менее 3 и не более 7 цветов. Листинг 13.2 показывает, как установить палитру для всего приложения. При создании объекта палитры для задания цвета кнопок и фона в ее конструктор передаются два параметра: первый — цвет для кнопок, а второй — основной цвет. Все остальные цвета палитры автоматически вычисляются на основе этих двух, для чего используется несложный алгоритм. г:;”"-...................................................... ....А,,..ти.- кДйстанг13,2.Ф$№.1пЫп'.ррр:^ . н ъпС тахп (хпС агдс, скат** а^дV) { ОАррПсаЪдоп арр(агдс, агдиО ; ОРаХеЬЪе ра1 (ОС::гей, ©Ъ: ;Ыие) ; 2АррИсаЁ1оп: :зеЪРа1еи:е(ра1}; } Резюме Из этой главы мы узнали, что цвета виджетов в любой момент могут быть изменены передачей палитры. Каждый из виджетов содержит свой собствен- ный объект палитры, который может быть изменен. Палитра виджета состоит из 3-х групп, соответствующих активному, неактивному и недоступному со- стояниям. В палитре могут участвовать не только цвета, но и образцы заполне- ния. Палитру, используемую в приложении, можно изменить для всех видже- тов сразу с помощью статического метода САррЫсаЫоп . •. земРаХессе ().
Часть III События и взаимодействие с пользователем
Глава 14 События "Что-то происходит, но ты не знаешь что. А вы знаете, мистер Джонс?" Боб Дилан. "Баллада худого человека" Обработка событий лежит в основе работы каждого приложения, имеющего пользовательский интерфейс. Событие можно охарактеризовать как меха- низм оповещения приложения о каком-либо происшествии. Например, нажа- тие пользователем кнопки мыши или клавиши клавиатуры приведет к созда- нию события мыши или клавиатуры, событие будет создаваться и при необходимости перерисовки содержимого окна и т. д. Очевидно, что основ- ная масса событий тесно связана с действиями, предпринимаемыми пользо- вателем. Но есть и события, создаваемые самой операционной системой, на- пример, событие таймера. Все события помещаются в соответствующую очередь для их дальнейшей обработки. Но ведь если "что-то происходит", то высылаются сигналы. Зачем же тогда нужны события? Механизм сигналов и слотов, по сравнению с событиями, представляет собой механизм более высокого уровня, предназначенный для связи объектов. На- пример, нажатие кнопки приводит к оповещению о происходящем всех под- ключенных к сигналу объектов. События оповещают объекты о действиях пользователя общего и детального характера (например, перемещение указа- теля мыши или нажатие какой-либо клавиши клавиатуры). Другими словами, воспользовавшись стандартными сигналами, вы можете сделать заключение о том, что кнопка была нажата, а узнать координаты указателя мыши в мо- мент нажатия не представляется возможным. Для получения подобного рода информации понадобятся объекты событий, часто содержащие дополнитель- ную информацию, которой может воспользоваться объект, получающий со- бытия. В частности, в объекте события мыши омоиаевтепъ передаются коор- динаты и код нажатой кнопки.
Использование событий особенно интересно при создании собственных вид- жетов, поскольку часто сигналы высылаются из методов обработки событий. Например, при щелчке мыши на виджете можно из метода обработки собы- тия шоизеРгеззЕуепЬ () выслать сигнал сИскей (). Примечание Следует учитывать, что в 01 все методы обработки событий определены как V^^^:иа1 ргосесъеа. Поэтому, при переопределении этих методов в унасле- дованных классах желательно определять их как ргоьесъеб. Есть еще одно отличие сигналов от событий — события могут быть обрабо- таны лишь одним методом, а сигналы могут обрабатываться неограниченным количеством соединенных с ними слотов. СМ предоставляет целый ряд классов для различного рода событий: клавиату- ры, мыши, таймера и других. На рис. 14.1 представлена иерархия классов со- бытий 01. 101СгарГ11С*§сапрРг11яРгорВу»п>-^| |ОАссва»»ЫеЕмеп1 «МВМММ1М1 .• л*мй?’«^ ? •>•»•»= э | аскмеЕурШ :< |0дгарЬ1<^$репвМоиарЕуеп1 ||22й5Й2^йЙ^ |аРупат1сРгораг1уСПапд«Еуап1 /1 (0Н1е0р«пЕиап>-г7?<;« • /’т 1 УаЕосубЕуелГ -,. |<МШрЕу*91: МММММ1 !0Н1<иЕу«п< 1|1ГОИИВ»М««И«1вЙ1 3 Тоигп—-'•^1 1атаитьйспркв<1Еувп1 | СЦд^цЦ1а»11в4^ДЛ| |4мр««Еурпи • *ЛР8«7« (оНм<мЁу«п1 | ОЗДог1си1Еу«п1з . . >„.^1 |ОЕЬошЕу»п1 Рис. 14.1. Иеоаохия классов событий ^3 3
Как видно из рис. 14.1, класс оиуепъ является базовым для всех категорий событий. Его объекты содержат информацию о типе произошедшего собы- тия. А для каждого типа события имеется целочисленный идентификатор, который устанавливается в конструкторе ОЕЛлепе и может быть получен при ПОМОЩИ метода ОЕуепЬ: : Суре (). Класс события ОЕуепе содержит методы ассерЫ) И хдпогеО, С ПОМОЩЬЮ которых устанавливается или сбрасывается специальный флаг, регулирую- щий дальнейшую обработку события предком объекта. Если был вызван ме- тод гдпоге (), то при возвращении из метода обработки события событие бу- дет передано дальше на обработку объекту-предку. Класс отприСЕ^епь является базовым для событий, связанных с пользова- тельским вводом (рис. 14.1). Этот класс реализует всего лишь один метод тодаЛхегзО. С его помощью все унаследованные от него классы могут по- лучать состояние клавиш-модификаторов <С(т1>, <8ЫЙ> и <А1О, которые могут быть нажаты в момент наступления события. Их значения указаны в табл. 14.1. Таблица 14.1. Некоторые значения модификаторов пространства имен ос Константа Значение (НЕХ) Описание ЫоМосИИег 0 Клавиши модификаторов не нажаты 5Ы € ШосИ С 1ег 2000000 Нажата клавиша <8ЫЙ> Соп!:го1Мод1^1ег 4000000 Нажата клавиша <С1г1> А1ЪМос11^1ег 8000000 Нажата клавиша <АИ> Обработка событий начинается с момента вызова в вашей основной про- грамме метода осогедррИсаЫоп: :ехес(). События доставляются всем объектам, произведенным от классов, которые унаследованы от ооьэесь. Некоторые события могут быть доставлены сразу, а некоторые попадают в очередь и могут быть обработаны только при возвращении управления циклу обработки события осогеАррИсаЫоп: :ехес(). <21 использует специ- альные механизмы для оптимизации некоторых типов событий. Так, напри- мер, серия событий перерисовки 0Ра1пЪЕ^епс в целях увеличения производи- тельности может быть "упакована” (объединена) в одно событие, с регионом рисования, составленным из регионов всех находящихся в очереди событий рисования. Тем самым метод обработки события рисования (ра!пг:Еуепъ ()) будет вызван только один раз.
Перегрузка специализированных методов обработки событий Переопределение специализированных методов является самым распростра- ненным способом их обработки. Для того чтобы обработать определенное событие, нужно унаследовать необходимый класс и переопределить нужный метод обработки события. В этот метод передается указатель на объект события, который содержит информацию о нем. Каждый метод полу- чает объект соответствующего типа, например, методы кеуРгеззЕуепс () и кеуКе1еазе^епС () получают указатель на объект класса ОКеуЕуепС. События клавиатуры Класс ОКеуЕуеп! Класс океуЕуепь содержит данные о событиях клавиатуры. С его помощью можно получить информацию о клавише, вызвавшей событие, а также А8СП-код отображенного символа (Атепсап 81апс1ап1 Соде Гог ТпГолпайоп 1п(егсЬап§е— американский стандартный код для обмена информацией). Объект события пе- редается В методы ОИхддеЬ::кеуРгеззЕуепС() И ОИМдеС::кеуКе1еазеЕуепЬ(), определенные в классе омМдее. Событие может вызываться нажатием любой клавиши на клавиатуре, включая <8Ый>, <СШ>, <А11>, <Езс> и <Р1>— <Р12>. Исключение составляют клавиша табулятора <ТаЬ>' и ее совместное нажатие с клавишей <8ЫЙ>, которые используются методом обработки онхддеь:: еуепс () для передачи фокуса следующему виджету. Метод кеуРгеззЕуепс () вызывается каждый раз при нажатии одной из кла- виш на клавиатуре, а метод кеуке1еазеЕуепс () — при отпускании. В методе обработки события можно определить с помощью метода дкеу^епс: :кеу(), какая из клавиш инициировала его. Этот метод возвраща- ет значение целого типа, которое можно сравнить с константами клавиш, оп- ределенными в классе 01 (табл. 14.2). Таблица 14Л. Некоторые значения перечислений кеу пространства имен <?с Константа Значение (НЕХ) Константа Значение (НЕХ) Кеу_5расе 20 Кеу_В 42 Кеу_ЫшпЪег51дп 23 Кеу_С 43 Кеу_0о11аг 24 Кеу_Б 44 Кеу_РегсепЪ 25 Кеу_Е 45
Таблица 14.2 (продолжение) Константа Значение (НЕХ) Константа Значение (НЕХ) Кеу_АтрегзапсЗ. 26 Кеу_Г 46 Кеу__Ароз СгорЪе 27 Кеу_<3 ' 47 Кеу_РагепЬеС 28 Кеу_Н < 48 Кеу_РагепН1дкс 29 Кеу_Х 49 Кеу_АзЪег1зк 2А Кеу-0* 4А Кеу_Р1из 2В Кеу_К 4В Кеу_Сотта 2С Кеу_Ь 4С Кеу_М1пиз 20 Кеу_М 40 Кеу_Регход 2Е Кеу_Ы 4Е Кеу_51аз11 2Р Кеу_О '4Р Кеу_0 30 Кеу_Р 50 Кеу_1 31 кеу_0 51 Кеу_2 32 Кеу_К 52 Кеу_3 33 Кеу_5 53 Кеу_4 34 Кеу_Т 54 Кеу_5 35 Кеу_Д1 55 Кеу_6 36 Кеу_V 56 Кеу_7 37 Кеу_Ю 57 Кеу_8 38 Кеу_Х 58 Кеу_9 39 Кеу_У 59 Кеу_Со1оп ЗА Кеу_2 5А Кеу_5ет1со1оп ЗВ Кеу_Васкз 1аз11 5С Кеу_Ьезз зс Кеу_Езсаре 1000000х Кеу_Едиа1 . 30 Кеу_ТаЬ 1000001 Кеу_(ЗгеаЪег ЗЕ Кеу_Васкзрасе 1000003 Кеу_0иез!:1оп ЗР Кеу_КеЪигп 1000004 Кеу_А 41 Кеу_ЕпПег 1000005 Кеу_ТпзегЪ. 1000006 Кеу_СарзЬоск 1000024 Кеу_Ве1е0е 1000007 Кеу_ЫитЬос к 1000025 Кеу_Раизе 1000008 КеуЦЗсго11Ьоск 1000026
Таблица 14.2 (окончание) Константа Значение (НЕХ) Константа Значение (НЕХ) Кеу_Ргд-пё, 1000009 Кеу_Е1 ЮОООЗО Кеу_Ноте 1000010 Кеу_Р2 1000031 Кеу_Епд 1000011 Кеу_РЗ 1000032 Кеу_Ье^Ё 1000012 Кеу_Р4 1000033 Кеу_Пр 1000013 Кеу_Г5 1000034 Кеу—КхдИС 1000014 Кеу__Р6 1000035 Кеу_Воюп 1000015 Кеу_Р7 1000036 Кеу_РадеПр 1000016 Кеу_Р8 1000037 Кеу^РадеБоутп ‘ 1000017 Кеу_Р9 1000038 Кеу_ЗЫ^11 1000020 Кеу_Р10 1000039 Кеу_Сопёго1 1000021 Кеу_Г11 100003А Кеу_Мееа 1000022 Кеу_Р12 100003В Кеу_А1Ь 1000023 Давайте кратко разберем некоторые из кодов клавиш, приведенных в табл. 14.2: □ коды от 20 до зр полностью совпадают со значениями А8СП-кодов (см. приложение Л); □ от зо до 39 соответствует цифровым клавишам, расположенным на кла- виатуре в виде горизонтального ряда прямо над клавишами букв; □ от 41 до 5а являются идентификаторами букв. Обратите внимание на то, что они совпадают со значениями А8СП-кодов заглавных букв (см. при- ложение А\, □ от юооозо до 1000052 являются функциональными. В общей сложности их 35, но в табл. 14.2 указано только 12, что соответствует обычной кла- виатуре; □ от 1000006 до 100009, а также 1000025 и 1000026 являются клавишами цифровой клавиатуры; □ от юооою до 1000017 являются клавишами управления курсором; □ от 1000020 до 1000023 соответствуют клавишам модификаторов; □ юооооо, 1000001, 1000004 и 1000005 можно объединить в отдельную группу, так как они также генерируют коды символов.
Если необходимо узнать, были ли в момент наступления события совместно с клавишей нажаты клавиши модификаторов, например <8ЫЛ>, <С(г1> или <А1е>, то это можно проверить с помощью метода тскНПегз (). С помощью метода еехе () можно узнать Опкоде-текст, полученный вслед- ствие нажатия клавиши. Этот метод может оказаться полезным в том случае, если в виджете потребуется обеспечить ввод с клавиатуры. Для клавиш мо- дификаторов этот метод вернет пустую строку. В этом случае нужно вос- пользоваться методом кеу(), который будет содержать код клавиши (см. табл. 14.1). ♦ Метод для обработки событий клавиатуры класса, унаследованного от 0И1бдес, может выглядеть следующим образом. уо1с1 МуйЛйдеС: :кеуРгеззЕ^гепЪ (ОКеуЕуепЪ* ре) { зтехСсЪ (ре->кеу()) { сазе ©С::Кеу_2: И (ре->тосИС1егз () & ОЬ: :5ЬИеМоа1Нег) { // Произвести какие-либо действия } е1зе { // Произвести какие-либо действия } Ьгеак; де€аи1Ъ: 0Ю1с1дее:: кеуРгеззЕуепЪ (ре) ; // Передать событие дальше } } В данном примере проверяется, не нажаты ли совместно клавиши <2> и <8ЫЙ>. Для проверки статуса значения, возвращаемого методом тоазлхегз (), используются значения, указанные в табл. 14.1. Класс ОРосизЕуепТ Когда пользователь набирает что-нибудь на клавиатуре, информацию о на- жатых клавишах может принимать только один виджет. Если виджет в дан- ный момент выбран для ввода с клавиатуры, то говорят, что он находится в фокусе. Объект события фокуса ОРосизЕуепь передается в методы обработ- ки.сообщений ЕосизХпЕуепе() и СосизОиеЕуепс (). Этот объект не содержит значимой информации. Основное назначение ОРосизЕуепс — сообщить о по- лучении или потере виджетом фокуса, для того чтобы можно было, напри- мер, изменить его внешний вид. Эти методы вызываются в том случае, когда виджет получает (СосизТпЕуепь ()) или теряет (СосизоиеЕуепе ()) фокус.
Событие обновления контекста рисования. Класс 0Ра!п1ЕуеМ 01 поддерживает двойную буферизацию (доиЫе ЬиНепп§). Ее можно отклю- чить ВЫЗОВОМ ОИхддеЬ::зеСАЪЬгхЬиСе(ОЬ::ИА_РахпС0пЗсгееп). Вполне ВОЗ- МОЖНО, последствия вас удивят: дело в том, что некогда выведенная в окно графическая информация вдруг исчезнет при изменении размеров окна при- ложения или после перекрытия его окном другого приложения. Чтобы этого не произошло, необходимо получать и обрабатывать событие 0Ра1пьЕуепс. В объекте класса ^раxпсЕVепе передается информация для перерисовки всего изображения или его части. Событие возникает тогда, когда виджет впервые отображается на экране явным или неявным вызовом метода зИо«(), а также в результате вызова методов герахпс() и ирдаье (). Объект события переда- ется в метод ра^пеЕVеп(: (), в котором реализуется отображение самого вид- жета. В большинстве случаев этот метод используется для полной перери- совки виджета. Для маленьких виджетов это приемлемо, но для виджетов больших размеров рациональнее перерисовывать только отдельную область, действительно нуждающуюся в этом. Для получения координат и размеров такого участка вызывается метод гедхоп(). Вызовом метода сопсахпзО можно проверить, находится ли объект в заданной области. Например: МуС1азз: храхпТЕуепИОРахпЪЕуепЬ* ре) { СРахпЬег рахпРег(ЪЪхз); ОКесЬ г(40, 40, 100, 100); хЕ (ре->гедхоп().сопЬахпз(г)) { рахпЁег.дгаэдКесЬ(г); ) } О графике мы еще поговорим в части IV этой книги. События мыши Мышь дает возможность пользователю указывать на объекты, находящиеся на экране компьютера, и с ее помощью можно проводить различные манипу- ляции над объектами, которые невозможно или неудобно выполнять с помо- щью клавиатуры. Самое большое преимущество мыши перед клавиатурой состоит в том, что указание на предметы реального мира — это естественное действие для человека, заложенное в нем с раннего детства, чего не скажешь о работе
с клавиатурой. Мышь можно охарактеризовать как продолжение руки чело- века, располагающееся в виртуальном пространстве, с помощью которого можно производить разного рода операции. Например, указывать на объекты, выбирать их, перемещать с одного места на другое. Реализация событий мы- ши сложнее других, так как программа должна определять, какая кнопка на- жата, удерживается она или нет, был ли произведен двойной щелчок и какие клавиши клавиатуры были нажаты в момент возникновения события. Класс ОМоизеЕуеп! Объект этого класса содержит информацию о событии, вызванном действием мыши, и хранит в себе информацию о позиции указателя мыши в момент вы- зова события, статус кнопок мыши и даже некоторых клавиш клавиатуры. Этот объект передается в методы тоизеРгеззЕуепь (), тоизеМелгеЕуепс (), тоизеКе1еазеЕVеп^ () И тоизеБоиЫеСМскЗ^епЬ (). Метод пюизеР^еззЕVеп^: () вызывается тогда, когда произошло нажатие на одну из кнопок мыши в области виджета. Если, нажав кнопку мыши и не от- пуская ее, переместить указатель мыши за пределы виджета, то он будет по- лучать события мыши, пока кнопка не будет отпущена. При движении мыши будет вызываться метод тоигеМсгуеЕуепь (), а при отпускании кнопки про- изойдет вызов метода тоизеПе1еазеЕуепс (). По умолчанию метод тоизеМсплеЕ>уепе() вызывается при перемещении указа- теля мыши, только если одна из ее кнопок нажата. Это позволяет не созда- вать лишних событий во время простого перемещения указателя. Если же необходимо отслеживать все перемещения указателя мыши, то нужно вос- пользоваться методом зеемоиаетгаскхпд () класса <2И1ддеь, передав ему па- раметр Ыие. Метод шоиаеПоиЫесИск^епЫ) вызывается при двойном щелчке кнопкой мыши в области виджета. Для определения местоположения указателя мыши в момент возникновения события можно воспользоваться методами д1оЪа1х(), д1оЬа1У(), х() и у(), которые возвращают целые значения. Также можно воспользоваться метода- ми роз () или д1оЬа1Роз (). Метод роз () класса омоизеЕуепь возвращает по- зицию указателя мыши в момент наступления события относительно левого верхнего угла виджета. Если нужна абсолютная позиция (относительно лево- го верхнего угла экрана), то ее получают с помощью метода д1оЬа1Роз (). Вызвав метод Ьисеоп (), можно узнать, какая из кнопок мыши была нажата в момент наступления события (табл. 14.3). Метод Ьиьеопз () возвращает би- товую комбинацию из приведенных в табл. 14.3 значений. Как видно из этой таблицы, значения не пересекаются, поэтому можно применять операцию | (ИЛИ) для их объединения.
Таблица 14.3. Некоторые значения перечисления моизевиыоп пространства имен ос Константа Значение Описание ЫоВиСЪоп 0 Кнопки мыши не нажаты ЬеГЬВиЁЪоп 1 Нажата левая кнопка мыши НхдЪСВиЬСоп 2 Нажата правая кнопка мыши МхдВи^Ёоп 4 Нажата средняя кнопка мыши Если необходимо узнать, были ли в момент возникновения события мыши нажаты клавиши-модификаторы <С(г1>, <8Ь1Л> и/или <А11>, то это можно проверить с помощью методатоашегао, реализованного в базовом классе ОТприСЕуепъ (см. рис. 14.1, табл. 14.1). При вызове тоизеОоиЫеСИскЕуепЬ () метод пюизеРгеззЕуепС. () вызывается дваж- ды, так как двойной щелчок обрабатывается как два простых нажатия. По умолча- нию интервал двойного щелчка составляет 400 мс, а для изменения этого интервала нужно вызвать метод зеЪВоиЫеСИскХпИегуа! () класса ОАррИсаЫоп. На рис. 14.2 показана программа, демонстрирующая обработку событий мы- ши. Ее реализация приведена в листингах 14.1—14.3. На рисунке она нахо- дится в состоянии перемещения указателя мыши с нажатой левой кнопкой и с нажатой клавишей <Сйг1>. □ МоиаеЕуеп! . „ Моугд ЬиИог1зГ=1еН хГ = П" у:?=’3 Рис. 14.2. Виджет, получающий события мыши #1пс1ис1е <01:(5и1> #1пс1иде "МоизеОЬзегуег.к" хпЁ ша1п(1п11 агдс, сИаг** агду)
ОАррИсаЫоп арр(агдс, агду); МоизеОЪзегл/ег юдЪ; «дЪ.гез1ге(250, 130); юдй.зкотеО ; геШгп арр.ехес(); } В функции та±п() производится создание объекта реализованного нами класса моизеоъзе^ег и с помощью метода гез!ге() устанавливаются разме- ры окна виджета. Вызов метода зЬоу? () отображает виджет на экране. #11п<1еС _МоизеОЬзе1лгег_11_ #деГ1пе „МоизеОЬзехллеГ—И— #1пс1ис1е <0ССи1> // ==================^========================================^======== с1азз МоизеОЬзе^ег : риЬИс 0Ьа1эе1 ( риЬИс: МоизеОЬзезп/ег (0М1ддее* рюдЪ = 0) ; ргоШсШск V^^Ъиа1 VО^<Д гпоизеРгезз^еп^ (фМоизеЕХлепе* ре) ; V^^Сиа1 VО^д тоизеКе1еазеЕуепЪ (ОМоизеЕХгепЪ* ре) ; VI г Ша 1 VО^<^ тоизеМскгеЕХгеп^ (ОМоизеЕ^леп^* ре) ; VО^(3. йшпрЕХгепй (ОМоизеЕХгепй* ре, сопзй 051:г1пд& зЪгМеззаде) ; 0511г1пд тодШегзТп^о (ОМоизеЕуепй* ре ОЗЪгхпд ЬиШопзХп^о (СМоизеЕХгепХ* ре #епд1 ± / /—МоизеОЬзездгеГ—Ъ._
В классе моизеоьзегуег определены три метода для обработки событий мыши: □ тоизеРгеззЕуепъ () — для нажатия на кнопку мыши; □ тоизеКе1еазеЕуепС () —ДЛЯ отпускания КНОПКИ МЫШИ; □ тоизеМоVеЕVеп^ () — для перемещения мыши. Метод аишрЕуепс () выводит информацию о состоянии события мыши. Для пре- доставления информации (в виде строки) о клавишах-модификаторах и кнопках мыши в классе определены методы тоахЕхехзтпГо () и ЬиеъопзХпГо (). #1пс1иде "МоизеОЬзегуег. Ъ" ц------------------------------------------------------------------------------ МоизеОЬзегуег: :МоизеОЬзегуег (ОМдлДдеЪ* раддЪ /*= 0*/) : 0ЬаЬе1 (ртлтдС) { зеЁАНдшпепЦОЪ: :А11дпСепСег) ; зеЁТехЬ("Моизе 1пСегасЪ1опз\п(Ргезз а тоизе ЬиЪЪоп)"); } //----------------------------------------------------------------------------- /^1гГиа1*^о1с1 МоизеОЬзегуег: гтоизеРгеззЕуепЪСОМоизеЕуепС* ре) { йитрЕХгепЪ {ре, "Моизе РгеззесЯ" ); } //----------------------------------------------------------------------------- /^1гЬиа1*Аго1с( МоизеОЬзегиег: :тоизеКе1еазеЕХгепС(СМоизеЕУепС* ре) { йитрЕХлегИ: (ре, "Моизе Ке1еазед") ; } //----------------------------------------------------------------------------- /^хгСиа!*/ \ло1<3. МоизеОЬзехзлег: :1поизеМо'\7’еЕХ7'епС (ОМоизеЕ\геп^* ре) { йигпрЕиепЬ (ре, "Моизе 1з МоV^пд"); } //----------------------------------------------------------------------------- уохс! МоизеОЬзегуег: гдитрЕХгепС(ОМоизеЕХлепЪ* ре, сопзЪ 05Ёг1пд& зЪгМзд)
{ зеСТехС(зСгМзд » + й\п ЬиССопз()=" + ЬиССопзТп^о(ре) + “\п х () = ” + ОЗСгхпд::пшпЬег(ре->х()) + " \п у {) =" + О^Сгхпд: : пшпЬег (ре->у ()) + "\п д1оЬа1Х() = " + ОЗЬгхпд:: пшпЬег (ре->д1оЬа1Х()) + •’ \п д!оЬа1У () =" + ОбСгхпд:: пшпЬег (ре->д1оЬа1У ()) + "\п тод1^1егз() = " + шод±€1егз1пСо(ре) ) / } //------------------------------------------------------------------------- ОЗСгхпд МоизеОЬзегуег: :то<31^1егз1пЬэ(0МоизеЕуепС* ре) { ОЗСгхпд зСгМосИ^хегз; 1С(ре->тосИ^1егз() & ОС: :5Ы€СМос11€1ег) { зСгМосИ^хегз += "5Ы^С } 1^(ре->тос11€1егз{) & :СопСго1Мсх11^1ег) { зЁгМосЫСхегз += "Сопёго1 •; } 1С(ре->тоЦ111егз() & ОС: :А1ЬМод1^хег) { зСгМосИСхегз += ”А1С”; } геСит зСгМодШегз; } //------------------------------------------------------------------,------ ОЗСгхпд МоизеОЬзе^ег: :ЬиССопз1пСо(0МоизеЕХгепС* ре) { ОЗСгхпд зЫВиССопз; (ре->ЬиССопз() & ОС::Ье^СВиССоп) { зСгВиССопз += "Ье^С "; ) (ре->ЬиССопз() & ОС::К1дкСВиССоп) { зСгВиССопз += "К1сй1С ";
1е(ре->ЬиЪСопз() & ОС: :М1с1ВиеСоп) { зСгВиЬСопз += "М1<±31е" ; } геСит зСгВиСЬопз; } В конструкторе класса вызов метода зесдЫдлтепс () с параметром дИдпсепсег выполняет центровку всей выводимой нами информации. В ме- тодах тоизеРгеззЕуепЬ(), тоизеВе1еазеЕ^епЬ() И шоизеМоуеЕуепЬ(), отсле- живающих события мыши, вызывается один и тот же метод — аитрЕVепс (), в который передаются указатель на объект события и строка, информирую- щая о методе обработки этого события. Метод тоа1Е1егз1пСо() предостав- ляет информацию о клавишах-модификаторах в виде строки, нажатие кото- рых проверяется вызовом метода шодШегзО. Информацию о нажатых кнопках мыши предоставляет метод Ьиесопз1п1о (). Вся информация собира- ется в методе ашпрЕуепс () в одну строку и выводится при помощи метода зе1Тех1(), Класс СМП1ее!Еуеп1 Ввиду того что последнее время часто используются мыши, оснащенные ко- лесиком, то рекомендуется реализовывать метод для обработки события про- крутки колеса — ОИИееЗ^еп»:, который унаследован ОТ ОТприЪЕуепе (см. рис. 14.1). Объект класса оиьееХЕуепе содержит информацию о событии, вызванном колесиком мыши. Объект события передается в метод мИееХЕ^епъ () и со- держит информацию об угле и направлении, в которое было повернуто коле- сико, а также о позиции указателя мыши, статусе кнопок мыши и некоторых клавиш клавиатуры. Наряду с методами Ьигьопз (), роз () и д1оЬа1Роз (), ко- торые полностью идентичны методам класса события омоизеЕуепъ, в классе ОИЬееЗ^епъ имеется метод ае1са(), с помощью которого можно узнать угол поворота колесика мыши. Положительное значение говорит о том, что коле- сико было повернуто от себя, а отрицательное значение — на себя. Методы еп{егЕуеп1() и 1еауеЕуеп1() Эти методы вызываются в том случае, когда указатель мыши попадает или покидает область виджета. Их можно переопределить, например, в том слу- чае, если требуется изменить внешний вид виджета. Метод епЬе^ЕVеп^ () по- лучает объект события типа дЕуепс и вызывается каждый раз, когда указа- тель мыши входит в область виджета. Метод 1еаVеЕVеп(: () получает объект события типа ОЕуепь и вызывается, когда указатель мыши выходит за преде- лы области виджета.
Событие таймера. Класс СГПтегЕуеп! Объект класса от1тегЕуепп содержит информацию о событии, инициирован- ном таймером. Этот объект передается в метод обработки события ЫтегЕуепь (). Объект события содержит идентификационный номер таймера. Например, для класса, унаследованного от онШдее, метод обработки этого события мо- жет выглядеть следующим образом: тоге! МуС1азз: : СхтегЕтеШ: (ОТыпегЕ^епЬ* е) { (етепе->Ъ1тег1с1() == туТтегМ) { // Произвести какие-либо действия } е1зе { ОМхйдеЪ:: ЪтегЕХгепЁ (е); // Передать событие дальше } } Более подробную информацию о таймерах вы найдете в главе 38. События перетаскивания (бгад & скор) Этой теме посвящена глава 30. Поэтому здесь мы ограничимся кратким опи- санием классов событий. Класс СЮгадЕп1егЕуеп1 Класс события ОПгадЕпЬег^епе унаследован ОТ класса ^^^адМОVеЕVеп^ (см. рис. 14.1). Объект класса содержит данные события перетаскивания. Если пользователь, перетаскивая объект, попадает в область виджета, то вы- зывается метод с^^адЕпЪе^ЕVеп^ (). Класс ООгадЬеауеЕуеп! Объект класса ^^^ад^еаVеЕVеп^: содержит данные события перетаскивания в том случае, если пользователь, перетаскивая объект, выходит за область виджета. При этом вызывается метод а^ад^еаVеЕVеп^ (). Класс ОйгадМоуеЕуеМ Этот класс служит для представления данных события перетаскивания в тот момент, когда данные находятся в области виджета. Возникновение этого события приводит к вызову метода ЦгадМоуе^епЬ ().
Класс СЮгорЕуеп! Объект класса ^^^орЕVеп^ передается в метод а^орЕVепь() при отпускании объекта в принимающей области виджета. Остальные классы событий Класс ОСЫ1с1Еуеп1 Это событие происходит в момент создания или удаления объекта-потомка. Объект события передается в метод сЪ^1аЕVепе(), который определен в классе ооьэесь. Вызовом метода осъИс^епг: :сЫ1ао можно получить указатель на этот объект. При помощи методов осинаЕуепе: :эааеа() и осъыаЕуепе: :гепкуиеа() можно узнать о создании и удалении объекта потомка. Класс СС!озеЕуеп1 Событие ос1озеЕуепь создается при закрытии окна виджета. Оно может быть вызвано пользователем или методом ошадес: с1озе (). Объект класса 0с1озеЕуепс передается в метод с1озеЕуепс (), в котором можно спросить пользователя, действительно ли он хочет закрыть приложение. Это имеет смысл в тех случаях, когда пользователь не сохранил свои данные. При помощи методов ассерео и хдпогеО устанавливается флаг, сообщаю- щий о согласии получателя события закрыть окно. Вызов ассере () приведет к тому, что после возвращения из этого метода окно будет спрятано методом мае (). Вызов хдпоге () оставит окно без изменений. Класс ОНШеЕиеп? Данное событие создается при нажатии пользователем кнопки свертывания приложения. Также это событие может быть вызвано методом ыае(), де- лающим виджет невидимым. Объект события онхаеЕуепь передается в метод 11^с^еЕVепе (). Класс ОМоуеЕуеп! Событие омоуеЕуепъ возникает при перемещении виджета. Для виджетов верхнего уровня это соответствует перемещению его окна. Объект события класса ^моVеЕVепс передается в метод тоVеЕVепс () и содержит информацию о старых и новых координатах виджета, которые можно получить вызовом методов роз () и о1ароз ().
Класс ОЗкотмЕчеп! Событие генерируется при создании виджета и.при вызове метода зЪо«(). Объект события о 5 и отцепе передается в метод зИоздВуепь (). Класс ОНез1геЕуеп1 Пользователь может изменять размеры окна при помощи мыши. При этом создается объект события ^кез^2еЕVепк. Объект передается в метод гегхге^епс () и содержит информацию о старых и новых размерах виджета, Которые МОЖНО ПОЛУЧИТЬ ВЫЗОВОМ МеТОДОВ зхге () и о1аЗхге (). Реакции на изменение размеров виджета могут быть следующими: □ перерисовка содержимого окна; □ изменение размеров виджетов-потомков. Пример, показанный на рис. 14.3 и приведенный в листинге 14.4, демонстри- рует перезапись метода ^ез^геЕVепе(). В окне отображается информация о текущей ширине и высоте окна, которая обновляется при изменении его размера. г . । Рис. 14.3. Демонстрация переопределения метода гезхгеЕуепп () #1пс1иде <0НЗи1> И ====================================================================== с1азз КезхгеОЬаегуег : риЬИс 0ЬаЬе1 { риЬИс: КезАгеОЬзегиег(ОМхДдеЪ* рюдС. = 0) : 0ЬаЪе1 (рюдП) { зеЬАНдптепП (ОН: :АИдпСепНег);
} ’ ргоЪесЪеск V^^Ъиа1 ллотс! гезхгеЕуепЪ (ОКезхгеЕХгепЬ* ре) { зе^Техе (□ЗЪга.пд ("Кез^гед") + " \п ушЭДЬ () =" + ОЗЪгхпд:: пшпЬег (ре->з1ге () .иМЪЪ ()) + " \п ИехдЫ: () =" + ОЗЪгхпд:: пшпЬег (ре->з1ге () . кехдкс ()) ); } }; //--------------------------------------------------------------------------- 1пй тахпСхпЪ агдс# скаг** а^дV) { 0Арр11са1:1оп арр(агдс, агду) ; КезхгеОЬзегуег тедЬ; юдЪ.гез1ге(250, 130); юдС.зкою(); ге^игп арр.ехес(); } В листинге 14.4 в функции ша1п( ) создается виджет (определенного нами класса кегххеоьгестег), размеры которого изменяются вызовом метода гез12е(). Метод гевхгеЕуепео вызывается всякий раз, когда пользователь или программа изменяют размеры окна. Метод з!ге() объекта события воз- вращает объект класса озхге, при помощи которого можно узнать текущую ширину и высоту виджета (методы ш1аеъо и ЪехдИъо). Значения ширины и высоты передаются в статический метод озегхпд: : пшпЬег (). Таким обра- зом они преобразуются в строки и присоединяются к основному сообщению, которое выводится при помощи слота ОЬаЬе! : : веЬТехС (). Реализация собственных классов событий Если вам не будет хватать событий, предоставляемых р1, и понадобится оп- ределить свое собственное, то необходимо поступить так, как это сделано для всех классов событий (X, а именно унаследовать базовый класс для всех событий ОЕуепъ. В конструкторе ОЕуепь нужно передать идентификационный
номер для типа события, который должен быть больше чем : :Пзег, для того чтобы не создать конфликт с уже определенными типами. Значение ^ЕVеп^1: :пзег равно 1000 (см. табл. 14.4). В созданном событии можно реа- лизовать все необходимые вам методы, для передачи дополнительной ин- формации. Например: с1азз МуЕиепЪ : риЬИс дЕХгепИ { риЫхс: МуЕХлепС: () : ОЕХлепЬ ((Туре) 1200) { } (251:г1пд 1п1о() { гееит "СизЪогпЕуеп^" ; } }; Свои собственные события можно высылать с помощью методов □СогеАррИсаехоп: : зепбЕуеп!: () ИЛИ ОСогеАррИсаСгоп: :розСЕVепС (), а ПО- лучать методами ООЬз есС : : еVепЪ () ИЛИ ООЬд есС : : сизЁоп^епС (). Переопределение метода еуеп1() Все возникающие в системе события направляются в очередь, из которой они извлекаются циклом событий, находящимся в методе ехес () объекта прило- жения осогеАррИсаЫоп или ОАррИсастоп. Объект класса ОАррИсаСхоп по- сылает события тем виджетам, которым они предназначены. Все объекты событий поступают в метод еVепе (), в котором определяется их тип и произ- водится вызов специализированных методов обработки, предназначенных для этих событий, например, вызов метода тоизе₽^еззЕVепь () при нажатии кнопки мыши. Это проиллюстрировано на рис. 14.4. Метод еVепI2 (), как и все остальные специализированные методы обработки событий, — виртуальный. Его можно переопределить, но это следует делать только в тех случаях, когда есть острая необходимость, так как такое переоп- ределение может изрядно усложнить исходный код всей программы. Если все события будут обрабатываться только одним методом еVепе(), то это может привести к появлению громоздкого метода, содержащего несколько тысяч строк для обработки всевозможных событий. Поэтому, если есть воз- можность, лучше всего перезаписывать соответствующие специализирован- ные методы для обработки событий. Обработку событий в методе еVепь() 10 Зак 3983
лучше всего применять для тех типов событий, для которых не существует специализированных методов обработки. ОЧ^ЁМСИСТЕМНЫХТОШТИЙ] ОС-программа СПИСОК ВИДЖЕТОВ ПРИЛОЖЕНИЯ Виджет! Виджет 2- - Виджет 3 Виджет 4 * Виджет 5- - еувпЦ)—»геаЬ«ЕуепЧ) ВиджетЫ Рис. 14.4. Схема доставки и обработки событий В метод еуепс () передается указатель на объект типа ОЕуепе. Все события унаследованы от класса ОЕуепъ, который содержит атрибут, представляющий собой целочисленный идентификатор типа события, и с его помощью можно всегда привести указатель на объект класса овуепе к нужному типу. В методе еуепс () программа должна определить, какое событие произошло. Для об- легчения этой задачи существует метод суре (). Возвращаемое им значение можно сравнить с предопределенной константой (табл. 14.4), что даст воз- можность привести это событие к правильному типу. Таблица 14.4. Некоторые типы событий Константа Значение Константа Значение - Ыопе 0 РоИзЪ 75 Тд-тпег 1 ЬауоиЁВедиез 1: 76 МоизеВиССопРгезз 2 Прда Ъ еКедиезЬ 77 МоизеВиеСопВе1еазе 3 ирйаЪеЬаСег 78 МоизеВий- еопПЫСИск 4 СопЪех(Д4епи 82 МоизеМсууе 5 ХприСМеЪЬод 83
Таблица 14.4 (продолжение) Константа Значение Константа Значение КеуРгезз б Ассезз хЫ 11 СуРгераг е 86 КеуКе1еазе 7 ТаЫеСМсгуе 87 РосизТп 8 Ьоса1еСИапде 88 РосизОиЪ 9 ЬапдиадеСЪапде 89 ЕпЬег 10 ЬауоиЁВхгесЁхоп- СИапде 90 ^еаVе 11 4 5Ёу1е 91 РахпЁ 12 ТаЫеЪРгезз 92 Мс^е • 13 ТаЫеСКе1еазе 93 Кезххе 14 ХсопВгад 96 СгеаЪе 15 РопЬСЪапде 97 Пез^гоу 16 ЕпаЫ ебСИапде 98 ЗИоте 17 Асйз^аМопСИапде 99 Н1бе 18 5Ъу1еС11апде 100 С1озе 19 IсопТехЬСЪапде 101 рихе 20 МодхIхедСИапде 102 РагепЪСЪфпде 21 Юхпс1оюВ 1 оскес! 103 ТкгеабСЪапде 22 МхпдоюПпЫ о с кеб 104 Мгпбо^АсС^а^е 24 ЮхпбоюЗЁаЬеСИапде 105 пботеИеас Ъ1 Vа е е 25 МоизеТгаскхпдСИапде 109 ЗИошТоРагепС 26 Тоо1Тхр 110 НхбеТоРагеп!: 27 МЪаЪзТкхз 111 ЮИее1 31 ЗЪаЪизТхр 112 ЮхпбоюТхЪХеСЪапде 33 АсЪхопСЪапдеб 113 МхпбоюХсопСЪапде 34 АсСхопАскЗеб 114 АррИса^хоп МхпботеХсопСкапде 35 Ас 1: х опКето Vед 115 АррИсаСхоп- Роп^СЪапде 36 РИеОреп 116 Арр!х са Ъ хопЬауоиЪ- ВхгесС1опСЪапде 37 ЗкогЬси!: 117
Таблица 14.4 (окончание) Константа Значение Константа Значение АррЫсакхоп Ра1е к кеСИапде 38 Ика к з Тк х з С1х скед 118 Ра1ек кеСИапде 39 АссеззхЫХхкуНеХр 119 СХхрЬоагд 40 Тоо ХВагСкапде 120 ЗреесЪ 42 Арр 1х са к х олАс к 1 \га кед 121 ЗоскАсХ 50 АррИсакхоп Леаскгтакед 122 ЗЪохХсиШуеггХсЯе 51 ОиегуПИа кз ТЫ з 123 Ое^еггедЛеХеке 52 ЕпкегИкакзТк1зМоде 124 ЛгадЕпкег 60 Ьеа VеП^1а к з ТЫ зМоде 125 ЛгадМоуе 61 ЗОгдегСкапде 126 ^^ад^еаVе 62 НоVе^Епке^ 127 Лгор 63 НоVе^^еаVе 128 ЛгадКезропзе 64 НоVе^МоVе 129 СЫ1дАддед 68 АссеззхЫНку БезсгхрЫоп 130 СЫ1дРо1хз11ед 69 РагепкАЬоикТоСкапде 131 СЫХдКепихтед 71 И^пЕVепкАск 132 ЗЫШхпдоюКедиез к 73 МепиЬагПрдакед 153 РоИзИКедиезк 74 Озег 1000 Перезапись метода еVепк() для класса, унаследованного, например, от ОЫддек, может выглядеть следующим образом: Ьоо1 МуСХазз::еуепк(ОЕиепк* ре) { 1Х (ре->куре() == ОЕуепк::КеуРгезз) { рКеуЕуепк* рКеуЕХгепЪ = зСаС1с__сазЪ<0КеуЕХлепС*> (ре) ; (рКеуЕКлепе->кеу () == ©е: :Кеу„ТаЬ) { II Произвести какие-либо действия гекигп Сше; } } (ре->куре() == ОЕХлеп!:: :Н1де) {
// Произвести какие-либо действия геШгп Сгие; } геСигп ОИ&ЗдеЪ::еуепъ(ре); } Метод возвращает ггие в том случае, если событие было обработано и не требует передачи дальше. После этого событие удаляется из очереди собы- тий. При возвращении Га1зе событие будет передано дальше — виджету- предку. Если ни один из предков не сможет обработать событие, то оно будет просто проигнорировано и удалено из очереди событий. Сохранение работоспособности приложения В некоторых ситуациях при интенсивных действиях в вашей программе мо- жет случиться так, что графический интерфейс программы "замрет" и станет неспособным обрабатывать события, связанные с интерактивными действия- ми пользователя (нажатие кнопки мыши или клавиши клавиатуры). Возьмем следующий пример: Сог (1пЬ 1 = 0; 1 < 1000; ++1) { // Выполнить трудоемкие вычисления } Этот код, если он исполняется в основном потоке, заблокирует на опреде- ленное время обработку событий, и, значит, пользовательские действия с ин- терфейсом все это время обрабатываться не будут, а также если окно этой программы будет перекрыто другим, то оно не будет перерисовываться. Один из возможных вариантов решения этой проблемы — исполнение по- добного кода в отдельном потоке (см. главу 39). Более простой способ — вызов метода осогеАррЫсасхоп: гргосезз^епкзО, который позаботится о том, чтобы все накопившиеся в очереди события были обработаны. Для этого наш пример должен быть изменен следующим образом: Гог (1ПГ 1=0; 1 < 1000; ++1) { // Выполнить трудоемкие вычисления 0Арр->ргосеззЕуепЪз(); //Доставить накопившиеся события } Теперь в каждой итерации цикла после выполнения действий производится обработка событий, что дает программе возможность перед выполнением оче- редных действий "вдохнуть воздух" и среагировать на накопившиеся события.
Резюме Наступление события вызывается каким-либо действием со стороны пользо- вателя,-например, щелчком кнопкой мыши, изменением размеров окна и т. д. Некоторые события вызываются самой программой. В этой главе вы узнали, что события являются механизмом оповещения более низкого уровня по сравнению с сигналами и слотами. Для получения доступа к информации о событии предусмотрен объект события, который передается в метод обра- ботчика события. Все методы обработчиков событий, за исключением метода еуепъо, относятся к группе специализированных обработчиков. Метод еуепс. () является центральным методом обработки событий. Сначала все со- бытия попадают в него, а он производит вызов специализированных методов для обработки соответствующих событий, например, при нажатии клавиши на клавиатуре вызывается обработчик кеуРгезз^епс (). В метод еуепс () пе- редается в качестве аргумента указатель на объект класса ОЕгепь, который содержит информацию о событиях/ Метод ОЕуепе:: ъуре () возвращает цело- численный идентификатор типа события. При написании собственных виджетов не следует обрабатывать все события в методе еVепе (), так как это может изрядно усложнить код. Более правиль- ным подходом является перезапись специализированных методов обработки событий. Событие перерисовки окна возникает тогда, когда виджет был частично или полностью перекрыт другим окном. Объект события 0Ра1пеЕуепь содержит информацию об участке, который должен быть перерисован. Для обработки события необходимо переопределить метод рахпеЕуепь (). События мыши обрабатываются методами тоизеРгеззЕуепъ (), тоизеМоуеЕуепЪ (), тоизеКеХеазеЕуепЬ () И тоизеОоиЫеСЫскЕц-епЬ (). Для определения местоположения указателя мыши можно воспользоваться мето- дами д1оЬа1Х(), д1оЬа1У(), х(), у(), роз() ИЛИ д1оЬа1Роз(). Для обработки событий клавиатуры предназначены методы кеуРгеззЕуепе () и кеуНе1еазеЕуепе (). Метод кеуРгеззЕуепс () вызывается каждый раз, когда пользователь нажимает на клавиатуре одну из клавиш. Метод кеупе1еазеЕ^епь (} вызывается при отпускании клавиши. Для определения своего собственного события необходимо задать число (идентификатор), которое будет определять тип события и не должно совпа- дать с уже существующими идентификаторами типов событий.
Глава 15 Фильтры событий "Тот, кто спрашивает, всегда получит ответ". Притчи Камеруна Как правило, событие передается тому объекту, над которым было произве- дено действие, но иногда требуется его обработка в другом объекте. В биб- лиотеке ос предусмотрен очень мощный механизм перехвата событий, кото- рый позволяет объекту фильтра просматривать события раньше объекта, которому они предназначены, и принимать решение по своему усмотре- нию — обрабатывать их и/или передавать дальше. Такой мониторинг осуще- ствляется с помощью метода ооЬэесе: :1пзСа11Еуеп1:Р11еег(), в который передается указатель на объект, осуществляющий фильтрацию событий. Важно то, что установка фильтров событий происходит не на уровне классов, а на уровне самих объектов. Это дает'возможность вместо того, чтобы насле- довать класс или изменять уже имеющийся (что не всегда возможно), просто воспользоваться объектом фильтра. Для настройки на определенные события необходимо создать класс фильтра, установив его в нужном объекте. Все по- лучаемые события и их обработка будут касаться только тех объектов, в ко- торых будут установлены фильтры. Фильтры событий можно использовать, например, в тех случаях, когда нуж- но добавить функциональность к каким-либо уже реализованным классам, не наследуя при этом каждый из них. После того как класс фильтра будет реа- лизован, его объекты можно будет устанавливать в любых объектах, произ- веденных от наследующих ось^есь классов. Это позволит сэкономить время на реализации, так как потребуется написать меньше кода, и, вместе с тем, значительно сократит временные затраты на отладку программы. Разработ- чику больше не надо заботиться о методах обработки событий для каждого из классов в отдельности, потому что это будет выполняться централизован- но, одним классом фильтра, имеющим силу для всех объектов, в которых он был установлен.
Реализация фильтров событий Чтобы реализовать класс фильтра, нужно унаследовать класс ооь^есс и пере- определить метод еуепсгисег (). Этот метод будет вызываться при каждом событии, предназначенном для объекта, в котором установлен фильтр собы- тий, до его получения. Метод имеет два параметра: первый — это указатель на объект, для которого предназначено событие, а второй — указатель на сам объект события. Если этот метод возвращает значение сгие, то это означает, что данное собы- тие не должно передаваться дальше, а Га1зе говорит о том, что данное собы- тие должно быть передано объекту, для которого оно и было предназначено. Пример, показанный на рис. 15.1, демонстрирует работу фильтра, который устанавливается в трех виджетах. Щелчок мыши на любом из них приводит к появлению окна сообщения, информирующего об имени класса виджета. Рис. 15.1. Программа, демонстрирующая перехват события #1пс1и<3е <0ЬСи1> #1пс1иде "МоизеРИСег.Ь" шС шахп ЦпС агдс, сЪаг** агду) { ОАррИсаЫоп арр(агдс, агду) ; 0ЫпеЕс11С ЬхС (''ОЫпеЕсНС“); СхЪ.хпзСаНЕуепЪРНЬег (пей МоизеРИСег (&ЬхЬ));
СхЪ. зИом () ; 0ЬаЬе1 1Ы (" 0ЬаЬе1 '•); 1Ы Апзеа11ЕуепГ:Р11Ъег (пего МоизеРЫкег (&1Ы)); 1Ы. зкозд () ; ОРизИВи^оп стд ("ОРизИВи^оп") ; сткЯ. 1пз1:а11ЕХлепСР111:ег (пете МоизеРИСег (йспкЯ)) ; спкЯ.зксллП); ге1:игп арр. ехес () ; } В функции тахпО, приведенной в листинге 15.1, создаются три виджета: 0ЫпеЕа1с, оьаЬе1 и оризивиссоп. В каждом из них, с помощью метода хпзьаНЕуепгрНьегО, устанавливается объект, произведенный от одного и того же класса фильтра событий. В конструктор создаваемого фильтра пе- редается его предок, которым выступает сам виджет, для которого устанав- ливается фильтр. Это позволит при уничтожении виджета автоматически уничтожить и объект фильтра. : №стинг|15& Фавщ ***«'«* аЪ к иЗЯ» # 1 йк1еС _МоизеЕ! 11:ег_11_ МеСхпе _МоизеР11Ьег_И_ #1пс1и<3е <ООЬз'ес1:> // ========:=========^======================:============================== С1азз МоизеРНЪег : риЬИс ООЬэесс { ргоЪесЬед: V^^^иа1 Ьоо1 еуеп^РЫГег (ООкдес!:*, ОЕуепЁ*); риЬИс: МоизеРНЪег(ООЬз'есЪ* роЬд = 0); ); В листинге 15.2 обратите внимание на прототип метода еVеп^:г^1ье^ (), кото- рый получает не только указатель на объект события, но и указатель на сам
объект, для которого это событие предназначено. Объект фильтра может де- лать с этим объектом все, что ему будет угодно, вплоть до удаления. #1пс1и<5е <01:Ои1> #1пс1иде "МоизеР! 1 Ъег. И" //--------------------------------------------- МоизеР! 11зег:: МоизеРИ^ег (ООЬ} ес± * роЬд / * = 0 * /) : ООЬзесЁ(роЬз) { } //-------------------------------------------------------------------------- /*^1гГиа1*/Ьоо1 МоизеРНЬег: :етепЪР11Ъег (ООЬэесе* роЬ}, ОЕХгепЪ* ре) { (ре->Суре() == :МоизеВШХопРгезз) { (((ОМоизеЕХгепе*)ре) ->ЬиЪЪоп() ~ ОЪ: :ЬеГЪВиЪ1:оп) { ОЗегхпд зЪгС1аззЫате = роЬэ->гпееаОЬзесе ()->с1аззМагае () ; ОМеззадеВох:: хп^оппаИхоп (0, ”С1азз ЫашеИ, зСгСТаззЫате) ; геСигп Ъгие; } } геСигп €а1зе; } В листинге 15.3 метод еVепср^1се^ о отслеживает событие типа О^епс: :МоизевиссопРгезз, соответствующее нажатию одной из кнопок мыши. Если событие относится к этому типу, то производится преобразова- ние указателя на объект события к указателю типа ^моизеЕVепс. Затем вызы- вается метод ЬиссопО класса дмоизе^епс, чтобы узнать, какая из кнопок мыши была нажата. Если была нажата левая кнопка, то в информационном окне сообщения выводится имя класса виджета, возвращается сгие и событие дальше не передается. В остальных случаях возвращается значение ^а1зе и событие передается дальше. В объектах возможна установка сразу нескольких фильтров, при этом по- следний установленный фильтр будет применяться первым.
Существует возможность глобальной установки фильтра событий, то есть фильтра, который будет действовать на все объекты приложения. Для этого нужно вызвать метод хпвъаИРНеегО объекта осогеАррИсаМоп или 0АррИсае1оп(). Этот фильтр будет получать и обрабатывать события раньше всех объектов приложения, то есть прежде чем его получают сами объекты или их фильтры событий. Данный метод может пригодиться при отладке приложения, но брать его за основу не рекомендуется, так как при этом сни- жается скорость доставки каждого отдельного события. Также возможно пе- резаписать метод Диспетчеризации Событий ОСогеАррЫсаЫоп: :ПоЫ1у () для полного контроля доставки событий. Резюме Иногда возникает необходимость обработки события в другом объекте. В 01 предусмотрен очень мощный механизм перехвата событий, который позволяет без наследования классов изменять реакцию объектов на события. Тем самым экономится время на написание и отладку программы. Чтобы реализовать класс фильтра, нужно унаследовать класс ооьйесь и переопреде- лить метод еуепСРИьег ().
Глава 16 Искусственное создание событий "Компьютеры бесполезны. Они могут только давать вам ответы”. Пабло Пикассо Иногда возникает необходимость в событиях, созданных искусственно. На- пример, это полезно при отладке вашей программы, для того чтобы имитиро- вать действия пользователя.. Для генерации события можно воспользоваться одним из двух статических методов класса осогеАррИсаъхоп— зепдЕуепсо или розСЕуепео. Оба ме- тода получают в качестве параметров указатель на объект, которому посыла- ется событие, и адрес объекта события. Разница между ними состоит в том, что зепйЕуепь () отправляет событие без задержек, то есть его вызов приво- дит к немедленному вызову метода события, в то время как розСЕуепс () по- мещает его в очередь для дальнейшей обработки. Рассмотрим это на примере приложения, имитирующего нажатие пользова- телем клавиш от <А> до <2> (рис. 16.1). ___; Еуеп1$'|ти1айап ——1" " | | Рис. 16.1. Программа, имитирующая ввод пользователя #1пс1ис1е <011Ои1> 1пк та1п (1п^ агдс, скаг** агду)
{ ОАррИсаЫоп арр (агдс, а^дV); ОЬхпеЕсНЛ ЬхЪ("Пзег хприЬ: *); ЪхЬ. зЬош (); ЬхЪ.гез1ге(280, 20); хпЬ 1; Еог (1 =0; х < ОС::Кеу_2 - 0С::Кеу_Д + 1; ++х) { ОСЪаг сЬ =65 + 1; хпС пКеу = ОЬ::Кеу_А +’ 1; ОКеу&лепС* ре. = пею ОКеуЕуепС (ОЕуепС: :КеуРгезз, пКеу, ОС: хЫоМодШег, ск) ; ОАррИсаСхоп::зепйЕуепС (&СхС, ре) ; } геСигп арр.ехес(); ) В исходном коде, приведенном в листинге 16.1, создается объект схс класса ОЫпеЕбхс, который будет выступать в качестве поля ввода. В цикле проис- ходит создание событий типа океуЕуепс. Первый параметр, передаваемый конструктору, задает тип события клавиатуры (здесь он соответствует собы- тию нажатия клавиши клавиатуры ОЕуепС: :кеу₽гезз). Второй параметр за- дает саму клавишу, которая была нажата. Третий — указывает на клавиши- модификаторы, которые могли быть совместно нажаты, в нашем случае это значение равно ос: :ЫомодИ1ег и означает, что никаких клавиш- модификаторов нажато не было (см. табл. 14.1). Последний, четвертый пара- метр указывает на представление клавиши в А5СП-коде (в примере это число начинается с 65, что соответствует заглавной букве "А", и циклически увели- чивается на единицу). Модифицировать объекты событий можно не всегда. При совместном созда- нии искусственных событий с фильтрами можно осуществить подмену са- мих объектов событий. Более подробно фильтры событий рассмотрены в главе 15. В качестве показательного примера использования подобного пе- рехвата события с целью его подмены можно назвать изменение назначения клавиш клавиатуры. Так как класс события клавиатуры не обладает метода-
ми, позволяющими его модифицировать, то каждое сообщение клавиатуры можно получить в объекте фильтра, и перед передачей дальше подменить его другим. В следующем примере происходит подмена клавиши <2> на клавишу <А> (рис. 16.2). Таким образом, нажатие пользователем клавиши <2> повлечет за собой отображение буквы А в поле ввода. Таким образом можно, например, имитировать измененную раскладку клавиатуры. ____ ЕчспгСЬапде Ри<р 16.2. Программа, демонстрирующая подмену события клавиатуры #хпс1ибе <0Ши1> #1пс1ибе "КеуРИеег.Ъ" 1пё таги (хпЪ агдс, сЪаг** агду) { ОАррИсаЬхоп арр(агдс, агду);. ОЕапеЕйдЛ Схе; ГхЬ.зЪоюО ; КеуРНЬег* рРИ^ег = пеш КеуРНЪег (&ЬхЪ) ; СхС. 1пзЪа11Еуеп1:Р11Гег (рРЫЪег); геЪигп арр.ехес(); В листинге 16.2 после создания объекта класса дЫпевахъ и вызова метода зЪоюО создается объект фильтра событий клавиатуры ррнеег, в конструк- тор которого в качестве объекта-предка передается адрес на однострочное текстовое поле ехи. После этого созданный фильтр привязывается к тексто- вому ПОЛЮ ПРИ ПОМОЩИ Метода 1пзЬа11ЕуепСР11еег ().
_КеуГИ^ег_Ъ._ #йе1хпе _КеуГ11Лег_11— #хпс1иде ,<0ЪОих> // ======================—===:==============“=========================== с1аз5 КеуРПЁег : риЫхс ООЬЗесЪ { рго^есХёй: Ъоо1 еуеп^Рх1еег(0ОЬэесЬ* роЬ^, ОЕЬгепЬ* ре) { х! (ре->Суре (.) == «ЭЕХлеп!:: :КеуРгезз) { И (((СКеу^еп1:*)ре)->кеу() == <21:: :Кеу_2) { ОКеуЕХгепГ:* ре = пе» ОКеуЕуепНОЕуеп!:: гКеуРгезз, ОЪ: :Кеу_А, ОЬ:: ЫоМосН^хег, "А" ); ОАррИсаЫоп:: зепбЕуепЬ (роЬ}, ре) ; ге^ига Ьгие; } } геЪигп 1а1зе; } риЫхс: КеуЕНъег (ООЬдесЪ* роЪд = 0) : ООЬэесЪ(роЬэ) { } }; #епсНС //_,КеуГ11Сег_11— В листинге 16.3 в методе еуепъвнъег () отслеживается идентификатор со- бытия о^епъ: :Кеу₽гезз, который соответствует событию нажатия клавиши клавиатуры. При обнаружении этого события его объект преобразовывается к типу океу&уепс, чтобы иметь возможность вызова метода кеу(), который определен в этом классе и позволяет получить код нажатой клавиши. Затем
создается и высылается новое событие нажатия клавиши <А>. После этого возвращается значение егие, и это означает, что событие не должно переда- ваться дальше. Если событие, переданное в параметрах метода еуепскисег (), не удовлетворяло двум поставленным условиям, то этот ме- тод вернет значение Еа1зе, и, тем самым, событие будет передано дальше. Резюме В этой главе мы узнали о возможности искусственного создания событий из самой программы. Для этого можно воспользоваться методами ОАррИсаьгоп:: зепдЕуепь () или ОАррИсаЫоп: урозЬЕуепЬ (). При совмест- ном использовании с механизмом фильтров событий это позволяет осущест- влять подмену объектов событий.
Часть IV Графика и звук
Глава 17 Введение в компьютерную графику "... как будто волшебный фонарик ос- вещает изнутри образы на экране..." Т. С. Элиот. "Песнь любви Альфреда Пруфрока" Графика — одна из наиболее быстро развивающихся отраслей компьютерной индустрии. Известно, что 70% информации человек воспринимает визуально, поэтому графика — это важнейший компонент для взаимодействия человека с компьютером. Для программирования компьютерной графики часто используются такие классы геометрии, как точки, двумерные размеры, прямоугольники, а также специальные классы для хранения цветовых значений. Классы геометрии Группа классов геометрии ничего не отображает на экране. Основное их на- значение состоит в задании расположения, размеров и в описании формы объектов. Точка Два класса оро±пс и орохпър служат для задания точек в двумерной системе координат. В двумерной системе координат точка обозначается парой чисел X и У, где X — горизонтальная, а У — вертикальная координаты. В отличие от обычного расположения координатных осей, при задании координат точки в 01 обычно подразумевается, что ось У смотрит вниз (см. рис. 17.1). Класс ОРохпе описывает точку с целочисленными координатами, а 0Ро±пер — с ве- щественными. Интерфейс обоих классов одинаков, в него входят методы,
позволяющие проводить различные операции с координатами, например, сложение и вычитание с координатами другой точки. При сложении/вычи- тании точек производится попарное сложение/вычитание их координат X и V. Пример, показанный на рис. 17.1, складывает две точки ре1 и ре2. 0РО1ПС ре1(10, 20); ОРотпе ре2(20, 10); 0Ро1пС реЗ; //(0, 0) реЗ = рС1 + рЪ2; 0 10 20 30 X 10 20 30-- рп Р<2 рга Рис. 17.1. Создание и сложение точек Объекты точек можно умножать и делить на числа. Например: ОРохпЪ рЬ(10, 20); ре *= 2; // ре = (20, 40) Для получения координат точки (X, V) реализованы методы х() и у о, соот- ветственно. Изменяются координаты точки с помощью методов геехо и зееУО. Можно получать ссылки на координаты точки, для того чтобы изменять их значения. Например: 0Ро1пе ре(10, 20); ре.гхО += 10; // ре = (20, 20) Объекты точек можно сравнивать друг с другом при помощи операторов == (равно) и । = (не равно). Например: 0Ро1пе ре1(10, 20); 0₽о1пе ре2(10, 20); Ьоо1 Ь = (ре1 == ре2); // Ь = егие Если необходимо проверить, равны ли координаты X и У нулю, то вызывает- ся метод 1зии11 (). Например: (2₽о1пе ре,- // (о, 0) Ьоо1 Ь = ре.хзИиНО; II Ь = егие
Метод шапЪассапЬепдсь () возвращает сумму абсолютных значений коорди- нат X и У. Например: ОРотпС рС(10, 20); 1пС п = рС.тапЪаССапЬепдСИО ; // п = 10 + 20 = 30 Возвращаемое значение является грубым приближением кл/х2+У2 . Этот метод был назван в честь улиц Манхэттена, расположенных перпендикуляр- но друг к другу. Двумерный размер Классы с>312е и сзхгер служат для хранения целочисленных и вещественных размеров. Оба класса обладают одинаковыми интерфейсами. Структура очень похожа на о₽о1пс, так как хранит две величины, над которыми можно проводить операции сложения/вычитания и умножения/деления. 0812е и <2312еГ, как и классы 0₽о1пс, о₽о1пср, предоставляют операторы сравнения ==, > = и метод 1зЫи11 (), возвращающий значение сги© в том слу- чае, если высота и ширина равны нулю. Для получения ширины и высоты вызываются методы () и Ъетдьс (). Изменить эти параметры можно с помощью методов зесн1асъ () и заснетдис (). При помощи методов гм1аси () й гИехдИс () получают ссылки на значения ширины и высоты. Например: 081ге 3120(10, 20); 1пС п = з12©.гад1асЪ()++; // п = 11; з!ге = (11,. 20) Помимо них класс предоставляет метод зса10(), позволяющий изменять размеры оригинала согласно переданному в первом параметре размеру. Второй параметр этого метода управляет способом изменения размера (рис. 17.2): □ ос:: 1дпогеАзресскаь1о — изменяет размер оригинала на переданный в него размер; □ ОС: гКеерАзреськаЫо — новый размер будет заполнять заданную пло- щадь, насколько это будет возможно с сохранением пропорций оригинала; П ос: :ке©рАзр©сскас1овуЕхрапа1пд — новый размер может находиться за пределами переданного в зса1© (), заполняя всю его площадь. Согласно рис. 17.2, изменение размеров з1ге1, з1ге2 и &1геЗ может выгля- деть следующим образом: 0812081201(320, 240); з1го1.зса10(4ОО, 600, ОС::1дпогеАзресСКаС1о); // => (400, 600) 08120 31202(320, 240);
з1ге2.8са1е(400, 600, ОЪ:гКеерАзресЁКаЪхо); // => (400, 300) ОЗхге 81263(320, 240); 81263.зса!е(400, 600, 0Ъ: :КеерАзреси1аГ1оВуЕзфапс11пд); // => (800, .600) 1дпогеАзрес1Ка11о Рис. 17.2. Изменение размеров оригинала Прямоугольник Классы окесь и опесс? служат для хранения целочисленных и вещественных координат прямоугольных областей (точка и размер), соответственно. Задать прямоугольную область можно, например, передав в конструктор точку (верхний левый угол) и размер. Область, приведенная на рис. 17.3, создается при помощи следующих строк: 0₽о1пЬ рЪ(10, 10); ОЗхге 8126(20, 10); ОКесЬ г(рЬ, зхге); Получить координаты X левой грани прямоугольника или У верхней можно при помощи методов х() или у(), соответственно. Для изменения этих коор- динат нужно воспользоваться методами веъх () и весу (). Размер получают с помощью метода зхгео, который возвращает объект класса оз1ге. Можно просто вызвать методы, возвращающие составляющие
размера— ширину «1аеъо и методом з.еъггзеО, а каждую И зеСНехдИС(). высоту кехдъсо. Изменить размер можно его' составляющую — методами зесихаьи () 10 20 30 X 10 ' 20 30-- уУ Рис. 17.3. Задание прямоугольной области точкой и размером Прямая линия Классы оЫпе и оЫпег описывают прямую линию или, правильней сказать, отрезок на плоскости в целочисленных и вещественных координатах. Пози- ции начальной точки можно, получить при помощи методов х1 () и у1(), а конечной — х2 () и у2 (). Аналогичного' результата можно добиться вызо- вами р1 () и р2 (), которые возвращают объекты класса оро1пь/оро1пгр, опи- санные ранее. Методы <3х() и Ду О возвращают величины горизонтальной и вертикальной проекций прямой на ось X и V, соответственно. Прямую, пока- занную на рис. 17.4, можно создать при помощи одной строки кода: ОЫпе Ипе(10, 10, 30, 20); 0 10 20 30 X —!—। ! > Рис. 17.4. Задание прямой линии двумя точками Оба класса оыпе и оЫпеР предоставляют операторы сравнения ==, < = и ме- тод 1зми11 (), возвращающий логическое значение ыие в том случае, когда начальная, и конечная точки не установлены.
Полигон Полигон (или многоугольник) — это фигура, представляющая собой замкну- тый контур, образованный ломаной линией. В эту фигуру реализуют клас- сы 0Ро1удоп и 0Ро1удопг, в целочисленном и вещественном представлении соответственно. По своей сути эти классы являются массивами точек 0Уесь.ог<0Ро1пь> и ‘0Уесьог<0Ро1пер>. Самый простой способ инициализации объектов класса полигона — это использование оператора потока вывода «. Треугольник представляет собой самую простую форму полигона (рис. 17.5), а его создание выглядит следующим образом: 0Ро1удоп ро1удоп; ро1удоп « СРохпЬ(10, 20) « 0Ро1пе(20, 10) « 0Ро1пё(30, 30); О 10 20 30 X Рис. 17.5. Задание полигона тремя точками Цвет Цвета, которые вы видите, есть не что иное, как свет с различными электро- магнитными частотами. На самом деле, человеческий глаз в состоянии вос- принимать очень малый диапазон этих частот. Цвет с наибольшей частотой, которую в состоянии воспринять глаз, — фиолетовый, а с наименьшей — красный. Но даже в таком небольшом диапазоне находятся миллионы цветов. Одновременно человеческий глаз может воспринимать около 10 тысяч раз- личных цветовых оттенков. Цветовая модель — это спецификация в трехмерной системе координат, ко- торая задает все видимые цвета. В <21 поддерживаются три цветовые моде- ли — ВОВ (Кед, Огееп, В1ие; красный, зеленый, голубой), СМУК (Суап, Ма- §еп1а, Уе11о\у ап<1 Кеу; голубой, пурпурный, желтый и "ключевой") и Н8У (Ние, 8а(игабоп, Уа1ие; тон, насыщенность, значение).
Класс СЮо1ог С помощью класса <эсо1ог можно сохранять цвета моделей КОВ и Н8У. Его,определение находится в заголовочном файле ОСо1ог. Объекты класса ОСоХог можно сравнивать при помощи операторов -= и !=, присваивать и создавать копии. Цветовая модель РОВ Наиболее чувствителен глаз к зеленому цвету, потом следует красный, а за- тем — синий. На этих трех цветах и построена модель КОВ (Кед, Огееп, В1ие — Красный, Синий, Зеленый). Пространство цветов задает куб, длина ребер ко- торого равна 255 (рис. 17.6) в целочисленном числовом представлении (либо единице в вещественном представлении). Как видно из рис. 17.6, цвет задается сразу тремя параметрами. Перрый па- раметр задает оттенок красного, второй — зеленого, а третий — оттенок синего цвета. Диагональ куба, идущая от черного цвета к белому — это от- тенки серого цвета. Диапазон каждого из трех значений может изменяться в пределах от 0 до 255 (либо от 0 до 1 в вещественном представлении), где О означает полное отсутствие оттенка цвета, а 255 — его максимальную насы- щенность.
Эта модель является "аддитивной", то есть, посредством смешивания, базовых цветов в различном процентном соотношении, можно создать любой нужный цвет. Смешав, например, синий и зеленый, мы получим голубой цвет. Для создания цветового значения КОВ нужно просто передать в конструктор класса осо1ог три значения. Каналы цвета класса осо1ог могут содержать необязательный уровень прозрачности, значение для которого можно пере- давать четвертым параметром в конструктор. В первый параметр передается значение красного цвета, во второй — зеленого, в третий — Синего, а в чет- вертый — уровень прозрачности. Например: 0Со1ог со1огВ1ие(0, 0, 255, 128); Получить из объекта фСоХог каждый компонент цвета возможно с помощью методов гей (), дгееп (), Ыие () и а1рИа () . Эти же значения можно получить и в вещественном представлении. Для этого нужно вызвать: геОР(), дгеепРО, ЫиеРО и а1рЬаР(). Можно вообще обойтись одним методом §е(В.§Ь(), в который передаются указатели на переменные для значений цве- тов, например: ОСоХог со1ог(100, 200, 0); те г, д, Ь; со1ог.деЬКдЬ(&г, &д, &Ь); Для записи значений КОВ можно, по аналогии, воспользоваться методами, похожими, на описанные, но имеющие префикс зес (вместо префикса деь, если он имеется), после которого идет заглавная буква. Также для этой цели можно прибегнуть к структуре данных окдЬ, которая состоит из четырех бай- тов и полностью совместима с 32-битным значением. Эту структуру можно создать с помощью функции дВдЬО или дкдЬаО, передав в нее параметры красного, зеленого и голубого цветов. Но можно присваивать переменным структуры фкдь и 32-битное значение цвета. Например, синий цвет устанав- ливается сразу несколькими способами: ОНдЬ гдЬВ1ие1 = дВдЪа(0, 0, 255, 255); // С информацией о прозрачности ОКдЬ гдЬВ1ие2 = дКдЫО, 0, 255); ОКдЬ гдЬВ1иеЗ = ОхООООООЕГ; При помощи функций дКей (), дСгееп (), дВ1ие () и дАХрЬа () МОЖНО получить значения цветов и информацию о прозрачности, соответственно. Значения типа окдЬ можно передавать в конструктор класса осо1ог или в ме- тод зеЬВдЬ(): ОНдЬ гдЬВ1ие = ОхООООООГР; 0Со1ог со1огВ1ие1{гдЬВ1ие); 0Со1ог со1огВ1ие2; со1огВ1ие2.зеЫ<дЬ(гдЬВ1ие);
Также можно получать значения структуры окдь от объектов класса осо1ог вызовом метода гдь (). Цвет можно установить, передав значения в символьном формате, например: 0Со1ог со1огВ1ие1("ЯООООВТ*); ОСоХог со1огВ1ие2; со!огВ1ие2.зеЪИалпеСоХог("#ООООГГ"); Цветовая модель Н8У Модель Н8У (Ние, ЗаТигабоп, Уа1ие — Оттенок, Насыщенность, Значение) не смешивает основные цвета при моделировании нового цвета, как в случае с КОВ, а просто изменяет их свойства. Это очень напоминает принцип, ис- пользуемый художниками для получения новых цветов — подмешивая к чистым цветам белую, черную или серую краски. Пространство цветов этой модели задается пирамидой с шестиконечным ос- нованием, так называемый Нехсопе (рис. 17.7). Координаты в этой модели имеют следующий смысл: О оттенок (Ние) — это "цвет" в общеупотребительном смысле этого слова, например: красный, оранжевый, синий и т. д., который задается углом в цветовом круге, изменяющимся от 0 до 360 градусов;
□ насыщенность (ЗаШгайоп) обозначает наличие белого цвета в оттенке. Значение насыщенности может изменяться в диапазоне от 0 до 255. Зна- чение, равное 255 в целочисленном числовом представлении либо едини- це в вещественном представлении, соответствует полностью насыщенно- му цвету, который не содержит оттенков белого. Частично насыщенный оттенок светлее, например, оттенок красного с насыщенностью, равной 128 либо 0.5 в вещественном представлении, соответствует розовому; □ значение (Уа1ие) или яркость — определяет интенсивность цвета. Цвет с высокой интенсивностью является ярким, а с низкой — темным. Значе- ние этого параметра может изменяться в диапазоне от 0 до 255 в целочис- ленном числовом представлении (либо от 0 до 1 в вещественном пред- ставлении). Установку значения цвета в координатах НЗУ можно произвести с помощью метода ССо1ог: : зеЫ^ () ИЛИ 0СО1ОГ: : зеЬНз^ (): со1ог.зеЪНзу(233, 100, 50); Для того чтобы получить цветовое значение в цветовой модели Н8У, нужно передать в метод деьнзлл() адреса трех целочисленных значений (или веще- ственных, если это деы^РО). Следующий пример устанавливает КОВ- значение и получает в трех переменных его НЗУ-эквивалент: 0Со1ог со1ог(100, 200, 0); 1пЬг11, з, V; со1ог. деЁНЗV (, &з, ; Цветовая модель СМУК Применение модели СМУК (Суап, Ма§еп1а, Уе11о\у, Кеу; голубой, пурпур- ный, желтый, "ключевой") чаще всего связано с печатью. Пространство цве- тов этой модели так же, как и с в случае с ВОВ, представляет собой куб с длиной сторон равной 255 (рис. 17.8) в целочисленном числовом представ- лении или единице в вещественном представлении. В отличие от НОВ, эта модель является "субтрактивной", то есть вычитаемой. В субтрактивной цве- товой модели любой цвет представляется в виде трех величин, каждая из ко- торых указывает, какое количество определенного цвета подлежит исключе- нию из белого. Таким образом, для получения черного цвета на печатающем устройстве не- обходимо задействовать все три составляющие: желтую, голубую и пурпур- ную. Такой подход не отличается экономичностью и на практике не всегда дает чисто черный цвет, в связи с чем в печатающих устройствах дополни- тельно используется черная краска. Вот именно поэтому черный цвет и пред- ставляет собой четвертую составляющую (Кеу) этой модели.
Примечание Конечно, логичнее было бы назвать последнюю компоненту не Кеу, а В1аск. Но В1аск начинается с буквы "В* и в этом случае могла бы возникнуть путаница с синим цветом (В1ие), чья первая буква уже задействована в модели В6В. Установка цвета для цветовой модели СМУК производится таким же обра- зом, как и в случаях с КОВ и Н8У. Класс осо1ог предоставляет методы деЁСтукО И деЬСтукКО ДЛЯ получения значений, а методы зеИСтукО и зесстукг () предназначены для их установки. Палитра Палитра представляет собой ограниченное (в большинстве случаев числом 256) количество цветовых значений. Цветовые значения адресуются при по- мощи индексов. Сами индексируемые цветовые значения можно задавать свободно. На рис. 17.9 отображается пиксел, имеющий значение цвета К.ОВ(200, 75,13), адресуемое индексом 3.
Рис. 17.9: Отображение пиксела, имеющего указанный в палитре цвет Предопределенные цвета В табл! 17.1 приведены константы именованных цветов, предопределенных в р1. Они представляют собой палитру, состоящую из семнадцати цветов. Конечно, этих цветов недостаточно для получения фотореалистичных изо- бражений, но они удобны на практике, особенно в тех ситуациях, когда тре- буется отображать основные цветовые значения. Примечание В общей сложности именованных цветов 19. Две константы, не приведенные в таблице, ос:: со1ог0 и ОС:: со1ог1, используются для рисования двухцвет- ных изображений. Таблица 17.1. Цвета перечисления а1оЬа1со1ог пространства имен ос Константа НбВ-значение Описание Ыаск (0, 0, 0) Черный теЫЪе (255, 255, 255) Белый «ЯагкСгау (128, 128, 128) Темно-серый дгау (160, 160, 164) Серый ИдкЪСгау (192, 192, 192) Светло-серый гед (255, 0, 0) ’ Красный дгееп (0, 255, 0) Зеленый Ыие (0, 0, 255) Синий
Таблица 17.1 (окончание) Константа АОВ-значение Описание суап (0, 255, 255) Голубой шадепЪа (255, 0, 255) Пурпурный уе11ою (255, 255, 0) Желтый дагкКесЗ. (128, 0, 0> Темно-красный дагкСгееп (0, 128, 0) Темно-зеленый с1агкВ1ие (0, 0, 128) Темно-синий дагкСуап (0, 128, 128) Темно-голубой дагкМадепЬа (128, 0, 128) Темно-пурпурный дагкУеИоуг (128, 128, О) Темно-желтый Класс осо1ог предоставляет методы Идьъег () и багкег (), с помощью кото- рых можно получать значения цвета, делая основное значение светлее или темнее. Эти методы не изменяют исходный объект цвета, а создают новый. Для этого текущий цвет в модели КОВ преобразуется в цвет модели Н8У и ее компонента "Значение" (Уа1ие) умножается (для багкего — делится) на множитель (выраженный в процентах), переданный в этот метод, а затем по- лученное значение преобразуется обратно в модель КОВ. Сделать красный цвет немного темнее можно следующим образом: 0Со1ог со1ог = ОСоХог(ОС::геб).багкег(160); Резюме Графика играет очень важную роль, ее использование можно встретить прак- тически во всех серьезных программных продуктах. (21 предоставляет ряд классов геометрии, необходимых при создании про- грамм с графикой. Объекты классов орохпс/орохпър хранят в себе координа- ты X и У, описывающие расположение точки на плоскости. Классы размера озхге/озхгер предназначены для хранения значений ширины и высоты. Классы окесс/окессг объединяют в себе величины, хранящиеся в объектах клас- сов ОРохпк/СРохпЬР и ОЗххе/оЗхгеР. Классы ОЬхпе/оЫпеГ и 0₽о1удоп/0Ро1удопР предоставляют возможность описания линий и полигонов. ф поддерживает три цветовые модели КОВ, СМУК и Н8У. КОВ — это очень распространенная цветовая модель, в которой любой цвет получается
в результате смешения трех цветов: красного, зеленого и синего. Цветовая модель СМУК получила большое распространение в типографии. В модели Н8У цвет задается тремя параметрами — оттенком (Ние), насыщенностью (8аШга1юп) и значением (Уа1ие). Представление цвета ТгиеСо1ог дает возможность получить любой нужный цвет. В него входят все представления, имеющие более 8 бит. Палитра — это массив, в котором каждому возможному значению пиксела в соответствие ставится значение цвета. Класс осо1ог предназначен для хранения цветовых значений и предоставляет множество полезных методов, с помощью которых можно конвертировать цветовые значения из КОВ, СМУК в Н8У (и наоборот), сравнивать их, де- лать светлее или темнее.
Глава 18 Легенда о короле Артуре и контексте рисования Сэр Артур осмотрел свой меч и остался им доволен. ‘'Что тебе больше нравится, — сказал Мерлин, — меч или ножны?" "Мне больше нравится меч", — сказал Артур. "Не мудр твой ответ,— сказал Мер- лин, — ибо эти ножны в десять раз дра- гоценней меча". Марк Твен, "Янки из Коннектикута при дворе короля Артура' Под именем Артур (АгШиг) подразумевается новая архитектура для рисова- ния, создание которой началось с желания использовать методы рисования ОРахпиег в ОрепСгЬ. Затем появилось множество других идей: от точного представления в вещественных координатах до отображения векторной гра- фики, которые получили свое воплощение в этой архитектуре. Три краеугольных камня этой технологии составляют классы ОРахпеег, ОРахпЬЕпдхпе И ^ра^п^:^еV^се .(рИС. 18.1). Рис. 18.1. Взаимосвязь классов архитектуры Артур 11 Зак. 3983
Класс 0Ра1пСЕпд1пе используется классами орахпсег и 0Ра1пспеу1се неявно и для разработчиков не интересен, если нет необходимости создавать свой собственный контекст рисования. Если же такая необходимость возникла, вам придется унаследовать этот класс и реализовать некоторые методы. Основной класс для программирования графики, с которым придется иметь дело, — это 0Ра1пеег. С его помощью можно рисовать точки, линии, эллип- сы, полигоны, кривые Безье, растровые изображения, текст и многое другое. Более того, ОРахпеег поддерживает режим сглаживания (апНаПазт^), про- зрачность и градиенты, о которых будет рассказано в этой главе. Контекст рисования орахпсоеухсе можно представить себе как поверхность для вывода графики. ОРахпыэеутсе — это основной абстрактный класс для всех классов объектов, которые можно рисовать. От него унаследована целая серия классов, показанных на рис. 18.2. Рис. 18.2. Иерархия классов контекста рисования В основном, рисование производится из метода обработки события ОРахпсЕуепе (см. главу 14). Если пользователь запустит программу и выпол- нит некоторые действия, вследствие которых будет перекрыто, частично или полностью, окно программы, то после того, как оно откроется, будет сгене- рировано событие перерисовки и вызван метод оиШдеС: :ра1псв^епе() для тех виджетов, которые должны быть перерисованы. Сам объект события
ОРатпЬЕуепь содержит метод гедхоп (), который возвращает область для пе- рерисовки. Метод ОРахпСЕуепс:: гесе () возвращает прямоугольник, который охватывает эту область. Для подавления эффекта мерцания (2< использует технику двойной буфериза- ции (ёоиЫе Ьийепп§). Двойная буферизация представляет собой очень рас- пространенное и простое решение этой проблемы. Суть заключается в фор- мировании изображения в невидимой области (буфере). Когда изображение будет сформировано, оно помещается в видимую область (буфер) за один раз. Это производится автоматически, и вам не нужно реализовывать код для ЭТОГО В ратпСЕуепЬ (). Эффекта прозрачности можно добиться, установив для рисования цвет, со- держащий значение прозрачности альфа-канала (см. главу 17). Это значение может изменяться от 0 до 255. Значение 0 говорит о том, что цвет полностью прозрачен, а 255 означает полную непрозрачность. Класс 0Рат1ег Класс драхпъег, определенный в заголовочном файле ОРахпСег, является исполнителем команд рисования. Он содержит массу методов для отображе- ния линий, прямоугольников, окружностей и др. Рисование производится на всех объектах классов, унаследованных от класса ОРахпсоеухсе (рис. 18.3). Это означает, что отображенное контекстом рисования одного объекта может быть отображено и контекстом другого. ОРа1п!ег Рис. 18.3. ОРахпСег и контексты рисования Чтобы использовать объект ОРатпсег, необходимо передать ему адрес объек- та контекста, на котором должно производиться рисование. Этот адрес можно
передать как в конструкторе, так и с помощью метода орахпсег: :Ьед1п(). Смысл метода Ьедхп () состоит в том, что он позволяет рисовать на одном контексте несколькими объектами класса ОРахп^ег. При использовании ме- тода Ьед1п() нужно по окончании работы с контекстом вызвать метод 0Ра1псег:: епб (), чтобы отсоединить установленную этим методом связь с контекстом рисования, давая другому объекту возможность для рисования (листинг 18.1). ОРахпЪег рахпеег!; ОРахпСег ра!пЪег2; ра1п1:ег1 .Ьед1п(СИ15) ; // Команды рисования рахпеег!.епд(); ра!пЁег2 .Ьедхп (ЬЫз) ; // Команды рисования ра!п<:ег2. епс1 () ; Но чаще всего используется рисование одним объектом ОРахпЪег в разных контекстах (листинг 18.2). жииийялагиайиивв^^ ^4 0Ра1пЬег рахпЪег; ра1пЪег. Ьедхп (ьЫз); / / контекст виджета // Команды рисования рахпГег.епд(); ОРххшар р!х(гесЪ()); ра1п^ег.Ьед1п(&р1х); //контекст растрового изображения // Команды рисования рахпЬег.епб(); Объекты ОРахпсег содержат установки, влияющие на их рисование. Это мо- гут быть трансформации координат, модификация кисти и пера, установка шрифта, установка режима сглаживания и т. д. Поэтому, для того чтобы
оставить старые настройки без изменений, рекомендуется перед началом из- менения сохранить их с помощью метода о₽а1пъег: :зауе(), а по окончании работы — восстановить, С ПОМОЩЬЮ метода ОРахпСег: : гезСоге (). Перья и кисти Перья и кисти — это основа для программирования графики с использовани- ем библиотеки Ос Без них не получится вывести даже точку на экран. Перо Перо применяется для рисования контурных линий фигуры. Атрибуты пе- ра — цвет, толщина и стиль. Установить новое перо можно с помощью мето- да ОРатпсег:: зеьреп (), передав в него объект класса о₽еп. Можно переда- вать и предопределенные стили пера, указанные в табл. 18.1. Таблица 18.1. Некоторые значения из перечисления Репзсу1е пространства имен с с Константа Значение Вид (толщина = 4) ЫоРеп 0 ЗоИс1Ыпе 1 БазкЫпе 2 БоСЫпе 3 БазИБоЁЫпе 4 М М М М 1 БазИБоЪБоЁЪхпе 5 Толщина линии является значением целого типа, которое передается в метод ОРеп:: зескхаъъ (). Если значение равно нулю, то это не означает, что линия будет невидима, а говорит о том, что она должна быть изображена как можно тоньше. Если необходимо, чтобы линия не отображалась вообще, то тогда устанавли- вается стиль ЫоРеп. Зачем же нужно перо, которое не рисует? Бывают и такие случаи, когда и пустое перо пригодится, например, когда нужно вывести че- тырехугольник определенного цвета без контурной линии. Цвет пера задается с помощью метода ОРеп:: зеесо1ог (), в который переда- ется объект класса осо1ог. Следующий пример создает перо красного цвета,
толщиной в три пиксела и со стилем "штрих". Объект пера устанавливается в объекте ОРахпсег вызовом метода весреп (). ОРахпЪег рахпЬег (СЫз); рахпЬег.зеЬРеп(2Реп(0Ь: :ге<3, 3, ОС: :ОазИЫпе)) Стили для концов линий пера устанавливаются методом 8еьсарзъу1е (), в который передается один из флагов оъ: :Р1аъсар (край линии квадратный и проходит через граничную точку), ос .: здиагесар (край квадратный и пе- рекрывает Граничную ТОЧКУ На ПОЛОВИНУ ШИРИНЫ ЛИНИИ) ИЛИ ОЬ: гКоипаСар (край закругленный и также покрывает граничную точку линии). Можно устанавливать стили и для переходов одной линии в другую — методом зеыо1пЗ(:у1е (), передав в него оъ: зШеегогсап (линии продлевают- ся И соединяются ПОД острым углом), ОС: :ВеVе1^О^п (пространство между линиями заполняется) или ос: :коип<Иохп (угол закругляется). Но эти пере- ходы будут видны только на толстых линиях. Кисть Кисть используется для заполнения непрерывных контуров, таких как пря- моугольники, эллипсы и многоугольники. Класс кисти овгизъ определен в заголовочном файле рВгикЬ. Кисть задается двумя параметрами — цветом и образцом заливки. Установить кисть можно методом ОРахпСег:: зеъвгизй (), передав в него объект класса овгизъ или один из предопределенных шаблонов, указанных в табл. 18.2. Если заполнение не нужно, то в метод ОРахпсег:: зегвгизИ () следует передать значение ыовгизъ. Таблица 18.2. Перечисление вгивкз<:у1е пространства имен ре (выборочно) Константа Значение Вид Константа Значение Вид ЫоВгизЪ 0 Вепзе4РаСГегп 5 ЗоИдРаЁЬегп 1 Пепзе5РаЪЪегп 6 0епзе1РаСЁегп 2 СепзебРаЪЁегп 7 Вепзе2РаЪЬегп 3 Пепзе7РаГ:Ъегп 8 ПепзеЗРаЁЪегп 4 НогРаСЪегп 9
Таблица 18.2 (окончание) Константа Значение Вид Константа Значение Вид УегРаЁЁегп 10 ЫпеагСгасИепЪРаЪЪет 15 СгоззРа^Сет 11 КасНаЮгасИепСРаСЪегп 16 ВОхадРаеЁет 12 СопхсаЮгасИепГРаИСего 17 ШИ'' ГОхадРаЪПегп 13 ТехЪигеРаЪЬегп 24 ЛхадСгозз РаЬЁегп 14 Следующие строки устанавливают красную кисть с горизонтальной штри- ховкой: ОРахпЁег рахпЪег (Йпз) ; рахпЛег. зеЪВгизк (рВгизЬ (ОЪ: : гед, ОС:: НогРаЬЪегп)) ; Если в табл. 18.2 не нашлось подходящей кисти, то можно создать свою соб- ственную с помощью стиля техЁигеРаЪЁегп. Для использования этого стиля нужно передать в метод зестех^игео растровое изображение. Также мржно использовать растровое изображение при создании кисти: рРххтар рхх (м ^гиИ:з. зрд") ; рахпЪег. зеСВгизИ (рВгизк(ре: :Ыаск, р!х)); рахпЬег.дгаюЕШрзе(0, 0, 300, 150); Пример, показанный на рис. 18.4, демонстрирует получившийся результат.
Г радиенты Градиент — это плавный переход от одного цвета к другому. В настоящее время применение градиентов стало очень популярно, ведь их использование может придать эффект объемности в ваших приложениях. В основе градиен- тов лежит гладкая интерполяция между двумя и более цветовыми значения- ми. р! предоставляет три основных типа градиентов: линейный (Нпеаг), ко- нический (сошса1) и радиальный (гасНа!). Линейные градиенты реализует класс оЫпеагвгаЛепс. Они задаются двумя цветовыми точками контроля и несколькими точками останова (со1ог зФрз) на линии, соединяющей цвета этих точек. Листинг 18.3 и рис. 18.5 иллюст- рируют эту возможность. Рис. 18.5. Отображение линейного градиента ОРахпСег рахпСег (СЫз) ; ОЬхпеагОгасНепС дгадхепС (0, 0, мхсЯСИ (), ЬехдИС ()); дгасНепС. зеССо1огАС (О, ОС:: геД) ; дгасНепС. зеССо1огАС (0.5, ОС:: дгееп) ; дгасИепС. зеССо1огАС (1, 0Ь:: Ыие); рахпСег. зеСВгизк (дгасИепС); рахпСег.агаюВесС(гесС()); Мы задали три цвета на трех различных позициях между двумя точками контроля. Позиции точек задаются вещественными значениями от 0 до 1,
где 0 представляет собой первую контрольную точку, а 1 — вторую. Цвета между этими точками будут интерполированы. Конический градиент реализуется классом осопгсаЮгадхепе и задается цен- тральной точкой и углом. Распространение цветов вокруг центральной точки соответствует повороту часовой стрелки. В листинге 18.4 приведена реализа- ция конического градиента, а результат показан на рис. 18.6. Рис. 18.6. Отображение конического градиента ОРахпСег рахпСег {Шз) ; ОСопхсаХСгадхепС дгадхепС (техдСЪ. () / 2, ЪехдИСО / 2, 0} ; дгадхепС. зеССоХогАС (0, ОС:: гед); дгадхепС.зеССоХогАС (0.4, ОС::дгееп); дгадхепС. зеССоХогАС (0.8, ОС:: Ыие); дгадхепС. зеССоХогАС (X, ОС:: гед) ; рахпСег. зеСВгизИ (дгадхепС); рахпСег.дгаадКесС(гесС()); Радиальный градиент реализует класс окаахаЮгааХепс и задается централь- ной точкой, радиусом и точкой фокуса. Центральная точка и радиус задают окружность. На распространение цветов за пределами точки фокуса влияет центральная точка или точка, находящаяся внутри окружности. Пример реа- лизации такого градиента приведен в листинге 18.5, а результат показан на оис. 18.7.
Рис. 18.7. Отображение лучевого градиента ОРахпСег ра 1пСег (Опз); ОРохпеЕ рПСепСег(гесЬ().сепЬег()); ОКасИаЮгаЛепЁ дгаЛепе (рГСепЪег, здхдЪЪО / 2, рЁСеп^ег) ; дга<31епЬ. зе^Со1огАЪ (0, ОС:: гед) ; дгасИепС. зе!:Со1огАС (0.5, 01::: дгееп) ; дгасНеп!:. зе!:Со1огА1: (1, 0Ъ:: Ыие) ; рахпЪег. зеСВгизк (дгасНеп!:); ра!п(:ег. дга^Кесе (гесЪ ()); Техника сглаживания (АпН-АНазтд) Одним из побочных эффектов, возникающих при рисовании геометрических фигур, является ступенчатость, проявляющаяся на контурах (рис. 18.8). Это и понятно, так как ступени есть не что иное, как пикселы, через которые про- ходит контур. Для подавления этого нежелательного эффекта используется техника сглаживания (Апб-АНа81п§). С ее помощью границы кривых можно сделать более гладкими, убирая ступени, образующиеся на краях объектов, что достигается добавлением промежуточных цветов. Это снижает скорость рисования, но улучшает визуальный эффект (рис. 18.8). Режим сглаживания распространяется на отображение текста и геометриче- ских фигур. Его можно включить в объекте класса ОРахпеег при помощи ме- тода зеекепйегШпЬ(): рахпЬег. зеЬНепдегНхпЬ (ОРахпЪег: :АпЪ1аИаз1пд, егие);
Рис. 18.8. Линия без сглаживания (А) и со сглаживанием (Б) Рисование Отображение фигур — задача несложная, ведь для этого не требуется вычис- лять расположение каждого выводимого пиксела, так как уже имеется целый ряд методов для вывода практически всех геометрических фигур. Например, вызовом метода агаипесс () можнонарисовать прямоугольник. Перед тем как приступить к рисованию, важно понять философию координат пиксела. Она заключается в том, что центр пиксела лежит в его середине. То есть пиксел, находящийся в самом верхнем леЬом углу, будет иметь коорди- наты (0.5, 0.5). Если мы попытаемся нарисовать пиксел с координатами (0, 0), то ОРахпсег автоматически прибавит к ним 0.5, и результатом будет (0.5,0.5). Этот сдвиг производится при выключенном режиме сглаживания. Если этот режим включен, и мы попытаемся нарисовать черный пиксел с ко- ординатами (50,50), то, в результате сглаживания, будут нарисованы сразу четыре серых пиксела с координатами (49.5,49.5), (49.5,50.5), (50.5,49.5) и (50.5,50.5). Но если мы попытаемся нарисовать черный пиксел с координа- тами (49.5,49.5), то в результате увидим только один пиксел. Рисование точек Для отображения точек применяется только перо. В листинге 18.6 на экране рисуются восемь точек (рис. 18.9).
ОРахпСег рахпСег(СЪхз); рахпСег. зеСРеп (ОРеп (ОС:: Ыаск, 3)) ; хпС п = 8; ^ог (хпС 1 = 0; х < п; ++х) { дгеа1 ЕАпд1е = 2 * 3.14 * х / п; дгеа! х = 50 + соз(ГАпд1е) * 40; дгеа1 у = 50 + зхп(1Алд1е) * 40; рахпСег. ЗгаюРохпС (ОРохпСК (х, у)) ; } Рисование линий Для рисования отрезка прямой линии из одной точки в другую используется метод бгаиЫпеО, в который передаются координаты начальной (х1, у1) и конечной (х2, у2) точек оро1псе (листинг 18.7, рис. 18.10). ОРахпСег рахпСег(СЪхз); рахпСег. зеСКепдегНхпС (ОРахпСег::АпСхаИазхпд, Сгие); рахпСег.(ЗгатеЬхпе (ОРохпСГ (10, 10), ОРохпСР (90, 90));
Метод йга»₽о1уЫпе () проводит линию, которая соединяет точки, переда- ваемые в первом параметре. Второй параметр задает количество точек, кото- рые должны быть соединены (то есть число элементов массива). Первая и последняя точки не соединяются. В листинге 18.8 рисуется фигура, пока- занная на рис. 18.11. ОРахпЪег рахпЪегСЪЫз) ; рахпЪег. зеСКепдегШпЪ (ОРахпЪег::АпЫа1 хазхпд, Сгие) ; 1пЪ п = 8; ОРохпЪР а [и] ; Сог {хпЪ 1 = 0; 1 < П; ++1) { дгеа1 ЕАпд1е = 2 * 3.14 * 1 / п; дгеа1 х = 50 + соз(САпд1е) * 40; дгеа1 у = 50 + з1п(1Апд1е) * 40; а[1] = 2Ро1пЪК(х, у); } Рис. 18.11. Соединение точек линиями Рисование сплошных прямоугольников Прямоугольник — это очень распространенная геометрическая фигура, по- смотрите вокруг — прямоугольные предметы окружают нас везде. ()1 содер- жит два метода для рисования прямоугольников без контурных линий: гшкесс () и егазекесс (). Их внешний вид задается только кистью. В метод Н11кесь () передаются пять параметров. Первые четыре параметра задают координаты (х, у) и размеры (ширина, высота) прямоугольника. Пятый пара- мето задает кисть.
В метод егазекесьо передаются только четыре параметра, задающие по- зицию и размеры прямоугольной области. Для ее заполнения использу- ется фон, установленный в виджете. Таким образом, вызов этого метода эквивалентен вызову ЕШкесе () с пятым параметром — значением, возвра- щаемым методом ра1есееваскдгоипасо1ог(). В листинге 18.9 приведен пример использования методов ^шнесео и егазевесс (), а результат пока- зан на рис. 18.12. —................----------..........................................г..... .............. ОРахпЁег рахпСег ( ёЫз ); ОВгизЪ ЬгизИ(ОС::гед, ОС::Эепзе4РаЪЁегп); рахп^ег Л111Кес^(10,. 10, 100, 100, ЬгизЬ); рахпеег.егазеКес!1 (20, 20, 80, 80); Рис. 18.12. Прямоугольники Рисование заполненных фигур Для рисования фигур также применяются методы, использующие перо о₽еп и кисть овгизй. Если требуется нарисовать только контур фигуры, без запол- нения, то для этого методом ОРахпеег:: зесвгизк () нужно установить значе- ние стиля кисти овгизЪ-. :НовгизЬ. А для рисования фигуры без контурной линии можно методом ОРахпЪег:: зеъреп () установить стиль пера ОРеп: :ЫоРеп. Метод агамкесео рисует прямоугольник. В него передаются следующие параметры: координаты (X, У) верхнего левого угла, ширина и высота. В этот метод можно передать также объект класса опесг (листинг 18.10, рис. 18.13).
ОРахпСег рахпСег(Скхз)? рахпСег. зеСНепдегНхпС (ОРахпСег:: АпСхаИазхпд, Сгие); рахпСег. зеСВгизЪ. (ОВгизк (ОС:: г ед, ОС: ШхадСгоззРаССегп)) ; рахпСег. зеСРеп (ОРеп(ОС: :Ъ1ие, 3, ОС: :ЭазЪЬхпе)) ; рахпСег.дгаздВесС(ОКесС(10, 10, 110, 70))? Рис. 18.13. Рисование прямоугольника Метод Огашкоппанессо рисует прямоугольник с закругленными углами. За- кругленность углов достигается с помощью четвертинок эллипса. Последние два параметра метода задают, насколько сильно должны быть закруглены углы в направлениях осей координат X и V, соответственно. При передаче нулевых значений углы не будут закруглены, а при присвоении им значения 100 прямоугольник превратится в эллипс. Прямоугольную область можно задавать объектом класса овесс (листинг 18.11, рис. 18.14). ОРахпСег рахпСег (СИхз); рахпСег. зеСНепдегНхпС (ОРахпСег:: АпСха!хазхпд, Сгие); рахпСег. зеСВгизЬ. (ОВгизИ (дгееп)) ; рахпСег. зеСРеп (ОРеп (Ыаск)) ; рахпСег.дгамВоипдЯесС(ОКесС(10, 10, 110, 70), 30, 30)?
Метод ага^ЕШрзеО рисует заполненный эллипс, размеры и расположение которого задаются прямоугольной областью (листинг 18.12, рис. 18.15). СРахпЁег радпСег (ьЫз) ; рахпЪег. зеЬКепЗегНтпС (ОРахпСег:: АпЪ1а1 газ 1пд, Сгие) ; рагп^ег.зеСВгизИ(ОВгизЪ(дгееп, ОВгизИ::СгоззРаЪЬегп)); рахпеег. зеСРеп (ОРеп (гей, 3, ОРеп:: Оо^Ыпе)); рахпеег.бгаздЕШрзеСОКесЪСЮ, 10, 110, 70)); Метод дга^сьогд () рисует хорду. Размеры и расположение эллипса задаются прямоугольной областью, а отображаемая часть — двумя последними пара- метрами, представляющими собой значения углов. Углы задаются в едини- цах, равных одной шестнадцатой градуса. Начальная и конечная точки будут соединены прямой линией. При положительных значениях двух последних параметров (углов) начальная точка перемещается вдоль кривой эллипса против часовой стрелки. Предпоследний параметр задает начальный угол. Последний параметр задает угол, под которым кривые должны пересекаться (листинг 18.13, рис. 18.16). ОРахпСег рахпЁег (ЪЫз) ; рахпеег.зеЁКепдегНхпЬ (ОРахпЁег: : АпМаИазхпд, Ёгие) ; рахпЁег.эеЪВгизЩрВгизЩОЁ: :уе11ою)); рахп^ег. зеСРеп (ОРеп (ОЬ: :Ыие)) ; ратпИег.агаадСЪога(СКесЬ(10, 10, 110, 70), 45 * 16, 180 * 16); Рис. 18.16. Рисование хорды В мире деловой графики пользуются спросом круговые диаграммы. Такие рисунки очень удобны для представления статистических данных. Метод
йгаюРхеО рисует часть эллипса. Начальная и конечная точки соединяются с центром эллипса (листинг 18.14, рис. 18.17). Последние два параметра ме- тода задаются в шестнадцатых долях градуса. ОРахпГег ратп^ег (Йпз); рахп^ег. зеЪКепдегНхпе (СРахпЬег::АпЫаИазгшд, Ъгие) ; рахпЁег. зеПВгизк (ОВгизЪ. (ОЬ: :Ыаск, Оё :: ВОхадРаЪСегп)) ; рахп^ег. зеЪРеп {ОРеп (ОС: : Ыие, 4)); рахпЁег.дгатеРхе(ОКесС(10, 10, 110, 70), 90 * 16, 270 * 16); Метод ага^ро1удоп () рисует заполненный полигон, последняя из заданных вершин которого будет соединена с первой (листинг 18.15, рис. 18.18). ОРахпСег ра!пЪег(СЫз); ра1пСег.зеСКеп<1егН1пС(0Ра1пСег: :АпГ1аИаз1пд, Сгие) ; рахпСег. зеСВгизИ (ОВгизИ (0^: : ИдИССгау)) ; ра1пСег.зеСР-еп(0Реп(0С: :Ыаск)) ; 1пС п = 8; 0Ро1удопГ ро1удоп; Ьзг (хпС 1=0; 1 < п; ++1) { дгеа1 САпд1е = 2 * 3.14 * 1 / п; дгеа1 х = 50 + соз(1Апд1е) * 40; дгеа1 у = 50 + з!п(САпд1е) * 40; ро1удоп « ОРохпёР (х, у); } рахпСёг. дга^Ро1удоп (ро1удоп) ;
Запись команд рисования Класс ОРхесиге— это контекст рисования, который предоставляет возмож- ность протоколирования команд класса ОРахпСег. С его помощью можно да- же записывать команды в отдельные файлы (называемые метафайлами), а потом загружать их снова, чтобы повторить ранее проделанные действия. Эти действия можно перенаправлять и на другие контексты рисования, на- пример: принтер или экран. В листинге 18.16 производится запись одной ко- манды рисования в файл туНпе.даЕ ОРхсСиге рхс; ОРахпСег рахпСег; рахпСег. Ьедхп (&рхс) рахпСег.дгаюЬхпе(20, 20, 50, 50); рахпСег.епд() хГ (!р^с.заVе( "туИпе.сЗаС")) { дПеЬидО « ”сап поС заVе СЬе €х1е”; } Листинг 13.17 демонстрирует загрузку команд из файла и их исполнение в другом контексте. Для отображения в другом контексте используется метод агамРхсеиге (). Первый параметр этого метода устанавливает позицию, с ко- торой начнется рисование, а во втором параметре передается объект класса ОРхсСиге. ОРхсСиге рхс; х! (! рхс. 1оад ("щуХхпе. <ДаС")) { дПеЬидО « "сап поС 1оад СЬе €х1е"; } ОРахпСег рахпСег; рахпСег. Ьедхп (сЬхз) рахпСег. ЗгаюРхсСиге (ОРохпС (0, 0), рхс) ; рахпСег. епд ()
Трансформация систем координат Класс ОРахпЬег предоставляет очень мощный механизм трансформации ко- ординат для отображения объектов. Это позволяет показывать изображения в повернутом, масштабированном, смещенном и скошенном виде (рис. 18.19). Рис. 18.19. Различные трансформации Каждая точка в двумерной системе координат описывается двумя координа- тами. Трансформация одинаково действует на все точки графического объек- та. Для трансформации в классе ОРахпеег определены следующие методы: сгапг1аее(), гса1е(), госанеО и гЪеаг(). Трансформации можно комби- нировать, но их порядок следования отражается на конечном результате. Например, если сначала провести операцию скоса, а затем операцию поворо- та и снова скоса, то тогда результат будет отличаться от результата, получен- ного в результате двух операций скоса и поворота. 9( предоставляет методы заVе() и гевсогео для сохранения и восстановле- ния состояний объектов класса ОРахпьег. Это очень удобно при получении извне указателя на объект класса ОРахпеег. Можно сохранить его состояние с помощью метода зауе (), а затем проводить с ним разного рода трансформа- ции. По окончании трансформации, вызов метода гезъоге() вернет объект класса ОРахпьег в исходное состояние. Например: рРа^п^1е^->заVе (>; рРахп^ег->11гапз1аее(20, 40); рРа!пЁег->гезЁоге(); Перемещение Часто требуется переместить изображение на экране. Класс ОРахпъег предо- ставляет для этого метод егапз1аее(), в который передаются два целочис- ленных параметра. В первом параметре передается значение перемещения
по оси X, во втором — по оси V. Положительные значения первого парамет- ра перемещают объект вправо, а отрицательные — влево. Положительные значения второго параметра смещают объект вниз, а отрицательные — вверх. Например, следующий вызов производит перемещение всех рисуемых объ- ектов вправо на 20 и вниз на 10 пикселов: ОРахпСег рахпСег; рахпСег.Сгапг1аСе(20, 10); Масштабирование Метод зса1е () изменяет размер изображения, в соответствии с двумя мно- жителями для ширины и высоты, передаваемыми в него. Значения меньше единицы производят уменьшение, больше — увеличение объекта. Например, после следующего вызова ширина всех рисуемых объектов увеличится в полтора раза, а их высота уменьшится наполовину: ОРахпСег рахпСег; рахпСег.зса1е(1.5, 0.5); Поворот Одной из основных операций в графике является поворот изображения на определенный угол. Для этого класс ОРахпСег содержит метод госасеО, в который передается значение типа аоиЫе, обозначающее угол в градусах. При положительных значениях поворот осуществляется по часовой стрелке, а при отрицательных — против. Следующий вызов приведет к изображению рисуемых объектов повернутыми (по часовой стрелке) на 30 градусов: ОРахпСег рахпСег; , рахпСег.гоСаСе(30.0); Скос Этот вид трансформации также важен в компьютерной графике. Он реализу- ется в классе ОРахпЪег методом зИеаг (). Первый параметр задает сдвиг по вертикали, а второй — по горизонтали. Следующий пример осуществляет скос вниз по вертикали: ОРахпСег рахпСег; рахпСег.зЪеаг(0.3, 0.0);
Трансформационные матрицы В каждом объекте класса ОРахпЪег хранится трансформационная матрица. Ее можно считать из него, а можно установить созданную матрицу транс- формации С ПОМОЩЬЮ метода ОРахпПег: : аеЪМаЬгХх (). Если для того, чтобы получить нужный результат, вам необходимо вызывать несколько методов трансформации, то эффективнее записать их в объект матрицы и устанавливать ее в объекте ОРаХпСег всякий раз, когда необходи- ма трансформация. Например: ОМаЬгхх таЬ; шаЬ. зсаХе (0.7, 0.5); таЪ.гЪеаг(0.2, 0.5); таС.гоЬаЬе(15); рахпСег. геЬМаСг1х(таЪ); ра!п1:ег.с1га«ТехЬ(гесЬ(), ОЬ: :А11дпСеп11ег, "ТгапзСогтед ТехЬ"),- Любая двумерная трансформация может быть описана матрицей размерно- стью 3x3: МП М12 0 М21 М22 0 Ох Оу 0 Если стандартных трансформаций недостаточно, можно определить свою собственную, при помощи значений мп, М12, М21, М22, ох и юу, которые за- даются в конструкторе класса омаъгхх и представляют собой действительные числа. В табл. 18.3 сведены вместе основные формы трансформации в мат- ричном представлении. Например, установка следующей матрицы в объекте <2₽а1пЬег будет соответствовать вызову его метода ыапз1аье (2 о, ю): ОМаЬгхх та С(1, 0, 0, 1, 20, 10); ра1.пЬег.8еСМаЬг1х(та1:) ; Таблица 18.3. Трансформации Элемент матрицы Перемещение Поворот Скос Масштабирование МП 1 соз(угол) 1 Горизонтальная компонента М12 0 31п(угол) Горизонталь- ная компо- нента 0 М21 0 31П(угол) Вертикальная компонента 0
Таблица 18.3 (окончание) Элемент матрицы Перемещение Поворот Скос Масштабирование М2 2 1 СО5(УГОЛ) 1 Вертикальная ком- понента ' Бх Горизонталь- ная компонента 0 0 0 Бу Вертикальная компонента 0 0 0 Графическая траектория (рат!ег раШ) Графическая траектория может состоять из различных геометрических объ- ектов: . прямоугольников, эллипсов и других фигур различной сложности. Ее основное преимущество заключается в том, что, единожды создав траек- торию, ее можно отображать сколько угодно раз, одним лишь вызовом мето- да СРагпЬег:: йгаюРаьи (). Пример на рис. 18.20 иллюстрирует траекторию, созданную из трех геометрических объектов: прямоугольника, эллипса и кривой Безье. Его реализация приведена в листинге 18.18. Рис. 18.20. Отображение графической траектории ОРахпСег РаСк. раСк; ОРохпСР рС1 (мхскк (), кехдкСО / 2) ; ОРохпСР рС2 (адхдСк () / 2, -кехдкС ()) ;
ОРохпёР ръз (()/2,2* ИехдЪЪ ()) ; ОРохпЬР рЬ4 (0, ЪехдЫ; () / 2); раЫ1. тотеТо (рЫ); раЫ1.сиЫсТо(рС2, р^З, рЪ4) ; ОКесЪ гесС(1Л716Ы1 () / 4, ИехдкЬО / 4, мЫЬИО / 2, ИехдЫО / 2); раЫ1. аскЭКесЁ (гесС) ; раСИ. аскДЕ11 хрзе (гесС) ; СРахпСег рахпГег (Шз) ; рахпСег. зеЬКепаегНхпС (ОРахпСег::АпСхаИазапд, Сгие); рахпСег. зеСРеп (ОРеп (ОС: : Ыие, б)); рахпСег. дгашРаСИ (раСИ) ; Отсечения Отсечения ограничивают вывод графики определенной областью (много- угольником или эллипсом). Если производится попытка рисования за этими пределами, то оно будет невидимым. Установка прямоугольной области отсе- чения производится с помощью метода аеъсиркесь (). Метод зессИрКесь () устанавливает прямоугольную область отсечения. Листинг 18.19 демонстри- рует отсечение фигуры эллипса прямоугольной областью. Результат показан на рис. 18.21. .............................................. ;Лйстинг18Л9.Отсечение прямоугольной областью 5 < ОРахпСег рахпСег (СЫз) ; рахпСег. зеСКепдегНхпС (ОРахпСег: :АпЫаИаз1пд, Сгие) ; ра1пСег.зеССИрКесС(0, 0, 100, 100); рахпСег. зеСВгизИ (ОВгизЪ (<2С:: дгееп)) ; рахпСег. зеСРеп (ОРеп (рС: : Ыаск, 2)) ; рахпСег.ДгаздЕШрзе(0, 0, 200, 100); Более сложные области отсечения устанавливаются методами ОРахпПег: : ЗеЁСИрКедхоп {) И ©Рад-ПЪег: : зеЁСИрРаСк (). В метод зеисИрКедхоп () передается объект класса окедхоп. В конструкторе класса можно задать область в виде прямоугольника или эллипса. Например, следующий вызов создаст прямоугольную область с координатами левого верхнего угла (10, 10) и шириной и высотой, равными 100: ОКедхоп гед!оп(10, 10, 100, 100);
Рис. 18.21. Отсечение прямоугольной областью Область отсечения в виде эллипса, вписанного в такой прямоугольник, соз- дается следующим образом: ОВедхоп гедхоп (10, 10, 100, 100, ОВедхоп: :Е1Ирзе) ; В качестве области отсечения можно использовать и полигон, передав его в конструктор. Точки в полигоне можно установить при помощи операто- ра «. Например: ОВедхоп хед1оп(0Ро1удоп() « 0Ро1пС(0, 100) « 2Ро1пС(100, 100) « 0Ро1пС(100, 0) « ОРохпЪ(0, 0) ); Объекты класса оведхоп можно комбинировать друг с другом, создавая до- вольно сложные области, при помощи методов ипхъеао, хпЬегзесСеаО, зиЬЪгасСеа() И хогеа(): □ метод ипГееао возвращает область, полученную в результате объедине- ния двух областей; □ метод хпСегзесееао возвращает область, полученную в результате пере- сечения двух областей; □ метод зиьегассеа () возвращает область, полученную в результате вычи- тания аргумента из исходной области; □ метод хогеа о возвращает область, содержащую точки из каждой области, но не из обеих областей. Например: ОВедхоп гедхопЦЮ, 10, 100, 100); ОВедхоп гедхоп2(10, 10, 100, 100, ОВедхоп::Е11хрзе); ОВедхоп гедхопЗ = гедхоп1.зиЬЪгасЬ(гедхоп2); рахпеег. зеЪСИрВедхоп (гедхопЗ);
Режим совмещения (сотрозЖоп тобе) Под режимом совмещения понимается то, как должны совмещаться пиксел источника с целевым пикселом при рисовании. Пикселы могут иметь уровень прозрачности. Этот механизм применяется для всех операций рисования, включая кисть, перо, градиенты и растровые изображения. Возможные операции проиллю- стрированы на рис. 18.22. Режим совмещения устанавливается с помощью метода ОРахпСег::зеьсотрозхЫопмойе(). По умолчанию режимом совмещения яв- ляется "ИСТОЧНИК сверху" ОРахпСег: :СотрозхСхопМос1е_ЗоигсеОуег. Если вам понадобится прохождение по пикселам растрового изображения, чтобы манипулировать альфа-каналом или цветовыми значениями, прежде всего проверьте, нет ли для решения вашей задачи подходящего режима со- вмещения. >3' △О/к А Зои'-се ЗоихеОгег ЗоитаН ЗоигсеОп ••А АП Рис. 18.22. Режимы совмещения 0ЬаЪе1* 1Ы(сопзС ОРахпСег: :СаотрозхСхопМоде& тосЯе) { 0ЬаЬе1* р1Ы = пеш 0ЬаЪе1; р1Ы->зеСРххед5х2е(100, 100) ; ОНесС гесС (р1Ы->сопСепСзКесС ()) ; ОРахпСег рахпСег;
ОИпаде зоигсеТтаде(гесС.зхгеС), дттаде: :Еогта11_АКСВ32_Ргети1е1р1хед) ; рахпЬег. Ьедхп (йзоигсеТтаде) ; рахпЪег. зеЪКепдегНхпк (ОРахпПег::АпЪхаИазхпд, Ёгие) ; рахпГ;ег.зе^Вгизк(ОВгизк(0Со1ог(0, 255, 0))) ; ра!п1:ег. <ДгамРо1удоп (ОРо1удоп () « гесЁ. ЬоЪСотЬе^Ё () « ОРохпЪ(гесЬ.сепСег() .х () , 0) << гес^.ЬоЪСотНхдИЁ() ); рахпЬег.епсЦ) ; фТтаде гези1С1таде (гесЪ. зхзе (), ОТтаде:: ЕогтаС_АКСВ32_Ргети1Схр11е6) ; рахпЁег. Ьедхп (&гези1Ытаде); рахпСег. зеЫгепс1егНхпе (фРахпСег: :Апеха1хазхпд, Сгие) ; рахпЬег. зеЪСотроз х ЬхопМоде (ОРахпСег:: Сотроз х ЪхопМос1е—ЗоигсеСКлег) ; рахпСег.зеЬРеп(2Реп(0Со1ог(0, 255, 0), 4)) ; рахпСег. зеЬВгизЪ. (ОВгизЪ (0Со1ог (255,0,0))); рахп!:ег. дгашЕИхрзе (гесЪ) ; рахпСег. зе^Ссягрозх^хопМоае (тоде); рахп^ег.дгаюТтаде(гесЬ, зоигсеТтаде); рахпЪег. епсЗ. () ; р1Ы->зеЪРххтар(0Рхх1пар:: ЕготХтаде (гези1С1таде)); геЁит р1Ы; } Функция 1ЫО, приведенная вг листинге 18.20, предназначена для создания виджетов надписей с установленными растровыми изображениями, иллюст- рирующими результат определенной операции совмещения. В виджете надписи методом зеЪР1хеаз1ге () устанавливается неизменяемый размер, который впоследствии служит для инициализации объекта прямоугольной области гесь. Этот объект используется для задания размеров исходного (зоигсеТтаде) и результирующего (гези1С1шаде) объекта растровых изобра- жений. В качестве исходного изображения мы рисуем, вызывая метод ОРахпьег: :йга«Ро1удоп (), треугольник, заполненный зеленым цветом. В результирующем объекте растрового изображения вызовом метода ОРахпьег: :агашЕ111рзе () рисуется окружность, заполненная красным цветом. Затем методом о₽а!пъег: :зессотроз11:емоае() устанавливается режим со- вмещения, переданный в функцию 1Ы () в переменной тоде, и растровое (зоигсеТтаде) изображение рисуется в результирующем изображении мето- дом ОРахпЬег:: Згамттаде (). Метод <2ЬаЬе1: :зеьР1хтар() устанавливает ре- зультирующее растровое изображение в виджете надписи. В конце возвраща- ется указатель на созданный виджет надписи.
хпЪ ша!п(!пС агдс, сИаг** агду) { ОАррИсаСхоп арр(агдс, а^дV); (ЖддеЁ адде; //ЬауоиЪ ЗеЬир ОСгхдЬауоие* рдгд = пем ОСг !<И»ауоиМ ; рдга->асЗкЗИ1с1деЪ (1Ы (ОРахпЬег:: Сотроз 1 ъ!опМоде_Зоиг се), 0, 0) ; рдгд~>ас1с1И1адее (пет/ 2ЬаЪе1 (" <СЕЫТЕК>5оигсе</СЕЫТЕК> ”), 1, 0); рдга->адсйЯсЗдее (1Ы (ОРахпЁег::Сатаров! Ь1опМоде__ЗоигсеСХгег), 0, 1); рдгд->ас!сй/1с1дее (пем 0ЬаЬе1 (• <СЕМТЕК>ЗоигсеСХлег</СЕМТЕК>") г 1, 1) ; рдг<!->аддМ!с1де11 (1Ы (рРахпЪег::Сотроз!ЫопМоде_8оигсе 1п), 0, 2); рдгс1->а<±ЗМ!(!деС (пет/ 0ЬаЬе1 (" <СЕЫТЕК>8оигсе1п< /СЕЫТЕК>"), 1, 2) ; рдгд->а<ИМ!(ЯдеЬ (1Ы (ОРахпЪег:: Сопроз!ь1опМоДе_ЗоигсеОи(:), 0, 3); рдгс1->аскЗЮ!<}де{1 (пет/ ОБаЬе! (”<СЕМТЕР>ЗошгсеОи1:</СЕЫТЕК>”), 1, 3) ; рдгд->ад(1М!дде1: (1Ы (ОРа!пЪег:: Сотроз 1 Ъ!опМос1е_ЗоигсеАСор), 0, 4) ; рдгс1->аддН!ддеЁ (пет/ ОЬаЪе! (" <СЕЫТЕК>ЗоигсеАГюр</СЕЫТЕН>"), 1, 4) ; рдг^->ад<Зй7!ддеЁ (1Ы (ОРахпЪег:: Сотроз 11:!опМос1е_С1еаг), 0, 5) ; рдг<!->ас1<ам!дде(: (пет/ ОЬаЬе! (" <СЕОТЕК>С1еаг</СЕОТЕК>"), 1, 5) ; рдга->аа<ЗИ1<Зде1: (1Ы («ЭРахпеег: : Ссп1роз!Ъ1опМо<1е^ЭезЪ!паЪ!оп) , 2, 0) ; рдгд->аскЗМ1с1дее (пет/ 0ЬаЬе1 (”<СЕЫТЕК>1)езе!па1:!оп</СЕЫТЕК>”), 3, 0) ; рдгд->асЗдМ1сЯдеЁ (1Ы (ОРахп^ег: : Сотроз 111!опМо<1е_ЭезС!паС!опС^ег), 2, 1); рдгс1“>аддМ!адеЁ.(пет/ 0ЬаЬе1 (”<СЕОТЕК>ОезЫпа11!опСХлег</СЕ№ГЕК>”), 3, 1); рдга~>а<!(ЯМ!ддеЪ(1Ы(ОРахпйег: :Сотроз!ъ!опМо<1е_резе!паС!оп1п) , 2, 2); рдгс!->асЯсМ.с1деС (пет/ рЬаЬе1 ("<СЕЫТЕК>1ЭезЪ1па^!оп1п</СЕЫТЕК>") , 3, 2) ; рдгд->аддИ!<1дее (1Ы (ОРахпЪег::Сотроз!1:!опМос1е—ВезСхпаЫопОиЪ) , 2, 3) ; рдг(1->а<Я<ЗДО!(ЯдеС (пет/ 0ЬаЬе1 (’’<СЕЫТЕК>Пез11!паС!оп0ие</СЕЫТЕК>И), 3, 3); рдгд->а<аам!адеЬ (1Ы (ОРахпЪег:: Сопроз!Ё1опМоде_ВезСхпаЪхопАЪор), 2, 4); рдгд->аа(ЗИ!с1деЪ(пет/ ОЬаЪе1 (И<СЕКТЕЕ>Пез^!па!:!опА1:ор</СЕМТЕК>”), 3, 4); рдгс1->ас1с1И!(ЯдеС (1Ь1 (2Ра!пЪег: : СотрозхМопМоде^Хог) , 2, 5) ; рдг<1->а<!<ЗИ!с1деЪ (пет/ ОЬаЪе! (" <СЕЫТЕК>Хог</СЕЫТЕК> "), 3, 5) ; т?дС. зееЬауоиЪ (рдгсЗ,) ; т/дС. зкот/ () ; ге^игп арр.ехес();
В основной функции, приведенной в листинге 18.21, создаются виджеты надписей с изображениями (вызовы функций 1Ы ()) и поясняющими надпи- сями, описывающими примененный режим совмещения. Все элементы раз- мещаются при помощи лейаута табличного размещения (рдга) на поверхно- сти виджета («де). Резюме Объект класса 0Ра1пЬег выполняет рисование на объектах классов, унасле- дованных от класса еРа1пгоеу1се. Класс ОРахпъег предоставляет методы для рисования точек, линий, эллипсов, растровых изображений, текста (см. гла- ву 20) и др. Для рисования класс орагпьег предоставляет перо и кисть. Перо используется для рисования дуг, линий и контуров замкнутых фигур. Кисти используются для заполнения внутренней части фигуры. Класс ОРахпеег со- держит в себе ряд методов для рисования точек, линий, эллипсов и т. д. Все команды рисования можно сохранять в объектах класса ОРхсСиге, который, в свою очередь, позволяет сохранять команды в файле и считывать их оттуда. Эффект мерцания возникает в том случае, когда пикселы за короткие проме- жутки времени перерисовываются разными цветами. Использование меха- низма двойной буферизации, встроенного в автоматически подавляют этот нежелательный эффект. (^1 поддерживает три типа градиентов: линейный, конический и лучевой. С их помощью можно придать объемность некоторым элементам вашей про- граммы. ' Режим сглаживания позволяет улучшить визуальный эффект, убирая ступен- чатость на контурах выводимых геометрических фигур. Класс ОРахпЬег предоставляет возможность проведения геометрических пре- образований, таких как перемещение, поворот, масштабирование и скос. Отсечения используются для ограничения вывода графики заданной обла- стью. Класс окедхоп служит для задания областей, которые могут иметь очень сложные формы. Графическая траектория позволяет задавать произвольные формы геометри- ческих фигур, соединяя геометрические фигуры вместе. Режим совмещения задает способ объединения пиксела источника и целевого пиксела в один.
Глава 19 Растровые изображения "Рисунок расскажет больше тысячи слов”. Древняя китайская мудрость Растровые изображения представляют собой набор цветовых значений, име- нуемых пикселами. Пикселы — это "клеточки", формирующие графический образ на устройстве вывода. Глаз человека не способен различать отдельные такие клеточки, поэтому мозг синтезирует общую картину, соединяя их в од- но целое. Форматы графических файлов Растровые изображения можно как записывать в файлы разных форматов, так и загружать из них. 01 поддерживает следующие растровые форматы: РИО, ВМР, ХВМ, ХРМ, РИМ, 1РЕС, МЫС, СП7, РГ4М, РВМ, РОМ и РРМ. Приведем описание некоторых наиболее распространенных форматов. Формат ВМР Формат ВМР (М1СГО8ОЙ ХУ1п<1о\У8 Вктар) является растровым форматом для ОС Мпдо№ и используется для хранения практически всех типов растровых данных, а также поддерживает любые разрешения экрана. Данные в этом формате почти всегда хранятся в несжатом виде, и поэтому занимают сравни- тельно много места. Структура формата ВМР тесно связана с интерфейсом прикладного программирования (АР1) для ОС ХУтсклУЗ. Формат ВМР нико- гда не рассматривался как переносимый и не использовался для обмена рас- тровыми изображениями между операционными системами, но с поддержкой этого формата в 01 все изменилось — он стал платформонезависимым.
Формат С1Р Формат СП7 (ОгарЫс® 1п!егсЬап§е Рогта1), произносится как "джиф" — это формат обмена графическими данными. Это один из самых популярных рас- тровых форматов в сети Интернет. Основное преимущество этого формата состоит в высокой степени сжатия без потерь, что достигается применением алгоритма сжатия (Ьетре!—2лV—ОД1сИ, по фамилиям разработчиков Лемпель, Зив и Велч). ОП7 поддерживает и анимацию. Недостатками этого формата являются- поддержка только 8-битной глубины цвета и требование лицензионных отчислений за каждую программу, использующую ЬХ\У- алгоритм. Формат РЫ(3 Формат РЫСт (РоПаЫе ЫеМогк ОгарЫс®), произносится как "пинг" — переносимая сетевая графика. Этот формат разработан как альтернатива ОП7 в пику юридическим сложностям, связанным с требованиями оплаты при ис- пользовании. Неофициальная трактовка названия РМО — "РМО’® №1 ОП7" ("РЫО — это не ОП7"). Подавляющее большинство ХУеЬ-браузеров поддер- живают этот формат, который не сложен в реализации, а по своим функцио- нальным возможностям даже превосходит ОП7. РМО распространяется бесплатно, что позволяет избежать бремени лицензионных платежей и па- тентных сборов. Как и в формате ОП7, обеспечивается сжатие данных без по- терь и поддерживается прозрачность, кроме того, поддерживается глубина цвета до 48 бит. Примечание В качестве альтернативы для анимированных файлов формата СНЕ можно ис- пользовать формат МЫ6, хранящий в себе серии изображений формата РМ6. Формат иРЕС Формат 1РЕС получил свое название от 1о1п1 РИсНо^гарЫс Ехрей®. Огоир — объединенная экспертная группа по фотографии (организация, разработав- шая стандарт и метод сжатия) и произносится как "джейпег". Этот формат разрабатывался с 1991 по 1993 год, после чего был стандартизирован. Его отличительная особенность — это очень высокая степень сжатия, но с поте- рей информации, поэтому файлы такого формата используются, в основном, для хранения фотографических изображений, так как именно на таких кар- тинках обеспечиваются наименьшие погоешности.
Формат ХРМ ХРМ (ХИхМар) — это распространенный в системе XII (1Ж1Х) формат. Мы привыкли к тому, что изображение хранится в виде двоичной информации, но в формате ХМР данные хранятся в виде исходного кода на языке С, кото- рый можно вставлять в свои программы. Это позволяет превратить обычный текстовой редактор в инструмент для создания и изменения растровых изо- бражений. Формат можно использовать для любых разрешений экрана и 24- битной глубины цвета. Примечание Ввиду того что формат неэкономично расходует дисковое пространство, его лучше использовать для небольших по размеру растровых изображений. На рис. 19.1 показано содержимое файла в формате ХРМ, представляющее собой код на языке С (в котором определен массив, хранящий растровые данные в формате ХРМ), и само растровое изображение. сопзк сКах* 1азаде_хрж[) » { /* ЬаХдНЪ псоХохж сЬаг8_рех_р1хв1 */ "15 16 1 1% '• с #000000“, ". с #848200", с #848284“, с #с1643аи", "втеттетв", "в в", “в . етет е , - в . етте "в . тоет . 4м, - е . твет . - в . ееееееее . - е . твет . ие -в ............... ие .. . +-, "в .. ее . +и, "в .. ее . -в .. ее . +-, вввввввввввввв ВИВ В ВИ ВИВ ВВВ№ вив внва вив вив» ВВВ ВИВИ ВИВ ВИВИ винввввввввиив^ випииииппинвив^ ВИНВВВВВВВВВВВ&' виивввввв вшвш виивввввв вквк ВИИВВВВВВ ВИВИ вввввввввввввв вж ▼ > ь- Рис. 19.1. Файл в формате ХРМ В первой строке файла стоит комментарий /*хрм*/, сообщающий о формате файла. За ним следует константный указатель на массив йпаде_хрт, задаю- щий растровое изображение. Первые два целых числа в строке задают шири- ну и высоту растрового изображения в пикселах (в нашем случае размер 16x16 пикселов). Третье целое число указывает количество цветов (в приме-
ре оно равно 4). Четвертое число определяет количество знаков на пиксел — в нашем случае это один знак. Следующие четыре значения задают цветовую палитру из четырех цветов, которые кодируются символом #, за которым следует шестнадцатеричная запись цвета в формате КОВ (такое же обозначе- ние цветов принято в формате НТМЬ). Символ отделяется от цветового зна- чения буквой с, которая является сокращением для слова со1ог (цвет). Примечание Можно вместо буквы с написать букву г, что дает возможность использования символических имен для цветов. Так, например, указав после этого символа ыопе, можно задать символ для прозрачного цвета. В примере на рис. 19.1 для черного цвета используется символ пробела, для коричневого — точка, для темно-серого — плюс, а для светло-серого—@. Затем следует расположение пикселов в матрице растрового изображения. Контекстно-независимое представление Контекстно-независимое представление не зависит от возможностей графи- ческой карты вашего компьютера и, следовательно, от графического режима, установленного в вашей операционной системе. Данные растрового изобра- жения помещаются в обычный массив, что дает возможность эффективного обращения к каждому из пикселов в отдельности, а также позволяет эффек- тивно производить операции записи и считывания файлов растровых изо- бражений. Класс СНтаде ОТшаде является основным классом для контекстно-независимого представ- ления растровых изображений. Этот класс унаследован от класса контекста рисования ^Ра^пь^еV^се, что позволяет использовать все методы рисования, определенные в классе ОРахпсег. Класс охшаде предоставляет поддержку для форматов, указанных в табл. 19.1. Таблица 19.1. Перечисление гохтас класса охтаде Константа Описание ЕогтаЬ—Тгг/а! 1<3. Растровое изображение недействительно РоппаЁ—Мопо Каждый пиксел представлен одним битом. Би- ты укомплектованы в байте таким образом, что первый является старшим разрядом
Таблица 19.1 (окончание) Константа Описание РоилаМопоЬ5В Каждый пиксел представлен одним битом. Би- ты укомплектованы в байте таким образом, что первый является младшим разрядом РогтаЪ—1пс1ех8 Данные растрового изображения представля- ют собой 8-битные индексы цветовой палитры (см. главу 77) ЕогтаТ_КСВ32 Каждый пиксел представлен тридцатью двумя битами. Однако значение для альфа-канала всегда равно значению ОхЕЕ, то есть непро- зрачно РогтаС_АКСВ32 Каждый пиксел представлен 32 битами Р оила АЛОВ 3 2_Рг ети! Т хр 1х ед Практически идентичен формату РогтаТ_АВСВ32, но код оптимизирован для использования объекта оттаде в качестве контекста рисования Значение формата можно узнать с помощью метода Согшаь (). Для конверти- рования растрового изображения из одного формата в другой предусмотрен метод со^егстоРоппае (), который возвращает новый объект отшаде. Дан- ные растрового изображения хранятся в объектах класса оипаде построчно, и в каждой строке пикселы расположены слева направо. Примечание При использовании глубины цвета в один бит каждый байт хранит данные восьми последовательно идущих по горизонтали пикселов (см. табл. 19.1). Ес- ли количество бит ширины изображения не делится на восемь, то последний байт строки будет содержать биты, значения которых можнр проигнорировать, а следующая строка начнется с нового байта, оттаде содержит массив указа- телей, указывающих на начало каждой строки изображения. С помощью метода зсапЫпе () можно получить адрес строки, номер которой соответствует значению, переданному в этот метод. Имеет смысл передавать в него индексы строк, лежащие в диапазоне от 0 до высоты растрового изо- бражения, уменьшенного на единицу. Чтобы создать объект этого класса, необходимо передать в конструктор ши- рину, высоту и формат растрового изображения. Например, следующая стро- ка создает растровое изображение шириной 320 и высотой 200 пикселов и глубиной цвета 32 бит. □Тшаде ппд(320, 240, 01шаде: :Рогтпа1:_КСВ32);
Содержимое для объекта оттаде также можно считать из файла, хранящего растровое изображение, передав имя файла в конструктор при создании объ- екта класса охтаде. Если данный графический формат поддерживается <Х то данные будут загружены и автоматически установится ширина, высота и формат. Например: рХшаде 1тд("Иза.зрд"); ~ В конструктор можно передавать и указатель на массив данных ХРМ. Например, согласно рис. 19.1, загрузка растровых данных будет выглядеть следующим образом: #1пс1ийе "1таде_хрт.11" ОЬпаде йпд (1таде_хрт) ; В качестве альтернативы можно воспользоваться методом 1оай() для считы- вания файла. Несомненное достоинство этого метода в том, что так можно загружать растровые изображения в любой момент. Первым параметром пе- редается имя файла, а вторым — формат. Формат файла обозначается стро- кой типа ип81дпе<з сНаг*, принимающей одно их следующих строковых зна- чений: ОП7, ВМР, 1РО, ХРМ, ХВМ или РЫС. Если во втором параметре вообще ничего не передавать, то класс охшаде произведет попытку распо- знать графический формат самостоятельно. Например: ОТшаде ипд,- 1шд.1оаа("11за.зрд”); При помощи метода зауе () можно сохранять растровое изображение из объ- екта класса оттаде в файл. Первым параметром передается имя файла, а вто- рым — формат, в котором он должен быть сохранен. Например: ОТшаде йпд(320, 240, 32, 0Со1ог::Ь1ие); 1шд.8ауе( “Ыие.эрд", “Л?С") ; Примечание Ввиду того, что фирма 11п)зу8 имеет патент на метод сжатия 1_2\Л/, используе- мый форматом О1Е, в некоторых странах создание файлов в формате 61Е без соответствующей лицензии является нелегальным. По этой причине 01 не пре- доставляет возможность сохранения в формате 61Е. Класс оттаде просто незаменим по эффективности, когда нужно изменить или получить цвета пикселов растрового изображения. КСВ-значение пиксе- ла с координатами (X, V) можно получить с помощью метода рххеКх, у). Для записи ВОВ-значения используется структура данных окдъ (см. главу 17). Например: ОНдЬ гдЬ = йпд.рххе!(250, 100);
Примечание Метод р1хе11пЗех() возвращает индекс палитры пиксела (см. главу /7) с ко- ординатами (X, У). Этот метод работает только для растровых изображений, имеющих глубину цвета 8 бит. Установить пикселу с координатами (X, У) новое КОВ-значение можно ме- тодом зеЪРххе!(х, у, гдЬ). . Примечание Для растровых изображений, имеющих глубину цвета 8 бит, значение гдЬ бу- дет соответствовать индексу палитры (см. главу 17), которое может быть уста- новлено вызовом метода зеъсоХог (). Например: ОКдЪ гдЬ = дКдЬ(200, 100, 0); 1тд.зе1:Р1хе1 (20, 50, гдЬ) ; Объект класса дхшаде можно отобразить в контексте рисования методом ОРахпъег: :с1га™1таде (). Перед тем как отобразить объект ОХтаде на экране, метод дгаюппаде () преобразует его в контекстно-зависимое представление (объект класса ОРХхшар). В листинге 19.1 приведен вывод изображения с по- зиции (0,0) (рис. 19.2). ^Листинг 19/1. Вывод растрового изображения < Л Л ./, ОРахпСег рахпЪег (ЪЫз) ; ОХшаде 1тд("Иза.эрд") ; ра хпЪег. ЗгаюХтаде (0, 0, 1тд) ; Рис. 19.2. Вывод объекта ОХтаде в контексте рисования
Если необходимо вывести только часть растрового изображения, то необхо- димо указать эту часть в дополнительных параметрах метода йгаадхтаде (). В листинге 19.2 приведен пример отображения участка растрового изобра- жения, задаваемого координатой (30, 30) и имеющей ширину равной ПО, а высоту — 100 пикселам (рис. 19.3). .....-................ „.........„„...д,.,.,™...,.. у..-. Ц1и<дйнг 19.2. Вывод части растровогоизображения ОРахпСег рахпСег (СЫз) ; ОТтаде хгод("Иза.зрд") ; рахпСег.дгатеТтаде(0, 0, ипд, 30, 30, 110, 100); Рис. 19.3. Вывод части объекта ОТшаде в контексте рисования Вызвав метод 1тгегСР1хе1з () И передав В него значение ОТтаде: : ТгнлегСКдЪ, можно управлять инвертированием пикселов. А передача значения ОТтаде: :1тлегикдЪа позволяет инвертировать не только пикселы, но и аль- фа-канал. В листинге 19.3 показана реализация этой возможности, а резуль- тат отображен на рис. 19.4. ^7”.......................... —’ .. ^истинг 19.3. Инвертирование пикселов • ОРахпСег рахпСег (ьЫз); ОТтаде хтд( "Иза.дрд") ; рад-пСег. дгаюТтаде (0, 0, 1тд) ; 1тд. 1тгегЪР1хе1з (ОТтаде: : ХтлегСЕдЪ) ; рахпСег. <±гаю1таде (ыпд. хНаск (), 0, 1тд) ; При помощи метода зса1ед() можно получить новое растровое изображе- ние с измененными размерами. Действие флагов ос:: тдпогеАзрессваНо и ос: гКеерАзресикаНо, управляющих изменением размеров, рассмотрено
в главе 17. Листинг 19.4 демонстрирует возможность изменения размеров растрового изображения, а результаты показаны на рис. 19.5. Рис. 19.4. Инвертирование значений пикселов ОРахпЁег рахпЬег (ЬЫз) ; ОТшаде йпд1 (* Иза. эрд”) ; рагпЁег. (ЗгаюТтаде (0, 0, ипд!) ; ОХшаде 1тпд2 = 1шд1. зса1ес1 (1шд1. () / 2, 1тд1. кехдЪЪ (), ОЬ:: ХдпогеАзресЪКаЫо) ; ра1пЬег. (Зга^Ттаде (ипд1. ш1<Ш1 (), 0, ипд2); ОТшаде ипдЗ = ипд!. зса1е<3 (ипд1. тоДЪЬ. () , ипд1. ИехдкЪ {) / 2, ОЪ:: ТдпогеАзресЪКаЁхо) ; рахпеег. сЗгаюТтаде (0, ипд!.кехдИЪ (), ипдЗ); СЭТтаде 1тд4 = 1тд1.зса1ед(ипд1.() /2, 1тд1.ИехдкЪ(), Оё: :КеерАзресГКае1о) ; рахпГег. дгаюТтаде (йпд!. юхскЪ () # ипд!. ИехдЫ: (), ипд4) ;
Рис. 19.5. Изменение размеров растрового изображения
Класс оттаде предоставляет возможность горизонтального или вертикально- го отражения растрового изображения. Для этого в метод пйггогеб () необ- ходимо передать два булевых значения, управляющих горизонтальным и вертикальным отражениями, соответственно. Метод пйггогеб () не изменяет растровое изображение объекта, из которого он был вызван, а создает новое. В листинге 19.5 реализуется и вертикальное, и горизонтальное отражение. Результат показан на рис. 19.6. | Листинг 19.5. Отражение'изображения /.ч?-: - "Г'г ? I ОРахпСег рахпЪег {еЫз) ; 01таде 1тд( "Иза.дрд") ; рахпЁег. (ЯгаюХтаде {0, 0, 1гпд) ; ра 1п1зег. дгаюХтаде (хтд. юд.<М1 () , 0, 1тд. паггогед (Сгие, егие) ) ; Класс О1таде как контекст рисования Как уже упоминалось, класс оттаде является контекстом рисования. Его це- лесообразно использовать в тех случаях, когда соображения качества и точно- сти отображения преобладают над скоростью. Одно из необходимых условий для использования объекта класса оттаде в качестве контекста рисования — формат изображения должен быть 32-битным, то есть оттаде: :Рогтас_АК<звз2 ИЛИ ОТтаде: :РогтаЪАКОВ32_Ргети1ЫрИе<1. Формат ОТтаде: :РостаЬАВСВ32_ РгетиТЫрИей является более предпочтительным, так как он оптимизирован для операций рисования. Операция рисования продемонстрирована в листин- ге 19.6. Результат показан на рис. 19.7 — окно программы динамически из- меняет размеры эллипса в соответствии с изменениями своих размеров. Рис. 19.7. Рисование
[Листинг! 9.6. Рисование на объекте стгааде йлл»............................;!............. ,«.«•..и.'..^л.1им;.б.|;1........;.л.>.............I уотс! ГтадеПгаю: грахпЬ^епНОРахпСЕ^епЬ* ре) { ОТшаде нпд (з!ге () , ОХгоаде: :Роппа1:_АКСВ32_Ргети1111р11ед) ; ОРахпЁег рахпЬег; ра1п!:ег. Ъедхп (&1тд) ; рахпЁег. хпхеЕгст ( ёЫз ); рад-пЬег. зеЪКепдегНхпЪ (ОРахпЬег:: АпЪхаИазхпд, Ыие) ; рахпЪег.егазеКесЬ(гесЪ()) ; рахпЪег .дгаюЕ111рзе(0, 0г з1ге() .техдСЩ ) , зхгеО.ЪехдЪЬО); ра!пСег.епд(); рахпСег .ЬедхпСЁЫз) ; рахпСег.дгаюХтаде(0, 0, хтд); рахпЁег.епд(); } В листинге 19.6, в методе обработки события рисования ра1псЕуепс () созда- ется объект класса оттаде с размерами виджета, которые возвращаются методом з12е(). После создания объекта рисования класса ОРахпеег в его метод Ьед1п () передается адрес объекта от'таде в качестве контекста рисова- ния. Метод 1п1еггош0 инициализирует объект рисования такими настройками из виджета, как цвет фона, стиль пёра, шрифт и т. д. Метод зескепйегнхпс () устанавливает режим сглаживания ОРахпсег:: АпЫаИазхпд; второй пара- метр сгие указывает, что этот режим надо включить (Еа1зе означало бы, что его надо отключить). Метод егазекесьо очищает прямоугольную область, указанную в его параметре. В нашем случае эта область соответствует теку- щей области виджета, которую возвращает метод гесс (). Рисование эллипса осуществляется методом бгаздЕИхрзе (). Вызов метода епа<) закрывает блок рисования на объекте отшаде. Это делается для того,- чтобы мы могли исполь- зовать объект рисования с контекстом виджета. Для этого его указатель пе- редается в метод Ьедхп (). Затем методом <3га«1таде () содержимое объекта Отшаде отображается в области виджета. Контекстно-зависимое представление Контекстно-зависимое представление позволяет отображать растровые изо- бражения на экране гораздо быстрее, чем контекстно-независимое, так как оно не требует проведения дополнительных преобразований. К объектам
контекстно-зависимого представления можно применять все методы класса ОРахпСег. Большинство процессоров графических карт обладают способно- стью отображать графические примитивы, например, линии, полигоны и по- верхности, не используя при этом основного процессора, благодаря чему очень сильно ускоряются графический вывод и работа самой программы. Класс ОР1хтар Этот класс унаследован от класса контекста рисования СРа1ги:оеи1се. Его определение находится в заголовочном файле ()Р1хтар. Объекты класса со- держат растровые изображения, не отображая их на экране. При необходи- мости этот класс можно использовать в качестве промежуточного буфера для рисования, то есть если требуется нарисовать изображение, то его можно сначала нарисовать в объекте класса ОРххтар, а потом, используя объект класса 0Ра1пьег, скопировать в видимую область. Для создания объекта этого класса в его конструктор нужно передать ширину и высоту. Например: фРххтар рхх(320, 240); Глубина цвета в создаваемом объекте класса ОРххтар автоматически будет установлена в соответствии с актуальным значением графического режима. Это значение можно узнать с помощью метода ОРххтар:: аеГаи1соерЫ1 (). Мож- но передать файл растрового изображения прямо в конструктор. Например: ОРххтар рхх (”^огезС.д рд”); Класс ОРххтар, как и оттаде, предоставляет возможность загрузки данных в формате ХРМ прямо в конструктор: #1пс1ибе " 1таде_хрт.И" ОРххшар рхх (1шаде_хрт) ; Объекты класса орххтар содержат не сами данные, а их идентификаторы, с помощью которых они могут обратиться к системе. Поэтому прямой доступ к каждому пикселу в отдельности будет очень медленным. В этом случае ра- зумнее будет воспользоваться классом оттаде. В распоряжении класса ОРахпСег имеются методы для сохранения и записи графических изображений — 1оаб () и зауе (). При проведении этих опера- ций будет осуществляться промежуточное конвертирование из объекта клас- са отшаде (или В него). Объект класса ОРххтар отображается в видимой области с помощью метода ОРахпСег:: ОгаиРгхтар о. Листинг 19.7 демонстрирует два разных варианта вызова метода ДгаиРххтар <). В первом варианте указана только позиция,
с которой нужно осуществить вывод. Во втором — вывод задается прямо- угольной областью, в которой должно отображаться растровое изображение. Результат показан на рис. 19.8. .............. .......................... .................................. ’ Листинг 197. Вывод растрового изображения /е ; * .............................................. л..;...................... ОРахпСег рахпСег(екхз) ; ОРххшар рхх (” ^огез11. зрд") ; рахпСег. (ЗгаюРххтар (0, 0, рхх) ; 2КесС г (рхх.мхсШ! () , 0, рхх.^хдСИ () / 2, рхх.ЪехдИС ()) ; рахпСег. ДгаюРххтар (г, рхх) ; Рис. 19.8. Разные способы отображения объекта класса ©Рххшар Класс ОР/хтарСасЬе Этот класс реализует кэш для объектов 0Р1хтар. Все операции производятся с глобальным объектом кэша, поэтому все методы этого класса определены как статические. С помощью метода 1пзегь () можно поместить объект клас- са ОРххтар, с ключом строкового типа, в кэш. Если растровое изображение было загружено из файла, то в качестве строки удобно использовать имя файла. Передавая строку-ключ в метод Лпа(), можно получить внесенное растровое изображение из кэша. В кэш имеет смысл помещать растровые
изображения, часто используемые в программе, чтобы избежать загрузки из файла при каждом обращении к ним. Класс ОВНтар Класс овхыпар унаследован от 0Р1хтар и определен в заголовочном файле рВйтар. Объекты класса предназначены для хранения растровых изображе- ний, обладающих глубиной цвета, равной одному биту. Это позволяет хра- нить изображения, имеющие только два цвета ос: :со1ого и ос: :со1ог1. Та- кое изображение называется двухуровневым (Ы-1еуе1). Класс используется в основном для хранения указателей мыши и масок прозрачности. Использование масок для ОР/хтар В объектах класса орххшар добиться прозрачности можно при помощи спе- циальной маски, для установления которой необходимо вызвать метод ОРххшар: :зеьмазк(). Размеры маски и растрового изображения, к которому она применяется, должны быть одинаковы. Маски создаются в объектах класса овхешар. Для их создания можно воспользоваться объектом класса ОРахпсег, при этом для прозрачного пиксела необходимо использовать цвет со1ог0, а для непрозрачного — со1ог1. Если маска не установлена, то все пикселы растрового изображения будут непрозрачны. Маски следует исполь- зовать в случаях острой необходимости, так как это существенно снижает быстроту вывода растрового изображения. На рис. 19.9 показан пример, в котором маской является надпись, выведенная заданным размером и шрифтом. - Рис. 19.9. Маска в виде текста .............................................................. ......я.......... ^Листинг 1918. Реализация маски в. виде текста ?. :.......................................-г..?.:...л........................ ОРххшар рхх (зхге ()) ; 0Ра1п1:ег рахп^ег; рахпСег. Ьед1п (&р!х) ;
рахпСег. скгау/Рххтар (гесЪ () , ОРххтар (” зЬехп. 5рд")) ; рахпСег. епс1 () ; ОВхСшар Нпр(зхге()); ЬофЛх11(); рахпСег.Ьедхп(&Ьтр); рахпСег.зеСРеп(ОРеп(ОС::со1ог1)); рахпСег.зеСРопС(ОРопС("Тхтез”, 75, ОРопС: :Во1с1)) ; рахпСег. дга^лтТехС (гесС () , ОС: : АИдпСепСег, "Пгам ТехС ") ; рахпСег.епд() ; рхх.зеСМазк(кспр) ; рахпСег.Ьедхп(СИхз); рахпСег. дгаюРххтар (гесС (), рхх); рахпСег.епб(); В листинге 19.8 приведен метод обработки события рахпЬЕуепс(). В первой строке создается объект класса ОРххтар, размеры которого устанавливаются в соответствии с размерами виджета (метод зхге ()). После создания объекта рисования объект растрового изображения р1х устанавливается текущим контекстом с помощью метода Ьедхп(). Метод йгашРххт^р() отображает в области объекта рхх растровое изображение 81ет.)р§, после чего работа с контекстом заканчивается вызовом метода епа (). Следующий шаг — это создание объекта маски Ьтр, размеры которого зада- ются исходя из размеров виджета. Методом 4x11 о маска инициализируется прозрачными значениями оъ::со1ог0 (этот цвет используется по умолча- нию). Текст на маске рисуется пером, имеющим значение цвета <эь: :со1ог1, и, следовательно, в тех местах, где пикселы получили это значение, после установки маски, будут показаны пикселы оригинального изображения. По- лученная маска устанавливается в объекте рхх с помощью метода зесмазк (). после чего изображение рисуется на контексте виджета. Создание нестандартного окна виджета Класс онхйдес, как и класс ОРххтар, содержит метод аеемазк (). С его помо- щью можно установить маску и сделать окно виджета не квадратным, а лю- бой другой формы. Программа из листинга 19.9, окно которой показано на рис. 19.10, демонстрирует эту возможность.
Рис. 19.10. Пример нестандартного окна программы 1пе тахпЦпе агдс, сЪаг** аг^) { ОАррЫса^хоп арр(агдс, агду) ; М1пс1о™ У71П; СРххтар рхх(":/ ипадез /ип1хо!с1з. рпд") ; >г1п. зеЁРххтар (рхх) ; ^1п.зеШазк(р1х.тазк()) ; тп. зИою () ; геСит арр. ехес () ; } В листинге 19.9 создается объект класса и1пбо«. В объект р1х загружается файл растрового изображения. После этого вызовом метода зееПхтарО ус- танавливается изображение, что можем сделать благодаря тому, что наш класс И1паои унаследован от оьаЪе1. Некоторые графические форматы, например, ОП7, РМт и ХМР, могут содер- жать прозрачность. При их загрузке в конструкторе или методом 1оаао ав-
томатически создается маска, которую можно установить с помощью метода зеЬМазк(). ............. Лиетинг19Л0.;Ф^ КлассЛУ|пс1о^ с1азз Юхпдсж : риЪИс <2ЬаЬе1 { ргАллаЪе: 0РО1П1: т_рЁРоз1е1оп; ргоСес^ед: V^^^иа1 Vо^с^ Мхпдозд: :тоизеРгеззЕХгепЪ(0МоизеЕ^еп1:* ре) { т_рЪРоз1111оп = ре->роз () ; } V^^I:иа1 то!с1 Мхпдсж: :тоизеМоVеЕVеп^(^МоизеЕVеп^1* ре) { тспге (ре->д1оЪа1Роз () - т_р^Роз1Ё1оп) ; } риЫхс: Шпс1оте(ОМхдде^* рюдС = 0) • : ОЬаЪеЦрюдЁ, 0^: : Егате1еззЮ1паоюН1пЪ | 0Г: :№тскж) ( } ); Для того чтобы убрать заголовок окна, мы передаем в конструктор оьаЪе1 значение модификации свойства окна ое: :Ггате1еззН1пе. Но для окна без заголовка нужно позаботиться о возможности его перемещения. Для этого в классе мхпаою (см. листинг 19.10) переопределяются методы событий мы- ши тоизеР^еззЕVепе () И тоизеМОVеЕVепЁ (), реализуя тем самым КОД, необхо- димый для изменения расположения окна на экране. Атрибут т_рироз1и1оп нужен для хранения координат указателя мыши относительно начала окна виджета. Если в растровом изображении нет маски, или файловый формат ее не под- держивает, то можно поступить следующим образом: класс ОРххтар содер- жит метод сгеапеНеиг1зМсМазк(), который позволяет создавать маски, ис- ходя из растрового изображения. Для этого берутся значения пикселов
четырех углов изображения и все пикселы с этим цветом устанавливаются прозрачными. Если все цветовые значения пикселов отличаются от левого верхнего пиксела, то тогда за текущее выбирается цветовое значение верхне- го правого пиксела. Если же и остальные два значения отличаются от право- го верхнего пиксела, то тогда за текущее значение принимается цвет нижнего левого пиксела. Резюме 0( поддерживает большое число форматов файлов растровых изображений. В их число входят такие популярные форматы, как ВМР, ОП7, РЫО, 1РЕО и ХРМ. Формат ХРМ применяется в основном для отображения иконок. Основное отличие форматов ХРМ и ХВМ от других состоит в том, что они содержит исходный код на языке С. Классы для хранения растровых данных подразделяются в Ог на контекстно- зависимые и контекстно-независимые. Для контекстно-независимого представления основным является класс Оипаде. Объекты класса охтаде способны содержать и изменять данные, на- ходящиеся в графическом режиме, который не поддерживается графической картой. Независимость от контекста позволяет эффективно получать и изме- нять цвета отдельных пикселов, а также проводить операции загрузки и со- хранения файлов. Класс охшаде содержит ряд методов, позволяющих прово- дить различные операции с пикселами, такие как отражение растровых изображений, изменение их размеров, инвертирование и др. При помощи класса 0Ра1пЪег на объектах класса охтаде можно рисовать линии, эллипсы, прямоугольники и т. д. Основным представителем контекстно-зависимого представления является класс ОРххтар. Объекты этого класса отображаются гораздо быстрее объек- тов класса охтаде. На объектах класса дрххтар также можно рисовать при помощи класса ОРахпьег. Эти объекты имеют глубину цвета, равную глубине цвета текущего режима графической карты. Унаследованный от 0Р1хтар класс ов1тар предоставляет возможность для хранения двухцветных рас- тоовых изобоажений.
Глава 20 Работа со шрифтами "Размышляющий найдет, что черта де- лает буквы, буквы — сдог, а слоги — слово, следовательно, слог был прежде слова, буква прежде слога, и 4ерта пре- жде буквы”. Карл Эккартсгаузен. "Ключ к таинствам натуры ". Шрифт имеет очень древнюю историю. Согласно историческим данным, первый буквенный знак появился более восьми тысяч лет назад. На протяже- нии тысячелетий буквы создавались вручную. Процесс печати был основан в Китае, примерно две с половиной тысячи лет назад. И вот — произошла вторая революция, и теперь мы видим шрифты на экранах наших мониторов. Эта глава посвящена масштабируемым шрифтам (гарнитурам). Гарнитура шрифта — это его внешний вид (рисунок шрифта). Масштабируемая гарни- тура — это идеальное математическое описание шрифта. Она позволяет ото- бражать его на экране без искажений и выводить на печать в различных размерах. Проведение соответствующих преобразований берут на себя спе- циальные функции растеризации, которые преобразуют математическое представление шрифта для его последующего отображения в растровую мат- рицу. Эти функции вызываются неявно и не накладывают на разработчика дополнительных временных затрат при разработке. В (^1 класс окопе является основным для работы со шрифтом. Объект этого класса задается целым рядом параметров: □ семейство шрифта; □ размер; □ толщина — нормальное или полужирное начертание; □ отображаемые знаки; □ стиль — ноомальный или наклонный.
При передаче объекта класса окопе в метод ошадеь:: зесгопе (), в виджете устанавливается шрифт, который будет использоваться при его отображении. Если требуется установить один шрифт для всего приложения, то объект класса окопе нужно передать в статический метод ОАррИсаЫоп:: зеъРопе (). содержит дополнительные классы для работы со шрифтами: ОРопСВаСаВазе, ОРопЪТпСо И ОРопЁМеСгхсз. Класс ОРопсоаЬаЪазе предоставляет информацию обо всех установленных в системе шрифтах. Для их получения вызывается метод йпШевО, кото- рый возвращает список шрифтов в объекте класса озегхпдЫзе. Класс ОРопсэаСаЬазе содержит метод вьуТезегхпд () для получения стиля шрифта ОТ ОРопЫпГо. Класс оропыпЕо служит для получения информации о конкретном шрифте. С помощью метода СашНу () можно узнать семейство шрифта. Методы 1еаИс() и Ъо1а<) возвращают значения булевого типа, информирующие о стиле (наклонности и жирности) шрифта. Класс <2Ропсмегг1сз предоставляет информацию о характеристиках шрифта, показанных на рис. 20.1. 1еНВеаг1пд() Левое пространство буквы НдМВеаНпдО шкищасьагПравое пространство Ширина > ^уквы буквы Ье‘|дЫ() Высота >вая линия шИ1*1(О81|1пд) Ширина строки с!езсеп<() Ппе8рас1пд() ' Высота подстрочного Пространство элемента строки Базовая линия Рис. 20.1. Характеристики шрифта Передавая в конструктор класса дропемесгхсз объект класса оропп, можно получать его характеристики. Методы 1е^свеаг1пд () и г!дЫ:веаг1пд () воз-
вращают в пикселах левое и правое пространство буквы, соответственно. Метод ИпеЗрасХпд () возвращает расстояние между базовыми линиями. Пе- редав в метод «г1аеъ(соп8Ь озъгхпдь, 1пь) строку и количество символов, узнают его ширину, если количество символов не передано, то возвращается длина всей строки. А высота возвращается методом ЪехдЪь (). Например: ОРопЕМеСгхсз йп(ОРопЕ("Соигтег", 18, ОРопЕ::Во1д)); ОЗЬгхпд зЕг = "ЗЬгл-пд"; д!)еЬид() « "ИтдИЕ:" « йп.и1с1Е11(зЕг) « *Не1дИЕ:" « Еш.кегдИЕ)) ; Для получения высоты надстрочного и подстрочного элемента шрифта необ- ходимо вызвать методы азсепЕ () и йезсепь (), соответственно. Высота над- строчного элемента — это максимальная высота символа над базовой линией шрифта (включая диакритические знаки), а высота подстрочного элемента — это максимальное значение, на которое символ может уходить ниже базовой линией шрифта. Вызвав метод ЬошкНпдкесЕ () и передав в него строку, можно получить объ- ект класса окесь, соответствующий прямоугольной области, необходимой для отображения текста строки. Этот метод удобно использовать для опреде- ления геометрии текста до начала его отображения. Отображение строки В объектах класса орахпъег методом ОРатпсег: :зеьропс() устанавливаются объекты класса оропе. В классе ОРахпЕег имеются 7 различных вариантов метода <ага«техЕ () для отображения текста установленным шрифтом, наибо- лее часто используются следующие два: □ с1га«техЕ ( 1пе х, 1пе у, сопзь сзьг1пд& вег) отображает текст рз2. Ко- ординату левого края текста задает параметр х, а параметр у указывает координату базовой линии. Параметр пьеп задает количество символов, которые должны быть отображены. По умолчанию параметр равен-1 и означает, что должны отображаться все символы; □ дгамТехЕ(сопзЕ 0Ро1пЕ& рЕ, сопзЕ 03Ег1пд& рзг, хпЕ пЬеп = -1) от- личается от вышеприведенного метода тем, что в первом параметре вме- сто х и у передается объект точки орохпе. В листинге 20.1 приведена реализация вывода на экране строки текста 'Ъгаж Ьше", а результат показан на рис. 20.2.
... ЛистинггрЛ. Вызов методов з^ЬГопЬО иДгакТекьО < 2Рахп1:ег рахпЬег (ЪЫз) ; рахпеег.зеЪЕопС(ОРопЪ(• Тхшез", 25, ОРопЪ::Ыоппа1)) ; рахпЪег. дгамТехЪ (10, 40, ” Бгаю ТехЁ") ; Г Iя Те Рис. 20.2. Отображение строки Для более тонкой настройки отображаемого текста класс ОРахпСег предос- тавляет другие варианты метода йга^техс(): йгаютехс(сопзе окесгь г, 1пс Надз, сопзе озсгхпдь зЬг) отображает текст зсг в заданной парамет- ре^ г прямоугольной области. С.помощью параметра Ладе можно повлиять на размещение и отображение текста. Значение этого параметра получается комбинацией значений, указанных в табл. 7.1 и табл. 20.1, с помощью логи- ческой операции | (ИЛИ). Таблица 20.1. Перечисления техъЫад пространства имен ос Константа Значение Описание Тех^51пд1еЫпе 0x0100 Игнорирует знаки новой строки (знак \п) ТехЪБопССИр 0x0200 Гарантирует, что в том случае, если текст будет выступать за пределы, он не будет обрезан ТехСЕхрапсЯТаЬз 0x0400 Замещает знаки табуляции \ ъ равносильным пространством Тех!: Зко^гМп етоп 1 с 0x0800 Знак & не будет отображаться, а следующий за ним символ будет подчеркнут и получит клави- шу быстрого доступа ТехШогсМгар 0x1000 Если строка выходит за пределы заданного прямоугольника, она будет перенесена В листинге 20.2 строка текста выводится по центру. Результат показан на рис. 20.3. Выводимая строка не помещается полностью в прямоугольной об- ласти, задаваемой переменной г, поэтому осуществляется переход на новую
строку с помощью флага техъиогаигар. Метод йга«кесс () вызывается для отображения границ прямоугольной области. ....................................................................... Пример 20.2. Вь13ов методов ве<:Гоп&0, ага^несЪ () и «1га’«ТехЬ О .' ОРахпСег рахпСег(СИхз); ОКесС г = ОНесС(0, 0, 120, 200); рахпСег.зеСЕопС(ОРопС(”Тхтез”, 25, ОЕопС::ЫогтаХ)); рахпСег.(ЗгатеНесС (г); ра хпСег. ЗгатеТехС (г, ОС: : АХ хдпСепСег | ОС: : ТехС^огдМгар, " Огате ТехС") ; Рис. 20.4. Текст, заполненный градиентом Текст можно залить градиентом (см. главу 18), как это показано на рис. 20.4. Для этого необходимо создать объект градиента (в нашем случае это будет линейный градиент) и при помощи метода оогадхепС:: зеССоХогАС () осуществить пере- ход цвета, например, от красного к зеленому и от зеленого к синему: ОЬхпеагСгадхепС дгас1хепС(0, 0, 500, 0); дгадхепС.зеССоХогАС(0, ОС::гед); дгасИепС.зеССоХогАС(0.5, ОС::дгееп); дгайхепС.зеССоХогАС(1, ОС: :ЬХие);
Полученный градиент необходимо передать в конструктор для создания объ- екта пера ОРеп. Затем надо установить в объекте ОРахпьег перо вызовом ме- тода зесреп()и шрифта методом зеегопсо, после чего можно отобразить текст на экране с помощью метода йга^техе (): ОРахпЁег рахпЬег (Шз) ,- рахп^ег.зеСРеп(2Реп(дгас11епЪ, 0)) ; рахпЬег. зе^РопЬ (ОРопё ("Типез", 50, ОРопС: :Ыогта1) ) ; ра 1пСег. дгатл/ТехС (60, 60, "ОгасИепС ТехЪ") ; Резюме Шрифты используются для вывода текста на контексте рисования. Они за- даются высотой, шириной и названием (семейством). ОРопъ является основным классом шрифта. При помощи класса огопЫпГо можно получить информацию о семействе шрифта. Красс оропсоасавазе предоставляет информацию о шрифтах, установленных в системе. Класс ОРопьмеегхсз дает информацию о целом ряде характеристик шрифта, например: высоте, ширине букв и др. Класс ОРахпсег содержит метод для установки шрифта, а также ряд методов, позволяющих отображать текст различным образом. Также текст может быть заполнен гоалиентом.
Глава 21 айь° -УР Графическое представление "А вот и тропинка. Она приведет меня прямо наверх... Но как она кружит! Прямо штопор, а не тропинка”. Льюис Кэрролл. "Алиса в Зазеркалье" Пря программировании графики мы зачастую имеем дело с целой массой объектов, двигающихся и перекрывающих друг друга. Все они должны быть отображены в режиме реального времени без побочных эффектов. И это представляет собой нелегкую задачу для разработчика. Графическое пред- ставление— это инструмент для управления и взаимодействия с большим количеством элементов двумерных изображений, включая их визуальное увеличение/уменыпение и поворот. Кроме того, оно берет на себя также и обнаружение столкновений (соШзюп ёе1ес1юп). Классы графического представления, подобно классам <2ТаЫеИ1адее, ОТгееИхадеЪ И ОЫзЫ-НадеЕ (см. главу 11), являют собой элементный подход, опирающийся на концеп- цию модель-представление (Моде1-У1е\у) (см. главу 12). Графическое представление базируется на трех понятиях: сцена (ОСгарЫсзЗсепе), представление (оСгарЫсзУхеот) И элемент (оОгарЫсзТЬетл) (рис. 21.1 и 21.3). Взаимодействие классов, вкратце, можно описать следующим образом: класс ОСгарЫсзЗсепе является моделью для графических элементов, которые реа- лизуются унаследованными от 0<згарЫсз11:ет классами, а класс <2бгарЫсзУ1е« — это представление, которое унаследовано от класса ОАЬзыасьзсгоИАгеа. Представления используются для показа элементов модели (объекта класса осгарЫезЗеепе), и с одной моделью может быть связано сразу несколько виджетов представления (рис. 21.2).
|ОУУ!с19е1уу^^< ОРа!п1Реу1се Рис. 21.1. Иерархия классов графического представления ООЫес! I -• > - МОДЕЛЬ - СЦЕНА Рис. 21.2. Взаимодействие представлений со сценой Сцена Класс сцены (осгарЫезЗеепе) является классом для управления элементами без их отображения. Как только какой-либо элемент сцены подвергся изме- нениям, объект класса осгарЫсзЗсепе запоминает его состояние. Перед тем как сообщить о необходимости перерисовки, вся область разделяется на по- добласти и производится анализ того, в какой из них были произведены из- менения. Эта операция производится очень быстро и способна выполняться в режиме реального времени для миллионов элементов. Высылкой сигнала сЬапдеао объект сцены сообщает представлениям о необходимости отобра- жения измененного содержимого, после чего представления отображают найденные области. Объект сцены (осгарЫезЗеепе) является контейнером, содержащим в себе объекты, которые произведены от классов, наследующих оогарЫсзтьеш
(см. рис. 21.3). Эти объекты являются данными без графического представ- ления. Элементы добавляются в сцену при помощи метода ОбгарЫсзЗсепе: : абсИЪет (). Для добавления элементов в сцену можно воспользоваться методами аскЗЕШрзе (), аскХЫпе (), а<Зс1РаеИ (), адйРххшар (),' аддРоХудоп (), а<МКесе () и ааатехъ (), которые неявно создадут соответствующий элемент, добавят его и вернут его указатель. Для того чтобы получить указатели на все элементы сцены, можно восполь- зоваться методом оогарЫсзсепе: :1сешз(). Если вас интересует один эле- мент с определенными координатами, то получить указатель на него можно с помощью метода оогарЫсБсепе: :1гетА1:о, который возвращает самый верхний элемент, находящийся на заданных координатах. Для создания объекта сцены в конструктор класса оогарЫсзЗсепе нужно передать объект прямоугольной области с вещественными параметрами окесър. Координаты области могут содержать и отрицательные значения. Представление Класс осгарЫезУхеш является виджетом, предназначенным для визуализа- ции содержимого сцены (оогарЫсзЗсепе). Подобное отделение данных от их графического представления позволяет отображать одну и ту же сцену (ОСгарЫсззсепе) в различных виджетах оогарМсзУхе». Благодаря тому, что класс осгарЫезУхезд унаследован от ОАЬзъгассзсгоИАгеа, при отображении появляются полосы прокрутки в случаях, когда пространства для показа сце- ны не достаточно. Все элементы, хранящиеся в оогарЫсзЗсепе, автоматиче- ски отображаются в окне представления. В окне представления мы не рисуем изображение, а просто отображаем элементы модели и управляем ими. Вид- жеты класса оогарЫсзуХе» связаны с моделью и получают уведомления вся- кий раз, когда им необходимо обновить свое изображение. В связи с этим отпадает необходимость заботиться о перерисовке изображения содержимого представления, так как оно происходит автоматически. Для того чтобы отцентрировать представление относительно определенной точки, можно вызвать метод 0СгарЫсзУ1е«:: сепсегОп (), передав в него ко- ординаты этой точки. Более того, одно из самых примечательных свойств, которое получил класс ОСгарЫсзУхе» в наследство от класса ОАЬзЪгасьзсгоИАгеа, это то, что в ка- честве области просмотра (У1е\урогС) можно использовать любой виджет. Это позволяет заменять ошхадеъ на ооьихадес (см. главу 23) и дает возможность выбора наиболее подходящего варианта для визуализации в процессе работы
программы. Установка виджета области просмотра производится вызо- вом метода 0АЪзегас^5сго11Агеа:: зе^Ухе^Роге (). Например, для того что- бы изменить область просмотра на виджет, поддерживающий ОрепОЬ, нуж- но сделать следующее: рУ1е\л7->зеЪУ1е\гфог11 (пето ОСШгйдеЬ) ; Использование матрицы трансформации, устанавливаемой методом зеимасг1х(), позволяет увеличивать, уменьшать и поворачивать отображае- мую в представлении сцену. Элемент Класс осгарЫезтёеш является основным для элементов (см. рис. 21.3). Это класс предоставляет поддержку для событий мыши и клавиатуры, перетаски- вания (дга^ & дгор), группировки элементов и определения столкновений (соШыоп йегесНоп). Также возможны следующие операции над элементами: □ установка местоположения методом зесгоз (); 9 скрытие и показ — методы мае () и зЬо« (); □ установка доступного/недоступного состояния с помощью метода зе^ЕпаЫе (; О трансформации — методы гоЪаЪе (), зсаХе (), ЫапзХаЬе (), зЬеаг () и зеЁМаЪгхх(); □ перерисовка — рахпьо. Этот класс не позволяет создавать объекты, так как он является абстрактным. Для создания объектов нужно воспользоваться готовыми унаследованными от него классами (рис. 21.3) или создать свой класс, унаследовав его от ОСгарЫсзТЬет И реализовав В нем методы раХпС () И ЬоипсНпдКесС (). Классы, унаследованные от ОАЬзСгасесгарЫсззкарехеет, представляют со- бой различные геометрические фигуры: эллипс (осгарЫезЕШрзеШет), по- лигон (<2СгарЫсзРо1удоп1еет), прямоугольник (оСгарЪХсзКесЫЬет) И текст (0СгарЫсзз1тр1еТехС1Еет). Если нужно создать класс для поддержки какой- либо другой формы, то, в большинстве случаев, лучше унаследовать именно рАЬз Ьгас ЬСгарЫсзЗИаре! Ъет. Наверняка вы уже заметили, что в диаграмме на рис. 21.3 присутствуют два класса для текстовых элементов. Это оогарМеззЫрХетехстьет и ОбгарЫсзтехысет. Класс оогарЫсззХтрХетехыъет предназначен для бы- строго отображения простого текста при малом расходе памяти. Если же вам потребуется отобразить форматированный текст, то для этого нужно будет
воспользоваться классом оогарЫсзТехътЁет, который обладает массой воз- можностей, позволяющих управлять текстовым документом, вплоть до его редактирования. ОА^1га^гарЬ^свЗЬаре11ёт| ~ГоёгарБ^8Е1ир8е1ит77^7^ ООЦасГ т-.7,,4 | аСгарЫсв11етАп1та{1оп ~1 —{ 0Сгар|Мс8Ра1ЬЦе1Г1 г-д.г .-,-Л ^Огар1м9аКесЩ«тт^^77| |ОСгарЫс8Ро|удопИет ( | аСгарЫсвИетСгоир ./Л [ 0.6гарЫс81апеИет ^222^5222^^5^53 |абгарЬ1с»ТахЩет .-1Г# Рис. 21.3. Иерархия классов элементов графического представления Рис. 21.4. Отображение элементов, положение котооых можно изменять пои помоши мыши
К готовым классам элементов также относятся линии (сгарЫегЫпетъеш), растровые изображения (оогарЫсзР1хтар1сет), векторная графика (ОСгарЫсзЗудТЪет) И анимация (оСгарЫсзАпхтаСхоп). При ПОМОЩИ класса ОСгарЫсзхьетОгоир можно объединять элементы в группы. На рис. 21.4 показано размещение четырех элементов на сцене. г,._ #1пс1ис1е <0СОи1> 1пС та1п(±пС агдс, скаг** агду) { ОАррЫсаСхоп арр(агдс, агду) ; ОСгарЫсзЗсепе зсепе (ОНесСР(-100, -100, 300, 300)}; ОСгарЫсзУхею У1ею(&зсепе); ООгарЫсзКесСТСет* рКесСТСет = пезд 0СгарЫсзКесС1Сет(0, &зсепе); рКесС1Сет->зеСРеп(ОРеп(ОС: :Ыаск)) ; рКесС1Сет->зеСВгизк(0ВгизЪ(0С: :дгееп)) ; рКесС1Сет->зеСВесС(0ВесСР(-30, -30, 120, 80)); рКесСТСет->зеСГ1адз (ОСгарЫсзХСет:: ТСетТзМоуаЫе); ОСгарЫсзРххтарТСет* рР1хтар!Сет = зсепе. асИРххтар (ОРххтар (”Иаиз. эрд")); рР1хтар1Сет->зеСР1адз (ОбгарЫ сз! Сет:: ТСетТзМоуаЫе); ОСгарЫсзТехСХЪет* рТехСТСет = зсепе.асИТехС ("Моуе из VIСИ уоиг хпоизе"); рТехС1Сел->зеСГ1адз (ОСгарЫсзТСет: :1Сеш1зМоуаЫе); ОСгарЫсзЫпеТСега* рЫпеТСеш = зсепе.аскаЫпе(ОЫпеР(-10, -10, -80, -80), ОРеп(ОС::гед, 2)); рЫпе!Сет->зеСР1адз (0СгарЫсз1Сет:: ТСетТзМоуаЫе); ухем.зкомО ; геСит арр. ехес ();
В листинге 21.1 создаются объект приложения арр и объекты классов ОСгарЫсзЗсепе и 0ОгарЫсзУ1е«. Виджет ухе» при создании получает адрес объекта зсепе, но, в качестве альтернативы, можно воспользоваться методом ОСгарЫсзУхеад: :зеЬЗсепе(). Объекту элемента (указатель ркесысет) при создании передается ссылка на объект класса осэгарЫсззсепе, что приводит к добавлению элемента в сцену. Аналогичный результат дал бы вызов метода осгарЫезЗеепе: гааатъето. Вызов метода зеьРепО устанавливает черный цвет пера для контур- ной линии элемента. Кисть, предназначенная для заливки фона элемента, получает зеленый цвет с помощью метода зеьвгизио. Метод аеькессо задает расположение и размеры прямоугольной области. Вызовами методов ООгарЫсаЗсепе:: аааР1хшар (), ОСгарЫсзЗсепе: :ааатехС {) И ООгарЫсззсепе: :аааЫпе() в сцену добавляются элементы растрового изо- бражения, текста и линии. Для того чтобы все добавленные элементы можно было перемещать мышью, эта возможность активизируется передачей в метод зеьпадз () значения ООгарЫсзТЬет: : ТЪетТзМсгуеаЫе. В завершение, вызовом метода зЪоадО сцена отображается в представлении и ее элементы становятся видимыми на экране. Элементы, подобно виджетам, могут содержать другие элементы, что позво- ляет осуществлять их группировку. Расположение элементов-потомков вы- полняется относительно предка, например: фЗгарЫсзЫпеТЬет* рЫпеТСет = зсепе.асИЬхпе (0ЫпеЕ(-10, -10, -80, -80), 0₽еп(0Ь: :гед, 2)); ООгарЫсзТехЫЬет* рТехЫЬега = зсепе.а<±ЯТехЬ( "СЫ1д") ; рТехЫЬет->зе(:РагепЫ1:ет(рЬ1пе1Ьет) ; Каждый элемент имеет свою собственную локальную систему координат, которая может использоваться для выполнения геометрических преобразова- ний, то есть быть подвержена трансформации, например: ОСгарЫсзТехЫЬет* рТехЫЬет = зсепе.адгЗТехЬГ'ЗИеаг") ; рТехС1Ьет->зЪеаг(-0.5, 0.0); Если предок подвергнется трансформации, то вместе с ним будут трансфор- мированы и его потомки. Класс оогарЫсзтеет предоставляет возможность определения столкновений элементов. Это задача выполняется с помощью методов оогарЫсз:: зЪаре () и оогарЫсзтьеш:: со1ИйенИ1еъ (). Если вы создаете свой класс элемента, то для определения столкновений необходимо будет перезаписать метод ООгарЫсзтсеш: :зЬаре(). Этот метод должен возвращать форму элемента
в локальных координатах. Благодаря этому класс оогарЫсзтьет будет само- стоятельно распознавать столкновения. События События модели могут передаваться отдельным элементам, находящимся в моде- ли. Класс ОСгарЫсзУхем является наследником класса 0АЬзСгас1:8сго11Агеа, поэтому можно переопределить любой из методов обработки событий этого класса и воспользоваться им. Можно, но это не совсем удобный подход. Пред- положим, что пользователь нажал мышью на один из элементов сцены. Для того чтобы выяснить, что это за элемент, нужно получить указатель на объект сцены вызовом метода оогарЫсзУхем: :зсепе(), затем определить, какие элементы сцены находятся на координатах указателя мыши, и выбрать из них самый верхний. И если элемент должен как-то отреагировать на это событие, то нужно будет вызвать соответствующий метод. Согласитесь, это не совсем удобно. Самая интересная возможность при обработке событий — это их обработка из самих элементов. Внутренне это работает следующим образом: представ- ление получает события мыши и клавиатуры, затем оно переводит их в собы- тия для сцены, изменяя координаты в соответствии с координатами сцены, и передает событие нужному элементу. Если элемент получает событие мыши, то сцена сама позаботится, чтобы координаты были приведены к локальным координатам элемента. И все это происходит без вашего участия. На рис. 21.5 показана иерархия событий, которые способен получать элемент. ”Т^гарЫсзЗсём^пНх^М^ййЁуёпЛ ^ОбгарЫс^^п^гадБгорЁуёпй^^ 10СгарЫсз5сепеНа1рЕуап1 ««1 |О6гарЫса8с!епеМоиаеЕ*епи4^ 5^Сгарк1м8сёпё^е1ЁуёпГ~~~^ Рис. 21.5. Иерархия классов событий графического представления
Элементы могут обрабатывать события клавиатуры, мыши, а также события, воз- никающие при попадании указателя мыши в их область ('^^^арИ^сзноVе^ЕVепс) И при вызове контекстного меню (^(3^арй^сзСопСеxI:МепиЕVепС). Существует возможность создания и обработки событий перетаскива- ния (<1га§ & дгор). Элементы могут как разрешать, так и запрещать поддерж- ку перетаскивания, вызывая метод зеЬАссерсогорз (). Для управления принятием сбрасываемых объектов необходимо переопределить методы бгадЕпСегЕиепС (), дгадМоуеЕуепС (), <^^ад^еаVеЕVепС () И «ЗгорЕуепС (). Для тогоч чтобы начать перетаскивание из элемента, нужно создать объект класса опгад, передав ему виджет представления, из которого происходит перетаскивание. Элементы- могут отображаться в нескольких представлени- ях, но перетаскивание происходит только из какого-то одного. Для того что- бы получить указатель виджета этого представления, нужно вызвать метод ОбгарЫезЗеепеЕуепс: :и1<3деь(). Для реализации перетаскивания из элемен- тов нужно переопределить методы обработки событий мыши. Теперь подведем итог пройденного материала и создадим программу, окно которой показано на рис. 21.6. В ней используются: □ собственный класс представления; □ собственный класс элемента; □ обработка событий; □ группировка элементов. Рис. 21.6. Графическое представление, отображающее два элемента сцены
#деС1пе _МуУ1ею_Ъ._ #1пс1ис1е <06гарЫсзУ1е^> // ===== = = = =:====^=======;=~==:=гг===== = =================================== с1азз МуУхею: риЬНс ОСгарЫсзУхею { О_ОВОЕСТ риЬНс: МуХЛьеМОСгарМсзЗсепе* рЗсепе, ОШс1деС* рюд1: = О) : ОСгарЫсзУхею (рЗсепе, рмдЪ) { } риЬНс з1о^з: ^1(1 з1оЪ2оот1п () { зса1е(1.1, 1.1); } \ло1с1 з1оЪ2оот0ие () { зса1е(1 / 1.1, 1 / 1.1); } тоги! 51оеКоСаСеЬе^ () { гоЬа^е(-5); ) з1оСКоеаСеК1дИе() { гоГаЪе{5); } }; #еп<И / / _Му71ем_И__
В листинге 21.2 приведен собственный класс представления Му71ем, который наследуется от класса 0бгарЫсзУ1е«. Класс муУ1е» предоставляет слоты для уменьшения, увеличения и поворота, которые мы соединим с сигналами со- ответствующих кнопок (см. листинг 21.4). Листинг : ....— И;3. Файл у\' _| 1». и.Я.»— ~ г с1азз 51тр1е1Ьеш : риЬИс ООгарЫсзХ^ет { ргллгаСе: епит {пРепШсЯЬЪ. - 3 }; риЬИс: V^^^:иа1 ОНес^Р ЬоипсИпдКесС () сопзГ { ОРохпГР рероз зЛхоп (-10 - пРепЮ1с1е11, -10 - пРепШсЯек) ; ОЗхгеР з1ге(20 + пРепЮ1сМ1 * 2, 20 + пРепМгЗЬИ * 2) ; ге^игп 0КесЪР(рЪРоз1Ыоп, зхге) ; } V^^1:иа1 рахпЪ (ОРахпЬег* ррахп^ег, сопзЬ СЗЪу1еОр1:1опСгар111сз1еет*, ОШддеЪ* ) { рра^пее^->заVе(); рра1пЬег->зеЁРеп(0Реп((}1:: :Ыие, пРепМхсИгИ)) ; ррахпЬег-хЗга^ЕШрзе (-10, -10, 20, 20); рра1П&ег->гезСоге(); } V^^^:иа1 тоизёРгеззЕХлепИ (ОСгарЫсзЗсепёМоизеЕХгепЪ* ре) { ОАррИса^хоп: : зе^ОлеггхдеСигзог (ОП: : РохпЁхпдНапйСигзог) ; ОСгарЫсз I Ъет: : тоизеРгеззЕХгеп!: (ре) ; } V^^^иа1 тоизеЯе1еазеЕХгепЬ (ОСгарЫсзЗсепеМоизеЕ^лепС* ре) { ОАррИса^хоп: :гезЬогеОгегг1деСигзог() ;
ОСгарЫсзГС-ет: -.пюизеКеХеазеЕуегЛ (ре) ; } }; Класс ЗхтрХеХсет, приведенный в листинге 21.3, является реализацией соб- ственного элемента. Метод Ьоипахпдкесс() необходим представлению для определения невидимых элементов и неперекрытых областей, которые должны быть нарисованы. В нашем примере этот метод возвращает прямо- угольную область, в которую вписывается окружность, с учетом толщины линии (репшасъ). Метод рахпс () отвечает за отображение элемента. В нашем примере это ок- ружность, которая рисуется синим пером толщиной 3 пиксела. Так как мы изменяем настройки объекта ОРахпьег с помощью метода зесреп (), то его состояние предварительно сохраняется методом зауе (), а в конце рисования восстанавливается в первоначальное состояние методом гезсоге(). При нажатии на кнопку мыши вызывается метод тоизеРгезз^епъ (), кото- рый изменяет указатель мыши на изображение, сигнализирующее о том, что элемент может быть перемещен, а затем передает указатель на объект собы- тия в метод шоизеРгеззЕуепс () унаследованного класса. Метод шоизекеХеазе^епс () вызывается сразу после отпускания кнопки мыши. Он восстанавливает исходное изображение курсора мыши и передает объект события дальше на обработку методу шоизеНе1езе^епс () унаследо- ванного класса. хпС тахпСхпС агдс, сЪаг** а^дV) { □АррИсаСхоп арр(агдс, агду) ; ОМхддеС аддС; ОСгарЫсзЗсепе зсепе (фНесСГ (-100, -100, 640, 480)); МуУ1ею* рУхею = лею МуУхею(&зсепе) ; ОРизИВиМюп* рспкЯгоотХп = лею ОРизЪВиССол("&2оот 1п"); (ЭРизИВиССоп* рстйгоотОиС = лею ОРизЪВиССоп (• 2&оот ОиС"); ОРизИВиССол* рспкЗКоСаСеЬе^С = лею ОРизИВиССоп (”&ЯоСаСе ЬеСсв); «ЭРизИВиССоп* рспкЗКоСаСеКхдИС = лею рРизЪВиССоп( "Ко&СаСе КхдИС"); рУхею->зеСКелдегНхлС (ОРахпСег::АлСхаИазхпд, Сгие); 13 Зак. 3983
51лпр1е1Ъет* р51лр1е1Иет = нею 51хпр1е1Иет; зсепе.абд!Сет(рЗипрХеХЪет) ; р31гпр1е11:ет->зеЁРоз (0, О) ; р31гпр1еХеет->зе&Е1адз (ОСгарЫсзХГет:: ХПетХзМсуиаЫе) ; ОСггарЫсзРХхтарХеет* рР1хтарХеет = зсепе. аск1Р1хтар (ОРххтар ("Ъаиз2. зрд")); рР1хтар1ееп1->зееРагепС1Пеп1(р511пр1е1еет) ; рР1хтар11:ет->зе1:Р1адз (ОСгарЫсзХЪет: :1ЪетТзМслгаЫе) ; ООЬз есЪ:: соппесЪ (рстй2оот1п, ЗЮЫАЬ (с!хскесЯ ()), рУ1еиг, 5ЬОТ (з1опгоат1п ()) ); ООЬзесП:: соппесГ (рапйгоотОи!:, ЗХОЫАЬ {с!1ске<1 ()), . рУхею, ЗЬ0Т(з1оС2ост0и1:()) ); ООЬз есЪ:: соппесе (рспкЗНоСаСеЬе^П, ЗХОЫАЬ (сИскеб ()), рУхею, 8БОТ (з1оЁКо!:аГеЬе^1: ()) ); ООЬз есЪ:: соппесЪ (рстсЗКоЪаееКхдЬЪ, ЗХОЫАЬ (с!хскесЯ ()), рУхе^, ЗШГ (зХоЪНоеа^еКхдЬС ()) ); //ЬауоиГ зе^ир ОУВохЬауоиЪ* руЬхЬауоиЪ пеот ОУВохЬауоиЪ; рVЬx^ауои^->ад<3^^с^деЁ (рУ±ею) ; руЬхЪауои1:->адс1ДО1ддей (рстйгоатХп) ; р^ЬхЪауои^~>а<Зс1М1с1де1: (рстЗгоотОиЬ) ; рVЬx^ауоиЪ->аа<ЗЮ^<^де^: (рспкЗКоПаЪеЪе^) ; рVЬx^ауоиI:->ас^сЗМ^адеЪ (рсгпЖо^а&еКХдИе) ; юдй. зеСЬауоиГ (р^хЬауои^) аддЬ.зЬою(); геЪигп арр.ехес(); В функции шахпо, приведенной в листинге21.4, создаются объекты сцены (зсепе), представления (указатель рУ1е»), элементы (указатель рзхтрхетьем
и рР1хтар1Еет) и кнопки, предназначенные для поворота (указатели рсшакоеаьеьеге и рсшакогаъекхдъе), увеличения и уменьшения (указатели рстдИоошТп И рстд2оот0ис). Вызов метода зеъпепаегнхпс() из объекта представления устанавливает в нем режим сглаживания, что необходимо для более мягкого отображения контуров элементов (на растровые изображения это не распространяется). Элемент определенного нами класса з1тр1е!Ьет добавляется в сцену при по- мощи метода оогарЫсззсепе: гааатъешо, а метод зееРозО устанавливает его положение на сцене. Мы даем возможность изменения местоположения элемента на сцене, для чего в метод зеьПадзО передается значение ОСгарЫсзХЪет: :Т^етТзМсгуаЫе. Созданному элементу растрового изображения (указатель рРххтартсет) с по- мощью метода зесрагепсо присваивается предок, которым является эле- мент, произведенный от созданного нами класса 31тр1е1еет. Вызов метода зеепадзо разрешает перемещение элемента при помощи мыши. Это мы сделали умышленно, для того, чтобы продемонстрировать взаимосвязь груп- пировки элементов отношением предок-потомок. Итак, обратите внимание, что когда мы перемещаем растровое изображение, то перемещается только оно, но если мы попытаемся переместить элемент окружности, то растровое изображение будет перемещаться вместе с ним, так как оно является его по- томком. В завершение, кнопки соединяются с соответствующими слотами созданного нами класса Муухеш, а элементы лри помощи вертикальной компоновки ОУВохЬауоис размещаются на поверхности виджета »дс. Виджеты в графическом представлении Класс оогарЫсзЗсепе предоставляет возможность размещения не только графических объектов, но и виджетов, причем благодаря механизму событий помещенные в качестве элементов виджеты не теряют своей функционально- сти и реагируют на действия пользователя, как обычные виджеты. Однако самое интересное свойство состоит в том, что, как только виджет становится элементом сцены, с ним можно выполнять те же геометрические преобразо- вания, что и обычными графическими элементами, а так же использовать ме- ханизм для определения столкновений. Это открывает большой простор для фантазии — ведь вы сможете теперь реализовывать пользовательские интер- фейсы очень необычного вида. Продемонстрируем эту возможность на кон- кретном примере и разместим в сцене три виджета (см. рис. 21.7).
Рис. 21.7. Виджеты в графическом представлении #1пс1ис1е <0«Зи1> 1пС та1п(1п1: агдс, сЪаг** агду) ( 0АррИсаЁ1оп арр(агдс, агду) ; ООгарЫсзЗсепе зсепе (ОКесЪР (0, 0, 400, 400)) ; ОСгарЫсзУхею у!ею(&зсепе) ; ОРизЪВиСЪоп спк! (”Ои111”); ОСггарЬхсзРгоху^хддеЪ* рргохуШс1де1: = зсепе. аддЮхддеС (&спкЯ); ОТгапз^опп ЪгапзЬэгт = рргохуУЛддеЁ->Ъгапз^опп() ; Ъгапз^охтп. ^гапз1аСе (100, 350);
Ъгапз1огтп.гоЪаее(-45, ОГ: :УАх1з); ЪгапзСопл.зса1е(8, 2); рргоху^16де1:->зеСТгапз1опп(1:гапзСопп) ; ООЬз есЪ:: соппесЪ (&ап6, ЗЮЫАЬ (с1 хскеб ()) , &арр, ЗЬОТ (ди11: ())) ; □Оха! 61а; 61а. зеШэ^сЪезУхзхЫе (егие); рргохуШбдеС = зсепе. абс&ЯбдеЪ (&сИа); Ьгапз&эпп = рргохуГО1ддее->Ъгапз^ог1п() ; Ьгапз^огтп.зса1е(4, 2); егапз^опп.гоеаее(-45/ ОЬ::УАх1з); рргохуШбдее- >зеЪТгапз Согт (егапз 1опп); □РгодгеззВаг ргЬ; ргЬ.зееРххедЗхге(500, 40); рргохуШбдеС = зсепе. аббМтбдее (&ргЪ) ; Ъгапз^огт = рргохуШ6дее->Ьгапз^огт(); егапз^огт.егапз1аее(20, 200); егап5Согт.зса1е(2, 2); егапз^огт.гоЪаее(80, ОЪ::УАххз); егапз^опп.гоеаее(30, ОГ::ХАх1з); рргохуМ16дее~>зееТгапз1опп (Ъгапз1охт); ООЬэ есе:: соппесе (&Ша, ЗТОЫАЬ (Vа1иеС^1апде<^ (хпЪ)), &ргЬ, 51ЮТ(зеГУа1ие(1пе)) ); V^еV.^оеаее(15); V^е^. зЪом() ; гееигп арр.ехес(); } В начале программы (листинг 21.5) мы создаем объект сцены (зсепе) и вид- жет представления (VI е«). Затем создаем виджет кнопки (^иИ и добавляем ее при помощи метода сцены ааакШдес (), который возвращает объект элемен- та сцены осгарИ1сзРгохун1адеь. С этим элементом можно производить гео-
метрические преобразования, для чего мы используем специальный класс дтгапзЕогтп. Этот класс предоставляет методы сдвига Ьгапз1асе(), поворота гоеаееО и масштабирования зса1е(). Обратите внимание на вызов метода гоьасе (): в нем помимо угла поворота мы также указываем и ось, вокруг ко- торой поворот нужно осуществить, в примере это ось V (ос: :уах1з). Геомет- рическое преобразование применятся к элементу (указатель рргохушадес) вызовом метода зеетгапзСогт(). Аналогично мы поступаем с виджетами 0в1а1 и ОРгодгеззВаг. Затем мы производим сигнально-слотовые соединения виджетов, так, например, кнопку (^ш1 мы связываем со слотом приложения ди1С(), а сигнал Vа1иеС11апдеа() виджета 0О1а1— СО СЛОТОМ 5ееУа1ие() виджета ОРгодгеззВаг. В завершение мы производим трансформацию сцены и поворачиваем ее на 15 градусов по оси 2. Обратите внимание на тот факт, что виджеты, расположенные в графическом представлении, полностью со- храняют свои функциональные возможности. Резюме Применение технологии графического представления идеально подходит для приложений, в которых должно содержаться большое количество графиче- ских элементов, которыми управляет пользователь. Классы осгарЫеззеепе, ОСгарЫсзУхе», а также оогарЫсзхьет и унаследованные от него, пред- ставляют собой очень мощное средство для работы с двумерной графикой. Взаимодействие этих классов друг с другом базируется на шаблоне "модель- представление". Это позволяет показывать один и тот же объект класса ОСгарЫсзБсепе в нескольких разных виджетах представления осгарЫсзУ1е«. Класс осгарЫезЗеепе можно представить как плоскость, на которой можно размещать элементы. Хотя того же эффекта можно добиться и с виджетами, так как они тоже обладают способностью отображать своих потомков, работа с графическим представлением позволяет более эффективно использовать процессор и память компьютера. Классы элементов, унаследованные от оогарЫсзхсеш, служат для представ- ления различных геометрических форм, а также текста, растровых, вектор- ных и анимированных изображений. Элементы способны получать и обраба- тывать события. Для создания своих собственных классов элементов нужне унаследовать этот класс. Элементы (осгарЫезхсет) должны помещаться В объект класса ООгарЫсзВсепе.
Глава 22 Анимация "Зри в корень". Козьма Прутков Понятие анимации происходит из ранних лет кино. Само слово переводится с латыни как "оживление неподвижных предметов". Принцип анимации — тот же самый, что используется в играх с "движущимися" картинками. Если бистро отображать одно изображение за другим, то создается иллюзия дви- жения. Обычные мультипликационные фильмы тоже состоят из целой серии рисованных картинок, в которых последовательно и незначительно изменя- ются позиции объектов относительно друг друга. Класс ОМоу/'е Для создания анимации можно воспользоваться классом ОРххтар, показывая изображения одно за другим. Но лучше воспользоваться уже готовым клас- сом смелее, который выполнит эту работу за вас. Объекты класса хранят в себе анимацию и могут возвращать отдельные изображения в объектах класса 0Р1хтар или оттаде. Этот класс поддерживает форматы МИО и ОП7. Если же вы ищете возможность воспроизведения видеофайлов, то, скорее всего, глава 28, описывающая модуль РЬопоп, и есть то, что вам нужно. В конструктор класса омслг1е передается имя анимационного файла. Проиг- рывание анимации начинается сразу после создания объекта. Также в этом классе определены конструктор копирования и оператор присвоения. На проигрывание анимации можно влиять следующими слотами: зеЬРаизеб(ЬооХ), зеЬЗрее6(), зЬор() И зЪагЪ(). Передача в метод зесРаизеДО значения сгие приостанавливает проигрыва- ние анимации, а Га1зе возобновляет проигрывание. В обоих случаях произво- дится высылка сигнала зьасесъапдеД!) со статусом состояния проигрывания.
Вызов метода дмоу1е:: в гагъ () запускает воспроизведение анимации, а зьор () его останавливает. Информацию о статусе проигрывания можно получить при помощи метода зьаье (), который возвращает одно из трех значений : □ омоу^е:: Раизеб сообщает, что проигрывание было приостановлено; □ дмоухе: :Кипп1пд означает, что анимация находится в состоянии проигры- вания; □ ОМспЯе: :МоКипп1пд сигнализирует о завершении при проигрывании ани- мации. Для того чтобы узнать количество кадров анимационного файла, нужно вы- звать ЕгашеСоипе (). Если необходимо получить растровое изображение акту- ального кадра, ТО нужно вызвать метод сиггепЬРххтар () ИЛИ сиггепЫтаде (), которые возвращают ссылки на объекты дрХхшар или охтаде. Самый простой способ показа анимации — это использование класса дьаЬех. Класс оьаЬе1 содержит метод зеемоухе (), с помощью которого можно уста- навливать объекты анимации. Пример, показанный на рис. 22.1, иллюстриру- ет эту возможность. Рис. 22.1. Показ анимации #1пс1ибе <0€<Зи1> 1пС та1п(1пЪ агдс, сЬаг** агдлг)
{ ОАррИсаЪхоп арр (агдсг агду); 0ЬаЬе1 1Ы ; ОМоухе тегу ("тоСхоп.тпд") ; 1Ы.^еI^МоV^е(&тоV) ; 1Ы.гез1ае(328, 270); 1Ы. зИою () ; тоу.з^агП(); ге^игп арр.ехес(); } Как видно из листинга 22.1, в программе создаются всего 3 объекта: прило- жение (арр), надпись (1Ы) и анимационный объект (тшуу), причем последний инициализируется файлом тойоп.тп§. Вызовом метода зесмстео анима- ционный объект устанавливается в виджете надписи. Метод гезхгеО уста- навливает размеры окна, то есть виджета надписи. После показа надписи (метод зЪою ()) вызовом метода зеаги () запускается анимация. ЗУС-графика 8УО— это формат масштабируемой векторной графики (8са1аЫе УесСог ОгарЫсз). Он был рекомендован в 2001 году Консорциумом Всемирной пау- тины 5УЗС (\Уог1<1 \У|с1е \УеЬ Сопзогбшп). Этот формат описывает двумерную векторную графику и анимацию в формате ХМЬ, и, следовательно, содержи- мое файлов можно изменять в обычном текстовом редакторе. На настоящий момент этот формат получил большое распространение и поддерживается почти всеми УУеЬ-браузерами. для поддержки этого формата предоставляет отдельный модуль <}(8у§. А это значит, что в проектных фалах (.рго) нужно не забывать прикреплять этот модуль, для чего достаточно просто добавить строку от += ^Vд. Класс для показа 8 УС-фай лов называется с^дИхадеь. Загрузить 8УО-файл можно, либо передав его в конструктор озуди1адеъ, либо воспользовавшись его ме- тодом 1оаа (). Метод 1оаа () примечателен тем, что в него можно передавать не только путь к файлу, но и объекты класса овуъеАггау. Само изображение создается вспомогательным классом ^^VдКепае^е^, который используется неявно, и если вы не отображаете анимацию самостоятельно, то о его суще- ствовании вы скорее всего даже и не вспомните. Класс озудкепаегег можно также использовать для помещения созданных им изображений в объекты
отшаде и овИлПддег. Показанный на рис. 22.2 пример иллюстрирует всю про- стоту использования класса озудихадее. #1пс1ис1е <0«Эи1> #1пс1ис1е <0С8уд> 1пе тахпЦпЬ агдс, сИаг **агду) { ОАррИсаЪхоп арр(агдс, агду); <2&удЮ1<Зде11 зуд (": /тоЪхоп. зуд"); зуд.зЪоюО ; ООЬд есЪ:: соппесЬ (зуд. гепбегег (), 81(ЗЫАЬ (герахп^Ыеедес! ()), &зуд, ЗЬОТ(герахпЁ())1 ); геСигп арр.ехес();
В листинге 22.2 мы создаем объект.класса озуди1бдее и передаем в его кон- структор 8УО-файл тоСюп.8У§, который находится в ресурсах. Затем мы просто вызываем метод зИо«(), чтобы отобразить сам виджет. Соединение сигнала гера1пЫ1еедес1 () объекта класса ОЗудКепдегег. СО СЛОТОМ гера±пс () класса озудИ1адес нужно для того, чтобы после создания каждого нового изображения оно отображалось виджетом озуди^адес. Резюме Анимация получила широкое распространение сравнительно недавно. Ани- мационные файлы состоят из некоторого количества неподвижных изобра- жений, которые при последовательном отображении с определенной скоро- стью создают иллюзию движения. Основным классом для простой анимации является класс омоухе, который обладает рядом методов для управления анимацией. Проще всего установить объект анимации в виджете надписи с помощью метода зесмоуГе (). Для отображения векторной графики или анимации в формате 8УС сущест- вует модуль а центральным классом для ее показа является виджет ^ЗVд^^ддеЁ.
Глава 23 Работа с ОрепОЬ "Реальность воображаема, а вообра- жаемое — реально". В. Соло Трехмерная графика, несомненно, одна из самых захватывающих тем в про- граммировании, которая существует с ранних стадий развития компьютерной техники. На протяжении продолжительного времени она применялась ис- ключительно в рамках правительственных проектов и в проектах разработки симуляторов полета. С недавнего времени трехмерная графика вышла за рамки научной деятельности и превратилась в целую индустрию, включаю- щую в себя такие сферы, как анимация (от спецэффектов до компьютерных игр), кино, виртуальная реальность, медицина и-многое другое. С каждым годом все больше людей задействованы в этой области. ОрепОЬ — это стандарт для двумерной и трехмерной графики, впервые вве- денный 8Шсоп ОгарЫсз в 1992 году. Это уже устоявшийся стандарт, и все вносимые в него изменения делаются с учетом гарантий нормальной работы ранее написанного кода. Сама же библиотека может быть создана кем угод- но, главное, чтобы она отвечала спецификации, установленной стандартом. С точки зрения программиста, библиотека ОрепОЬ представляет собой мно- жество команд для создания объектов и проведения сложных операций — от сглаживания (Ап11-АПаып§) до наложения текстур. Для ее использования достаточно усвоить несколько простых правил, и это даст возможность реа- лизации замечательных программ. Хотя ОрепОЬ и является платформонезависимой, но все равно, для использо- вания ОрепОЬ-программ на разных платформах потребуется провести ряд преобразований кода программы для осуществления привязки контекста воспроизведения (гепс!епп§ соШех!) с оконной системой платформы. Исполь- зование ОрепОЬ "в оправе" 01 освобождает, разработчиков от каких-либо из- менений текста исходного кода, что обеспечивает для ОрепОЪ-программ
полную платформонезависимость. Модуль ОЮрепСЬ позволяет использо- вать потенциал возможностей ОрепОЬ в 01-приложениях. Благодаря проду- манности системы рисования АгЙшг, все операции ОРатпсег (см. главу 1,8) также могут быть применены и для ОрепОЬ. Но самое большое преимущест- во использования ОрепОЬ состоит в возможности работы с трехмерной гра- фикой. Трехмерная графика — это истинная мощь библиотеки ОрепОЬ. Основные положения ОрепОЬ Библиотека ОрепОЬ не является объектно-ориентированной. При работе с библиотекой разработчик имеет дело только с функциями, переменными и константами. Имена всех функций'ОрепОЬ начинаются с букв д1, а кон- стант — с сь_. В имена функций входят суффиксы, говорящие о количестве и типе передаваемых параметров. Например, прототип функции д1Со1огЗЕ () говорит о том, что в нее должны передаваться три значения с плавающей точкой (рис. 23.1). Поэтому при описании функций в ОрепОЬ, чтобы не по- вторяться, принято вместо числа передаваемых аргументов и их типа ставить символ *. Итак, общий вид для выше упомянутой функции будет выглядеть следующим образом д1со1ог* (). При этом подразумевается, что речь идет не об одной функции, а о целой серии функций, начинающихся с д1со1ог. Тип аргумента Число аргументов . Команда Библиотека ОрепСЬ Рис. 23.1. Формат команд ОрепОЬ В табл. 23.1 указаны символы суффиксов и типы, используемые в ОрепОЬ. Таблица 23.1. Суффиксы и типы ОрепОЬ Суффикс Тип ОрепОЬ С++ Эквивалент Описание ь (ЗЬЬуЬе сЪаг Байт $ СЬзЪогЁ зкогЬ Короткое целое I СЫпЬ. 111П Целое г С1Л1оаЪ С1оаЬ С плавающей точкой
Таблица 23.1 (окончание) Суффикс Тип ОрепСИ. С++ Эквивалент Описание а (ЗЬдоиЫе БоиЫе С плавающей точкой двойной точ- ности иЬ СЬиЪуЬе ипзЬдпед ЪуЬе Байт без знака из СЬизкогЬ изпЬдпед. зИогё Короткое целое без знака и! СЬихпЬ ипзхдпеб. хпЪ Целое без знака ОЬепиш епшп Перечисление V Массив из п параметров Суффикс V говорит о том, что функция принимает массив. Например, массив из трех значений с плавающей точкой в функцию д1Со1огЗЕу() передается следующим образом: СЬ*1оа* а[] = {1.0*, 0.0*, 0.0*} д1Со!огЗ*у(а) ; Классы СИ для работы с ОрепОЬ Все классы <21 для поддержки ОрепОЬ собраны в модуле (ЗЮрепОЪ, в кото- ром определены следующие шесть классов: □ Ось содержит некоторые константы для работы ОрепОЬ; □ осгшаде»: унаследован от класса оихадеь. Его основное назначение — осуществлять связь ОрепОЬ с виджетом. Объекты класса ооынаде* мо- гут использоваться в качестве контекста рисования для ОРахпсег (см. главу 18)', □ ОСБРогтас — класс для хранения настроек ОрепОЬ. В объектах этого класса можно устанавливать различные режимы и передавать их в объек- ты класса оиШдес. Метод осш1адеъ:: *огшае () возвращает текущий объ- ект настроек; □ осъсопеехь представляет собой контекст ОрепОЬ (набор переменных со- стояния). осъи1адес создает объект этого класса автоматически. Для по- лучения текущего контекста вызывается метод осьиШде*:: сопеехе <); □ Осьсо1оипар используется для индексирования цвета и зависит от исполь- зуемого цветового режима; □ 0СБ₽1хе1Ви**ег содержит в себе буфер изображения ОрепОЬ (рЬиПег).
Реализация ОрепСЬ-программы Чтобы воспользоваться ОрепОЬ, необходимо унаследовать класс оагжадес, который организует соединение с функциями библиотеки ОрепОЬ. В унасле- дованном от оошхадес классе необходимо, по меньшей мере, переопреде- лить три виртуальных метода: хпХЪхаИаеСЬ (), гез1ге<ЗЬ () И рахпесь (). Эти методы определены в классе оетжадее как V^^^:иа1 ргоъесъеа. Метод 1п1Ыа11ге<зь () вызывается сразу после создания объекта. Это требу- ется для проведения инициализаций, связанных с ОрепОЬ. Метод вызывает- ся, если объекту, унаследованному от класса дотжадеь, присваивается кон- текст ОрепОЬ. Назначение метода гезггесь (1пь зд^аеи, 1пе ъехдъс) схоже с назначением метода обработки события изменения размера гезхгеЕуепс (). Этот метод вызывается при изменении размеров объекта, произведенного от класса, на- следующего оогЖбдес. В параметрах метода передаются актуальные разме- ры виджета. Назначение метода рахпъсь () схоже с назначением метода обработки собы- тия рисования ра1пе&^епс (). Метод вызывается в тех случаях, когда требу- ется заново перерисовать содержимое виджета. Это происходит, например, после вызова метода гезхиесь (). На рис. 23.2 показано приложение, которое отображает четырехугольник с вершинами разного цвета, со сглаживанием. Рис. 23.2. Сглаживание цветов вершин четырехугольника
ТЕМРЪАТЕ = арр <2Т += орепд! НЕАПЕКЗ = ОСЬОиай.к 50ЦКСЕ5 = ОСЬОиаа.срр \ шахп.срр ТАКОЕТ = ../ООЬОиаа В листинге 23.1 обратите внимание на опцию дт проектного файла (см. гла- ву 3), в которой указан параметр орепд!. Это нужно для того, чтобы во время компоновки программы подсоединялся модуль (^ОрепСЬ. #1пс1иае <0АррИсаЪ1оп> #1пс1иае "ОСЗЬСиаа.к" 1пС ша1п(1п11 агдс, скаг** агду) { дАррИсаЪхоп арр(агдс, агду) ; ОСЬОиаа од10иаа,- од10иаа.гез12е(200, 200); од10иаа. зкою (); ге^игп арр.ехес(); Программа, приведенная в листинге 23.2, лишь создает объект класса ооьоиаа, унаследованный от о<зш1адеп. #1Гпае^ _ОСЬ0иаа_К_ #аеГ1пе _ОСЬдиас1_К_ #1пс1иае <0СШ1аде^> // =============================== с1азз ОСЬОиаа : риЬНс ООЕЖадеЪ {
ргоЁесЁед: V^^^:иа1 VО^с^ 1П1Ъ1а112еОЬ( ) ; ^гЁиа! тохс! гезхгебЬ (ллё пШсЯЫх, 1пС пНехдкЁ) ; V^^Ёиа1 ’7О1с1 рахпЁОЬ ( ) ; риЬИс: ОСЬОиай(рМхйдеЁ* рмдС = 0) ; }; # епсИ С / /_06ЬОиас1_11_ В листинге 23.3 класс осьоиай определяется как класс, наследующий 0СШ1йдеь, в котором переопределены три метода: 1п1с1аИгесь(), гезхгеСЪО ИрахпЪСЬС). ____1..-.^..^..:.______ ООЬОиад:: ОСЬОиад (ОИхйдеЁ * рзддЪ / * = 0 * /) : ОСЗШтйдеС (рюдЪ) { 4 } Конструктор, приведенный в листинге 23.4, не имеет реализации, он просто передает указатель на объект предка р«д(: конструктору наследуемого класса. «гр--:....................... швмй»^^ /*У1гЁиа1*/то1д ОСЬОиас!:: 1п1Ь1а1±2еОЬ() { дд1С1еазгСо1ог (2е: :Ыаск) ; } В листинге 23.5 методом дд1с1еагсо1ог() устанавливается цвет для очистки буфера изображения. 91 автоматически конвертирует значения осо1ог в цве- та ОрепОЬ, чтобы обеспечить разработчикам единообразный подход для раз- работки приложения. Примечание В ОрепбЬ цвет очистки устанавливается при помощи функции д1С1еагСо1ог (). Использование метода дд!С1еагСо1ог () вместо этой функции дает возмож- ность передавать цвета в объектах класса 0Со1ог.
/*у!гЬиа1*/то1<1 ОСЬОиад: :гез12еОЬ(1пС пИ1дЕЪ, хпС пНехдЬЬ) ( д1МаЪг!хМо<1е (<Л>_РКОДЕСТК»1) ; дХЬоасИйепЫЬу () ; д1У1еироге(0, 0, (СЬ1пС)пЮ1с1Ы1, (ОЬхпЫпНехдкЬ); дЮгОюСО, 100,' 100, 0, -1, 1) ; } В листинге 23.6 функция дтмаъгххмоде () устанавливает матрицу проектиро- вания текущей матрицей. Это означает, что все последующие преобразования будут влиять только на нее. Вызовом функции дхьоавтаепъхеу () текущая матрица устанавливается в единичную. В ОрепОЬ существуют две матрицы, применяющихся для преобразования координат. Первая — матрица модели- рования (тос1е1у1е\у та(пх) — служит для задания положения объекта и его ориентации, а вторая — матрица проектирования (рго]есбоп гпа1пх) — отве- чает за выбранный способ проектирования. Способ проектирования может быть либо ортогональным, либо перспективным. Метод геахгесь () — это самое удобное место, чтобы установить видовое окно (У1еу/роП). Видовое окно устанавливается вызовом функции д1У1е»Рог<: () и представляет собой прямоугольную область в пределах окна виджета (окно в окне). В нашем случае видовое окно совпадает со всей обла- стью виджета. Соотношение сторон видового окна задается параметрами функции дЮгедо (). Первый и второй параметры задают положения левой и правой отсекающих плоскостей, третий и четвертый— верхней и нижней отсекающих плоскостей, пятый и шестой — передней и задней отсекающих плоскостей, соответственно. / *V^^^иа1* /ОСЬОиад:: ра1п«ЗЬ () { д1С1еаг(СЬ_СОЬОК_ВЦЕРЕК_ВХТ | СЬ_ОЕРТН__ВЦЕКЕВ_В1Т) ; д1Вед1п(СЬ_0ПАОЗ) ; д1Со!огЗШ, 0, 0); д1Х7егЪех21(0, 100); д1Со1огЗ€(0, 1, 0);
д17егС.ех2{ (100, 100); д1Со1огЗГ(0, 0, 1); д1УегСех2^(100, 0); д1Со1огЗС(1, 1,1); д1УегЪех2 {. (0, 0); д1Еп<1 () ; } Перед формированием нового изображения нужно функцией д!С1еаг () очи- стить буферы изображения (сь_соьок_впррек_в1т) и глубины (<зь_пертн_ вотрек_В1т). Последний служит для удаления невидимых поверхностей. Для очистки буфера изображения будет использован цвет, установленный мето- дом дд1Со1ог(). Единица информации ОрепОЬ — вершина. Из вершин строятся сложные объ- екты, при создании которых нужно указать, каким образом они должны быть соединены друг с другом. Способом соединения вершин управляет функция д1вед1п(). В нашем примере флаг сь_оцабз говорит о том, что на создаваемых вершинах должен быть построен четырехугольник (листинг 23.7). При помощи функции д1со1ог* () задается текущий цвет, который распространяется на по- следующие вызовы функции д1Уег<:ех* (), задающих расположение вершин. Функции задания вершин должны всегда находиться между д1Вед1п() и д1Еп<1(). В нашем примере каждая вершина имеет свой цвет, и, поскольку по умолчанию в ОрепОЬ включен режим сглаживания цветов, это приводит к соз- данию радужной окраски области прямоугольника. Режимом сглаживания цве- тов управляет функция д1зйааемоае1 (). Ее вызов с флагом сь_рьат отключает режим сглаживания, а передача флага сь_змоотн включает его. Разворачивание Ореп(л1_-программ во весь экран Так как класс ооьиШде!: унаследован от оиШдее, он обладает всеми свойст- вами, присущими этому классу. Развернуть программу на полный экран очень просто — для этого нужно заменить вызов метода з1ю« () на вызов ме- тода зЬоадГиИЗсгееп (). Это приведет к тому, что виджет верхнего уровня перекроет своим окном все остальные и займет весь экран. Это очень удобно, так как можно отлаживать программу в маленьком окне, а когда она будет готова, просто поменять зЬоы () на зЬошРиИЗсгееп (). Производительность ОрепСЬ-программы в полноэкранном режиме полно- стью зависит от возможностей видеокаоты.
Графические примитивы ОрепСЬ ОрепОЬ предоставляет средства для рисования графических примитивов, таких как точки, линии, ломаные и полигоны, которые задаются одной или несколькими вершинами. Для этого необходимо передать список вершин. Программа, показанная на рис. 23.3, отображает различные фигуры, постро- енные на одних и тех же вершинах. Рис. 23.3. Отображение фигур, построенных на одних и тех же вершинах #1пс1ис1е <0АррИсаЪ1оп> #1пс1ис1е "ОСЫЭгаю.Ъ" хпС тпа1п(1пС агдс, сЪаг** а^дV) { ОАррИсаЫоп арр(агдс, а^дV) ; ОСЬОгаю одЮгам; одЮгау/.гезхге (400, 200); одШгам. зЪою () ; гееигп арр. ехес ();
В листинге 23.8 в основной программе создается виджет одШгам класса ОСЬОгаю. #1^пдеС _0<ЗЬВга\л7_И_ Ме^хпе _ОСЫЭга^_И_ #1пс1ийе <0ОШ1<1деЪ> с1азз ОСЫ>га™ : риЬНс ООЬЖадеП { ргоСесееб: V ^^^иа1 хгоха хпхЫаИгеОЬ ( ) ; V ^^(:иа1 тоха гезхгеСЬ (хпЪ пШаъИ, хп11 пНехдкС) ; V ^^Сиа1 то ха рахпЪСЬ ( ) ; риЬНс: ОСЬОга^ОДОхбдее* рэдде = 0); тоха с1гауг(1пе хОГ^зе!:, хпё уО^зеЪ, ОЬепиш Ьуре) ; }; #епй1 / /__0СЫ)га^_]1_ Класс осызгаю наследуется от класса осзшхадеъ и перезаписывает три его виртуальных метода: хпхехаИгесь (), гезхзеоь () и рахпгсь () (листинг 23.9). В классе определен метод агам(), получающий координаты х и у, с которых нужно начинать строить фигуру. Тип фигуры задается третьим параметром. т;..... СХЗЬОгате: :СХЯЛ)гаю(0Ю1адеС* р«д^/*= 0*/) : ООЬШадеЪ(рмдЪ) { } В листинге 23.10 приведен конструктор класса ооьпгаэд, который не выпол- няет никаких действий, а просто передает указатель на объект-предок в кон- структор класса осшхадеъ.
/^1гЕиа1*/то±<1 ООЫ>гам:: 1п1Ь1аИге61> () { дд1С1еагСо1ог (ОЬ: :«ЫСе) ; } В листинге 23.11 в методе гпгМаИзевьо устанавливается белый цвет для очистки буфера изображения. .л;;ДЙ / *Ёиа! * /то1с1 ОСЫЭгам:: гез 1 аеСЬ ( хпё пШдЪИ, 1пЪ пНехдЪС) ( д1Маег1хМоае{6Ь_РКа7ЕСТ10Ы) ; дТЬоагЯМепеаХу (}; д1У1е*фог1: (0, 0, (СЫпе) пШсНЛ., ((ЗЫпЛ;) пНехдЪЪ) ; , дЮгЫю(О, 400, 200, 0, ~Х, 1); } В листинге 23.12 действия метода гезххеоь () аналогичны действиям листин- га 23.6. /^1г(:иа1*Лго1.с1 ОСЫЭгам: :ра1пЫЗЬ() < д!С1еаг (ОЬ_СО1ХЖ_ВЦРЕЕЕ_В1 Т | <ЗЩЭЕРТН._ВЦБГЕВ_В1Т) ; дга«(0, 0, ОЬ_РОЮТЗ); дга«(100, 0, СЬ^ЫИЕЗ); (Зга»(2ОО, 0, СЬ_ЫМЕ_ЗТЙ1Р) ; <3гаы (300,0, СЬ_ЫЫЕ_ЬООР) ; <Зга«(0, 100, СЬ_ТВ1А1*ЗЬЕ_ЗТН1Р) ; дгазд(100, 100, <ЗЬ_РОЬУООЫ); дгам(200, 100, СЬ_0ОАОЗ); йгам(300, 100, ОЬ_ТК1А№ЬЕЗ) ;
В листинге23.13 приводится метод рахпеаьо, в котором после очистки буферов изображения и глубины производится серия вызовов мето- да йга«() (см. листинг 23.14). В этот метод передаются координаты х и у, с которых будет начинаться рисование фигуры. Тип фигуры передается третьим параметром. Тип еь_ротытз говорит о том, что отображаться должны только точки. При построении фигуры типа оь_ыыез каждая пара вершин задает отрезки, которые, как правило, не соединяются друг с дру- гом. Тип сь_ыые_зтк1Р задает ломаную линию и используется, в основ- ном, для аппроксимации кривых. Если требуется получить замкнутый кон- тур, то нужно указать одну и ту же вершину в начале и в конце серии вершин. Тип оь_ыые_ьоор также задает ломаную линию, но последняя ее точка соединяется с первой. Тип сь_тк1мкзье_зтн1р задает треугольники с общей стороной. Каждая вершина, начиная с третьей, комбинируется с двумя предыдущими и определяет очередную ячейку. Тип сь_роьусод задает многоугольник. При построении фигуры типа оь_опалз каждые че- тыре вершины задают четырехугольник. При построении фигуры типа сь_тн1А№ЬЕЗ каждые три вершины задают треугольник. ........................................................ IЛ истинг 23Л 42 Файл ОСЬОга^.срр. Метод Атак О > тоМ ООЫЭгаю: :дгам(1п11 хО^зеЬ, хпё уО^^зеЬ, СЪепит Ёуре) { 1ПС п = 8; д1Ро1пЪ312е(2); д1Вед!п(Суре); д1Со1огЗЕ(0, 0, 0); Ьэг (1пЪ 1=0; 1 < п; ++1) { С1оа1: ^Апд1е = 2 * 3.14 * 1 / п; хпе х - (1ПС) (50 + соз(САпд1е) * 40 + хО^зей) ; 1пе у = (1ПП) (50 + з1п(1Апд1е) * 40 + уО^зеЬ); д1\7егПех2С(х, у); } дХЕпсЦ); В листинге 23.14 приведен метод йга^о, который осуществляет построение фигуры заданного типа пуре с позиции, определяемой переданными коорди- натами. Для задания размеров точки служит функция д1Ро1пез1ге(). В на- шем примере ее размер устанавливается равным 2. Функция д1Со1ог*() устанавливает черный цвет для вершин. Вызов функции дХУегиех* () задает расположение вершин.
Трехмерная графика На рис. 23.4 показан результат работы программы, отображающей пирамиду в трехмерном пространстве, поворот которой относительно осей X и У осу- ществляется при помощи мыши. Рис. 23.4. Пирамида ...... । Листинг23Л5.Файдта!п.срр1- #1пс1и<1е <0АррИсаЪ1оп> #1пс1ис1е "ОСЬРугатМ.ТГ1 1пХ та1п(1ПГ: агдс, сИаг** а^дV) { «ЭАррИсаЫоп арр(агдс, а^дV) ; ОСЬРугатлЛ од1Ругат1д; од1Ругат1с1.гез12е(200, 200); од1Ругат1сЗ.. з1том () ; геЪигп арр. ехес () ; ) В основной программе (листинг 23.15) создается ОрепбЬ-виджет класса ОСгЬРугатхс!.
..................._..,.....,.„™^—......................— ............................................... #1Епс1еЕ _0СЬРугат1с1_]п_ Ме^1пе _0СЬРугаш16_И_ #1пс1ис1е <0СШ1с1де1:> // ====================================================================== с1азз ОСЬРугапйд : риЫхс ООЬМтбдеЬ {. рггуаЁе: ОЬихпЁ т_пРугат1с1; ОЬ^ХоаЬ т_хКоЪаЁе; . СЬЕ1оа1: т_уКоЪаЪе; (ЗРохпЪ т_рЪРо31С10п; ргоСесЁей: V^^^иа1 тоха хпхйхаИгеОЪ ( ); V^^1:иа1 то 1а гезхгеОЬ {хпЪ пМхдЁЪ, хпЬ пНехдЫ:); V^^^:иа1 то ха рахп^СЬ ( ); V^^^:иа1 уоха тоизеРгеззЕтопЪ (рМоизеЕ^лепС* ре ); V^^^:иа1 тоха тоиз еМсллеЕХлепЪ (ОМоизеЕуепЪ* ре ); СЬихпЪ сгеаСеРугапид (СЬ^1оаЪ ЕЗхге = 1.0Е ); риЫ1с: ОСЬРугат1а(0Ю1адеЕ* раддЁ = 0); }; #еп<Н Е / / _СХЗЪРугат1д_к_ В классе осьругатха (листинг 23.16) определена переменная т_пругат1а, ко- торая хранит номер дисплейного списка объекта пирамиды. Примечание Дисплейные списки позволяют выделить конкретный набор команд, запомнить его и вызывать всякий раз, когда в нем возникает необходимость. Этот меха- низм очень похож на вызов обычных функций или на механизм записи графи- ческих команд, предоставляемый классом ОРхсСиге. Для запуска команд дис- плейного списка необходимо знать присвоенный ему уникальный номер. Переменные т_хноъаъе, т_уНоъаъе нужны для хранения углов поворота по осям X и V. Переменная т_р!1Роз1е1оп хранит координату указателя мыши в момент нажатия. Кроме трех методов, унаследованных от оошхадес, пере-
определяются методы Обработки события МЫШИ тоизеРгезз^епЬ{) и тоизеМс^еЕ^епе () для осуществления поворота пирамиды. Метод сгеасеРугапйд () создает дисплейный список д ля отображения объекта пирамиды. ........................................................ 1Листинг23.17.ФайлООкРугат1с1.срр.Конструктор.оаьруга1п1а ; < ОСЬРугапйД:: ОСЬРугапад (<2М1с1деЪ * рюдЪ / * - 0 * /) : ОСШтйдеЬ (рюдЪ) , пикКоЪа^е (0) , 1П_уКоЪа1:е (0) { } Задача кЬнструктора класса осьругапйд состоит в инициализации перемен- ных-членов для поворота и передаче указателя на виджет предка конструкто- ру наследуемого класса осьмхадеъ (листинг 23.17). .......................................................... Листинг 23Л8. Файл РЭиРугат1с1.срр. М$ТРД 1л1Нж11«евь О . ....... «т.'^.аа^'.^.»..^.ааЕ^.^|'>Г^аа.»>.'1г>.<.<..а;а.а..а.'>..,.аи.а.а.',^........а........ Л. /^1г*:иа1*/^о1с1 ОСЬРугатЫ:: хпх^хаИгеОЬ () { дд1С1еагСо1ог (ОС: :Ыаск); д1ЕпаЫе(ОЬ_ПЕРТН_ТЕ5Т) ; д15кааеМоае1 (СЩРЬАТ) ; т_пРугапш! = сгеаЪеРугапшЗ (1.2 С); При инициализации с помощью метода дд1С1еагСо1ог () устанавливается мер-- ный цвет очистки буфера изображения (листинг 23.18). Функция д1ЕпаЫе() устанавливает режим разрешения проверки глубины фрагментов. Режим сгла- живания цветов по умолчанию разрешен, поэтому его необходимо отключить, передав в функцию дТЗЪааемоае () флаг <зь_втдт, иначе боковые грани пира- миды будут иметь радужную окраску. Вызов метода сгеаееРугаш1а() (см. лис- тинг 23.23) создает дисплейный список для пирамиды и возвращает его номер, который присваивается переменной т_пРугапйа. Параметр, передаваемый в этот метод, задает размеры самой пирамиды. ............................................... ^Листинг23.1,9^файлракРугатМ.срр.МетодгеяХгеоьр .. , > ' ма'а^А^^аакакак'м.На,а1а.1.ам1мш.«а^а.<«>11а.............................................................................................................. /^1геиа1*/^о1д. ОСЬРугапй<1: :гез12еСЬ(1пй п&Лскзк, хпё пНегдИе) { д1\71еюрогЪ (0, 0, (СЫпЪ) , (вЫпЪ) пНегдЫ:);
д1МаЫ ххМоде (СЬ_РКЛТЕСТ10Ы) ; д!Ьоас11<9епЫ<:у() ,- дХРги8Ъиш(-1.0, 1.0, -1.0, 1.0, 1.0, 10.0); В листинге 23.19 функция дХУХе«Роге() устанавливает размеры видового окна равными размерам окна виджета. Функция дХмасгХхмойеО делает те- кущей матрицу проектирования. Вызов функции дНюаатаепЫеуО присваи- вает матрице проектирования единичную матрицу. Функция дХРгизешпО задает так называемую пирамиду видимости. Параметры задают положения левой, правой, верхней, нижней, передней и задней отсекающих плоскостей. Последние два значения должны быть положительными и отсчитываться от центра проецирования.вдоль оси 2; по ним устанавливается значение пер- спективы. /*у1гЪиа1*/то1с1 СЮЬРугатМ: :ра1пЪСЬ() { д1С1еаг(СЬ_СОЬОК_ВЦГГЕЯ_В1Т | СЬ_ПЕРТН_ВЦЕРЕК_В1Т); д1МаЁГ1хМоае(С;ь^ЮВЕЬУ1ЕМ) ; дХЬоасМеп^дЛу () ; д1Тгапз1а1:е^ (0.0, 0.0, -3.0); , дХКоЪаЪеС(пихКоЪаЪе, 1.0, 0.0, 0.0); дХКоЪаЪеС(щ_уНоЪа1:е, 0.0, 1.0, 0.0); д1Са11Ыз1: (т_пРугат!<1) ; } При рисовании после очистки буфера изображения текущей устанавливается матрица моделирования, служащая для задания положения объекта и его ориентации (листинг 23.20). Функция дХЬоаатаепЫхуО присваивает матри- це моделирования единичную матрицу. Функция дХТгапзХаье() сдвигает начало системы координат по оси 2 на 3 единицы. Функция дХкоьаье () по- ворачивает систему координат вокруг осей X и У на угол, задаваемый пере- менными т_хкоьасе и т_увоъаее. Передача в функцию дХСаХХЫзс () номера дисплейного списка пирамиды отобразит ее.
г..,,II ,^11. 1.11 । и 11 1 1 1 14 /^1г<1иа1*/\го1(1 СХЗЬРугатхд: :тоизеР^е5^ЕVеп^: (^МоизеЕVепЪ* ре) { пир^Розл-Ъхоп = ре->роз () ; } При нажатии пользователем кнопки мыши переменной т__р11Роз1Ыоп при- сваиваются координаты указателя мыши (листинг 23.21), ОСЬРугатхд: :тоизёМсэтлеЕуеп11 (ОМоизеЕуепС* ре) { т_хНоЪаЬе += 180’* (аь^1оаЪ) (ре->у() - пиР^Роз^-^^оп.у()) / ИехдкеО; т_уНоЪаее += 180 * (ОЬЕ1оа1:) (ре->х() - т_рЪРоз11:1оп.х()) / ш1с№1(); ирдаЬеСЬ(); Щ-рЪРоззЛхоп = ре->роз () ; } В методе обработки события перемещения мыши (листинг 23.22) вычисля- ются углы поворота для осей X и У. Вызов метода ирдапесьо обновляет изображение на экране, используя новые углы поворота. Переменной т-рерозхШоп присваивается актуальная координата указателя мыши. ОЬи1п1: ООЬРугатгск :сгеа!:еРугат1с1(ОЬС1оаГ: ^312е/*=1.0С*/) { ОГли-пЛ п * дЮепЬхзПз (1) ; д1Ые^ЫзС(п, СЬ_СОМР1ЬЕ); д1Вед1п((ЗЬ-ТК1АЫСЬЕ_РАЫ) ; дд1Со1ог (ОЪ: : дгееп) ; дГУегЪехЗС(0.0, Е51ге, 0.0); д1Уег!:ехЗС (-^Зхге, -^31ге, С31ге); дГУегСехЗС(ГЗхге, -€31гег Г31ге); дд1Со1ог:уе11ою); д1Уег€;ехЗ^(±312ег -ГЗхге, -€31ге); дд1Со1ог (0Ъ: :Ыие); дГУегЪехЗ^С-ГЗхге, -ГЗхге, -131ге); дд1Со1ог (фЪ: : мЫ Ъе) ; д1УегСехЗС(-Е312е, -ЕЗхге, ^81ге);
дХЕпдО ; д1Вед1п(СЬ_оиАОЗ); дд1Со!ог(ОЬ::гед); дГУегкехЗС(-131ге, -СЗхге, €31ге); д1УегСехЗГ(Е512е, -ЕЗххе, СЗхге); дГУегЬехЗЕ(СЗхге, -СЗхге, -ЕЗхге) ; дТУегЪехЗС(-СЗхге, -СЗхге, -ГЗхге); д1Епс! () ; д1Еп<ЗЬ1з(: () ; геЬигп п; } Метод сгеаьеРугашхд () создает дисплейный список для отображения пи- рамиды и возвращает его номер (листинг 23.23). Функция дЮепЫзсзО возвращает первый свободный номер для идентификации дисплейного спи- ска. Этот номер передается в функцию дХыемЫзьо. Второй параметр, оъ_сомр1ье, говорит о том, что команды нужно лишь запомнить. Все коман- ды, находящиеся между функциями д1ыемЫзъ () и дТЕпдЫгс (), помещаются в соответствующий дисплейный список. Тип сь_тк1аы<зье_гам задает тре- угольники с общей вершиной, которая идет первой в списке. Следующие две вершины задают треугольник. Затем каждая последующая вершина, совме- стно с предыдущей, задает следующий треугольник. А для типа фигуры сь_0идоз каждые четыре вершины задают четырехугольник. Резюме ОрепОЬ прост в изучении и давно используется в качестве стандартного АР1. Это уже устоявшийся стандарт, действующий на протяжении 17 лет. Библио- тека 01 предоставляет модуль для поддержки ОрепОЬ. Для 01-программ с использованием ОрепОЬ следует перезаписать три метода в унаследован- НОМ ОТ ООЬОТхддеЪ классе: , гезггеСЬ () И раьп^СЬ (). Преимущество ОрепОЬ заключается в возможности работы с трехмерной графикой. Единица информации ОрепОЬ — вершина. Перечисляя вершины, можно создавать довольно сложные объекты. Для того чтобы запустить программу в полноэкранном режиме, нужно заме- нить в основной программе вызов метода зЬо«() на метод зЪошЕиНЗсгееп ().
Глава 24 Вывод на печать "Начать пользоваться новым методом на практике легче, чем выверить его; причем чем точнее метод, тем с большей осторожностью его надо использовать". Р. Ф. Бейле В большинстве случаев приложения должны предоставлять пользователю возможность вывода на печать. Это обстоятельство может испугать многих разработчиков, и причиной тому является ряд проблем: различные возмож- ности принтеров, разница в отображении шрифтов на экране и принтере, платформозависимые различия в программировании принтеров и т. д. 01 бе- рет на себя решение большинства задач, существенно облегчая задачу разра- ботчиков. Класс ОРНп1ег Класс ОРгхпсег является основным для вывода на печать. Благодаря тому, что класс унаследован от ^Ра^пь^еV^се, вывод на печать аналогичен выводу на экран. Для вывода на принтер могут применяться те же методы класса ОРахпъег, что и для всех остальных контекстов рисования. Класс принтера ОРгхпсег обладает бблыпим числом настроек, чем все ос- тальные классы, унаследованные от класса ОРахптеуХсе. Можно, например, установить размер листа, количество копий и т. д. (^С предоставляет диалого- вое окно печати, в котором пользователь сам производит необходимые на- стройки. Это окно реализовано в классе 0Ргхпи>ха1од (см. главу 33). Также можно производить эти настройки и программно: □ при помощи метода 8еьог1епьаЫоп () можно задать ориентацию страни- цы, передав флаг ОРгхпъег::Рогегахс для горизонтального или 0Рг1псег: :Ьапй8саре для вертикального расположения страницы;
□ метод зеылтсорхез () устанавливает количество выводимых на печать копий; О с помощью метода зесргошто () можно задать диапазон страниц для печати; □ метод зеьсоТогмоде () управляет цветным и черно-белым режимами печа- ти. Для цветного режима нужно передать в метод флаг орг1пъег: :со1ог, а ДЛЯ черно-белого — ОРгхпЪег: :Сгаузса1е; □ вызовом метода зесраде51хе () изменяется размер листа. В метод нужно передать одно из значений, перечисленных в табл. 24.1. Таблица 24.1. Перечисления Радез1ге класса 0Рг1п(:ег Константа Размер (мм) Константа Размер (мм) Константа Размер (мм) АО 841x1189 ВО 1030x1456 В10 32x45 А1 594x841 В1 728x1030 С5Е 163x229 А2 420x594 В2 515x728 СоттЮЕ 105x241 АЗ 297x420 ВЗ 364x515 ПЬЕ 110x220 А4 210x297 В4 257x364 ЕхесиСа^е 191x254 А5 148x210 В5 182x257 РоИо 210x330 Аб 105x148 Вб 128x182 ЬесЯдег 432x279 А7 74x105 В7 91x128 Ьеда1 216x356 А8 52x74 В8 64x91 ЬеЪЪег 216x279 А9 37x52 В9 45x64 ТаЫохс! 279x432 Вместо печати на принтер можно перенаправить вывод в файл. Для этого не- обходимо передать полное имя файла методу зееоиериер11еыате(). Если в метод передать пустую строку, то перенаправление вывода в файл будет проигнорировано, и вывод будет произведен на печатающее устройство. Имя печатаемого файла можно установить методом ееыэосМате (); отметим, что это не имя файла, в который перенаправлена печать, а имя задания на печать. Кроме перенаправления печати в файл, который содержит команды принте- ра, можно генерировать файлы в формате РОР (РопаЫе Оосшпеп! Рогта1, платформонезависимый формат электронных документов), и это так же про- сто, как и вывод на печать: нужно передать в метод зесоиериъРогшае () зна- чение ОРгхпЪег: : Рй^ГогтаЁ. Класс ОРггпЬег содержит метод зеСРопЬЕтаеаа1пдЕпаЫеа{) для внедрения шрифтов в документ-носитель, для того чтобы быть уверенным, что полу-
ценный файл содержит все необходимые шрифты. Это полезно в том случае, если вы хотите использовать файлы не только на той платформе, на которой они были созданы. Чтобы отменить операцию печати, нужно вызвать метод аЬогьо, который вернет значение булевого типа, сигнализирующее о результате выполнения операции отмены. Пример, показанный на рис. 24.1, иллюстрирует вывод на печать. При нажа- тии кнопки Ргш1 (Печать) откроется диалоговое окно настроек принтера, и после подтверждения будет осуществлен вывод картинки на печать. Рис. 24.1. Вывод на печать #1пс1иде <ОЁби±> #1пс1и<1е "РгхпЪег .И" 1пе та1п(1пЪ агдс, скаг** а^дV) { ОАррИсаЁхоп арр(агдс, а^дV) ; ОШддеЪ утдЬ ; РгхпЪег* рргхпЪег = пею Рг1пЪег; ОРизИВиЪЪоп* рспк! = пеш ОРизкВиСЁоп("&РГ1ПЁ") ; ООЬзесЬ: : соппесС (рспк1, ЗКЭМАЬ (с11скед ()),
рргхпЪег, 8ЬОТ (зТоЪРгый: ()) ); //ЬауоиЬ зеГхир ОУВохЬауоиЬ* рVЬx^ауоие = пеиг 0\7ВохЬауои1х ; р^хЬауоиЪ->5еШагд1п (0) ; рVЪx^ауоиС->зе^8рас^пд(0); руЪхЬауоиЁ~>асИИ1с1де1: (рргхпЬег) ; р^хЬауоиЪ->а<ИЮ1с1де1: (рспкЯ) ; юдЬ. зеСЬауоиЬ (руЬхЪауоШх); зддЁ.гез12е.(250, 320); ^д^.зкоздО ; геЪигп арр.ехес(); } В программе, приведенной в листинге 24.1, создаются виджеты принтера (указатель рргхпсег на объект класса Ргхпъег, описанного в листингах 24.2—24.6) и кнопки (указатель рстй). Сигнал сНскесЦ) виджета кнопки соединяется со слотом з1оърг1пь () виджета принтера. _Рг1п1хег_11_ МеСхпе _Рг1пСег_И_ #1пс1ис1е «2Ю13деС> с1азз ОРгхпСег; с1азз ОРахлЫЗетхсе; с1азз РгхпЁег : риЬНс ОИхсЗдеЬ { 0_ОВОЕСТ рго^а^е: ОРгхпЪег* щ_ррг1пСег; ргоЪесПеск 14 Зак. 3983
удхЪиа! рахпИБХлеп^ (ОРахпЬЕуепЬ* ре ) ; тоже! дгаю (0Ра1пиЭеу1се* ррд); г риЬИс: РгтпСег (ОМЫдеЪ* раддЁ = 0) ; V^^^иа1 -Ргхп^ег(); риЬИс 310113: уо1с! з1о1:Рг1пС () ; }; #епс11 С / /_Рг 1пЪег_11_ Определение класса ргхпъег, приведенное в листинге 24.2, содержит указа- тель на объект принтера ОРгхпсег. Метод агаад о получает указатель на кон- текст рисования. В классе определен деструктор, в котором освобождается динамически выделяемая память для объекта принтера. Слот з1оьрг1пс() вызывается для выполнения вывода на печатающее устройство. рЛисуй //---------------------------------------------------------------------------- РгхпСег: :Рг1пеег(0М1адеЪ* раддЪ/*=О*/) : ОМхЗдеП (ридЬ) { т_ррг1пСег = пею <ЭРг1пЪег; } //------------------------------------------------------------------------- /* VIIСиа1*/РгхпСег::-РгхпЬег() { деХеЪе псрргхпЪег; } Как видно из листинга 24.3, в конструкторе динамически создается объект принтера — указатель щ_ррг±псег. Этот объект необходимо, по завершении работы программы, удалить, и лучше всего сделать это в деструкторе. /^1гЁиа1*/ тоге! РгхпСег: :ра1пЪЕуепе(ОРахпЪЕу’епЬ* ре) { дгаад^Ыз) :
В методе обработки события перерисовки, приведенном в листинге 24.4, вы- зывается метод йгаю ()ив него, в качестве контекста рисования, передается указатель еыз. При создании диалогового окна (переменная а1д) в его конструктор пере- даются указатели на объект принтера (ш_ррг1пеег) и на виджет предка (лис- тинг 24.5). Второй параметр нужен для того, чтобы диалоговое окно было отцентрировано относительно основного окна. той! РгхпЪег::з1о^Рг1пП() { 0Рг1пЫ)1а1од <Пд(щ_ррг±пСег, Й11з) ; с11д. зеШхпМах (1, 1) ; (<51д.ехес() == фС1а1од: :Ассер^еЗ) { Згаю (т_ррг1пЬег) ; } Методом зеим^пМахО устанавливается диапазон страниц, которые можно печатать. В нашем случае это всего одна страница, поэтому этот диапазон устанавливается от 1 до 1. Вызов метода ехес () откроет диалоговое окно, в котором пользователь мо- жет выбрать принтер и настроить опции печати. При нажатии на кнопку ОК метод возвращает оп1а1од: :Ассерсеа. После этого объект 0Рг1псег полно- стью готов к использованию и его указатель т_ррг1п<:ег передается методу дгаю(). ПРИМЕЧАНИЕ После того как в диалоговом окне будут произведены все необходимые изме- нения, можно вычислить количество страниц для печати при помощи зна- чений, возвращаемых методами дАЬзЪгасСРг хпЫЯаХод:: ГоРаде () и ОАЬзСгасСРгхпеЩаТод:: СгошРаде (), отняв от первого второе и прибавив единицу. Для нашего примера это выглядит следующим образом: 1пё пРадез = (11д. ЁоРаде () - с!1д. отРаде (} + 1; Примечание Чтобы получить следующий лист для рисования и напечатать на нем, нужно вызвать метод ОРгхпЪег: :пе^Раде() и передать указатель т_ррг1пеег в ме- тод агам().
РгхпЪег: :<Зга*Н0Ра1пЪСеу1се* ррд) { ОРахпЪег рахп^ег (ррс1) ; ОКесС г(рахпЁег.V^еюро^^()); рахпСег. зеСВгизк (ОС: : *Й11 Се) ; ратпСег. (ЯгаюКесС (г) ; рахпСег. дга^Ыпе (0, 0, г. шхйСк (), г .ИехдИС ()) ; рахпСег. сЬгатеЫпе (г. теМск (), 0, 0, г. ИехдкС ()); рахпСег. зеСВгизк (ОС:: ЫоВгизк); рахпСег. зеСРеп (ОРеп (ОС:: гей, 3, ОС:: ПазЫАпе)) ; рахпСег. дгашЕИ 1рзе (г) ; рахпСег. зеСРеп (ОС: :Ыие); рахпСег.зеСРопС(ОРопС(• Типез", 20, ОРопС::Иойпа!)); рахпСег. сЬгатеТехС (г, ОС: : А1 хдпСепСег, " РгхпСег ТезС ”) ; В метод агам (), приведенный в листинге 24.6, передаются указатели на кон- текст рисования. Подавляющее большинство принтеров не могут использо- вать всю площадь листа для печати. Поэтому, чтобы избежать вывода на не- доступные для принтера места, нужно опросить прямоугольную область вывода, используя метод ора1псег: :у1е«рогь(). Исходя из полученных раз- меров, в контексте рисования с помощью методов ага^несь (), агамЫпе (), ага«Е1Ирзе () и дга^техсо отображаются прямоугольник, линии, эллипс и текст. Методами зеСРеп() и аеьвгизЫ) устанавливаются перья и кисти, имеющие различные цвета и образцы. Метод зеьгопсо устанавливает шрифт для выводимого текста. Резюме Благодаря тому, что класс ОРггпсег унаследован от класса 0Ра1пы>ет1се, вы- водить информацию на печатающее устройство так же просто, как рисовать на экране. Объекты класса принтера 0Рг1пъег могут быть настроены пользо- вателем при помощи специального диалогового окна или программно, вызо- вом целого ряда методов. Класс принтера предоставляет также возможность создания файлов в формате РОР.
Глава 25 Разработка собственных элементов управления "Карта — это не территория; имя — это не сам объект". Альфред Коржибски Создание собственных виджетов — задача несложная. Прежде всего, нужно хорошо подумать над тем, какой из виджетов классовой иерархии 01 облада- ет наибольшим количеством необходимых вам качеств, чтобы использовать его в качестве базового. Это поможет существенно сэкономить время при разработке нового виджета. Примеры создания виджетов Допустим, нам нужен виджет текстового поля, способный принимать только числа в шестнадцатеричной системе счисления. Реализация такого виджета может ограничиться наследованием класса оыпевак: и определением конст- руктора нового виджета (листинг 25.1), в котором создается и устанавливает- ся объект контроллера (уайбаСог) вызовом метода зеьуаиаасог (). V П»стмнг2БЛ, Класси с1азз НехЬхпеЕсНЬ : риЬИс дЬхпеЕс&Ь { риЬИс: НехЫпеЕсНЬ(ОИдхЭдеЬ* рзддЬ = 0) : ОЫпеЕсНЪ(ршдН { 1= ОКедЕхр гхр (" [0-9А-Га-С] +"); БееУаИдаЬогСпеэд ОКедЕхрУаИдаЬог (гхр, еЫз));
Пример, приведенный в листинге 25.1, представляет собой частный случай. Приведем список рекомендаций для общего случая, пункты которого могут быть проигнорированы, если в них нет необходимости: □ переопределите методы обработки событий, на которые должен реагиро- вать новый виджет. Это могут быть: рахпСЕуепС (), гезГгеЕуепе (), шоизе- РгеззЕуепе (), гпоизеНе1еазеЕуепЬ () И др.; □ подумайте, будет ли создаваемый класс виджета наследоваться дальше, если да, то вполне возможно, что понадобится объявить некоторые из его атрибутов не как рггуаье, а как ргосесЬей; □ подумайте и примите решение, какие сигналы будет высылать виджет; □ решите, какие слоты будутопределены в классе виджета; □ перезапишите методы зХаенхпсо и зхгеРоПсуО, для того чтобы новый виджет без проблем мог использоваться компоновками. Примечание Можно обойтись и без перезаписи метода з1геРоИсу(), вызвав метод зесзхгеРоИсу () и передав ему нужные значения из конструктора создавае- мого класса. Последний пункт списка нуждается в отдельном пояснении. Как вы уже знаете (см. главу б), классы компоновки отвечают не только за расположение виджетов, но и управляют их размерами. Виджеты гарантированно будут иметь приемлемые размеры, если при его размещении используются значе- ния, возвращаемые методами э12ен1пе () и зхгеРоЫсу (). Метод З12ен1пе() возвращает объект класса оз1ге, который информирует о том, какие размеры необходимы виджету. Это зависит, прежде всего, от со- держимого виджета. В листинге 25.2 создаются два виджета кнопок, имею- щих надписи различной длины. Вызовы метода зхгенхпсо возвращают, для каждой из созданных кнопок, разные значения. Вызов рста1->зхгенхпс() возвращает значение (75,23), а рста2->зхгеНхпс () — значение (145,23). 05Ьг1пд зСг = "ВиЪЪоп"; ОРизЪВиЬЪоп* рстд1 = пею ОРизкВи^оп (зЬг); 051ге з1ге = рстй1->312ёН1п1:(); // з!ге = (75, 23) зЬг = "ТЫз 13 Vе^у 1опд ЬиЫхоп 1аЬе1"; ОРизИВиЫюп* рспк32 = пем ОРизЪВиЬЪоп(зЪг) ; зхге = рспк12->з12еН1ПЁ(); // зхге = (145, 23)
Метод ои1адеь: :з12еРо1хсу() возвращает объекты класса озхгеРоПсу, в которых содержится информация, влияющая на интерпретацию значения, возвращаемого методом з1генхпьо при изменении размеров окна. Объект создается передачей в конструктор класса озхгеРоИсу двух флагов — для вертикального и горизонтального направлений, указанных в табл. 25.1. Таблица 25.1. Перечисление зхгетуре класса <251геРоИсу Константа Описание Рххед Должно учитываться только значение, возвращаемое мето- дом зхгенхпс (), другими словами, размер виджета изме- нять нельзя М1П1тиш Виджет не должен быть меньше значения, возвращаемого ’ методом зхгеНхпъ () Маххтит Виджет не должен быть больше значения, возвращаемого методом вхзеШпЪ () РгеСеггей виджет может быть больше или меньше значения, возвра- щаемого методом з1геН1пс (), то есть виджет может как растягиваться, так и сжиматься М1пгтитЕхрапд1пд Виджет не должен быть меньше чем зтгеНхпЪ (). Класс компоновки постарается предоставлять виджету как можно больше места ЕхрапсИпд Виджет может быть больше или меньше зхгенхпь (), но класс компоновки постарается предоставлять виджету как можно больше места. Другими словами, виджет может как растягиваться, так и сжиматься, но предпочтительнее его растягивать Тдпогед Значение, возвращаемое методом зхгеНхпЪ (), не прини- мается во внимание. Однако класс компоновки постарается предоставлять виджету как можно больше места Возьмем, например, виджет класса 03сго11У1е«. Невозможно заранее опре- делить его размер. Благодаря полосам прокрутки с этим виджетом можно работать, даже если его размер будет меньше размера, возвращаемого мето- дом зххенхпсо. Поэтому значение, возвращаемое методом зхгенхпьо, должно приниматься во внимание как рекомендуемый размер. Но чем боль- ше будет размер виджета озсгоПУхем, тем удобнее будет им пользоваться. Для обеспечения подобного поведения, в конструкторе этого класса вызыва- ется метод аесзхгеРоИсу (), в который передаются два флага Ехрапйхпд для горизонтали и вертикали: 8вЬЗх2еРо1хсу(03х2еРо1хсу(08хгеРо1хсу: гЕхрапдхпд.ОЗхгеРоИсу: :Ехрапдхпд);
В листинге 25.3 приведена программа, окно которой показано на рис. 25.1. Она демонстрирует создание собственного виджета индикатора прогресса. Само приложение состоит из индикатора прогресса и полосы прокрутки. Значение, отображаемое электронным индикатором, изменяется в зависимо- сти от указателя текущего положения полосы прокрутки. Рис. 25.1. Демонстрация собственного индикатора прогресса #хпс1ис!е <2ЬОих> #хпс1иде "СизЁотИхйдеЪ.Ъ" хпЪ тахп(хпЁ. агдс, сИаг** а^дV) { ОАррИсаЁхоп арр{агдс, агду) ; ОМхддеС аддЪ; СизЁотИхйдеЬ* рею = пеу/ СизЪоШхбде^; 05сго11Ваг* рЬзЬ = пего ОЗсгоИВаг (ОЪ: хНогхгопЪа!) ; рИзЪ->зе^Капде(0, 100); ООЬэ ес!::: соппесЬ (рЪзЬ, ЗКЗЙАЬ (уа1иеС11апдед (хШ:)), рем, 5ЬОТ(з1оЪ5е!:Ргодгезз(хпС)) ) 9 //ЬауоиЪ зеЪир ОУВохЬауоиЪ* руЬхЪауоиЪ = пею ОУВохЕауои!:; руЬхЬауоиС->а(Зс1М16дее (реш); руЬхГ»ауоие->ад(Зй1хддее (ркзЬ);
«дЪ. зеЪЬауои11 (рVЬx^ауои^:) ; юдЪ. зкою () ; геЬигп арр. ехес () ; } В функции ша1п() создаются виджеты разработанного нами индикатора прогресса — указатель рс™, и полосы прокрутки — указатель рИзЬ (лис- тинг 25.3). После этого сигнал Vа1иес11апде<^(^I1е) полосы прокрутки соеди- няется со слотом зТоЕЗеергодгезз (хпъ), служащим для отображения значе- ний целого типа индикатора прогресса, при помощи метода соппесъ (). #1й1(1е€ _СизеотИ1йдеС_И_ #1пс1ис1е <ОРгате> И =========ь======„===^===============:========:=======:=:^===г:============= с1азз СизЪаггМхддеЪ : риЬЫс ОРгате ( 0_ОВОЕСТ ргоСесЬеск 1пЪ т_пРгодгезз; V^^пиа1 то1с1 рахп^^епе (ОРатпЪЕХгепе*) ; риЬНс: СизСагйМ1<ЗдеЪ(0М1сЗдее* рмд^ = 0); V^^^;иа1 051 хе зхгеНхпСО сопзС; зхдпаТз: тога ргодгеззСИапдед (1п^); риЬНс 51оЪз: То1<2 з1оЪЗе1:Ргодге88(1пЪ п) ; }; #епШ Е / /_Сиз&атМ1бдеЪ_
Определяемый в листинге 25.4 класс Сизьотихадеь содержит целочисленный атрибут щ_пРгодгезз, необходимый для хранения текущего значения индика- тора прогресса. Переопределяются методы рахпсв^епс () и зхгенхпс (). По- следний информирует компоновку о желаемом размере. В классе определя- ется сигнал ргодге8зсьапдеа(1по, который будет высылаться каждый раз при изменении значения индикатора прогресса. Слот з1осзеьргодгезз() управляет установкой значения индикатора прогресса. СизЕопМШдеС: :СизЕ«йИ1ддеЕ(0Н1ддеЕ* ридЕ/*= 0*/) : ОРгате(ршдЕ) , т_пРгодгезз(0) { зеЕЫпеИхдСЬ (3) ; зеЕРгатеЗЕу1е(Вох | Зипкеп); зеЕ81аеРо1хсу(0312еРо11су: хМйзйпит, ОЗхгеРоИсу: :Р1хес1) ; В листинге 25.5 приведен конструктор класса. Методы зеЕЫпеишьъ () и зеЕРгатезсу1е () устанавливают толщину и стиль рамки. Первый параметр 0312еРоИсу: :М1пйпит, передаваемый В метод зеЕ31геРо11су(), говорит классу компоновки о том, что ширина виджета не должна быть меньше значения, возвращаемого методом зхгенхпсо. Второй параметр озхгеРоИсу: :Р1хес1 сообщает, что высота виджета должна быть равна значению, возвращаемому методом зхгенхпс (). й / ^1г1иа1 * / лгоШ СХ131атМ1с1де1:: рахгИЕуеп! (0Ра1п1Етеп1 *) { 0₽а1п1ег ра1п!ег (1Ыз); ОЫпеагСгаЛеп! дгасЗхеп! (0, 0, юхсИк ()', ЪехдЫ ()) ; 11оа1 2 = пщРгодгезз / 100.01; дгасИеп!. зе!Со!огА1 (0, 01: : Ыаск); дгасНеп!. зе!Со1огА1 (1, 01:: дгееп); ра±п1ег, Ш1Кес1 (гес! (), 01: :Ыаск);
рахпСег.зеСВгизЪ(дгадхепС); рахпСег. (ЗгамйееС(0, 0, (1пС)(«хйСИ() * С), ЬехдИС()); рахпСег.зеСРеп(ОРеп(ОС::дгееп)); ОЗСгхпд зСг = 05Сгхпд().зеСИит(пцаРгодгезз) + рахпСег.с1гаиТехС(гесС(), <2С: :А11дпСепСег, зСг) ; дгаиРгаюе(&рахпСег); Метод рахпсЕч-епсо производит отображение индикатора прогресса (лис- тинг 25.6). Объект рахпСег инициализируется контекстом виджета. Вызов метода сшнесс () закрашивает виджет в черный цвет. Линейный градиент и ширина прямоугольной области для заливки градиентом вычисляются в соответствии с текущим значением прогресса (переменная с). По окончании рисования прямоугольной области устанавливается перо зеленого цвета. Строковой пере- менной зсг присваивается приведенное к строковому типу значение индикатора прогресса, после чего оно отображается с помощью метода бгаитехс () в центре индикатора (флаг дидпсепсех). Адрес &рахпсег передается в метод ОРгате:: бгаадРгате () ДЛЯ ТОГО, чтобы класс ОРгате СМОГ отобразить СВОЮ рамку. той! Сиз^спШддеЁ:: з1оЪ8еГРгодгезз (хпе п) { пипРгодгезз = и > 100 ? 100 : п < 0 ? 0 : п; гераа-пЪ () ; етхЬ ргодгеззСЪапдесЯСпоаРгодгезз); } Приведенный в листинге 25.7 слот з1оезе11Ргодгезз () управляет установкой значения индикатора прогресса (атрибут щ_п₽годгезз) и следит за тем, чтобы значение индикатора прогресса не выходило за пределы диапазона — от 0 до 100. После присвоения значения методом герахпс () производится перерисов- ка и высылается сигнал ргодгеззсъапдед (хпе) с его актуальным значением. /*^гЪиа1*/ ОЗххе СизЪопйИадеЬ:: зхгеНхпЪ () сопзЪ { геЪигп 031ае(200, 30);
В листинге 25.8 метод зхгеШпг: () информирует класс компоновки о размере, который желателен для виджета. Резюме Унаследовав класс ошхадеъ или его наследника, можно создавать свои эле- менты управления. Скорость реализации нового виджета во многом зависит от удачного подбора базового класса. При создании нового класса виджета важно помнить, какие методы событий необходимо перезаписать, а также какие сигналы и слоты будет предоставлять виджет. Классы, базирующиеся на онхадес, предоставляют интерфейс оповещения о размере. На вершине этого интерфейса находится класс дзхаеРоИсу, с по- мощью которого виджет может сообщить, желательно ли подвергать его уменьшению/увеличению, отдельно по вертикали и горизонтали. С помощью метода з1ген1пп() класс компоновки узнает о размерах, необходимых вид- жету, и интерпретирует их в соответствии со значением объекта озггеРоИсу.
Глава 26 Элементы со стилем "Есть многое на свете, друг Горацио, что и не снилось нашим мудрецам". Шекспир. "Гамлет" Один из немаловажных аспектов при программировании с использованием библиотеки 01 — это управление видом и поведением приложения (1оок & Гее1). Ввиду того, что (^-приложения создаются для большого числа платформ, необходим механизм для изменения их вида и поведения. Это по- зволяет добиться, чтобы его внешний вид не выбивался из колеи при запуске в какой-либо операционной системе и не создавалось впечатление того, что программа "чужая" на этой платформе. В 01 внешний вид компонентов может быть свободно изменен, и это можно использовать для того, чтобы разнообразить внешний вид приложения, если вы относитесь к группе программистов, считающих стандартный вид непод- ходящим для своих программ. Например, при написании компьютерной игры вы наверняка захотите большей дизайнерской свободы и попробуете придать элементам управления новый вид. К изменению стиля можно прибегнуть и в том случае, когда нужно добиться того, Чтобы ваши программы узнавали по внешнему виду. Конечно, можно создать классы виджетов, которые будут иметь свой собст- венный облик, отличный от стандартного. Но это неудобно, так как каждый раз придется реализовывать новые классы и изменять исходный код. 01 пре- доставляет специальные классы стилей, позволяющие изменять внешний вид и поведение для любых классов виджетов. Стили программы можно изме- нять даже в процессе ее работы, а это значит, что пользователю можно пре- доставить в меню целый список стилей, чтобы он смог выбрать оптимальный для себя. Стили можно создавать самому или использовать уже готовые, встроенные в библиотеку 01.
Возможность реализации и использования классов стилей, не зависящих от кода программы, дает большую свободу, позволяющую разделить разработку проекта, как минимум, на две команды, которые могут работать независимо друг от друга. Одна команда может работать над кодом самой программы, а другая над ее дизайном. Созданные второй командой классы или С88- файлы стилей могут использоваться и в других 01-проектах. В контексте 01, стиль — это класс, унаследованный от озьу1е и реализую- щий возможности для рисования рамок, кнопок, растровых изображений и т. д. 01 делает каждый виджет ответственным за свое отображение, что по- вышает скорость отображения и гибкость. На рис. 26.1 изображена иерархия классов стилей. Класс озьу1е является ба- зовым для всех стилей. | ОС1в,Дп1рок^у1? 1 ЙМарЩу1е^ Рис. 26.1. Иерархия классов стилей Примечание Класс 08<:у1е обладает поддержкой языков с написанием справа налево: пользователю необходимо запустить приложение, указав в командной строке опцию -геуегзе. Этот класс определяет целый ряд методов для изменения облика и поведения всех виджетов приложения. Для создания своего собственного стиля можно
переопределить эти методы. Класс озсу1е имеет только одного потомка — класс осоштопгсу1е, который является общим для всех классов стилей, опре- деляя часто используемые методы. Поэтому предпочтительнее наследовать именно этот класс, а не озъу1е. Непосредственно от класса 0Соттопзму1е наследуется целый ряд классов, называемых встроенными стилями 01. Встроенные стили По умолчанию, вид и поведение 01-приложения определяется операционной системой, на которой оно запущено. Но это можно изменить и использовать, например, в ОС ХУпк1о\У8 стиль МойГ. 01 предоставляет следующие классы встроенных СТИЛеЙ (рис. 26.1): 0И1пДоюаЗЪу1е, 0МоЫ€ЗСу1е, 0Р1азЫдиеЗЬу1е, 0СОЕЗьу1е и ос1еа1оокзьу1е . Эти стили эмулируются и доступны на любых платформах, кроме стилей ои1пао«8хрзеу1е и омасзеу1е, которые доступны только на "родных" платформах. Стиль можно установить, вызвав любое 01- приложение с командной опцией -зсу1е, или же программно. Для того чтобы установить стиль в приложении, нужно вызвать метод зеьзеу1е () и передать ему объект стиля или строку с его названием: ОАррИсаПоп.:: зеГ5Гу1е (пею 0МоЫ€ЭДу1е); В этом случае вызывается статический метод олррПсаЫоп:: веьз<:у1е (), которому передается динамически созданный объект стиля. Классы стилей наследуются от класса ооь^есъ, не используя механизм объектной иерархии, и ответственность за уничтожение созданных объектов несет класс ОАррПсаЫоп. Передача объекта стиля в метод эесзьу1е() приводит к тому, что этот метод сначала уничтожает, с помощью оператора ае1есе, старый объект стиля. По- этому можно создавать объекты стиля непосредственно в самом методе зесзсу1е (), как показано в листинге 26.1, и не создавать дополнительных указателей на эти объекты. #1пс1шЗе <0Ь(Зи1> И--------------------------------------------------------------------------- 0М1с1деГ* зГу1е<ЗМ1(1деГ (05Гу1е* рзГу1е) { ОСгоирВох* рдг = пею ОСгоирВох (рзГу1е->теГаОЪ}есГ () ->с1аззЫате ()) ; 05сго11Ваг* рзЬг = пеад 08сго11Ваг(ОГ::Ног12опГа1);
ОСкескВох* рсЬк = леш ОСЬескВох("&СИеск Вох"); ОЗНдег* рз1д. = лею ОЗХхдег (ОЪ: :НогхгопЬа1); 4 ОНасНоВиЪЪоп* ргаб = пем ОКабхоВиЬСоп(”&ВасНо ВиЪЪол"); ОРизЪВиЪЪол* рапс! = лею СРизЪВиЪЪол("&Ризк ВиЬЬоп"); //ЬауоиЪ зеЁир 0\/ВохЪауоиЪ* руЪхЬауоиЪ = пем ОТВохЬауоиЬ; руЪхЬауоиЪ->а<±ЗИхддеЬ (рзЬг); руЪхЬауоие->ад<ЗИхс1деП (рсЪк) ; руЪхЬауоиГ->адс1Юхддее (рз!с1); руЬхЬауои^->ас1<ЗМх(1дее (рга<3) ; руЬхЬауоие->а<1сЗЮх(1де1: (рапс!) ; рдг->зейЬауоиЪ (руЬхЬауоиГ); 0ЫзС<0М1с1де1:*> рмдЪЫзЪ = : :дГ1п(1С1111дгеп<0М1(3>деЁ*>(рдг); ^огеасИ(0И1с1деЪ* рыдЪ, рюдЪЬхзе) { радде->зее31:у1е(рз1:у1е); } геЪигп рдг; } //----------------------------------------------------------------- 1пП шахп(1пЪ агдс, сЪаг** агду) { ОАррИсаЬхоп арр(агдс, агду); . ОМхйдеЁ ^дЪ; //ЬауоиЁ зеГ:ир ОНВохЬауои!:* рНЬХЬауоиЪ = лею ОНВохЬауоиС; рИЬхЬауои11->адсЗИ1(1де1: (зЬу1е<ЗМ1ддеЪ (пеот 0Юхпс1омз8Ъу1е)); рЪЪхЬауои1:->айсМ1ДдеЪ (зЁу1ес&Ш1деЪ (лею 0МоЫ€31:у1е)); рЪЬхЬауоиЪ“>а<3<ЗИх(адеЪ (зЪу1ес1Н1аде<: (пеы 0СТ)Е5еу1е)) ; рЪЪхЬауоиЪ->аскаетх(2де<: (зЬу1е<анхадеЪ(пею 0С1еал1оокзЗПу1е)) ; рИЬхЬауоиС->а<Я<2МхйдеЪ (з!:у1есЗИх<адее (ле» 0Р1азЪхдиеЗГу1е)); у»д1:. зеЪЬауоиГ (рЪЬхЬауоиГ:) ; утд^.зЪоюО ;
Рис. 26.2. Пример отображения виджетов с различными стилями В (^1 можно смешивать различные стили, то есть задавать каждому из видже- тов в отдельности свой собственный стиль. Этого делать не рекомендуется, но, тем не менее, в целях демонстрации мы проигнорируем это правило, для того чтобы показать различные стили (рис. 26.2). В функции та1п() создается виджет «дь для горизонтального размещения виджетов (листинг 26.1). В функцию зеу1еаи1адес () передается указатель на объект стиля, который создает виджет и возвращает указатель этого виджета. В самой функции создается виджет группы (указатель рдг), в конструктор которого передается имя класса стиля при помощи метода с1агаыаше (), что возможно, так как базовый класс стилей унаследован от класса ооьэесе. За- тем в созданном виджете группы (рдг) посредством вертикальной компонов- ки (руЬхЬауоис) размещаются остальные создаваемые виджеты. Наконец, вызов глобальной шаблонной функции дПпдсинагеп () формирует список всех потомков виджета рдг, и в цикле ГогеасЪ устанавливается стиль для ка- ждого виджета с помощью метода 8есзиу1е (), в который передается указа- тель на объект стиля. В примере, показанном на рис. 26.3, стиль изменяется для всего приложения. Выбрать нужный стиль можно при помощи выпадающего списка. Рис. 26.3. Поимео изменения стиля для всего поиложения
#1пс1ибе <0АррИса!:1оп> #1пс1иде "МуАррИсаЁхоп. Ъ" 1П1: шахп (1пё агдс, сЪаг** агду) { ОАррИсаЁхоп арр(агдс, агду); МуАрр11саГ:1оп туАррИса^хоп; тпуАррИсаЪхоп. зИою () ; геПигп арр,ехес(); } В листинге 26.2 создается виджет класса муАррИсаЫоп. Определение этого класса приведено в листинге 26.3, а реализация — в листинге 26.4. _]4у?фр11сае1оп_11_ МеИпе _МуАрр11саЬ1опЦх_ #1пс1и<1е <0Ю1с1деЁ> // =======================================:================:================ с1азз МуАррЫса^хоп : риЬНс 0М1дде1: { 0_ОВЛЗСТ риЬНс: МуАррИса^хоп (ДОМдеС* радде = 0); риЬНс 81о1:з: то1<1 з1оЪСЪапде5Ьу1е(сопзЪ 05Ёг1пд& зПг) ; }; #епд1 € / /_МуАрр11са1:1 оп_И_ Класс муАррИсаЫоп (листинг 26.3) наследуется от классалЖДдеъ. Помимо конструктора, в классе реализуется слот з1о!:СИапде5Ёу1е(сопзп озъг!пд&), который будет соединен с виджетом выпадающего списка.
“ТГ“!.................................................... Листинг26.4.^>айлМуАррНса<1оп.срр ‘ $ 4/ V' ч < ‘ #хпс1ис1е <ОеСих> #хпс1иде ”МуАррНсаехоп.11и //------------------------------------------------------------------------ МуАррИсаехоп: :МуАрр1хсаС1оп(О^хддее* рзддЪ/*= 0*/) : ОИхйдее(рюдЪ) ( ОСотЬоВох* рсЬо = пею ОСошЬоВох; ОЗрхпВох* рзрЬ = пете ОЗрхпВох; ОЗИдег* рз1й = пеад ОЗИдег (ОЪ: :Ногх2опМ1) ; ОКасНоВиССоп* ргасЗ. = лею ОКабхоВиееоп (и&Кадхо Виееоп"); ОСкескВох* рскк = пею ОСИескВох (" &СЪеск Вох"); ОРизЪВиЪЪоп* рспк! = лею 0РизЪВиИ1оп( '’&РизЪ. ВиСЪоп"); рсЬо->асЗд1Ъешз (ОЗЪгтпдЬтз^ () « "Юхпдоюз" « "МоеИ:" « ”СЛЕ" « пС1еап1оокз" « "РХазехдие" ); соппесе (рсЬо, 51ОЫАЬ{асе1уаее<1(сопзе 08Ггхпд&)), ЗЬОТ(з1оЪСкапдеЗеу1е(сопзё 03егхпд&)) ); //ЬауоиП зе^ир ОУВохЬауоие* риЬхЪауоиЪ = пем ртВохЬауоиП; р^хЬауоие->адсЗИ1с1дее (рсЬо) ; * рVЬx^ауоие->адсЗ^^(^дее (рзрЬ) ; р^ЬхЬауоие->а<±ЗМ1ддее (рз1д) ; руЪхЪауоиЪ->аскЗИх<1дее (ргаф ; рVЬx^ауоие->а<3<ам^с^дее (рскк) ; рVЪx^ауоие->а(3.<ЗМ^<2дее (рспкЯ); зееЬауоиЪ(руЪхБауоие); } //------------------------------------------------------ тоМ МуАррИсаЁхоп: : з1оССЪапдеЗСу1е (сопзЬ ОЗегхпдй зПг)
{ 03Ьу1е* рзЪу1е = 031:у1еРасёогу: :сгеаЬе(з<:г); ОАррИсаЪхоп: :зеС8Ьу1е(рзЬу1е) ) В листинге 26.4 в конструкторе класса муАррТхсаъхоп создается виджет выпадающего списка, в который методом ааатсетзо добавляются име- на стилей из списка. После этого метод соппесЫ) соединяет сигнал асЫуаЬеб(сопзЬ ОЗЪгхпдЬ) СО СЛОТОМ з1оЬСИапде31:у1е (сопзЪ ОЗЫхпдЬ). При выборе нового элемента списка слот получает строку со стилем, вы- бранную пользователем. Она передается в статический метод 05су1еРассогу: :сгеасе(), который создает соответствующий объект стиля. Далее, указатель на объект стиля передается в статический метод зесзьу1е (), который осуществляет установку стиля для всего приложения. Создание собственных стилей Каждый виджет имеет указатель на объект стиля озеу1е. Его можно полу- чить вызовом метода снхддес: :зъу1е(). Виджеты вызывают методы озьуХе в различных ситуациях: при наступлении событий мыши, при событиях пе- рерисовки, при вызове метода зхгенхпе () менеджером компоновки и т. д. Собственные стили обычно создаются наследованием встроенного стиля и перезаписью некоторых методов. Поэтому прежде чем реализовывать свой собственный стиль, нужно решить, какой из классов стилей унаследовать. Можно унаследовать и сам класс <23^у1е, переопределив необходимые мето- ды. Но это потребует гораздо больше усилий, чем при наследовании класса, уже обладающего необходимыми* свойствами. Воспользовавшись подобным классом, вам потребуется перезаписать лишь несколько методов, в которых будут реализованы все необходимые отличия. Класс озеу1е — это абстрактный класс, который является интерфейсом для реализации стилей. Для рисования элементов управления вам, в основном, придется иметь дело со следующими методами, определяемыми классом 05Ьу1е: О агамРгхшхсхуе () —предназначен для рисования простых элементов управления; □ ага«сопего1 () — служит для рисования элементов управления; □ ага«сотр1ехсопсго1 {) — обрабатывает рисование составных элементов упоавления.
Каждый из ранее указанных методов для рисования принимает специальный идентификатор, который сообщает о том, что должно быть сделано. Для пе- редачи дополнительной информации предусмотрен параметр 03Еу1е0рЫоп. Все данные этого класса определены как риЬНс, то есть можно напрямую обращаться к атрибуту: □ версия (уегзгоп) — номер версии. Если вы наследуете класс озсуХеОрЫоп или унаследованный от него класс, номер следует увеличить на единицу; □ тип (ьуре) — целочисленный идентификатор типа; □ статус (зсасе) — содержит информацию о состоянии виджета, например: доступен (епаЫеа), активен (асг^е), в нажатом состоянии (ргеззеб) и т. д.; □ расположение (ахгесЫоп) — расположение текста, по умолчанию это значение равно он: гЬеСьтопХдие, но может принимать и значение ОС.: :Кхд1гЕТоЬеЕь; □ прямоугольная область (гесь), необходимая для рисования элемента; □ . шрифт (Еопе.) — информация о шрифте; □ палитра (раХеесе) — информация о палитре. В большинстве случаев вам не понадобится больше информации, чем предоставляется классом озеу1еорЫоп. Но если вы создаете не только свой собственный стиль, но и свои собственные виджеты, то, унаследовав класс озеуХеОрЫоп, можете передавать любую дополнительную инфор- мацию, необходимую для ваших виджетов. Добавьте в унаследованный класс необходимые атрибуты и не забудьте позаботиться о типе и версии. Например: с1азз МуЗСу1еОр^1опРгодгезз : риЬНс <ЭЗЪу1еОрС1оп { епит {Туре = 50_РгодгеззВаг}; епит {Уегзхоп = 1} ; хпе пМахйтдп; 1пС пМ1п1тит; пРгодгезз; ОЗЬгхпд ЗЫ; ОС: : А1 хдптепЁ ЬехЪА1 хдптепЪ ; }; Теперь, когда вы в общих чертах ознакомились с параметрами, самое время перейти к рассмотрению самих методов для рисования элементов уп- оавления.
Метод рисования простых элементов управления В группу простых элементов управления входят индикаторы, флажки, рамки и другие подобные им элементы. Для изменения их внешнего вида нужно переопределить метод (ЗгаюРгхтхЪдлге (): тоМ сЗгаюРгхтхЪзлге (РгхтхЪхтеЕ1етеп1: е1ет, сопзЪ ОЗЬуТеОрСхоп* рорЪ, ОРахпСег* ррахпСег, сопзЪ ОМхддеЬ* р«дъ = О ) Первый аргумент — это значение, которое сообщает, с каким простым эле- ментом мы будем иметь дело. Второй — это объект класса <эзеу1еОрЫоп, который содержит в себе дополнительную информацию для стиля. Третий параметр — это указатель на объект ОРахпСег, необходимый для рисования элемента. Четвертый параметр является необязательным. По умолчанию он равен нулю, но может содержать указатель на виджет, который может ока- заться полезным при рисовании. Метод рисования элементов управления В группу элементов управления входят такие виджеты, как, например, кноп- ка и индикатор прогресса. Для изменения их внешнего вида необходимо перегрузить метод ЗгаюСопЪго!(): тохс1 дга«Соп!хго1 (СопСго1Е1егпепе е1ет, сопз^ 05^у1е0рЬхоп* рорЁ, ОРахпСег* ррахпЪег, сопзЪ 0ДОхс1деЬ* р«дЁ = О ) Первый аргумент — это значение, сообщающее о том, какой элемент управ- ления должен быть нарисован. Три последних аргумента аналогичны аргу- ментам метода бгаюРгхтхЪЗ^е {). Метод рисования составных элементов управления В эту группу входят элементы управления, состоящие из нескольких частей, например, полоса прокрутки, выпадающий список, виджет счетчика и т. д.
Для отображения их внешнего вида необходимо реализовать метод <±га«Сотр1ехСопСго1(): <Зга«Ссп5>1ехСопЬго1 (СспрХехСопЬго! сопСго1, сопзЪ 05Су1еОрЬ1опСсяпр1ех* рорЪ, ОРахпЬег* рраХпеег, сопзЪ ОИхддеЬ* ркдЬ = О ) Первый аргумент — это значение составного элемента. Вторым параметром метод получает указатель не на озсу1еорЫоп, как предыдущие два рассмот- ренных метода, а на 05ьу1е0рЫопСотр1ех. Этот класс унаследован от ОЗЪуХеорЫоп и дополняется информацией о подэлементах, которые должны рисоваться, и об активных подэлементах, то есть тех, которые находятся непосредственно под указателем мыши или на которых был произведен щел- чок мышью. Реализация стиля простого элемента управления Кнопка является очень распространенным элементом управления, который может присутствовать во многих составных элементах управления, таких как, например, виджет выпадающего списка осотЬовох. Поэтому целесооб- разно рисовать прямоугольную область для кнопки в методе рисования про- стых элементов, для того чтобы любой элемент управления, нуждающийся в рисовании кнопки, мог воспользоваться этим. Пример, изображенный на рис. 26.4, демонстрирует стиль, изменяющий вид кнопки. Рис. 26.4. Пример реализации стиля кнопки #1пс1ис1е <0ЬСи1> #1пс1ис1е "СизСсяпЗСуХе.Ъ" 1пС шахп (1п€ агдс, сЪаг** агду)
ОАррИсаЁхоп арр(агдс, аг^) ; 0М1с1деЪ УтдЪ ; ОРизИВи^Хоп* рспкЗА = пей ОРизкВиЪЪоп (”Ви^Ёоп1п); ОРизИВиЁЁоп* рсшдВ = пете ОРизИВиЕ^оп ("Ви^оп2"); ОРизИВи^оп* рсткЮ = пеад ОРизИВи^оп (”Ви^опЗ"); //Ьауоий зе^ир ОУВохЬауоиЁ* руЬхЪауоие = пет; ОТВохЬауоиЪ ; р^хЬауоиЬ->а<1с№1с1де{: (рспкЗА); р^хЬауоиЁ->асЯсМ1с1де1; (рспкЗВ) ; р^хЬауои^->а<И/Яс1де1: (рспкЯС) ; мдй. зе^ЬауоиЁ (ру’ЬхЬауоиЁ) ; арр.зеЪЗЁу1е(пею СизЪотЗЪуХе); тядЪ. зИою () ; геЪига арр. ехес () ; В функции тахп (), приведенной в листинге 26.5, создаются три виджета кно- пок, произведенных от класса ОРигъвиъьоп. Вызов статического метода зесзъу1е () устанавливает стиль Сизсотзеу1е. .. г Листинг 26.6. Файл Сшйот! ......................... _СизеотЗЁу1е_Ъ #сЯе^1пе _СизЁот5Ёу1е__Н. #1пс1иде <0М1пдот;з51:у1е> с1азз ОРахпЁег; // ==™===========^^==================================================== с1азз СизЪсзт5Ёу1е : риЬИс 0М1п<1ошзЗЪу1е { риЬИс : V^^^иа1 VО^д роИзИ (ОЮхддеЁ* рюдЁ);
V^^Ша1 хгоХсЗ. ипроХХзЫОЮХДдее* рюдЁ) ; V^^^:иа1 Vо^(^ <±гаюРг1пцЛзлге ( РгХпи-^длгеЕХетепС еХет, сопзС ОЗ^уХеОрСхоп* ОРаХп^ег* сопз11 0М1с1деЬ* рорЪ, рраХп^ег, рюдЪ = О ) сопзё; ); #епсИ I / /_Сиз^отЗЁуХе_Н_ В листинге 26.6 класс стиля сизьотзеуХе наследуется от класса ок1паоздззсу1е. При этом переопределяются методы ро11зЬ() и ипроИзЫ), которые присут- ствуют в классе озеуГе. Не все стили нуждаются в переопределении этих ме- тодов. В нашем случае это необходимо для того, чтобы изменять внешний вид кнопки при каждом появлении указателя мыши в ее области. Также в унаследованном классе мы переписываем метод ага«Рг1тИ:^е(), предназначенный для отображения простых элементов управления (рптШуе соп1го! е1етеп1). Первый параметр е1еш содержит в себе идентификатор эле- мента, который должен быть нарисован. Второй аргумент — это указатель на объект класса озьу1еОрЫоп. Третий параметр — это объект 0Ра1пеег (указа- тель ррахпиег), который будет использоваться для рисования элемента. Па- раметр ридь представляет собой указатель на виджет, который может быть преобразован к нужному типу виджета. уо!<1 СизЪашЗЬуТе: :роИзк(0И1с1деС* ридС) { И (доЬ5есЬ_сазЁ<0РизЪВи1:Ьоп*>(р«де)) { рндС->зеСА1:1:г1ЬиЬе (ОС: :КА_Нсллег, Сгие) ; } Метод ро 11з11 () вызывается только один раз при каждом рисовании виджета (листинг 26.7). Мы переопределили его для того, чтобы присвоить атрибуту ОС::ИА_ноуег кнопки ОРизИВиНоп значение Ьгие. Это нужно, чтобы при каждом попадании указателя мыши в область кнопки (^1 создавал событие перерисовки. В этом случае мы можем придать кнопке немного другой вид.
.....2а.......................:...! уохд СизЁотЗ^уХе:: ипро1Хзк (ОМхддеЪ* рмдЬ) { (доЬдес1:_сазЁ<ОРизЪВиС1:оп*> (раддЁ)) { радд11“>зеЪАЁЁг1ЬиСе (Оё :: та^Нсгуег, ЕаХзе); } Метод ипроИзЫ) должен производить отмену всех модификаций, сделан- ных методом роИзйо (листинг 26.8). В нашем случае для простоты мы ог- раничились присвоением атрибуту оь: :ид_нс^ег значения Еа1зе. г*’“ ............................................................... тоХ<1 СизСатЗЬуХе: :дгатеРг1пй.Ъгуе( РгХлиХ^еЕХетепЬ еХет, сопзё ОЗЪуХеОрЫоп* рорЪ, ОРаХпЬег* рраХпйег, сопзЬ ОИХйдеЪ* рюдП ) сопзе ( зюзЛсИ (еХет) { сазе РЕ—РапеХВиЬЬопСоттапа: { сопзЬ 05ЬуХеОрЁ1опВи1Хоп* рОрЫопВиЪЁоп = дзЁуХеорЁхоП—саз!:<соп5Ъ С5ЬуХеОрЪ1опВи1:Ьоп*> (рорЪ) ; (р0р*:1опВи1Хоп) { ЬооХ ЬБотт = (р0рЁ1опВиЪСоп->зЬа11е & 8ЪаЬе_Зипкеп) || (рОрЪ1опВиЪЬоп->зЁа^е & 3^а^е_Оп); ОРХхтар рхх = ЫЭомп ? 0Р1хтар(": /ипадез/ЬЪпргз-Ипр") : ОРХхтар (" : / хтадез /ЬСп. Ьтр") ; рраХпСег-хХгат/РХхтар (рОрЪХопВи1:Ёоп->гесЬ, рХх) ; ЬооХ ЬНоVе^ = (р0р!:ХопВиеСоп->з1:аСе & 5ЪаЬе_ЕпаЬХес!) && (рОрС1опВи^Ёоп->зЪаЪе & З^аЪе—МоизеОуег); (ЬНо^ег) { рраХп^ег->^ХХХКесС (рОрЪ1опВиЪ^оп->гесЬ,
0Со1ог(25, 97, 45, 83) ); } ) Ьгеак; } <ЗеЕаи1(1: 0И1п<Зоиз81:у1е: :<ЗгашРг1т1Ъ^е(е1еп1, рорЬ, ррахпСег, рндЪ) ; Ьгеак; } геЬит; ) В самом методе используется переключатель з«1есЬ. В нем задается секция саге, в которой выясняется элемент, который нам предстоит отобразить. В нашем примере это кнопка. 08еу1е0рЫоп содержит все, что нужно знать о виджете для того, чтобы на- рисовать его. Для получения специфической информации, присущей кноп- кам, мы приводим его к типу озъу1е0рс1опви(:еоп. Приведенный указатель объекта мы используем для получения информации о состоянии и размере прямоугольной области виджета, чтобы правильно его отобразить. В зависимости от переменной Ьпо^п мы используем различные растровые изображения — для отображения йажатой кнопки (Ыпргз.Ьтр) и кнопки в нормальном состоянии (Ып.Ьтр). Изображение кнопки рисуется при помо- щи метода 0₽а1пЪег: : йгаиРххтар (). Переменная ьноуег принимает значение сгие, когда указатель попадает в об- ласть кнопки,. В этом случае с помощью метода ОРахпсег: хнпнессо мы рисуем поверх кнопки заполненный прямоугольник с таким уровнем про- зрачности, который затемняет кнопку. Последний параметр ршде, передаваемый в метод — это указатель на виджет, с которым мы работаем. Как вы уже успели заметить, мы не использовали его вообще, так как практически вся необходимая нам информация содер- жится в объекте озеу1еОрЫоп. Но если вам когда-нибудь придется восполь- зоваться указателем на виджет р«де, то не забывайте, что его передача не яв- ляется обязательной, а это значит, необходимо проконтролировать, что этот указатель не равен нулю. Обычно, когда создается стиль, изменяются только некоторые элементы. Совсем необязательно обрабатывать все случаи и изменять абсолютно все.
Поэтому в секции аеЕаи1е вызывается метод унаследованного класса для об- работки всех остальных случаев. Использование О31у1е для рисования виджетов ОЗЬуХе обособлен от виджетов, что дает возможность рисовать элементы управления на любом контексте рисования. Это может понадобиться, напри- мер, в тех ситуациях, когда вы разрабатываете свой виджет и хотите, чтобы он был унаследован от одного класса, а выглядел как другой элемент управ- ления, доступный в (X Например, вы унаследовали класс надписи (оьаЬеХ), но хотите, чтобы виджет надписи выглядел как кнопка. Для этого в методе обработки события рисо- вания нужно сделать так, как показано в листинге 26.10. Пример получивше- гося окна приведен на рис. 26.5. Рис. 26.5. Надпись, выглядящая как кнопка УО1<3. МуЬаЪе!: :ра1пЬ^епЁ (ОРахпЪЕуепЬ*) 05Ъу1еРа1пЪег рахпЬег (Шз); 03Ъу1е0рЁ1опВи1Хоп ор^хоп; орЫоп. хпаЛГгот (Ы11з) ; орЁхоп. ЪехЪ = "ТЫз 1з а 1аЪе1м ; ратпСег. <5га«Соп^го1 (<23Ьу1е: : СЕЦРизЪВиЬЪоп, орС1оп) ; } Первым делом мы создаем объект класса озьуХеРаХпсег. Этот класс "знает" и о текущем стиле виджета, и об объекте класса ОРатпсег. Затем мы создаем
объект озсуХеОрЫопРизИвиеноп, потому что мы хотим нарисовать кнопку. Вызов метода 1п1ьггот() скопирует опции виджета, такие как прямоуголь- ная область, палитра, шрифт, состояние и т. д. Это самый быстрый способ инициализации. После копирования одна из опций инициализируется тек- стом ТЫз 18 а 1аЬе1. В заключение вызывается метод агашсопего1 () из объек- та класса озсу1е₽а1пеег, который рисует кнопку нажатия. Использование каскадных стилей документа Реализация собственного стиля может оказаться очень сложным, кропотли- вым и утомительным занятием. Этот факт послужил толчком для внедрения в нового способа создания стиля. За основу был взят С88 (Са8сасНп§ 8гу1е 8Ьее<8, каскадные стили документа), используемый в НТМЬ. В этот язык был адаптирован для виджетов, но концепция и синтаксис языка остались теми же. Это позволило полностью отделить создание стиля от приложения. Более того, для создания стиля знание С++ совсем не обязательно, что позво- ляет привлечь к работе над приложением дизайнеров, не имеющих навыков программирования. Программа 91 Эе81§пег (см. главу 45) предоставляет воз- можность интеграции С88-стилей, что упрощает их просмотр на примерах различных виджетов. По своей сути, каскадный стиль есть не что иное, как текстовое описание стиля. Это описание может находиться в отдельном файле, обычно имеющем расширение Ц88. Его можно установить в приложении, используя метод ОАррИсаЫоп:: зеезъу 1езИеее (), или в отдельном виджете при помощи ана- логичного метода ошадее: :зеезеуЛезИееь (). А можно подключить каскад- ный стиль в командной строке, указав после ключа -зеу1езИеее имя файла С88-стиля. Это очень удобно, так как можно очень быстро опробовать стиль в действии на любой (^-программе. Пример: МуАрр -зСу1езЬееС МуЗЬу1е.дзз Основные положения Типичный синтаксис параметров и целевых элементов С88 выглядит сле- дующим образом: селектор {свойство: значение} Целевой элемент называется селектором. Содержимое фигурных скобок, идущих следом за названием виджета, называется определением селектора. Селектор указывает, для какого из виджетов будет использоваться определе-
ние. Например, следующая строка устанавливает красный цвет текста для виджета однострочного текстового ввода: ОЫпеЕсИЛ {со1ог: гей} Определение состоит из свойств и значений, а если их несколько, то они от- деляются друг от друга точкой с запятой, например: ОЬаЬе! { со1ог: гей; Ьаскдгоипс1-со1ог: эдЫЪе; } Цвет можно задавать названием, в формате КОВ или в НТМЬ-стиле: 0ЪаЬе1 { со1ог: гдЬ(255, 0, 0); Ьаскдгоипй-со1ог: #ЕЕЕЕРЕ; } При помощи свойства Ьаскдгоипа-йпаде мы можем задать растровое изо- бражение для заднего фона виджета. Например: рЬаЬе! { Ьаскдгоипй-йпаде: иг1(рте.рпд); } Если в разных селекторах используются одинаковые определения, то их можно сгруппировать в единую директиву. Для этого элементы перечисля- ются через запятую, после чего указывается определение: ОРизЪВиЬЬоп, ОЫпеЕеНс, <ЭЬаЬе1 {со1ог: гед} Указание имени виджета означает, что определения будет применены также и к унаследованным от них класоам. Для того чтобы исключить унаследо- ванные классы, нужно указать перед его именем точку. Например: .РизйВиЬЬоп {со1ог: гед} Для применения определений сразу ко всем виджетам приложения, нужно указать вместо имен звездочку (*). Можно ограничить применение к виджетам с определенным именем объекта, так, например, оьаЬе1#муъаЬе1 инструктирует задействовать описанный да- лее стиль только у виджетов оьаЬе1 с именем объекта "МуЬаЬеГ. Если вы хотите применить стиль к собственному классу, который находится в пространстве имен, например к классу с таким описанием: патезрасе МуЫатеЗрасе { с1азз МуС1азз : риЬИс ОЮхддеЪ {
то к нему в С88 можно обратиться и изменить его внешний вид следующим образом: МуЫатеЗрасе—МуС1азз { Ьаскдгоипд-соХог: ЬХаск; со1ог: угЫСе; } Каскадный стиль не чувствителен к регистру, например: СОЬОК. = со1ог = СоЬоВ. и т. д. Исключение составляют лишь имена классов. Комментарии в стандарте С88 помещаются, как и в С/С++, внутрь последо- вательности символов /* ♦/, например: /* комментарий */ Изменение подэлементов Многие виджеты формируются из составных элементов, так называемых под- элементов. Для задания стиля таких виджетов необходимо получить доступ к подэлементам. Делается это добавлением классификатора подэлемента по- сле имени класса. Например, для изменения кнопки со стрелкой элемента выпадающего списка осотЬовох нужно поступить следующим образом: ОСошЬоВох: :<1гор-аоип {йпаде: иг1 (р!с.рпд) } В табл. 26.1 сведены некоторые из самых распространенных подэлементов. ’ Таблица 26.1. Некоторые подэлементы Подэлемент Описание Возможные виды ::3оюп-агготе Стрелка вниз. Имеется, например, у видже- та выпадающего списка и у счетчика : :дохлтп-ЬиЪЬоп Кнопка вниз. Имеется у виджета счетчика ▼ ] : :(1гор-с1ош1 Стрелка виджета выпадающего списка ж ! ::ХпйХса^ог Индикатор кнопки флажка или переключа- теля, а также группировки кнопок : : 1Ёеш Элемент меню, строки состояния СИ стр'. : ипепи-хпсНсаЪог Индикатор меню кнопки нажатия, обычно это стрелка Ь :
Таблица 26.1 (окончание) Подэлемент Описание Возможные виды Надпись группы Тй:1е : :ир-аггоу? Стрелка вверх. Имеется у виджета счетчика ::ир-ЬиЬЁоп Кнопка вверх. Имеется у виджета счетчика ж Управление состояниями Помимо подэлементов, можно указывать состояния (табл. 26.2). При этом определение применяется к виджету только в том случае, если он находится в определенном (указанном) состоянии. Например, следующее правило бу- дет применяться в том случае, если указатель мыши будет находиться над кнопкой: ОРизЪВиЬЪогпИсхуег {со1ог: г ед} Таблица 26.2. Некоторые состояния Обозначение Описание :скескед Активировано :с1озес1 Виджет находится в закрытом либо свернутом состоянии : сНзаЫеД Виджет недоступен : епаЫед Виджет доступен :Иосиз Виджет находится в фокусе ввода :коVе^ Указатель мыши находится над виджетом : хпде^еппхпа^е Кнопка находится в промежуточном неопределенном со- стоянии Выключено (для виджетов, которые могут быть в фиксиро- ванном состоянии нажато/не нажато) :оп Включено (для виджетов, которые могут быть в фиксиро- ванном состоянии нажато/не нажато) :ореп Виджет находится в открытом или развернутом состоянии :ргеззед Виджет был нажат мышью :ипскескед Деактивировано -
Состояния можно объединять. Следующий пример говорит о том, что прави- ло должно применяться тогда, когда указатель мыши находится поверх вид- жета, находящегося в активированном состоянии: 0СЪескВох:11сллег :скескед {со1ог: «ЫЬе} Если необходимо сделать так, чтобы правило срабатывало в тех случаях, ко- гда виджет находится в одном из нескольких состояний, то можно поступить следующим образом: ОСИескВохгИсллег, ОСИескВох:скескед {со1ог: иЫСе} Также можно применять правило, когда состоянйене имеет места: ОСИескВох: !1ютег {со!ог: иЫЬе} Пример Как принято говорить, все познается в сравнении. Поэтому для того, чтобы нам было с чем сравнивать, давайте создадим стиль, идентичный созданному нами ранее (см. рис. 26.4). /* 51пр1е 5Ъу1е */ 0Риз11Ви11Ъоп { т1п-«1дЬИ: 8 Орх ; } ОРизЪВиЪЪоп { Ьогс1ег-ипаде: иг1 (: / зЬу1е/Ыт. Ьтр) 5рх; ЪогсЗег-^гбЪЪ.: 5рх ; ОРизЪВиЁЁопхкс^ег { Ьогбег- ипаде: иг1 (: / зЪу1е/ЪСпРгус! . Ьтр) 5рх ; Ьогдег-юхсиИ: 5рх ; } рРизИВи^Ёоп:ргеззед { Ьогдег-ипаде: иг1 (: / зЪу1е/Ытргз.Ьтр) 5рх; Ьогдег : 5рх ; } 15 Зак. 3983
Реализация стиля приведена в листинге 26.11. Первый селектор устанавлива- ет минимальную ширину кнопки равную 80 пикселам. Второй селектор опи- сывает кнопку в обычном состоянии, третий — при наведенной на него мы- ши, четвертый— в нажатом. В этих селекторах, при помощи свойств Ьогдег-йпаде и ЬохЗег-^аеь, устанавливаются толщины границы элемента и растрового изображения равными 5 пикселам, а также задаются сами изо- бражения, находящиеся в ресурсе. #1пс1ис1е <0ЬОи1> хпС шахп (1пС агдс, сИаг** агду) { ОАррИсаСхоп арр(агдс, агд\г) ; ОМхсЗдеЬ аддЪ; ОРизЪВиЬеоп* рспкЭД = пею ОРизЪВиЪЪоп (”ВиЪЪоп1”); ОРизИВиССоп* рстйВ = пеот ОРизИВи^Соп (ЙВиЪЪоп2”); ОРизЪВиЪЪоп* рспкЯС = пею ОРизИВиЪЪоп (’*ВиСЪопЗИ) ; //ЬауоиГ зеСир ОТВохЬауоиЪ* р^хЬауои!: = пеад ОУВохЬауоиЪ; руЪхЬауоиС->а<ЗсЗМ1сЗде1: (рспкЗА); руЪхЬауоиС->аддМ1дде^ (рспкЭВ); рVЬx^ауоиЪ->а<^д^^(Здее (рстДС); годе. зеСЬауоиЬ (рхлЬхЬауоиС); ОРИе Ше(":/зСу1е/з1шр1е.дзз") ; ^Ие.ореп(СРИе: :Неас1Оп1у); 05Сг1пд зегСЗЗ = СЬаЪ1п13Сг1пд(^Ие.геа<аА11()); дАрР’>зейЗЬу1еЗЪееЪ(зегС38); зддЬ.зкою(); геЬигп арр.ехес(); } В листинге 26.12, как и в листинге 26.5, мы создаем три виджета кнопок, ко- торые при помощи класса компоновки размещаются вертикально. Основные
отличия заключаются в том, что мы загружаем стиль из файла, записываем его содержимое в строковую переменную зысзз и вызовом ОАррИсахЬоп: :зее5еу1е5Иееъ() применяем его в приложении. Для более простых случаев, например для того, чтобы сменить цвет фона кнопок при попадании на них указателя мыши, мы могли бы ограничиться только одной строкой кода: дАрр->8еЬЗеу1еЗИееС("ОРизйВиСеоп::Ъоуег {Ъаскдгоипд-со1ог: Ыие)"); При реализации стилей возможно также применять различные градиенты, возьмем например линейный градиент и изменим внешний вид все той же кнопки (рис. 26.6). Рис. 26.6. Кнопки с градиентом ОРизпВиЬЬоп { лйп-мШЬИ: 16рх; Ьаскдгоипд: дИпеагдгасИепС(Х1:0, у1:1, х2:0, у2:0, зЬор:1 гдЬ(133,133,135), зЬор:0.4 гдЬ(31, 31, 33) ); со1ог: здЫСе; ЬогсЗег: 1рх зоХхд шЫЬе; > Ъогдег-гасЦиз: 5рх; В первой строке мы задаем минимальную ширину кнопки равной 16 пиксе- лам (свойство шхп-шШёЪ). Затем, используя свойство Ьаскдгоипд, задаем ли- нейный градиент. Для этого определяем первую (х1, у1) и вторую (х2, у2) точки контроля, а также две точки остановки: 1 и 0.4. В результате мы полу-
чим вертикальный градиент, идущий от светло-серого к темно-серому цвету. Для того чтобы текст кнопки легко читался, мы устанавливаем ему белый цвет (свойство со1ог). А чтобы сама кнопка смотрелась красивее, мы опре- деяем для нее наружный контур толщиной в один пиксел белого цвета (свой- ство Ьогаег) и закругляем его углы (свойство Ьогаег-гаДхиз). ОРизЪВиЪСогкЪсдгег { Ьаскдгоипс!: д1хпеагдгасИеп1:(х1:0, у1:1, х2:0, у2:0, зГор:1 гдЬ(133,133,135) , зИор:0.4 гдЬ(31, 31, 33), зСор:0.2 гдЬ(0, 0, 150) ); } В листинге 26.14 для состояния, когда мышь находится поверх кнопки, мы задаем градиент при помощи трех точек остановки. Заметьте, что цвета пер- вых двух остались идентичными заданным в листинге 26.13, а последняя точка задает синий цвет. Это создает эффект голубого подсвечивания. СРизЬВиПюпхргеззед { Ъаскдгоипд: дИпеагдгасНеп!;(х1:0, у1:0, х2:1, у2:0, зСор’О гдЪа(1, 1, 5, 80), 5ЁОр:0.6 гдЪа(18, 18, 212, 80), зиор:0.5 гдЬа(142, 142, 245,80) ); Ьогйег: 1рх зоНа гдЬ(18, 18, 212); В случае когда кнопка нажата, мы создаем горизонтальный градиент при по- мощи трех точек остановки (листинг 26.15). Это делается для того, чтобы пользователь сразу заметил изменение состояния кнопки. 0РизЬВи1:Соп:с11ескеа { Ьаскдгоипа: дНпеагдгадхепС (х1:0, у1:0, х2:0, у2:1, зЪор:1 гдЬ(133,133,135), зСор:0.4 гдЬ(31, 31, 33) ); Ьогаег: 1рх зона гдЬ (18, 18, 212);
Кнопка может также после нажатия находиться в активированном состоянии (1о§§1е), и для того, чтобы пользователь сразу распознал, что кнопка нахо- дится в этом состоянии, мы просто перевернем градиент в обратную сторону и отобразим его от темно-серого к светло-серому цвету (листинг 26.16). ОРизИВиССоп: скескеб: ксллег { Ьаскдгоип<1: дИпеагдгасНеп!:(х1:0, у1:1, х2:0, у2:0, зЪор:1 гдЬ(31, 31, 33), зСор:0.4 гдЬ(133,133,135), зСор:0.1 гдЬ(0, 0, 150) ); В активированном состоянии пользователь может навести указатель мыши на кнопку, поэтому мы также и в этом состоянии при помощи трех точек оста- нова сделаем эффект подсвечивания (листинг 26.17). Резюме Библиотека (21 предоставляет возможность использования в программах различных стилей (1оок & Гее1) и их смены в процессе работы программы. Стиль может устанавливаться как для всего приложения, и так для каждо- го виджета в отдельности. Как правило, один стиль устанавливается сразу всем виджетам приложения, для чего используется статический метод ОАррИсаЫоп: : зеЪЗЪу1е (). Каждый стиль имеет отличия во внешнем виде и поведении. В распоряжении программиста имеются готовые стили, которые можно установить в прило- жении. На основе этих классов можно создавать свои собственные стили. Нужно лишь унаследовать один из них и переписать методы, необходимые для реализации отличий. В каждый из методов передается ряд параметров, предоставляющих всю необходимую информацию для отображения видже- тов. Дополнительная информация доступна в объектах класса озъуХеорЫоп. <2^ предоставляет и более простой способ реализации стилей, который бази- руется на каскадных стилях С88. Каскадные стили состоят из последователь- ности правил стиля. Правило стиля задается указанием селектора, опреде- ляющего подходящие элементы, и собственно определением стиля.
Глава 27 Звук "Откуда звуки? С воздуха? С земли?" Уильям Шекспир. "Буря" По мнению психологов, человек воспринимает посредством слуха около 30% информации. Поэтому не стоит исключать возможность использования в своих приложениях информации звукового характера. Программа должна быть привлекательна для пользователя не только с точки зрения зрительного эффекта, но также и слухового восприятия. При этом нельзя забывать, что звук в компьютере часто отключен, так что полностью полагаться на него нельзя. Воспроизведение звука Вы наверняка замечали, что в критических ситуациях некоторые программы для привлечения вашего внимания используют короткий звуковой сигнал. Чтобы воспользоваться подобным приемом в своих программах, можно про- сто вызвать статический метод олррИсаЫоп: :Ьеер(). Нужно учесть, что издаваемый звук — громкий, поэтому пользоваться им лучше только в слу- чаях крайней необходимости. Для серьезного программирования вам этого, естественно, будет недостаточ- но и наверняка потребуется нечто большее, чем выдача простого предупреж- дающего звукового сигнала. Наверняка, как минимум, потребуется воспроиз- вести какой-нибудь звуковой файл. Применение Если вы хотите использовать более тонкие возможности воспроизведения файлов, такие как например управление позицией воспроизведения, громко- стью, внедрением эффектов, можете сразу перейти к главе 28, описывающей возможности модуля Рйопоп.
Для проведения подобного рода.операций 01 предоставляет класс озоипа. В этом классе для воспроизведения звука имеется статический метод р1ау (), которому нужно передать полное имя звукового файла. Это самый простой способ проигрывания звукового файла и, вместе с тем, самый экономичный с точки зрения использования ресурсов памяти. Например: ОЗоипсЯ: :р1ау ("тизЯс/уевСегсЬау .^тау"); Если вашей программе необходимо проиграть звуковой файл несколько раз подряд, то этим статическим методом уже не обойтись, и потребуется соз- дать объект класса озоипй. Он предоставляет метод зесьоорз (), который ус- танавливает количество повторов при воспроизведении звукового файла. Пе- редача значения -1 создаст бесконечный цикл повторений. Этот метод должен вызваться до запуска метода р1ау (): ОЗоипсЯ зоигкЯ ("тизхс/уезСегсЯау. юау"); зоип<Я. зеЪЪоорз (3); / / 3 раза зоипб.рХау (); Если вам нужно узнать количество оставшихся повторений, то нужно вы- звать метод озоипй:: 1оорзкета1п1пд (), который вернет интересующее вас значение. Чтобы остановить проигрывание звукового файла, нужно вызвать метод озоипа: :8Ьор(). Для рассматриваемого примера операция остановки воспро- изведения выглядит следующим образом: зоип<Я.8Ёор() ; Но этот метод применяется, разумеется, в том случае, когда создан объект класса «ззоипй и вызван его метод р1ау (). Если вам вдруг потребуется узнать, было ли закончено проигрывание до конца или оно было прервано, то можно вызвать метод сзоипй:: 1зг1п1зЪеа<), кото- рый вернет сгие в том случае, если проигрывание было выполнено до конца, или 1а1зе, если оно было остановлено. Если же воспроизведение продолжа- ется, то метод хзРХпхзкеа () вернет значение Еа1зе. Проверка возможности воспроизведения Файлы для проигрывания должны быть записаны в формате XV АУ. Это рас- пространенный формат в ОС ХУ1пс1о\У8. Однако в этом формате могут быть представлены различные типы звукозаписи: □ со сжатием и без него; □ стерео или моно; □ 16- или 8-битовый звуковой канал; О с частотой в пределах от 11 до 48 килогерц.
Все данные в формате УГЛУ корректно распознаются классом озоипй. И если в операционной системе звук настроен правильно, то проблем с его воспро- изведением быть не должно. Но программа должна сама контролировать возможность выдачи звука для того, чтобы своевременно проинформировать пользователя о возможных проблемах. Для этого класс сзоипй содержит слот 05оипс1: : хзАуаПаЫе (). Вызовом метода озоипа:: 1зАуаШЫе () можно узнать о готовности системы к выдаче звука. Этот метод возвращает значение сгие, если система готова и никаких проблем со звуковым файлом нет, а в противном случае этот метод вернет значение Еа1зе. Пример программы, воспроизводящей звук Рассмотрим создание приложения с тремя кнопками, от нажатия которых зависят дальнейшие действия (рис. 27.1). Кнопка Р1ау (Воспроизвести) пред- назначена для воспроизведения звука, а кнопка 81ор (Остановить) останавли- вает его. Кнопка Ьоор (Цикл) является выключателем (Ю§§1е Ьшюп) (см. главу 8), то есть после нажатия она не возвращается в исходное состоя- ние и может находиться в одном из двух положений — включено (оп) или выключено (о(Т). В данном примере это очень удобно, поскольку позволяет задавать программе два режима воспроизведения: при выключенном состоя- нии этой кнопки звук будет проигрываться только один раз, при включен- ном— бесконечное количество раз, пока не будет остановлен нажатием кнопки 81ор (Остановить). Рис. 27.1. Программа, воспроизводящая звук Х11:КЕОЦ1КЕЗ = паз ТЕМРЬАТЕ = арр НЕАБЕКЗ = ЗоипдРХауег .И ЗООКСЕЗ = ЗоипдР1ауег.срр лахп.срр ТАКСЕТ = .. /ЗоипдРХауег
Обратите внимание на первую строку листинга 27.1 — параметр хи :КЕ<2О1кез не требуется для ОС \Ут<клУ5, но нужен для платформ, базирующихся на XII, так как для них необходима поддержка КА8 (Меитогк АшНо Зуйет, сетевая система звука), которую можно загрузить с ЬМр://гаЙ8сап.сот/па5.Ыт1. ЧМй! ..211 #хпс1ис1е <0АррИсаЪ1оп> #1пс1иде " Зоипс1Р1ауег .к" 1пё та±п(±пЁ агдс, скаг** агду) { фАрр!1сае1оп арр(агдс, агду); ЗоипйР1ауег зоипйР1ауег; зоип<1Р1ауег. зкою () ; гееигп арр.ехес(); } В листинге 27.2 создается объект класса 5оипа₽1ауег. #1^пде^ _8оипдР1ауег_И_ #с!е111пе _5оип<5Р1ауег_Ъ_ #1пс1ис1е <0И1с1деС> с1азз 05оипс1; // ==гггг===============================================±===^=^=======г===:=== с1азз 8оипсП?1ауег : риЬНс ОМхдде!: { О_ОВСГЕСТ ргхуаЬе: ОЗоипд* щ_рзп<1; риЬНс: ЗоипдР1ауег (0М1<1де11* р«д1: = 0);
риЬИс зХойз: то1а з1оЬР1ау ( ); хгохсЯ зТоЪЗЪор ( ); хго1а з1о!:ЬоорТодд1ед(Ьоо1); }; #епсН С / /_5оипаР1ауег_И_ В листинге 27.3 определяется класс Зоилар1ауег. В нем содержится указа- тель (т_рзпа) на объект класса озоила, конструктор и три слота з1осР1ау(), з1оезеор() и з1осьоортодд1еа{). Эти слоты будут соединены с соответст- вующими кнопками для проигрывания, остановки и режима циклического воспроизведения. рДиЬп #1пс1иае <01:Ои1> #1пс1иае ”ЗоилаР1ауег.И" //-------------------------------------------------------------------------- 8оипаР1ауег: :ЗоипаР1ауег (ОМ^адеС* рюдЁ/*=0*/) : ОЮхадеЪ(рюдС) { И (’ ОЗоипа: : ^зАVа^1аЫе ()) { фМеззадеВох:: юагахлд (ъЫз, "Ыо Зоила", "Зоггу, I салХ р1ау ЪЬе зоила” ); } ОРизЬВи^Ёол* рстаРЬау = лею 0РизИВи1Хол (”&Р1ау"); ОРизЪВиГХол* рстаз^ор = лею рРизИВШХол ("&31:орИ) ; ОРизИВиГХол* рспкЗЬоор = лею рРизИВШХол ("&Ьоор”); рстаЬоор->зеЪС]1ескаЫе (Ьгие) ; рсшаьоор->зеЪСкескеа(Ьгие); соллесЁ (рстаР1ау, ЗЮЫАЬ (с1 хскеа ()) , ЗЬОТ (з1о!:Р1ау ())) ; соллесС (рстпазсор, ЗЮЫАЬ {сИскеа ()), ЗЬОТ (з1оЁЗСор ())) ; соплесЁ (рспкЗЬоор, ЗЮЫАЬ (Ъодд1еа (Ьоо!)) , ЗЬОТ (з!о1:ЬоорТодд1еа (Ьоо1))) ; т_рзла = лею 03оила( "тизхс/уезЪегаау.угагу", ПЫз) ;
//ЪауоиГ: зеЪир ОНВохЬауриЪ* рИЬхЬауоиЪ = пею ОНВохЬауоиЬ; рИЬхЬауоие->а<Дс1М1ддеС (рсзпдР1ау) ; . рЪЬхЪауоиЪ->ааа^1адеЪ (рстазъор); рЪЬхЬауои!:->аса.(ЗМ1с3.деЪ (рсзпс1Ьоор); зе^ЪауоиС (рЪЬхЬауоиЪ); 51оеЬоорТодд1ед(Ыие) ; } //---------------------------------------------------------------------------- уо±а 5оип<ЗР1ауег: :51оЪР1ау() { щ_рзп<1->р1ау (); } //---------------------------------------------------------------------------- тохд 5оип<1Р1ауег:: з1оЪ5Пор () { пиэзпд->зЪор () ; } И----------------------------------Л.----------------------------------------- уо1с! 5оип(ЯР1ауег: :з1оСЬоорТодд1ед(Ьоо1 Ь) { щ_рзпа->зе1:Еоорз (Ь ? -1 : 1); т_рзпД->зЪор () ; } В конструкторе класса 5оипар1ауег (листинг 27.4) с помощью метода хзАуаИаЫе () проверяется возможность воспроизведения звука. Далее соз- даются три виджета кнопок: для проигрывания (указатель рстар1ау), оста- новки (указатель рстазеор) и установки циклического режима воспроизведе- ния звука (указатель рстаьоор). Для того чтобы кнопку циклического воспроизведения сделать выключателем, в метод зеЁСЬескаЫе {) передается значение сгие. Далее методом зеъсиескеао выключатель устанавливается во включенное состояние. Слот з1осьоортодд1еа(Ъоо1) соединен, посредст- вом сигнала еодд1еа(Ьоо1), с кнопкой Ьоор (Цикл). Сигнал Ёодд1еа(Ьоо1) высылается каждый оаз пои изменении состояния выключателя. Если кнопка
находится во включенном состоянии, то выполняется бесконечный цикл вос- произведения, а если в выключенном — воспроизведение выполняется один раз. Обратите внимание на слот з1осьоортодд1ей(Ьоо1): после установки значения в методе зесьоор () вызовом метода зеор () проигрывание останав- ливается. Это делается для того, чтобы пользователь мог запустить проигры- вание звука уже в новом режиме. Резюме В этой главе мы познакомились с простейшими возможностями, предостав- ляемыми для воспроизведения звука. Мы рассмотрели методы для вос- произведения, прерывания и установки количества повторений, а также узна- ли, как определить готовность системы воспроизведения звука.
Глава 28 ..ф Мультимедиа "Я думаю, поэтому отображаю”. Джерри Зайденберг Когда-то, в незапамятные времена, элементы мультимедиа использовались только в играх и специализированных презентациях, теперь же мультимедиа являются важнейшей составляющей множества приложений, в том числе и УУеЬ-приложений, и их применение расширяется с астрономической скоро- стью. На сегодняшний день мультимедиа играют огромную роль в образова- нии, а в перспективе, несомненно, будут играть еще большую. Мультимедиа задействуют множество каналов восприятия человека, что упрощает усваи- вание, понимание и запоминание информации. Таким образом, самый деше- вый компьютер с мультимедийной программой способен превратить вашу комнату в эффективный центр обучения. Мультимедиа можно смело назвать одной из наиболее популярных технологий за всю историю существования компьютерной техники. В настоящее время о них говорят все. Так что же такое мультимедиа? Существует мудрое народ- ное высказывание "Новое — это хорошо забытое старое", в данном случае это выражение подходит как нельзя лучше, так как само понятие мультимедиа уходит в далекие 50-е годы. Именно тогда использование на лекциях и в пре- зентациях нескольких проекторов и звукового сопровождения положило осно- ву самому этому понятию, но по-настоящему слово приобрело популярность и вошло в обиход только с началом использования этой технологии в компьюте- рах. Значение мультимедиа происходит из латыни: "тикик" означает "много", а "гоескипГ — "средство передачи информации". То есть понятие мультиме- диа— это не что иное, как одновременное использование различных типов информации. Под это определение подходят также и телевидение, фильмы, видеоконференции, караоке, компьютерные игры, компьютерные тренинги, У/еЬ-браузеры и т. д. Словом, осознаем мы это или нет, но мы с вами уже давно живем в мире мультимедиа. В предлагаемой главе мы ознакомимся с модулем РНопоп, обеспечивающим воспроизведение видео и звука в 01.
Путешествие к истокам РЬопоп Проект стартовал под именем КЛЕММ. Это имя означало мультимедийный АР1 для КВЕ4. Но само имя было труднопроизносимо и, к тому же, не отве- чало целям, преследуемым этим проектом, так как выходило далеко за рамки одного КЛЕ. Поэтому было решено взять другое имя, и так появилось новое имя РЬопоп. РЬопоп— это квант колебательного движения атомов кристалла, которые можно отождествить с колебаниями звука. Существуют также и оптические РЬопоп, которые в свою очередь можно отождествить с видео. В общем, ви- део плюс звук — это мультимедиа, а значит РЬопоп. Приятно осознавать, что понятие РЬопоп было введено русским ученым Игорем Таммом. Итак, основная причина появления РЬопоп была связана с тем,.что КЛЕ- программы (начиная с версии 4) и (^-программы должны были запускаться под ХУ1П(1оу/8 и Мас 08 X и использовать платформозависимые решения мультимедиа на этих платформах, которые, естественно, отличались от Ыпих. К тому же ситуация с Ьших усложнялась еще и тем, что для нее и по сей день существуют множество конкурирующих между собой мультиме- дийных решений, таких как, например, ОЗСгеашег, Хше и т. д. Поэтому РЬо- поп является уровнем абстракции над конкретными платформозависимыми решениями. Его цель — предоставление простого и стабильного АР1, приме- нимого на любой поддерживаемой платформе. Это достигается посредством бэкэндов (Ьаскепд). Работает РЬопоп следующим образом: приложения делают вызовы РЬопоп, а он, в свою очередь, трансформирует их и перенаправляет к выбранному платформозависимому модулю. Таким образом разработчик может проигры- вать видео, записывать звук с микрофона, увеличивать и уменьшать гром- кость звука, и осуществлять другие операции, оперируя лишь несколькими строками кода, которые работают на любой из поддерживаемых платформ. Примечание Под Мпдоууз для использования модуля РЬопоп необходимы Оиес1Х и О!гесЬ 8Ьош версии 9 или выше. Другими словами, вам понадобится загрузить и про- инсталлировать М!сгозоП 0|гес1Х 9 80К. О том, как это сделать, вы можете прочитать в оригинальной документации 014 в разделе "РЬопоп О/вплеиЛ Если же вы используете У!зиа1 С++ 2008, то вам ничего не придется делать, так как он уже обладает всем необходимым. Внимание! К сожалению, имеется ряд неудобств, связанных с использованием РЬопоп с М1п6М/ С++. Ввиду целого ряда трудностей компиляции бэкэнда О1гес<8Ьош 9
под МтСЛ/У, вам придется использовать в №!пскпм8 альтернативный бэкэнд, ба- зирующийся на \Л_С, МР!ауег или (я&геатег. Более подробную информацию об ис- пользовании \Л_С-бэкэнда можно получить на странице: ЬПр^7собе.доод1е.сот/ р/рЪопоп-у1с-тр1ауег. Важно отметить, что при создании РЬопоп не ставилась цель реализовать все возможности, присущие мультимедиа. Предпочтение было отдано простому способу использования общедоступных функций, и вполне возможно, что многим из разработчиков могут понадобиться больше возможностей, чем РЬопоп может на настоящий момент им предоставить. Об этом необходимо помнить. Архитектура РЬопоп Теперь, прежде чем перейти к чему-то конкретному, разберем для лучшего понимания в общих чертах классы, составляющие модуль РЬопоп, и их взаи- модействие друг с другом. Все классы модуля находятся в пространстве имен РЬопоп, поэтому при объявлении объектов их имени должно предшествовать РЬопоп::. Если вас утомляет это делать перед каждым классом модуля РЬо- поп, то воспользуйтесь директивой ив1пд пашеврасе РЬопоп в начале вашего файла реализации. На рис. 28.1 показаны основные входящие в модуль РЬо- поп классы. Рис. 28.1. Классы РЬопоп РЬопоп состоит из трех основных элементов: медиаобъект, путь и вывод дан- ных. Медиаобъект представлен классом меагаоьэ'есъ. Он является, пожалуй,
самым важным классом в РЬопоп, именно с его помощью можно открыть ме- диафайл на локальном диске, СО или ОУО, а также и поток медиаданных. Этот класс управляет также источником мультимедиа, которым может быть, например, видео- или звуковой файл, и предоставляет простые слоты для управления воспроизведением, как например зсаге (), знор (), раизе (). Месторасположение медиаисточника описывает класс меахазоигсе. Он уста- навливается В ОМеСаОЬзесС ВЫЗОВОМ метода зеЪСиггепЪЗоигсе (). Метаобъект также получает метаинформацию. Эта информация доступна сразу же при открытии нового медиаисточника и носит индивидуальный ха- рактер. В общем случае это может быть информация об исполнителе, авторе, названии альбома и т. д. Доступ к отдельным ее составляющим можно полу- чить, используя ключ в виде строки. Например: СЗСгхпд зЫАгГхзЬ = пед1а->1пеСа1>аСа("АНТ13Т")) ; Сигнал тесаоасасъапдеао высылается, как только объект принял новые ме- диаданные. Класс Аиахооисрис реализует вывод данных на звуковую карту компьютера. Для вывода видео предназначен класс Ухаеоихадес, унаследованный и от оихадес, что позволяет ему отображать видео в контексте рисования виджета. Заметьте, что классы меахаоьзесс, АиахоОирис, ЕСЕесь, у1аеои1адес также наследуют класс меахаыоае, благодаря чему являются медиаузлами. Медиа- узлы можно неявно соединять друг с другом при помощи класса Рась. Для этого существует функция сгеаСеРасъ(). Таким образом можно не только соединять медиаобъекты с медиавыводом, но еще и внедрять в путь звуковые эффекты. Принцип взаимодействия медиаузлов показан на рис. 28.2. Рис. 28.2. Принцип работы компонентов РНопоп
Все эффекты должны наследоваться от класса ЕЕ^есь. Для эффектов преду- смотрены два вспомогательных класса Е«есе₽агатеСег и Е«есеи1<3де«:. Первый описывает один-единственный параметр эффекта, второй предостав- ляет виджет для его изменения. Класс оЬэесспезсгхрЫоп предназначен для описания объектов РЬопоп и в настоящий момент используется только для предоставления информации об эффектах и объектах звукового вывода. Для этого в нем определены метод аезсг1рс1оп(), возвращающий описание самого объекта, и метод пате(), возвращающий имя объекта. Каждый медиафайл имеет продолжительность, отличную от других медиа- файлов, а это значит, что после загрузки каждого нового файла пришлось бы менять диапазон озиаег. Кроме того, громкость устанавливается в децибе- лах, а это значения с плавающей запятой, но озиаег манипулирует только с целочисленными значениями, то есть нам было бы необходимо реализовать в нашей программе дополнительные преобразования. Для удобства модуль предоставляет два виджета ползунков зеекзиаег и УоТшпезиаег, которые существенно упрощают процесс программирования. Их использование сни- мает необходимость во всех промежуточных преобразованиях и инициализа- циях, связанных со спецификой медиа, которые бы вам пришлось проводить для стандартного виджета ползунка озиаег. Класс мейтаСопсгоИег управляет необязательными возможностями медиа- файлов или устройств. Например, некоторые ОУЭ имеют разделы (сЬар(егя), и этот класс позволит вам перемещаться между ними. И, наконец, если вы собираетесь создать простой медиаплеер то, вполне возможно, что класс У1аео₽1ауег существенно упростит вашу задачу. Это уже готовый компонент, который содержит в себе меахаоьд’есс, 71аеои1адес, АийхоОиерис, уже соединенные между собой посредством Раьь. Если вам по- надобятся указатели на медиаузлы, то их можно получить при помощи мето- дов теахаоь^есъ (), аиахоОисрис () и 5л±аеои1адеъ (). Вам останется только вставить объект класса У1аео₽1ауег в свою программу и, при необходимо- сти, предоставить пользователю виджеты для взаимодействия с самим плее- ром: кнопки Р1ау, Раизе, 81ор и т. д. Но если вам нужно проигрывать только звук, то не используйте у1аеоР1ауег, а воспользуйтесь классами месЛаоЬзесе и АидхоОисрие, как это будет показано в листинге 28.1. Быстрый старт Теперь когда мы немного ознакомились с архитектурой РЬопоп, настало время для практического примера. Как мы уже говорили ранее, медиафайл можно проиграть в РЬопоп при помощи всего нескольких строчек кода. Листинг 28.1
иллюстрирует это высказывание. В нем реализована простая программа вос- произведения звукового файла. Примечание Для того чтобы программу можно было откомпилировать, не забудьте добавить в проектный (.рго) файл строчку от += рЪопоп. #1пс1ис1е <0АррИсаЬ1оп> #хпс1ийе <рЬопоп> хпЪ шахп(хпг агдс, сЬаг** агду) { ОАррХхсаЪхоп арр(агдс/ агду) ; РЬопоп: :МейхаОЬэес<: шейха; РЬопоп: :АийхоОиериС ао(РЬопоп: гМизхсСаЬедогу); РЬопоп:: сгеаЁеРаЬЬ (йшейха, &ао) ; ООЬзесЬ: :соппесЬ(йтейха, 51СЫАЬ(^хпхзЬей()), &арр, ЗШГ(ди1Ь())); шейха. зеЪСиггепЬЗоигсе (РЬопоп: :Мейха5оигсе("тизхс/уез^егйау.здау“)) ; тейха.р1ау() ; геЪигп арр.ехес(); } Центральными объектами для воспроизведения звука являются мейхаоьэесе и АийхоОиериъ. В листинге 28.1 мы создаем объекты этих классов шейха и ао, причем в конструкторе класса АийхоОириь мы указываем категорию мизхеса^едогу, которая сообщает о том, что создаваемый объект воспроиз- водит только звуковые файлы. Сам же Аийхооиерие будет проигрывать все форматы звуковых файлов, поддерживаемые платформозависимым модулем. Данные, которые производит мейхаокдесь, нуждаются в пути вывода, то есть нам необходимо провести соединение с объектом вывода звука. Для этого мы вызываем функцию сгеаъеРасьо и указываем адреса наших медиаузлов шейха И ао. Как только проигрывание звукового файла будет завершено, нам нужно, что- бы и программа завершила свое выполнение. Для этого соединяем сигнал медиаобъекта Е1п1зИеа (> со слотом объекта приложения дизх ().
Вызов метода веесиггепьзоигсе () устанавливает в медиаобъекте местона- хождение источника. В нашем случае мы просто указали путь к файлу, но могли так же просто указать оог1 файла, находящегося на удаленном компь- ютере, или объекты классов, унаследованных от оюоеу1се. Примечание Для открытия СО, 070 либо 700 первым параметром в конструкторе МесИаЗоигсе первым параметром нам нужно было бы передать РЬопоп: :Са, РЬопоп: :1Ьга либо РЬопоп: :7са соответственно, а во втором указать строку с именем устройства. Нужно иметь в виду, что имя зависит от платформы, на которой запущена программа. Примечание Существует также возможность постановки медиаисточников в очередь, чтобы, на- пример, проиграть не только един файл, а целую серию файлов. Для постановки ме- диаисточников в очередь служит метод епдиеие (). Например, добавив в листин- ге 28.1 после зеССиггепСЗоигсе() строку тед1а.епдиеие("ЬезЪ.гарЗ"), мы бы сформировали список проигрывания из двух файлов. И, наконец, для запуска проигрывания мы вызываем метод р1ау (). Вот и все, теперь мы готовы для следующего шага, который будет заключаться в напи- сании простого медиаплеера. Создаем простой медиаплеер В предыдущем примере (листинг 28.1) мы рассмотрели применение класса АиахоОирис для воспроизведения звука. Для полноценного медиаплеера нам не хватает теперь только видео. Мы уже знаем, что за вывод видеопотока отвечает класс Ухаеоихадес. Этот виджет автоматически выберет доступ- ное устройство для его вывода, для ^тдоууз это будет ВцесгЗЬом/, для Мас 08 X — ОшскЛте, а для Ьтих — О81геатег. Проиллюстрируем ис- пользование этого класса в реализации простого медиаплеера, показанного на рис. 28.3. #1пс1иде «ЭАрр11са(:ъоп> #1пс1ис1е ’'Мес11аР1ауег. И ” 1пЪ та1п(1пЪ агдс, сЪаг** а^дV) { ОАррИсаГхоп арр{агдс, агд^);
МедхаРХауег тесИаРХауег; тедхаР1ауег. зИоте () ; геЬит арр. ехес () ; } Рис. 28.3. Простой медиаплеер В основной программе (листинг 28.2) мы просто создаем виджет нашего ме- диаплеера (тейхаРХауег). #хГпс1еЕ _МесНаР1ауег_11— МеНпе _Мед1аР1ауеГ—Ъ_ #хпс!ис1е <ОЮхскуеЁ> патезрасе РЬопоп { с1азз Аис11о0и11риЬ; с1азз МедхаОЬзесЪ; } сХазз МесИаРХауег : рйЬХхс ОМхсЯдеС {
0_овггЕст рг алтаре: РЬопоп: :Аис11о0иЬрие* ш_рао; РЬопоп: :Ме61аОЪзесЬ* т_рте61а; риЬНс: Ме<ИаР1ауег (СММдеС* рюдЪ = 0); риЬНс з1оЪз: уоха з1оЪЬоаа () ; }; #епс11 € / /_Мес11аР1ауег_Ь_ В определении класса меахаР1ауег (см. листинг 28.3) нам нужны два указа- теля на классы АиахоОиЪриь (указатель щ_рао) и меахаоЬэесъ (указатель т_ртеа1а), но нам необходимо сделать предопределение (Гог^агй йес1агайоп) этих классов, разумеется, в именном пространстве РЬопоп. Для загрузки ме- диафайлов нам также понадобится слот з1оъьоаа (). Меа1аР1ауег: : Меа1 аР1ауег (фДОхадее * рмдЪ / *=0 * /) : 2ШадеС (раддЁ) { т_ртеа1а = пею РЬопоп: :Меа1аОЬэесЪ (ЬЫз); т_рао = пею РЬопоп: :АиахоОиСриС(РЬопоп: :У1аеоСа1:едогу, Г:Ьхз) ; РЬопоп: :У1аеоШаде12* рллл/ = пею РЬопоп: : УхаеоМхадеЪ; РЬопоп:: сгеа^еРаЬЬ (т_ртеа1а, т_рао); РЬопоп:: сгеаЪеРаЪЬ (т_ртеа1а, рую) ; рРизЬВиНюп* рстаР1ау = пе^7 <2РизЬВиееоп ("&Р1ауИ) ; рРизЬВие^оп* рстазСор = пею ОРизЬВиЁЪоп("&Зеор”); рРизЬВиНюп* рсхпаРаизе = пею ОРизЬВиЪЬоп("Р&аизе") СРизЬВиеЪоп* рслкЗЪоаа = пею ОРизЬВиЁЪоп("йЬоаа”); РЬопоп::7о1ите311аег* рз1ауо1ите = пе^ РЬопоп: :Уо1итеЗНаег; РЬопоп:: ЗеекЗНаег* рзТаЗеек = пею РЬопоп: :5еекЗНаег; рз1а5еек->зеШеа1аОЬдес(: (т_ртеаха) ; рз1аУо1ите->зеЬАиа1оОиЪриЁ (т_рао);
рз1<3№о1ите’>зеЪ0г1епЪаЪ1оп (ОЪ: :Уеге1са1) ; рз1<5Уо1ите->зеШах1хп1Ш1Не1дИ^ (30); соппесЪ (рстйР1ау, 51(ЖАЬ (с1 хскед ()), пс_ртесНа, ЗЬОТ (р1ау ())) ; соппесЁ. (рстс131:ор, 81(ЗЫАЬ (сИскей()), псртеДха, ЗЬОТ (агор ())) ; соппесЪ (рстйРаизе, ЗТСЗЯАЬ (сИскед ()), пиртедха, ЗЬОТ (раизе ())) ; соппесЪ (рсткЗЬоаД, ЗЮЫАЬ (сИскед()), ЗЬОТ (з1оСЬоад())) ; //ЬауоиЁ зеЪир рНВохЬауоиЁ* рИЪхЬауоиЬ = пею рНВохЬауоиЁ.; рИЬхЬауоиЪ->аскЗМ1с1деЪ (рстдР1ау) ; рЪЬхЬауои^->асМИ1с1де1: (рстЗРаизе); рЫэхЬауоиЪ->аасЭШадеЪ (рстсЯЗЪор) ; рЪЬхЬауоиЁ.->ай<ЗМ1йдеЁ. (рз1д5еек); рЪЪхЬауоиЪ->а<±ЗМ1ддеЪ (рстсЗЬоасЗ) ; рЪЪхЬауои1;->аа(аи1адеЁ(р81с^о11Ш1е) ; ' ОХТВохЬауоиЪ* р^ЬхЬауоиМ = пею ОУВохЬауоиЪ; руЬхЬауоиЬ->адсЗИ1с1деЪ (рлМ ; руЪхЬауоиЪ->аскаЬауоиЪ (рЪЬхЬауоиЪ) ; зе^ЬауоиЁ. (руЬхЬауоиЬ) ; } В конструкторе (листинг 28.4) мы создаем объекты классов меахаоьзесс (указатель т_рте<11а), АидхоОиЬриЪ (указатель т_рао) И УгдеоИхддеЪ (рхги). В конструкторе АиШооиьрис передается значение категории У1аеосаеедогу, сообщающее о том, что наш плеер не ограничивается только проигрыванием звука. Затем мы соединяем узлы вывода видео и звука с медиаобъектом, по- сле чего создаем кнопки для управления проигрыванием Р1ау, 81ор, Райке, Ьоа<1. Для создания регулятора громкости и ползунка управления позицией мы используем виджеты УоХшпегпаег и кееккиаег, предоставляемые РЬо- поп. Для изменения позиции проигрывания необходимо, чтобы ползунок мог управлять медиаобъектом, для этого мы устанавливаем связь, вызывая метод зесмеЛаоЬз есъ () и передавая в него указатель на метаобъект (указатель псртеШа). Регулятор громкости тоже нуждается в управлении объектом звуко- вого вывода, поэтому мы вызываем метод зеьАиШооиъриь () и передаем в не- го указатель объекта звукового вывода (указатель ш_рао). Вертикальное по- ложение регулятору громкости придается вызовом метода зеьог1епъаЬ1оп() с параметром УегЫса!. Далее соединяем сигналы сИскеао кнопок управ-
ления Р1ау, 81ор и Раи$е с соответствующими слотами медиаобъекта. Кноп- ка Ьоад соединяется с написанным нами слотом зХогьоад (). уо1<3 МедхаРХауег:: аХоЬЬоай () { ОЗСгхпд зЬг = 0Р11еО1а1од:хдеЬОрепГНеИатеСО, "ЬоасГ, (! зЬг. хзЕ^прСу ()) { щ_р«пед1а->зеЪСиггепЬЗоигсе(РЬопоп::Мес11аЗоигсе(зсг)); } Слот з1оььоаа(), приведенный в листинге 28.5, отображает окно стандартного диалога открытия файла. После выбора пользователем файла, он устанавливается в медиаобъекте при помощи уже знакомого нам метода зеесиггепьзоигсе (). Теперь упомянем о некоторых полезных возможностях класса Ухдеоихадес, которые не были задействованы в нашем примере. Виджет ухаеоихадеь пре- доставляет возможность изменения отображаемых видеоданных. Например, можно изменить яркость изображения вызовом метода зеевгхдЬепезз () или контраст вызовом метода зессопыаэъ (). В эти методы можно передавать значения от -1 до 1, причем нейтральное значение — это 0. Класс ухаеоихадес по умолчанию изменяет разрешение самого видео, чтобы оно полностью поместилось в отведенной виджету области, сохраняя при этом пропорции. Такое поведение можно изменить, передав в слот зе(:Зса1еМоае () значение УхаеоШадеЬ:: Зса1еАпасгор. Возможно также включить полноэкранный режим. Для этого в классе У1аеои1адес содержится слот епсегРиИзсгееп (), а для возврата в оконный режим существует слот еххьгипзсгееп (). Этого же можно добиться, вос- пользовавшись слотом зеЬРиИЗсгееп () и передав в него аргумент булевого типа, ьгие для полноэкранного режима либо Са1зе для оконного режима по- каза. Узнать, в каком режиме показа в настоящий момент находится виджет, МОЖНО ВЫЗОВОМ слота хзРиИЗсгееп (). Резюме Одной из целей разработки РЬопоп было упростить написание приложений, имеющих дело с мультимедиа. Этот модуль предоставляет разработчикам одинаковый АР1 для воспроизведения мультимедиа (звука и видео) на всех поддерживаемых платформах. В качестве медиа могут выступать так же
и файлы, находящиеся на удаленном сервере, поэтому можно в качестве пути передавать <эиг1. При помощи модуля РЬопоп можно осуществлять такие операции: □ запускать и останавливать воспроизведение, в том числе делать паузу, пе- рематывать вперед и назад; ' □ изменять уровень громкости или полностью отключать звук; □ отображать состояние медиапотока; □ получать дополнительную информацию, например, имя автора сюжета или его название. Класс мебхаоьзесь управляет воспроизведением и записью и предоставляет медиаданные. Класс АиаЛоОиърис выводит звуковые данные на устройство, а виджет У1аеошадеъ выводит видео. Все эти медиаузлы могут быть соеди- нены друг с другом, используя Раеь. Эффекты — это тоже медиаузлы, кото- рые могут быть вставлены между медиаобъектом и объектом вывода посред- ством РаСИ. Медиаобъект позволяет ставить источники в очередь на проигрывание. Как только проигрывание одного источника будет закончено, начинается проиг- рывание следующего. Для простых задач проигрывания можно использовать класс 71аеоР1ауег, который дает возможность получить результат очень быстро.
Часть V Создание приложений
Глава 29 Сохранение настроек приложения "Изменение может стать вашим со- юзником ... Изменение станет вашим врагом, если оно застанет вас врасплох". Бак Роджерс, вице-президент 1ВМ Возможность изменения и сохранения настроек приложения очень удобна для "приспособления" интерфейса программы под конкретного пользователя. На самом деле, пользователю будет очень приятно, если при запуске прило- жения будут восстановлены настройки, примененные при его предыдущем сеансе работы: приложение будет находиться на том же месте, иметь те же размеры и выглядеть так, как удобно этому пользователю. Также необходимо сохранять названия и пути последних документов, чтобы пользователь мог быстро их выбрать. Данные настроек приложения представляют собой совокупность ключей и значений. Ключ (кеу) представляет собой строковое значение — имя, с по- мощью которого можно программно получать и устанавливать адресуемое ключом значение (кеу уаШе). Место, где хранятся эти данные, зависит от конкретной платформы. Напри- мер, приложение, запущенное в ОС \У1п4о\у8, сохраняет данные в системном реестре: например, в ветках реестра НКЕТ_ЬОСАЬ_МАСНП'4Е\8оЙ'Л'аге или НКЕТ_СЦККЕНТ_и5ЕК\8ой\уаге. Примечание Системный реестр ОС У\Лпс1ошз — это центральная база данных для хранения установок приложений и системы. В Ыпих для хранения данных будет использоваться директория $НОМЕ/.д1 или $ОТВШ/е1с.
В 91 для работы с настройками используется класс озесгХпдз. При его соз- дании в конструктор можно передать имя компании и название программы. Например: 03еЬЬ1пдз зеЬЬ1пдз("ВНУ","МуРгодгаш"); Если настройки должны использоваться в нескольких местах, то лучше централизованно установить имя компании и название вашей програм- мы, для чего в классе осогелррИсасХоп определены статические методы зеСОгдапгзаЬгопЫаше () И зеЬАррИсаЫопИате (). Например: ОСогеАррИсаЪгоп: :зеС0гдап1заЫопМал1е("ВН\7") ; ОСогеАррИсаЫоп:: зеЪАррИсаЫопМате ("МуРгодгат") ; Для записи настроек приложения используется метод зеъуа1ие(). Первым параметром в метод передается ключ, а вторым— значение. Если такого ключа не существует, то он будет создан. Настройки базируются на исполь- зовании класса ОУагХапъ (см. главу 4), что позволяет записывать значения следующих типов: Ьоо1, аоиЫе, хпе, озъгхпд, окесъ, оттаде и т. д. Сохра- нить настройки приложения можно одним из следующих способов: зеЬСХпдз.зеЬУаХиеС/ЗеСЫпдз/ЗЪгХпдКеу”, "ЗСгхпд Уа1ие"); зеСЬхпдз.зеЬУаТие("/ЗеЫхЛпдз/ТпСедегКеу", 213); зеЬЫпдз. зеЬУа1ие (" /ЗеСЬ1пдз/Воо1еапКеу", Ыие) ; Для получения данных нужно воспользоваться методом уа1ие(), который возвращает значения типа рУагЛапс. Благодаря этому класс озесЫпдз пре- доставляет методы для чтения разных типов — полученные значения типа ОУагЛапг нужно лишь привести к нужному вам типу. Это существенно об- легчает задачу. В метод уаХие () можно передавать два параметра. Первый параметр — это сам ключ, а второй — значение ключа, которое будет воз- вращаться в том случае, если ключ не найден. Получить настройки можно следующим образом: ОЗЪгхпд зёг = зеЪ1:1пдз^а1ие(м/5е1:Ё.1пдз/8ег1пдКеу", .Ш5Ьг1пд() ; 1п€ п = зеЪ^хпдз. Vа1ие (" /ЗеЪЪхпдз/ШЬедегКеу”, 0) . ЪоТпё (); Ьоо1 Ь = зеЁМпдз.Vа1ие (И /5еЪЫпдз/Воо1еапКеу", ^а1зе) . ИоВоо1 (); Для удаления ключей и их значений нужно передать имя ключа в метол гешоуе () класса ОЗееьХпдя: зеЪЬЛпдз.гешоуе (" /ЗеЕЬЛпдз/ЗЬгЛпдКеу"); Чтобы не передавать в методы ключ полностью, можно задать его префикс методом ЬедЛпОгоир (). После использования ключей, связанных с этим пре- фиксом, необходимо вызвать метод епасгоир (). Методы префиксов разреша- ется вкладывать друг в друга, получая, тем самым, длинные ключи. Проде- монстрируем вложение методов ЬедЛпСгоир () и епасгоир () друг в друга: зесЫпдз. ЬедЛпСгоир (" /ЗеЬЫпдз ") ; зеЕЫпдз. ЬедхпОгоир (” /Со1огз")
хпС пКеё. = зееЫпдз^а1ие( "/гесР); зеГ^хпдз. епсЮгоир () ; зеЁСхпдз.ЬедхпСгоир (" /СеотеСгу") ; хпЪ пИйсМ! = зейЬхпдз^аХиер/юхдСЪ") ; зеЪ^хпдз. епсЮгоир () ; зеЬЁхпдз. епсЮгоир () ; Программа, приведенная в листингах 29.1—29.8, создает окно, показанное на рис. 29.1. Надпись информирует о количестве запусков приложения. Виджет выпадающего списка управляет изменением расцветки текстового поля и предоставляет два режима — С1а§81с (Классический) (черный текст, белый фон) и Вог1апс1 (стиль расцветки фирмы Борланд) (желтый текст, синий фон). Флаг ОгеаЫе есШ (Отключить редактирование) включает/выключает режим редактирования текстового поля. Все вышеперечисленные настройки и размеры окна сохраняются при его закрытии и восстанавливаются при сле- дующем запуске программы. Рис. 29.1. Программа, сохраняющая и восстанавливающая настройки #1пс1иде <0АррНса1:1оп> #1пс1и<1е "МуРгодгат.Ъ’1 1пЪ та1п(1гП: агдс, сЪаг** а^дV)
ОАррИсаЪхоп арр(агдс, агду) ; МуРгодгаш туРгодгат; туРгодгат. зЪоад () ; ге1:игп арр. ехес (); } В листинге 29.1 создается виджет класса МуРгодгаш. ' ^.Листинг 29.2. Файл МуРгодгйт.й% ,г-V • ’ ‘ ’ • _МуРгодгат_И_ #<3е€1пе _МуРгодгат__Ъ_ #1пс1иде <0ШддеЬ> #1пс1иде <03еЪЪ1пдз> с1азз ОСотЬоВох; с1азз ОСЪескВох; с1азз ОТехЪЕДдЛ; с1азз 0ЬаЬе1; // ====================================================================== с1азз МуРгодгат : риЬИс ДОМдеС { 0_ОВгГЕСТ ргхтаЪе: ОЗеЁЁхпдз п\_зеЬМпдз; ОСотЬоВох* т_рсЬо; ОСЪескВох* т_рсИк; ОТехЁЕЛЁ* т_рЁхЪ; 0ЬаЬе1 * т_р1Ы ; 1пС т_пСоип^ег; риЬИс: МуРгодгат(О^хддеЁ* рюдЁ = 0) ; V^^!;иа1 -МуРгодгат () ;
То1с1 геааВе^Ъхлдз () ; риЬНс з1оёз: то1а з1оЪСкескВохС11ске<1 ( ) ; \ло1а з1оССотпЬоВохАс1:^а11ед(1п11) ; }; #елаИ //„МуРгодгаш_И_ Определение класса муРгодгат, приведенное в листинге 29.2, содержит атри- бут т-зеьсхпдз класса озесъхпдз. Также класс содержит указатели на виджет выпадающего списка (т_рсЬо), на флажок (т_рскк), на виджет текстового поля (псрехе), на виджет надписи (щ_р1Ь1) и счетчик количества запусков программы (т_лсоилеег). В классе муРгодгат определены методы для записи и чтения данных настроек программы (шг^еЗеСЁхпдзО и геаавеъехлдз ()). Также в нем определены два слота з1о^снескеавохс11скед() и зТоЁСошЬоВохАсе^аъеао для выполнения действий, вызываемых изменени- ем состояний флага и выпадающего списка. МуРгодгат:: МуРгодгат (ОЮхддеЪ * ридЬ / *=0 * /) : 0Ю1адеС(ридЪ) , щ_зеЪЫпдз ("ВН\7", "МуРгодгат”) { щ_р1Ы = лею 0ЬаЬе1; пср^Ё = пею ОТехЪЕаН:; щ_рсЬо = лею ОСотЬоВох; т_рсИк = лею ОСкескВох("ЩзаЫе еа1Ъ"); щ_рсЬо->ааа!1гет("СТаззхс") ; т_рсЬо->ааа11:еп1( "Вог1алаи) ; соллесЁ (щ_рс1ак, 51СЫАБ (с! 1скеа ()), ЗЪОТ (з1оеСкескВохС1 хскеа ())); соплесЁ (щ_рсЬо, 31СЫАЬ(асЪгуаЪеа(гпё)), 5ЪОТ (з 1 оЪСотЬоВохАсЁ^аЪеа (хлС)) );
//ЬауоиЪ зеЪир ОУВохЬауоиС* руЬхЬауоиЪ = пею фУВохЬауоиЪ; СНВохЬауоиЬ* рНЬхЬауои1: = пем ОНВохЬауоиЪ; руЬхЬауоиС->зеЁМагд!п (5) ; рЪЬхЬауоиС->зеЪ5рас1пд(15) ; руЬхЬауоиЪ->зе1:Зрас1пд (15) ; руЪхЬауоиС->ад(ЗСТ1ддеС (п1__р1Ы) ; рVЪx^ауои^:->а(16Ш(ЗдеЬ (щ_рЪхЪ) ; ркЬхЬауоие~>а(МММдеЬ (т_рсЬо) ; рЪЪхЬауоие->ас!д1лГ1(ЯдеС (щ_рсИк) ; руЪхЪауоиЁ->айдЪауоиГ (рЪЬхЬауоиС) ; зеЪЬауоиЪ (р^ЬхЬауоиЪ) ; } В листинге 29.3 производится инициализация объекта настроек т_зеы:1пдз двумя строками: названием фирмы "ВНУ" и названием продукта "МуРго§гаш". В конструкторе класса создается виджет надписи (т_р1Ы), виджет текстового поля (ш_рсхь), виджет выпадающего списка (т_рсЬо) и виджет флажка (ш_рсИк). Методом а<М1Ьет() в виджет выпадающего спи- ска добавляются опции С1а551с и Вог1апс1, обозначающие текущую расцветку текстового поля. Сигнал с11ске<1() виджета флажка (указатель ш_рсЬк) со- единяется со слотом з1оьсЬесквохсИскед(), а сигнал ась^аьеб(хпс) вид- жета выпадающего списка—.со слотом з1оесошЬовохАсс^асеа(1пь). Затем вызывается метод геаазесЫпда (). В завершение, созданные виджеты раз- мещаются при помощи компоновок (см. главу 6). "(гохс! МуРгодгат: : геадЗеЬЪхпдз () { т_зеЪЁ1пдз .ЪедхпСгоир (” /ЗеЁЁхпдз"); ОЗЬгхпд зСгТехе = т_зе^хпдз.Vа1ие("/СехЪ", "”) .ЪоЗЪгхпд() ; хпГ 1пе пМ1<М1 = т_зе^Ъхпдз. Vа1ие {" , ю1с1Ъ11 ()) . ЪоТпё () ; пНехдИ^ = т_зе^Ыпдз. Vа1ие (" /ЪехдЫ:" , Ие1д111: ()) . ЁоТпЪ () ; 1пЬ пСотЬоТЁет = пцзеЬЫпдз. Vа1ие (" /ЫдИИдИС ’’, 0) . СоТпЪ () ; Ьоо1 ЬЕс11С = ш_зеЪ1:1пдз. Vа1ие (" /едх^", Ъгие) . ЪоВоо! ();
т_пСоипЪег = щ_зе!111хпдз. Vа1ие (" /соипЪег", 1) . ёо1пЪ () ; ОЗЪгхпд зЪг = ОЗЪгхпдО .зеШш1<пипСоипЪег++) ; п\_р1Ы->зеГТехГ("ТЫз ргодгат каз Ъееп зЪагНеа ” + зОг + " йхтез"); т_р^х1:->зе11Р1ахпТехЪ (зСгТехС) ; гезххе (пМхбЪк, пНехдЪЪ) ; п^Рс11к->зе!:Скескед (ЪЕйхе); з1оЬСкескВохС1хскес1() ; 1п_рсЬо~>зеЁ:СиггепЫп<1ех (пСошЬоТ^еш) ; з1оССо!1йэоВохАс1:^аЁес1(пСотЬо11:ет) ; лизеЪГхпдз. епсКЗгоир (); } В листинге 29.4 приведен метод чтения настроек геаазеъехпдз (), в котором прежде всего методом Ьедхпвгоир () объекту класса озе^ъхпдз передается префикс ключа. Это делается для того, чтобы не передавать в методы чтения настроек полный ключ. Строковой переменной з^гтех!: присваивается зна- чение ключа /ъехъ, прочитанное методом геадЕпыу (). В том случае, если ключ с именем /сехс не существует, то метод вернет значение, указанное во втором параметре, то есть пустую строку. Аналогично производится инициа- лизация переменных пмшеъ и пнегдис, предназначенных для хранения раз- меров окна, пСошЬоггет, хранящей индекс опции виджета выпадающего спи- ска, ЪЕсНе, хранящей значения разрешения редактирования и т_псоипъег, ведущей подсчет количества запусков программы. После прочтения виджеты изменяются в соответствии с полученными значениями. Значение счетчика т_пСоипьег увеличивается на единицу. В завершение, для снятия установ- ленного префикса ключа производится вызов метода епдогоир (). / *V^^Ъиа1 * /МуРгодгаш:: -МуРгодгаш () { адгх^еЗе^Ъхпдз(); } 16 Зак. 3983
Деструктор, приведенный в листинге 29.5, — это самое удобное место, где можно произвести запись настроек приложения, так как он вызывается при уничтожении виджета. Для записи настроек используется метод югХееЗеЪЪхпдз(). Примечание Вместо деструктора можно воспользоваться методом обработки события с1озеЕVеп^: (), который вызывается при закрытии окна виджета. МуРгодгат::югХпеЗеЬЪхпдз () { т_8еСЬ1пдз.ЬедхпСгоир("/ЗеЪЪХпдзи); хпдз. зеСТаХие (и / соипЪегИ, пипСоипЪег); т_зеЪ!:1пдз. зеЁУаХие (" / СехЪ" , т_рЪхЁ->ЁоР1а1пТех1: ()); п^зееехпдз. зеЁУаХие (” , юМеЪ. ()) ; хП—ЗееЫпдз. зеЪУаХие (" /ИехдЫ:", ЪеХдЫ: <) >; гП-зе^еХпдз. зеЁУаХие (" /ЫдЫхдЫ:", псрсЬо->сиггепЪ1пс1ех ()) ; пизеППхпдз. зеЬУаХие (" /есН±", пцрскк-эазСкескеЦ ()) ; т_зеСЪХпдз. епсЮгоир (); В методе «гхьеЗеьсхпдз () после установки префикса ключа с помощью ме- тода ЬедхпсгоирО записываются настройки приложения. Производится се- рия вызовов метода зеста1ие(), где первым параметром указывается имя ключа, а вторым — его значение (листинг 29.6). уоха ^Ргодгат: :з1оЬС11ескВохС11скес1() { п\_рЕх(:->зеГ:ЕпаЫед( !щ_рскк->1зСИеске<1()) ; } Метод з1оеспесквохсИскеа() устанавливает виджет текстового поля в ак- тивное или неактивное состояние в зависимости от состояния виджета флаж- ка, возвращаемого методом хзсъескеас) (листинг 29.7).
уо1<1 МуРгодгат: :51оЪСатЬоВохАсЁ^аЪес1(1пЪ п) { 0Ра1еЪЪе ра1 = псрЪхЪ->ра1еГ:Ёе (); ра1. зеЪСо1ог (ОРаХеЁХе:: Ас Ъзлге, 0Ра1еЪЁе::Вазе, п ? ОЬ: :с1агкВ1ие : ОЪ: :шЫЪе ); ра1. зеЪСо1ог (рРа1еЪЪе: : АсПалге, 0Ра1еЗДе::ТехС, . п ? :уе!1сж : ОЪ: :Ыаск ); . т_р1:хЪ->зеСРа1еЬ11е (ра!); Метод з1опсошЬоВохАсъ^аееа(), приведенный в листинге29.8, устанавли- вает цвет фона и шрифта виджета текстового поля в зависимости от индекса элемента выпадающего списка п. Изменение палитры производится только для активного состояния виджета ОРаХеъъе: -.Асеалге (см. главу 13). Управление сеансом Нельзя исключать и такую ситуацию, когда пользователь решил завершить сеанс работы в операционной системе и забыл, что работал с программой над одним из документов. После завершения сеанса все несохраненные данные будут безвозвратно утеряны. К таким данным относятся как документы, так и настройки самой программы. Для предотвращения подобного программа должна отслеживать такие случаи, чтобы вовремя напомнить пользователю о необходимости сохранения измененных документов. Для реализации этого необходимо унаследовать класс оАррНсаьхоп и переоп- ределить методы ОАррИса^хоп: : сопппа-ЫЭаЪа () И ОАррИсаСхоп: :заVе3^1аЁе(), которые вызываются при завершении сеанса работы в операционной системе. В качестве параметра эти методы получают объект класса озеззхопмападег. С этим объектом можно работать дальше, например, вызвать метод аНо^хпьегасЫоп (), чтобы узнать, можно ли отобразить диалоговое окно для предоставления возможности управления процессом пользователю. Ме- тодом сапсе1 () можно прекратить процесс выхода из системы, но это долж- но происходить только в исключительных случаях. При помощи метода зескезсагънхпг () можно сделать так, чтобы при следующем запуске опера-
ционной системы программа запустилась автоматически. В этот метод нужно передать одно из следующих значений: □ ке5Ьагыгкипп1пд — значение по умолчанию, программа будет запущена при следующем запуске операционной системы, если она была запущена при выходе из системы; □ кезьагьАпуиау — программа должна автоматически запускаться при каж- дом запуске операционной системы; □ кезЪагЫтшесИаьеХу — программа должна запускаться сразу же при за- пуске операционной системы; □ кезъагеыеуег — программа не должна автоматически запускаться. Метод ОАррИсаЫоп:: 1з5еззо1пКезьгоге<3() возвращает значение, с по- мощью которого можно проверить, была ли программа запущена автомати- чески (сгие) или ее запустил пользователь (1а1зе). Программа, показанная на рис. 29.2, по завершении сеанса работы в опера- ционной системой выводит сообщение. Беззюл Рис. 29.2. Программа, контролирующая завершение сеанса работы в ОС #1пс1иае <0ЬОи1> // ===:===========г==:=====================5^=========================е====== с1азз МуАррИсаЫоп : риЫхс ОАррИсаЫоп { риЫхс: //----------------------------------------------------------------------- Мугфр1хсаЁхоп(хпЁ агдс, сИаг** агду) : САррИсаЫоп (агдс, агду) ( } И----------------------------------------------------------------------- ухгЬиа! -МуАрр1хса1:хоп()
И---------------------------------------------------------------- ухгПиа! уохд сотпиЪПаЬа (03езз1опМападег& зт) { ОМеззаде&ох:: хп^огтпаЪхоп (0, иВ1а1од", “Уои аге ехзХхпд орегаСхпд зузеет" ); } }; //------------------------------------------------------------------- хпС тахпСхпЪ агдс, сИаг** агду) { МуАррИсаЬхоп арр{агдс, агду); 0ЬаЬе1 1Ы(”Ех1С ЪИе орегаМпд эузЪет ёо зее СИе с11а1од Ьох", 0); 1Ы. з!юю () ; 4 геЁигп арр.ехес(); } В листинге 29.9 класс муАррИсаЫоп наследуется от класса одррИсаЫоп. В этом классе переопределяется метод сошла изаса (), который будет вызван при завершении работы операционной системы. При его вызове на экране отобразит- ся окно с соответствующим сообщением. В основной программе вместо создания объекта класса <2Арр1 хсаЫоп создается объект класса муАрр1 хсаЫоп. Резюме СК предоставляет возможность хранения информации о конфигурации при- ложений, необходимых для того, чтобы дать пользователю возможность на- страивать приложение под себя. Данные настроек приложения — это сово- купность ключей и их значений. Ключи — это значения строкового типа, состоящие из подстрок, разделенных символом /. Значения могут иметь тип, поддерживаемый классом оуагхапъ. Все значения можно читать методом уа1ие () и записывать методом зеЬУа1ие (). Механизм управления сеансом работы в операционной системе позволяет отслеживать завершение ее работы. Это используется для оповещения поль- зователя о необходимости сохранения измененных документов до заверше- ния работы операционной системы.
Глава 30 Буфер обмена и перетаскивание — У вас есть сильные программисты? — Есть, а зачем они вам? — Нужно несколько компьютеров пе- ретащить. При работе на компьютере пользователи часто работают с несколькими при- ложениями и нуждаются в обмене данными между ними. Для поддержки та- кой возможности существуют две распространенные технологии: использо- вание буфера обмена и перетаскивание объектов. Надо заметить, что часто эти технологии используются и при работе только в одном приложении, а не для обмена данными между программами. Буфер обмена Буфер обмена (СНрЬоагд) обеспечивает возможность обмена данными между разными приложениями. Это один из самых популярных способов копирова- ния данных из одного приложения в другое. Он представляет собой область памяти, к которой могут иметь доступ все запущенные в системе приложе- ния. Любое из них может записывать или считывать информацию из буфера обмена. Программы, работающие с буфером обмена, должны предоставлять стандартные команды: вырезать (сЩ), скопировать (сору) и вставить (ра$1е), и эти команды необходимо снабдить ускорителями <С1г1>+<Х>, <С(г1>+<С> и <С1г1>+<У>, соответственно. Для работы с буфером обмена в 01 используется класс осИрЬоага. Не стоит пытаться создавать объект этого класса, так как он создается при запуске при- ложения автоматически и может существовать в приложении только в единст- венном числе. Объект класса осИрЬоага высылает сигнал ааьасьапдеа () каждый раз, когда одно из приложений помещает в буфер обмена новые данные.
Если необходимо контролировать данные, размещенные в буфере обмена, то этот сигнал соединяют с соответствующим слотом. Например: соппесС (дАрр->с11рЪоагс1 (), ЗТСЫАЬ (даЪаСЪапдед ()), ридЪ, ЗЬОТ (з1оЫ)а1:аСопГ:го1 ()) ); Данные можно помещать в буфер обмена при помощи методов зеетехъ (), зеЪРЬхтар (), зе^Ттаде () ИЛИ зеЪМипеРаЪа (). Например: ОСИрЬоагд* рсЬ = ОАррИсаЫоп: :с11рЬоагс!(); рсЬ->зеЁТехЁ ("Му Тех!;") ; Примечание При помощи метода зеыИшеПаЬа () можно помещать в буфер обмена данные любого типа. Метод принимает указатель на объект класса, унаследованного от ОМхтеЗоигсе. ОМхтеЗоигсе— это абстрактный класс, являющийся основой для типов данных, которые могут быть преобразованы в другие форматы. С ПОМОЩЬЮ методов ИехЪ (), хтаде (), рххтар () И тхтеПаЬа () данные ПОЛу- чаюг из буфера обмена. Например: ОСИрЪоагд* рсЬ = ОАррИсаЪхоп: гсЫрЬоагсЦ) ; ОЗПгхпд з1г = рсЬ->ЪехЁ(); (!зЬг.1зМи11()) { дВеЪидО « "СИрЬоагд Тех!:: " « зЪг; } Перетаскивание Перетаскивание (с!га§ & дгор)— это мощная технология обмена данными между приложениями. Она предоставляет пользователю более интуитивный, чем буфер обмена, механизм обмена данными. В настоящее время, поддерж- ка перетаскивания является неотъемлемой частью практически любого при- ложения. Процесс перетаскивания выглядит следующим образом: пользова- тель нажимает левую кнопку мыши, когда указатель мыши находится на объекте, и, удерживая кнопку, перетаскивает объект из окна одной програм- мы в окно другой. Это позволяет обращаться с виртуальными объектами как с объектами реального мира, перетаскивая их с одного места на другое. Од- ним из ярких примеров является Кесуск Вт (Корзина) на рабочем столе ОС ^Утдоууз, в которую сбрасывают ненужные файлы. Класс онхйдеъ обладает всеми необходимыми методами для поддержки тех- нологии перетаскивания, а некоторые из классов иерархии виджетов содер- жат ее полную реализацию. Поэтому, прежде чем приступить к реализации
перетаскивания, необходимо убедиться в том, что оно не реализовано в вид- жете. Например, класс отехъЕЛь (см. главу 10) предоставляет возможность перетаскивания выделенного текста. Для проведения перетаскивания <21 предоставляет класс опгад, а для разме- щения данных различных типов при перетаскивании — класс омхтеоаъа. Обозначение М1МЕ означает МиШригрозе 1пСегпе( МаП ЕхСепзюп (многоце- левые расширения почты Интернета). Он предусматривает пересылку тек- стовых сообщений на различных языках, а также изображений, аудио- и ви- деоинформации и некоторых других типов данных. К примеру М1МЕ-тип 1ех1/р1а1п означает, что данные представляют собой обычный А8СП-текст, а 1ех1/Ь№11 означает, что данные — это форматированный с помощью языка НТМЬ текст. Для растровых изображений используется тип вида 1та§е/*. Например, для ]р§ М1МЕ-типом является 1та§е/)р§. Если вы используете данные собственного типа, которые могут интерпретироваться только лишь вашим приложением, то тип должен иметь вид аррКсаПоп/*. В табл. 30.1 све- дены наиболее часто используемые типы. Таблица 30.1. М1МЕ-типы М1МЕ-Тип Описание аррПсаГюп/* Данные собственного приложения, которые не могут интерпрети- роваться другими программами аибю/* Звуковые данные, например, аисНоМау 1таде/* Растровое изображение, например, гтаде/рпд тобе1/* Данные моделей, зачастую трехмерные, например, тобе1/угт1 Текст, например, 1ех1/р1а!л у|бео/* Видеоданные, например, уМео/тред Как мы уже упоминали ранее, реализация М1МЕ в 01 представлена классом ОМйпеОаса. В этом классе определены методы для записи данных различных типов: □ цветовых значений — зеХСоХогОаХа (); □ растровых изображений — зеЫтадеОаСа (); □ текстовой информации — зестехъ (); • □ гипертекстовой информации в формате НТМЬ — зегнып1 (); □ списков (ссылок) ЦКЬ (ШпИогт Кезоигсе ЬосаСог, единообразный опреде- литель ресурса) — зеспгХз (). Этот метод часто применяется для перетас- кивания (Ьайлов.
На все случаи вышеперечисленных методов, естественно, не хватит, так как может понадобиться перетаскивать и принимать свои собственные типы дан- ных (например, эруковые данные). Как поступать в подобных ситуациях? Для этих случаев в классе омхшеоаса определен метод зеепаьа {), в который первым параметром нужно передать строку, характеризующую тип данных, а вторым — сами данные в объекте класса овуСеАггау (см. главу 4). Можно поступить и иначе — унаследовать класс омхтепаса и перезаписать методы Гогтпа!:в () И ^е1:^^еVе^а^а (). Программирование поддержки перетаскивания можно условно разделить на две части: первая часть включает в себя код для перетаскивания объекта (4га§), а вторая реализует область приема для сбрасываемых в нее объектов (дгор). Также вторая часть должна распознавать, в состоянии ли она принять перетаскиваемый объект или нет. Рисунок 30.1 показывает процесс перетас- кивания с соответствующими методами возникающих событий. Рис. 30.1. Процесс перетаскивания и возникающие события Реализация с1гад Реализация первой части перетаскивания начинается с перезаписи методов тоизеРгеззЕуепС () И тоизеМсууеЕуепС (). В первом ИЗ ЭТИХ МеТОДОВ сохраня- ется позиция указателя мыши, в которой была нажата кнопка. Эта позиция пригодится в методе тоиземоуевуепь () для определения момента старта опе- рации перетаскивания. Рисунок 30.2 демонстрирует перетаскивание текстовой информации из окна виджета в окно редактора.
— Сиад •Д| 1-с "и .' :< Гг«з :2б^ддаЫе1еч1 тоизе ЬиПоп Ш аск! Же асиоп ?Жеп уои На? ' ок "Л/ШдеТз Цл се Ьоск шс!деШ аге сопЖтег гнс1де18 Жеу с 1ес1 Ю а Ьгт с!аск >.мс1де1з эге по1 р!асед т а Рог Не!р,. ргезз Р1 СДР Г Рис. 30.2. Перетаскивание текста #1€пс1е1 _,Вгад_Н_ 1: ▼ 1 #де11пе _Огад_к_ #1пс1иде <0«5и1> #1пс1иде " 5шд1. хрт" // =^============^==3;======================^= с1азз Вгад : риЬНс 0ЬаЬе1 { о овагЕСт ргдлгайе: <2Ро1пС 1П_рШгадРо5; то1с1 зЬагЫЭгадО { ОМхтеОаЪа* рМхтеГЭаЪа = пею ОМзтеПаЪа; рМ1л1еПаЪа->зеЬТехЪ (ЪехЛ ()) ; ООгад* рВгад = пеад ОИгад (СЫз); рСгад->зеСМ1теВа±а (рМхтеВаЪа) ; рВгад->зеЁР1хтар (ОРххтар (1тд1_хрт));
рБгад->ехес(); } ргоСесСед: V^^еиа1 уохд тоизеРгеззЕуепС (ОМоизеЕуепС* ре) { (ре->ЬиС1:оп() == ОЪ: :Ье^СВиССоп) { щ_рССгадРоз = ре->роз (); } ОЮ1<ЗдеЪ: :тоизеРгеззЕХгепС (ре) ; } V^^^иа1 уо1с1 тоизеМсгуеЕуепС (ОМоизеЕуепС* ре) { 1Г (ре->ЬиССопз () & ОН: :Ье1СВиССоп) { 1пЪ <11 з Сапе е = (ре->роз() - псрСОгадРоз) .тапкаССапЬепдСкС) ; И (сНзСапсе > ОАррИсаСхоп::зСагСВгадРхзСапсе()) { зСагСВгад (); } ) ОИПйдеС: :гюизеМсллеЕХгепС (ре) ; } риЬИс: Эгад(да1<1деС* р«дС = 0) : ОЬафе!("ТЫз 1з дгаддаЫе СехС”, раддС) { } }; #еп<ИС //_Ргад_И_ Определение класса Огад, приведенное в листинге 30.1, содержит атрибут т-рнэгадРоз, предназначенный для сохранения положения курсора мыши в момент нажатия левой кнопки. Инициализация этого атрибута производит- ся в методе шоизеРгезз^епь () только в том случае, если была нажата левая кнопка мыши. Метод тоиземоуеЕуепс () нужен для распознавания начала перетаскивания. Это сделано потому, что нажатие левой кнопки мыши и последующее пере- мещение указателя не всегда говорит о желании пользователя перетащить объект, так как у пользователя могла просто дрогнуть рука, случайно пере- местив указатель мыши. Чтобы быть уверенным, необходимо вычислить рас- стояние между текущей позицией и той позицией, в которой была нажата
левая кнопка мыши. Если это расстояние превышает величину, возвращаемую статическим методом зЕагыогадШзъапсе () (обычно 4 пиксела), то можно считать, что перемещение указателя мыши было неслучайным и пользова- тель действительно хочет перетащить выбранный объект. Затем вызывается метод зьагьпгад (). В этом методе создается объект класса ОМхтеОаса, в который, вызовом метода зестехс. (), передается перетаскивае- мый текст. Затем создается объект перетаскивания класса оогад, в конструк- тор которого передается указатель на виджет, из которого осуществляется перетаскивание. Осторожно! Передача указателя на виджет вовсе не означает, что этот виджет будет пред- ком и будет нести ответственность за уничтожение объекта перетаскивания. На самом деле ответственность за уничтожение объектов перетаскивания несет только менеджер перетаскивания. Уничтожение перетаскиваемого объекта производится в любом случае и не играет роли, отпущен он в принимающей зоне или нет. Вызов метода зепР1хтар() устанавливает небольшое растровое изображение, следующее вместе с указателем мыши при перетаскивании. Метод ехесо запускает операцию перетаскивания. В этот метод можно также передавать значения, влияющие на внешний вид пиктограммы, находящейся рядом с курсором мыши и поясняющей смысл действия перетаскивания. Например для копирования это значение ОС: :Сорудсе1оп, для перемещения — ОС: :МсгуеАсЫоп, ДЛЯ СОЗДаНИЯ ССЫЛКИ — ОС: :ЫпкАсС1ОП. По уМОЛЧДНИЮ ЭТО значение устанавливается равным ос: :Мсл/еАсс1оп. Реализация скор На рис. 30.3 показан виджет, способный принимать сбрасываемые в него объекты (в данном случае — файлы). После сбрасывания виджет отображает полное имя файла. #1^пс1е€ __Пгор_Ъ_ ЯдеНпе _Пгор_11_ #1пс1ис1е <рЬ6и1> // ========5================^=================:============;=г====ь========== с1азз ©гор : риЬИс 0ЪаЬе1 { 0_ОВСГЕСТ
ргоЬес^ед: V^^еиа1 тодЛ сЬгадЕпСегЕуепС(0ПгадЕп1:егЕуепП* ре) { (ре->т1теБаГ:а () ->ЬазГохтаЁ. (" йехЪ/игх-ИзЪ")) { ре->ассерЪРгорозе<аАс±1оп() ; } } V^^еиа1 тохд дгорЕуепЁ (ОРгорЁуепЬ* ре) { 0ЫзГ<диг1> иг1ЫзЪ - ре->т1теВаЪа () ->иг1з (); ОЗЬгхпд зЫ; ^огеасЬ(0Иг1 иг1, иг1Ыз1:) { зСг += иг!. СоЗЬгЗ-пд () + И\п" ; }* зеЪТехС("Ргорред:\п" + зЪг) ; риЬИс: Пгор(0И1<ЗдеЪ* рюдЬ =0) : ОЪаЬе!("Вгор Агва", рюдЬ) { зеЬАссерШгорз (егие) ; } }; #епсШ: //_Эгор—И_ ЙЗр ~ / * Сатриш > » Мт Рис. 30.3. Виджет, принимающий сбрасываемые объекты
Для того чтобы виджет был в состоянии принимать сбрасываемые объекты, в конструкторе класса Бгор производится вызов метода зеСАссерыэгорз (), в ко- торый передается ыие (листинг 30.2). Кроме того, для получения сбрасываемых объектов необходимо переопределить методы «агадЕпьегЕуепс () и агорЕуепе (). Метод агадЕпеегЕуепс () вызывается каждый раз, когда перетаскиваемые объекты пересекают границу виджета (см. рис. 30.1). В этом методе виджет сообщает о готовности или неготовности принимать перетаскиваемые объек- ты, при этом указатель мыши из перечеркнутого круга превращается в стрел- ку с прямоугольником (возможно, со знаком плюс, если пользователь нажал при перетаскивании клавишу <С1г1>), в противном случае внешний вид ука- зателя остается без изменений. Обычно виджет не способен принимать данные всех типов, поэтому необхо- димо проверять на совместимость тип данных перетаскиваемых объектов с помощью метода ЬазРогтаъ (). Примечание Полный тип М1МЕ состоит из типа и подтипа, разделенных косой чертой. В на- шем случае он имеет вид *1ех(/иП-11зГ. ВЫЗОВ метода ассерЪРгорозедАсЫоп () объекта события ООгадЕпСегЕуепЬ сообщает о готовности виджета принять перетаскиваемый объект. Примечание Класс оогадЕпЪегЕуепи унаследован от класса опгадМоуеЕуепг (см. рис. 14.1). Этот класс предоставляет методы ассерь () и гдпоге (), кото- рыми можно воспользоваться для разрешения (или запрета) приема перетас- киваемых объектов. В эти методы можно передавать объекты класса окесь. С их помощью можно ограничить размеры принимающей области в самом вид- жете. Чтобы разрешить, например, сбрасывание объектов во всей области виджета, можно сделать следующий вызов: ассерс (гесь ()).' Метод агорв^епс () вызывается при сбрасывании перетаскиваемых объектов в пределах окна виджета, что происходит в момент отпускания левой кнопки мыши. Вызов метода иг1з() объекта класса ом1теоаеа возвращает список файлов в переменную иг1Ызе. Далее в цикле ЕогеасЪ вызовом метода <2иг1:: ьозьгхпд () извлекаются строки, которые объединяются в одну с сим- волом возврата каретки ("\п") в качестве разделителя. После этого получен- ная строка отображается методом зеьтехъ (). Создание собственных типов перетаскивания Возможности не ограничены перетаскиванием данных определенных ти- пов, таких как, например, текст или растровые изображения. Перетаскиваться может информация любого типа. Для этого необходимо определиться с иден-
тификацией типа для перетаскиваемых данных для того, чтобы принимаю- щая сторона могла принять решение — допускает она их или нет. Идентифи- кация достигается использованием строки тЫеТуре, которая представляет перетаскиваемые данные, эта же строка используется принимающей сторо- ной для получения доступа к данным. Таким образом, основной метод для создания объекта перетаскивания мог бы выглядеть примерно так, как пока- зано в листинге 30.3. Му1)гадС1азз:: зСагШгад () { ОТгоаде ыпд ("пйга. эрд”) ; ОВуЬеАггау даЪа; ОВи^ег ЬиС (ЬдаЬа) ; фМипеОаЪа* рМхтёОа^а = пею ОМхтеЛаЪа; Ьи^.ореп(0101)е\п.се: :Юг1(:еОп1у) ; ^Iпд.заVе(&ЬиI, "ЛРС"); рМ1теЕ)аЁа->зеСВаЪа (” хгсаде/эрд", сЗаЪа); <2Югад* рЮгад - паю <ЭБгад (Ы11з) ; рПгад->зеем1теОаЪа(рМ1те1)аеа) ; р1)гад->ехес (01:: :Мс^еАсП1оп); В листинге 30.3 создается объект растрового изображения, данные которого будут подвергнуты перетаскиванию. Конечно, мы могли бы просто восполь- зоваться методом омхшеоаъа:: зеЫшадеОаса (), но в целях демонстрации соз- дания собственного типа для перетаскивания, будем исходить из того, что этого метода нет. Итак, для того чтобы поместить данные в бинарном виде в объект класса омйпеоаъа, мы создаем объекты классов овуееАггау и ови«ег. При помощи метода охтаде: :зауе() мы записываем данные в объект класса ОВиХЕег, который управляет бинарным массивом (йаъа). После чего мы пере- даем этот массив в метод омхшеоаса:: зеызаса (). Обратите внимание на пер- вый параметр этого метода— строка 1та§е/]р§ является идентификатором для перетаскиваемого типа данных. Если перетаскивание должно работать только в пределах вашего приложения, то можно поступить еще проще и повысить эффективность: избежать проме- жуточного копирования данных в овуъедггау и использовать их напрямую.
Этот способ заключается в следующем. Ввиду того что приложение работает в одном адресном пространстве, можно напрямую передавать адреса и указа- тели на объекты, записывая их в М1МЕ-объектах. Разумеется, сами данные не должны быть локальными. Продемонстрируем это подход на примере, где в качестве данных передаются указатели на виджеты, с помощью которых принимающая сторона может получить доступ к сброшенному виджету. В программе, показанной на рис. 30.4, мы видим два виджета, которые можно передавать друг другу, а также и самим себе, посредством перетаскивания. Рис. 30.4. Виджеты, принимающие сбрасываемые виджеты #1пс1ис1е <0АррИсаЪхоп> #хпс1иде "ЮхйдеП.Ъ" хпё шахп (1пС агдс, скат* а^дV [ ]) { ОАррИсаЪхоп арр(агдс, а^дV) ; , ЮхсЯдеС Мхс1деЁ юдЬ2; юдЪ1. зеШхпаоюТхГ1е ("ИШдеЫ") зддЪ1. веЁОЬдесШаше (“Юхс1деЪ1"); юдЪ1.гезх2е(200, 200); юдЫ.зИоюО;
юдЪ2. зеШ±пс!оадТ11:1е ("йЛддеСЗ") ; у/дП2 . зеШЬзесСЫате ("Ш(1деП2и) ; «дС2.гез!ге(200, 200); у?дС2.з]1ою() ; геЪигп арр.ехес(); } В основной программе (листинг 30.4) мы создаем два виджета класса мхддеь, реализация которого приведена в листингах 30.7—30.13. Для того чтобы визуально отличать один виджет от другого, мы устанавливаем заголовки при помощи метода зепр^пдоутше (). А для программного различия мы присваиваем этим объектам имена "\Ук1§е1Г’ и вызовом метода зеЪОЬэ есЪНате(). с!азз ШДдеШхтеОаЪа : риЬИс ОМхтеБаЪа { ргдлгаЁе: ОИтЗдеЬ* т_раддП; риЬИс: ЮИдеЬМхтеОаЪа () : ОМатеОаеа () { } У1ГЁиа1 -ИМдеШхтеОаЬа () { } зЁаЫс ОЗЁгхпд пшпеТуреО { геЬигп ” арр1 хса^хоп/у^хйдеЁ"; } \то1с! зеСМхйдеГ (0Ю1с1деС* рюдъ) { пириде = рмдЁ;
зеЫОаЪа (ишпеТуре (), ОВуСеАггау ()) ; ) ОМпЗдеЬ* «1<ЗдеС () сопзи { геЬига щ_ридС; Наш М1МЕ-класс (листинг 30.5) в качестве данных должен предоставлять указатель на виджет, поэтому мы определяем в классе ихбдеемипеоаса атри- бут щ_р«дп. Статический метод пшпетуре () возвращает М1МЕ-тип, в нашем случае это "аррКсаНопЛуИ^еС". Первая часть этой строки "аррНсайоп" гово- рит о том, что данный тип может использоваться только внутри этого прило- жения и нигде больше. Вторая часть "\У1ф»еГ сообщает, что перетаскиваемые данные— это виджет. Указатель на сам виджет устанавливается методом зеьи1адеео. В этом методе мы не просто присваиваем нашему указателю псрзддъ новый адрес, но и устанавливаем тип вызовом метода зесоаеа (), для чего передаем строковый идентификатор М1МЕ-типа и пустой объект ОВуЬеАггау. Указатель на виджет возвращает метод итддее (). ГЯГТ----.....................................---------------------------- и»___............________________________________________________________ с1азз Ю1<ЯдеЁ1>гад : риЬНс фВгад { риЬНс: Ю1<ЗдеЫ)гад(ОИхддеЪ* рзддЫЭгадЗоигсе = 0) : ОПгад(раддМ>гад5оигсе) { } хгоз-й зеШхддеЪ (ОМхдде^* рюдС) { Мхаде^хгпеОаЁа* ртй = пеад йМдееМипеОаСа; рта->зеШ1с1де1: (ридС); зеШ1тёВа^а(ртс1); } }; Класс Итйдееогад (листинг 30.6) — это класс для объекта перетаскивания, он наследуется от класса оогад. В этом классе мы реализуем метод зесн1адеь (),
в котором создается объект нашего М1МЕ-класса И1с1деем1теоаьа. В этом объекте вызовом метода зеъшадеъо устанавливается переданный в метод указатель (рюдс). В завершение, метод аеъммпеоаса () устанавливает М1МЕ- объект (указатель рта) в нашем объекте перетаскивания. ............................... г............. ^Листинг30:7;;Файл ШитСК. . •• .. .................................................. #Ипс1еЁ _ШадеЪ_Ъ. Ме{:1пе _Ю1с1деЬ—И #1пс1иде <0Ро1пк> #1пс1иДе <ОЬаЬеЛ> // ========================;==================^=======^================= с1азз Ю1ддеЪ : риЬИс 0ЬаЪе1 { О_СШЕСТ рггуаке: ОРохпк щ_рЫ)гадРоз ; зСагСПгад(); ргокескед: V ^^^иа1 5^01(3. тоизеРгеззЕуепк. (фМоизеЕ^епк* ) ; V I г Ша 1 VО^Й тоизеМоуеЕуепк (ОМоизеЕуепС* ) ; V ^^Сиа1 тоМ агадЕпЁегЕХгепЪ (ОПгадЕпкегЕуепЪ*) ; V ^^Сиа1 То1<3 дгорЕ^епЪ (ОИгорЕуепС* ); риЬЫс: М1йде1:(ОЮхЙдеЪ* рюдк = 0) ; }; #епсШ: //_Ю1с1дек_К_ Теперь реализуем класс виджета, который будет поддерживать наш новый М1МЕ-тип (листинг 30.7). Для того чтобы отображать текстовую информа- цию, унаследуем его оьаЬе1. Этот виджет в состоянии не только принимать, но и генерировать объекты перетаскивания. Поэтому в нем перезаписаны все четыре необходимых для этого метода событий, которые уже знакомы нам из предыдущих прймеров этой главы.
.. Листинг 30.8. Файл ЗДйдеН.срр. Конструктор ...»-»} - ,,, < г МхддеЁ: :М1с1деЁ(0И1дде11* рид^/*=0*/) : 0ЬаЬе1(рмдЪ) { зеСАссерШгорз (^гие) ; } В конструкторе, приведенном в листинге 30.8, для того чтобы наш виджет мог принимать перетаскиваемые объекты, мы передаем значение ъгие в ме- тод веЬАссерЪПгорз(). ГЛИСТЙнгЗд;9.Файл\№!с1дё1.сррМетод«еаг1:ргад() уо1с! ШвдеС:: зЪагШгад () { ЮхйдеСОгад* рОгад = пем М1с1дееОгад(11111з); рВгад->зеШ1дде11 (Шз) ; рОгад->ехес (:: СоруАсС1оп) ; В методе з^агеогадО мы создаем объект перетаскивания и в качестве ис- точника передаем в его конструктор указатель сЫз (листинг 30.9). В качест- ве виджета, который будет перетаскиваться, мы передаем указатель сЫз в метод зесшадес (). Для начала процесса перетаскивания вызывается метод ехес(). /^1гЪиа1*/ то 1(3. МхйдеЁ: :тоизеРгеззЕуепС (ОМоизеЕХгепЬ* ре) ( 11: (ре~>ЬиЪЬоп () == ОЪ: :Ье€ъВи1Хоп) { т_рС0гадРоз = ре->роз(); } ОМхддеЬ: :тоизеРгеззЕХлеп!:(ре) ; } В методе шоизеРгеззЕ^епс () мы запоминаем возможное начало позиции пе- ретаскивания в атрибуте щ_рСПгадРоз (листинг 30.10).
[ листинг‘30Л1.л-*, ' ' * /^1гЬиа1*/ Vо^с^ ИхддеЬ: :тоизеМоVеЕVепЬ(^МоизеЕVеп^* ре) { 1:Е (ре->Ьи1:СоП8 () & ОЁ: :Ье^ЬВиЁЁоп) { 1пС ЛзЁапсе = (ре->роз() - т_рЫ>гадРоз) .гпапЪаГХапЬепдЁЩ) ; 11: (сИзПапсе > ОАррНсаЁхоп: :зПагШгадП1зЁапсе()) { з^агЫЗгад () ; } } (МиЗдеЁ: :тоизёМсллеЕ\гепЬ (ре); Метод тоиземсгиеЕ^тепЕ <) (листинг 30.11) нужен для распознавания начала перетаскивания. Он полностью аналогичен одноименному методу, приведен- ному в листинге 30.1, где можно найти более подробное его объяснение. / ^1гЪиа1 * / ^о!с1 ШддеЬ:: дгадЕпЁегЕХгепЬ (ОПгадЕпЬегЕтлепЁ* ре) { (ре-хпхтераЁа () ->ИазГоппаЬ (ШЗдеЪМхтеОаЁа: :типеТуре ())) { ре->ассерЬРгорозедАсе1оп(); } Метод а^адЕп^:е^ЕVеп1: () (листинг 30.12) разрешает прием перетаскиваемого объекта только в том случае, если его тип совпадает с "аррНсабопЛуИ^еГ и для проверки вызывается метод Ъазгогтаь (). Строку идентификации типа возвращает статический метод м1адеем:ипеваьа: :ш1тетуре (). / ♦ ^гЬиа1 * / МхйдеЁ: : ДгорЕуепИ ((ЗОгорЕуепЁ* ре) { сопз11 ИхсЯде^МтеОаЁа* ртикЗ = дупаш1с_сазЬ<сопзе м1дде€М1теОаЪа*> (ре-хпххпеЦаПа ()); 1С (ротпй) { ОИхддеС:* р\л^дЬ = рттй->^1ддеЪ ();
ОЗСгхпд зПг("ММдеЪ 1з <3горред\п ОЬзесШаше:%1") ; аеСТехС (зЫ. агд (рндЪ->оЬзесЪЫате ())) ; } } После сбрасывания мы вызываем метод питеОаИа (.) объекта события для по- лучения указателя на объект перетаскивания (листинг 30.13). Еще раз убеж- даемся, при помощи динамического преобразования типа, в том, что объект произведен от класса ихадеьмхтепаьа. Если все прошло удачно, то получаем указатель сброшенного виджета вызовом метода к1<3дее (). И в завершение выводим информацию о его имени при помощи метода зестехг (). Резюме Основной задачей буфера обмена является поддержка простейшей формы обмена информации между различными приложениями. С его помощью одна программа может записывать данные в буфер обмена, а другая — читать их из него. Хорошее приложение характеризует интуитивность в обращении. Использо- вание технологии перетаскивания (дга§ & скор) — это верный шаг к дости- жению этой цели. Вга§ & дгор — это процедура обмена данными между раз- ными приложениями. Ее принцип основывается на перетаскивании объектов одного приложения в другое (или в то же самое). Объектами могут быть файлы, текст или прочие типы данных. Такие данные могут быть сразу авто- матически обработаны принимающим приложением, если оно поддерживает типы перетаскиваемых объектов. Например, Проводник ОС ЧЛПпскжз часто является источником для перетаскивания списка файлов. Для того чтобы пе- ретащить объект, нужно лишь просто нажать на него левой кнопкой мыши, не отпуская кнопки, переместить его в нужное место и отпустить кнопку. предоставляет класс опгад для транспортировки данных посредством с!га§ & скор и класс омхтеоаса для размещения этих данных. Чтобы из виджета можно было перетаскивать объекты, необходимо переоп- ределить методы тоизеРгезз&уепь () и тоиземоуеЕуепь (). Для получения пе- ретаскиваемых объектов необходимо переопределить методы агадЕпьегЕуепс () и агорЕуепъ (), а также вызвать (обычно это делается в конструкторе) метод зеЬАссерыэгорз (), передав В него Ъгие. позволяет создавать свои собственные типы данных для перетаскивания.
Глава 31 Интернационализация приложения Штирлиц просматривал электронную поч- ту. Незаметно входит Мюллер. У Штир- лица на экране бессмысленный набор сим- волов. "Шифровка!!!" — подумал Мюллер. "Коь8" — подумал Штирлиц. Современные коммуникационные технологии до предела сокращают не только время, но и расстояния. Наш огромный мир плотно опутан средствами коммуникации. Но именно эти современные технологии накладывают до- полнительные требования на реализацию программ. Уже не обойтись без поддержки интернационализации, то есть возможности выбора языка интер- фейса, чтобы каждый пользователь мог выбрать свой родной язык. Для поддержки интернационализации в ваших (^-приложениях требуется проделать следующие шаги: 1. Подготовить приложение к интернационализации (если оно еще не го- тово). 2. Запустить утилиту 1ираасе. 3. Перевести команды созданной программы (в этом может помочь прило- жение ос Ыпдихзь). 4. Запустить утилиту 1ге1еазе для генерации двоичных файлов переводов, которые впоследствии будут загружаться в объект класса отгапз1асог. Рассмотрим эти шаги поподробнее.
Подготовка приложения к интернационализации Нужно стремиться делать ваши приложения готовыми к интернационализа- ции. На практике это значит, что при программировании все строки, предна- значенные для вывода на экран, нужно заключать в статический метод ы (), определенный в классе ооьзесе. Текст, переданный в этот метод, является источником для перевода. Обычно в качестве источника перевода используется текст на английском языке. Пер- вым параметром в метод гг() передается текстовая строка. Второй пара- метр— комментарий, используемый для предоставления дополнительной информации переводчику, он не является обязательным и может быть проиг- норирован. Например: зеСТехС(Сг("Уез")); Важно помещать в функцию ы () не только текст, предназначенный для ото- бражения на экране, но и обозначения валюты, клавиши ускорителей и дру- гие отличительные особенности, присущие конкретной стране. Интернацио- нализация, на самом деле, это гораздо больше, чем просто перевод текстов с одного языка на другой, она включает в себя также локализацию, в которую входит, например, адаптация разнообразных форматов дат и цифр. Напри- мер, представление числа 2876,56 в английском варианте — 2,876.56, а в не- мецком — 2.876,56. Строки преобразуются в числа и наоборот в соответст- вии с языком и географическим расположением. Например: 2Ьоса1е епдИзк(0Ьоса1е: гЕпдИзк, *рЬоса1е: :Ш1Ёе<ЗК1пд6ат) ; ОЗЬгхпд зСг = епдИз11.Ёо5Пг1пд(2876.56) ; // зЪг = 2,876.56 0Ьоса1е дегшап(0Ьоса1е::(Зегтап, 0Ьоса1е::Сеппапу); зПг = дегтап.Со51:г1пд(287б.56) ; //зЬг = 2.876,56 Утилита 1ирс!а1е После передачи в метод сг () всех нужных строк текста можно приступать к созданию файлов перевода. Для этого необходимо воспользоваться специ- альной утилитой ХирйаСе. В данном случае назначение метода ьг () сводит- ся к указанию утилите текста в программном коде, который нуждается в пе- реводе. Из строк, переданных в метод сг(), будут созданы отдельные Т8- файлы (Тгап$1абоп Зоигсе, источник перевода) — файлы переводов. Чтобы создать для программы, приведенной в листинге 31.1 и показанной на рис. 31.1, файлы русского и немецкого переводов н локализовать программу, нужно выполнить следующий вызов: 1ирдаЪе шахп.срр -Сз птаЛп—ги.Ез таЛп_сЯе.Сз
Рис. 31.1. Сообщение на английском языке #1пс1ис1е <ОЬ(Зи1> 1пС 1ла1п(1пЬ агдс, скат** аг^) { ОАррИсаЫоп арр(агдс, аг^) ; ОЬаЪе! 1Ы (ООВдесЁ: : Ъг ("Не11о”) ) ; 1Ы.зИою() ; геЪигп арр. ехес (); В результате будут созданы файлы переводов в формате ХМЬ, содержащие следующий код: <!1Х)СТУРЕ ТЗхТЗ> <сопЁех1:> <пате>20Ьэ есЪ< /паше> <теззаде> <зоигсе>Не11о</зоигсе> <Ъгапз1аС1оп Ъуре="ип^1п1зИед"></Ъгап81аЪ1оп> </теззаде> </соп^ехЪ> </Т8> В этот файл можно внести перевод "от руки". Для этого в теге сгапзХаЫоп нужно удалить атрибут ьуре вместе с его значением и добавить перевод в текстовую зону тега. Например, для русского варианта тег ыапзХаЫоп бу- дет выглядеть так: <Ъгапз1аЪ1оп>Здравствуй</Ъгапз1аЪ1оп> Подготовленные файлы переводов необходимо включить в файл проекта. Для этого в него необходимо внести следующую строку: ТКАЫЗЬАТЮЫЗ = та1П—ги.Ёз та1п_с!е.Ъз
Программа 01 Ыпдшз! Одна из самых важных задач в интернационализации — это переводы. Нуж- но предоставить целую серию переводов для всех строк вашего приложения, для каждого поддерживаемого языка. Можно, конечно, вносить переводы и от руки, как мы это только что показали, но гораздо удобнее воспользоваться специально предназначенной для этого программой. Программа входит в пакет (X и предоставляет более удобный способ для редактирования файлов переводов. Применение этой программы незаменимо в больших про- ектах. Для начала редактирования требуется запустить программу, передав ей в качестве параметра файл перевода. Следующая команда запускает ОС Ыпдихзе и загружает в него файл русского перевода (рис. 31.2): Ипдихзе та!п_ги. из • 1г 2ЛТТТТТТТТ'" - 31 [гпдлз! Р(1е ЕЛ Тгап51айсп Шйайоп РЬгагез ^0^ > с СогиехГ 15 X >:? Соп1ех1 Мегт .у $оигсеЮ1 ? ООЬ}«г 0.1 § НеПо ИВ® а х $оиг се5 опб 5оггд5 г? X ' агдс, сНаг** агду} ; ЗАрркаЬоп арр(агдс, агду); ' осаЬе! : 1Ы.5ПС«0г геГип арр.еккО; } Боигсе Нейо Ки^ип 1гап51а(юп Привет Кинып 1гап«йа1ог соштепЬ РЬга&ез апй диегаег 5 х <5 х Баигсе рНга$е Тгап$1а!юп ОеКтПоп Рис. 31.2. Программа О1 ЫпдЫз!
Слева показана группа элементов перевода, то есть содержимое нашего Т8- файла. В верхнем правом окне показана таблица со следующими колонками: Зошгсе 1ех1 (Исходный текст), ТгапккШоп (Перевод) и Вопе (Состояние). Если текст переведен, в поле Оопе (Состояние) будет стоять галочка, в про- тивном случае — знак вопроса. В среднем окне в поле Тгап81аНоп (Перевод) можно вводить и изменять перевод исходного текста 8оигсе 1ех1 (Исходный текст). Так как приложения часто используют одни и те же фразы в разных местах, то окно РЬгааеа апд {*ие$5е$ (Фразы и догадки) автоматически может предлагать, на основании этих фраз, варианты перевода. После завершения работы с файлом перевода, его необходимо сохранить. Для этого выберите команду меню И1е | 8ауе (Файл | Сохранить). Можно преобразовать Т8-файл в рМ-файл (Мезза^ез П1е, файл сообщений) для дальнейшего использования перевода в приложении. Для этого выберите команду меню РИе | Ке1еазе (Файл | Релиз), после чего появится стандартное диалоговое окно сохранения файла, в котором нужно указать имя файла и нажать кнопку 8ауе (Сохранить). Утилита 1ге1еаае. Пример программы, использующей перевод Для конвертирования файлов переводов в рМ-файлы можно воспользоваться одной из трех утилит ХирдаЬе, р11лп§ш$1 и 1ге1еазе. Но лучше всего эту опе- рацию проводить с помощью утилиты 1ге1еазе, так как именно она обеспечи- вает нам лучший фактор сжатия для рМ-файлов. Следующая команда соз- даст для каждого файла перевода (Т8-файла), указанного в файле проекта, свой рМ-файл: 1ге1еазе щургоэесъ.рго Полученные РМ-файлы передаются в объект класса отгапз1асог. Объект класса отгап81асог отвечает за перевод текстов с одного языка на другой. Этот перевод производится посредством рМ-файла, загруженного в объект класса <2Тгапз1аьог, вызовом метода 1оад(). Загрузку других РМ-файлов можно производить в любой момент исполнения программы. Программа, приведенная в листинге 31.2, отображает на экране сообщение на русском языке, соответствующее английскому "НеПо" (рис. 31.3). Рис. 31.3. Переведенное сообщение на русском языке
..?ТТМ'™";. ц*«!!!55*8Л..........................................................::... 1ПЁ тахпЦпЪ агдс, сЪаг** агду) { ОАррИсаехоп арр(агдс, агду); ОТгапзХаЪог ЬгапзХа1юг; рЪгапзХаЪог. Хоад ("И1аХп_ги. срп", арр. 1пзГа11Тгапз1а1:ог (&Сгапз1а1тог) ; ОЪаЬеХ 1Ы (ООЬд ес1::: ёг (”Не11оп)); 1Ы. зИсж () ; ге^игп арр.ехес(). ; } В листинге 31.2 создается объект класса отгапаХаъог. Вызовом метода 1оад() в него загружается файл перевода та1п_ги.дт, указанный в первом параметре этого метода. Второй параметр задает директорию, содержащую файлы переводов. В нашем случае, вторым параметром передается строка, содержащая точку, — это говорит о том, что рМ-файлы находятся в той же директории, что и само приложение. Вызов метода ХпзсаИТгапзХаеог () из объекта класса ОАррИсаЫоп применяет созданный объект переводчика ко всему приложению. Хорошим тоном считается предоставление пользователю возможности изме- нять язык интерфейса выбором пункта меню или в диалоговом окне настрой- ки приложения. После смены языка пользователем требуется в процессе ра- боты программы динамически сменить язык. Для этого можно создать отдельный метод, который может выглядеть следующим образом: уоХсХ МуРгодгаш::з^ХЪсЪЬапдиаде (1пё п) { 0Тгапз1аЪог ЪгапзХаСог; зюхСсИ (п) ( сазе КП551АЫ: ЁгапзХаЁог. ХоасЦ "шургодгаш_ги. <дш" ,”.“); Ьгеак; сазе СЕКМАЫ: СгапзХаСог. ХоасХ ("шургодгаш_с1е. дт" Ьгеак; } дАрр“->ХпзЬаХХТгапзХаСог (йЬгапзХаЬог) ;
Также нелишне будет в случае, если пользователь еще не успел определиться с выбором языка (например, при первом запуске программы), установить текущий язык в соответствии с выбранной локализацией на платформе. Выбранную лока- лизацию можно узнать при помощи статического метода оьосасе::зузьет(). Этот метод возвращает объект класса оьосаъе, а вызов метода пате () этого объ- екта возвращает нам строку вида "язык_СТРАНА". Например, для США эта строка будет выглядеть следующим образом "еп_П8". В соответствии с этой строкой можно будет загрузить необходимый файл перевода. Приведем для на- глядности небольшой отрывок, позволяющий это сделать: 0Тгалз1аЪог Ыапз1а1:ог; ОЗСгхпд зЪг = 0Ьоса1е: :зу511ет() .пате О ; (зёг == ••еп.ПЗ’1) { ЪгапзХаНог. ХоасЗ.('’тургодгат_из.дт"//загружаем английский //перевод для США } е1зе (зСг -= "<Хе_СН") { Ъгапз1аЪог. Хоад ("тургодгат_йе. дт”, **.м); //загружаем немецкий перевод //для Швейцарии } • е1зе (зЪг == "ги") { Ггапз1аСог. 1оас1 ("тургодгат_ги.суп"//загружаем русский перевод } дАрр->1пзЪаХХТгапзХаЪог(&егапзХаЬог); Резюме Всего 4 шага отделяют обычное приложение от приложения с поддержкой интернационализации. Статический метод его класса ооьзесс играет сразу две роли. Во-первых, он помогает утилите 1ир<1а(е находить в программе текст, нуждающийся в переводе. Во-вторых, он является методом для пере- вода текста. Этот метод очень эффективен в поиске строк перевода, поэтому не нужно бояться снижения производительности программ. Чтобы подготовить перевод, нужно запустить программу 1ирс1а<е, которая создаст файл перевода с расширением (8 (Т8-файл) на основании исходного кода в С++-файлах. Этот файл необходимо перевести "от руки" или с помо- щью программы 01 Для использования перевода в программе необ- ходимо переработать файлы перевода в ОМ-файлы. Это преобразование про- изводится при помощи утилиты 1ге1еа8е. Загрузка рМ-файлов в объект класса отгапзХаеог осуществляется мето- дом 1оа<з(). После этого объект перевода устанавливается в приложение С ПОМОЩЬЮ метода 1пзЬа11Тгапз1аСог() класса ОСогеАррИсаЪхоп.
Глава 32 Создание меню — А что у нас сегодня в меню (Мели)? — Филе! (Н1е) Виктор Свитковский Меню является важной и неотъемлемой частью практически любого прило- жения. Главное меню находится в верхней части главного окна приложения и представляет собой секцию для расположения большого количества команд, из которых пользователь может выбирать нужную. В приложениях исполь- зуются меню четырех основных типов: □ меню верхнего уровня; □ всплывающее меню; □ отрывное меню, которое можно отделять от основного; □ контекстное меню. В библиотеке меню реализуется классом омепи. Этот класс определен в заголовочном файле <2Мепи. Основное назначение класса — это размеще- ние команд в меню. Каждая команда представляет объект действия (класс ОАсЫоп, см. главу 35). Все действия или сами команды меню могут быть со- единены со слотами для исполнения соответствующего кода при выборе ко- манды пользователем. Например, если пользователь выделил команду меню, то и меню, и объекты действий высылают сигнал.Ьоуегед (), и если вам по- требуется произвести в этот момент какие-либо действия, то их нужно со- единить с соответствующим слотом. Анатомия меню Пользователю будет легче привыкнуть к работе с новой программой, если ее меню будет похоже на меню других программ. На рис. 32.1 показаны состав- ляющие типичного меню.
1 команда с _ ) пиктограммой клавишное сокращение меню верхнего уровня ( подменю ( разделитель / недоступная А__команда_. Рис. 32.1. Анатомия меню Основной отправной точкой меню является меню верхнего уровня. Оно представляет собой постоянно видимый набор команд, которые, в свою оче- редь, могут быть выбраны при помощи указателя мыши или клавиш клавиа- туры. (<Ак> и клавиш управления курсором). Команды меню верхнего уров- ня предназначены для отображения выпадающих меню, поэтому их не следует использовать для других целей, так как это может изрядно озадачить пользователя. Старайтесь логически группировать команды и объединять их в одном выпадающем меню, которое, в свою очередь, будет вызываться при выборе соответствующей команды меню верхнего уровня. Класс омепиваг отвечает за меню верхнего уровня и определен в заголовочном файле ОМепиВаг. "Горячие" клавиши информируют пользователя о комбинации клавиш, с по- мощью которой выполняется то же действие, что и при выборе соответст- вующей команды меню. Например, для отображения окна А|^ои101 (О 01), в примере, показанном на рис. 32.1, используется комбинация клавиш <С1г1>+<0>. Старайтесь использовать стандартные комбинации для "горя- чих" клавиш. Некоторые из них указаны в табл. 32.1. Таблица 32.1. Некоторые стандартные комбинации для "горячих"клавиш Клавиши Описание Клавиши Описание <Езс> Отменить текущую опе- рацию <С1г1>+<7> Отменить предыдущее действие <Е1> Вызвать файл помощи <С1г1>+<Х> Вырезать <8ЫЙ>+<Е1> Вызывать контекстную помощь <С1г1>+<0> Копировать
Таблица 32.1 (окончание) Клавиши Описание Клавиши Описание <С1г1>+<М> Создать <С1Н>+<У> Вставить <С1г!>+<0> Открыть <С1г1>+<Е4> Закрыть активный доку- мент МО1-приложения <С!г1>+<Р> Печать <С1г1>+<Е6> Активировать окно про- смотра следующего доку- мента МО1-приложения <С1г1>+<8> Сохранить <8ЫЙ>+<С1г1>+<Е6> Активировать окно про- смотра предыдущего до- кумента МО1-приложения По возможности, для всех пунктов меню должны быть определены клавиши быстрого вызова. Это позволит пользователю выбирать команды не только при помощи мыши, но и при помощи клавиатуры, нажав подчеркнутую бук- ву (в названии команды) совместно с клавишей <А1(>. Например, для выбора команды ЕхИ (Выход) нужно нажать <А1о+<Е> (см. рис. 32.1). Основные отличия подобного рода комбинаций для быстрого вызова от "горячих" кла- виш состоят в следующем: □ такие комбинации состоят из буквенной клавиши и клавиши <А11>; □ они встречаются не только в меню, но и в диалоговых окнах; □ они представляют собой контекстное исполнение команд. Например, что- бы вызвать диалоговое окно АЬоиС (О (ЗД, нужно открыть меню Мели (Меню) комбинацией <АП>+<М>, а затем нажать <А11>+<А>. Стрелка у команды 8иЬМепи (Подменю) (см. рис. 32.1) говорит о том, что при выборе этой команды появится вложенное подменю, в нашем случае — Те§1 (Тест). Вложенное подменю удобно для того, чтобы разгрузить меню, если оно содержит большое количество команд. Для удобства понимания программы рекомендуется, чтобы степень вложенности не превышала двух. Разделитель — это черта, которая отделяет одну группу команд от другой. Команда с флажком используется для управления режимами работы прило- жения. Установленный флажок сигнализирует об активированной команде меню. Пиктограмма команды отображает команду меню в графическом виде. Это очень хороший прием для дополнительной иллюстрации действий самой команды. Иногда встречаются команды, которые нельзя исполнить в определенный момент времени. В таких случаях приложение должно делать такие команды
недоступными, то есть делать их выбор невозможным. Недоступные коман- ды меню отображаются другим, как правило — серым, цветом. Также помните, что в случаях, когда команда меню вызывает диалоговое окно, в ее название принято добавлять в конец многоточие. Это правило, правда, не распространяется на вызов простых окон сообщений. В листинге 32.1 реализуется меню, показанное на рис. 32.1. -ч. * .... #1пс1ис1е <ОС.ойх> #1пс1ис1е "1тд4.хрт' 1пЬ ша±п(1пЪ агдс, сЪаг** агдгу) ( ОАррНсаЪхоп арр(агдс, а^дV); ОМепиВаг шпиВаг; ОМепи* ртпи = пего ОМепи( "&Мепи") ; ртпи->аскДАс111оп (" &АЬоиС ОЪ", &арр, ЗЬОТ (аЬоиЪОЁ: ()) , 01:::СТВЬ + 011: :Кеу_0 ); ртпи->аск±5ерага11ог () ; 0Асё1оп* рСЪескаЫеАсЪхоп = ртпи->а<Я<ЗАсЪ1оп ("бсСкескаМеТ^ет”); рСкескаЬ1еАс1:1оп->зеЪСкескаЫе (1:гие) ; рСкескаЬ1еАсЪ1оп->5е1:С11ескес1 (Ъгие); ртпи->а<±ЗАсе1оп (ОРххтар (1тд4_рдзт), ” &1соп1Ъет") ; ОМепи* ртпиЗиЬМепи = пем 0Мепи( "&ЗиЬМепиИ, ртпи); ртпи->а<±ЗМепи (ртпиЗиЪМепи) ; ртпи5иЬМепи->ад<ЗАс111оп( "йТезЪ"); ОАсЪхоп* рВ1заЫес1Ас1:1оп = ртпи->адбАсЫоп( м&В1заЫед1Ъет");
рО!заЫеаАсс!оп->8еСЕпаЫеа(Са1зе); рп1пи->аск15ерагаЪог () ; ргппи->ас1с1Асе1оп ("&Ех!Ь", &арр, ЗЬОТ (ди! Ц))); шпиВаг.ааамепи(ртпи); шпиВаг.зЪои(); геСит арр. ехес (); } Чтобы создать полноценное меню, потребуется к каждой команде меню верхнего уровня присоединить соответствующее всплывающее меню. За всплы- вающие меню отвечает класс омепи. Итак, для создания меню Необходимо иметь виджет класса омепиВаг— указатель ртпиваг и, по меньшей мере, один виджет класса омепи — указатель ршпи (листинг 32.1). Для добавления всплывающего меню к меню верхнего уровня нужно пере- дать в метод ааалсЫопО название команды. Каждый метод ааалсЫопО возвращает указатель на объект действия оасЫоп. Пользуясь этим указате- лем, можно получить доступ к команде меню. Вызов метода зеьсъескаые () объекта действия (в нашем случае рсъескаЫедсЫоп) предоставляет возмож- ность установки флажка. Дальнейший вызов метода зессЬескеа () устанав- ливает состояние флажка. В нашем примере в этот метод передается значе- ние сгие, а это значит, что он будет находиться во "включенном" состоянии. Метод ааадсЫопо принимает четыре параметра. Первый задает название команды меню, в котором можно указать букву для быстрого вызова, поста- вив перед ней символ &. Не упускайте из виду того, что разные команды ме- ню должны использовать разные буквы для быстрого вызова, в противном случае одна из них окажется недоступной. Вторым параметром передается указатель на объект, содержащий слот, который вызывается при выборе этой команды. Сам слот передается третьим параметром. Последний параметр за- дает комбинацию для "горячей" клавиши. В нашем примере для отображения диалогового окна АЬоп! 01 (О 00 используется комбинация <С(г1>+<0>. Нажатие этой комбинации приведет к тому же действию, что и выбор соот- ветствующей команды меню, а именно — будет вызван слот объекта прило- жения аЬоигое (). Для составления комбинаций "горячих" клавиш можно воспользоваться табл. 14.2. Вызов метода ааазерагаьог () добавляет разделитель в меню.
Указателем на объект действия (рв1заЫеАс(:1оп) можно воспользоваться также и для того, чтобы сделать некоторые из команд меню недоступны- ми — с помощью метода зеЬЕпаЫед(). В метод ададсЫоп() первым параметром можно передавать объекты растро- вых изображений для установки пиктограммы команды. Отрывные меню предоставляет возможность реализации отрывных меню (Теаг оТТ шепи). Нажатие мышью на прерывистую линию приводит к тому, что всплывающее меню отделяется от меню верхнего уровня, превращаясь в отдельное окно, которое свободно перемещается (рис. 32.2). Такое меню очень удобно, на- пример, для настройки конфигураций программы. Рис. 32.2. Отрывное меню #1пс1ис1е <0Ъ(Зи1> 1пё та!п(1пе агдс, скаг** агду) { 0Арр11саЬ1оп арр(агдс, агду); ОМепиВаг ртпиВаг; ОМепи* ртпи = пею ОМепи ("йМепи"); ртпи->зеЁТеагО^СЕпаЫед (Ыие) ; ртпи->асЯ(ЗАсС1оп (’’ Тоетой!");
ртпи->ас!с1АсС1ОП (" 1Ъе1П&2") ; ргкпи->ад<ЗАсС1оп (” ТЪеш&З ”); рпши->адс1АсЫоп (” &ЕххС", &арр, ЗЬОТ (дизХ ())); рипиВаг. аскЗМепи (ртпи) ; ргппиВаг. зИою (); ге^ига арр.ехес() ; } Чтобы задать отрывное меню, необходимо вызвать метод зе^теагоИЕпаЫед () виджета меню ршпи, передав ему значение сгие< — это отобразит линию от- рыва на верхнем бордюре всплывающего меню. Контекстные меню Визитной карточкой профессионального приложения является наличие кон- текстного меню. Контекстное меню — это меню, которое открывается при нажатии правой кнопки мыши. Для его реализации, как и в случае всплы- вающего меню, используется класс омепи. Отличие состоит лишь в том, что это меню не присоединяется к виджету омепиВаг. На рис. 32.3 показано окно программы и контекстное меню, отображаемое при нажатии правой кнопки мыши. В этом меню пользователь может выбрать одну из трех команд — Кед (Красный), Сггееп (Зеленый) или В1ие (Синий), которые задают соответст- вующий цвет фона окна. Рис. 32.3. Контекстное меню _Соп1:ехШепи_11_ #с!еГ1пе _СопЪехШепи_
#1пс1и<Яе <0Г:(Зи1> // ================================================================ сХазз Соп^ехЁМепи : риЬНс ОТехЬЕсНЬ { 0_ОВСГЕСТ рга^аЪе: фМепи* хтсртпи; ргоЬесЁед: V^^еиа1 Л7о1с1 соп^ех^Мепи^епГ: (ОСопЪех^епиЕХгепЬ* ре) { т_ршпи->ехес(ре->дХоЬаХРоз()); } риЬХхс: СопЪех131епи(0Ю1с1дее* рзддЪ = 0) : ОТехСЕсЬХ.(рмдЪ) { зеЁКеадОпХу(Ьгие); пирхппи = пею ОМепи(ЪЫз); т_ртпи->аддАсЪ1ОП ("&КесГ) ; пиртпи->аддАсЪ1оп ("&Сгееп"); щ_ртпи->аддАс!:хоп (’'&В1ие”); соппес С (т_ртпи, 5ТОМАЬ{Ъг1ддегеа(0АсЪ1оп*)), 5ЬОТ (зХоЪАсЁгуаЪеа (ОАсЪхоп*)) ); } риЬНс з1оёз: То1с1 з1оСАсС^аЬе<1(2Ас^1оп* рАс^хоп) { ОЗЪгхпд з^гСоХог = рАсЬХоп->ЬехЪ() .гетскге( ; 5еЪНШХ(05Мг1пд(’,<ВОПУ В6С0Ь0К=%Хх/В0ПУ>”) .агд(зПгСоХог)) ; } }; #еп<31 С / /_Сопеехи4епи_И_
В листинге 32.3, в конструкторе класса сопьехсмепи, создается виджет кон- текстного меню — указатель т_ршпи. С помощью метода ааалсЫоп () добав- ляются команды меню. Метод соппесьо соединяет сигнал меню сг1ддегес1(<2Ас(:1оп*) со слотом .зХоСАсЕгуаьесЦОАссХоп*). Сигнал высыла- ется каждый раз при выборе пользователем одной из команд меню. Этот слот получает указатель на объект действия. Благодаря тому, что наш класс сопьехьмепи унаследован от класса отехеваИ:, мы можем устанавливать цвет фона при помощи строки в формате НТМЬ — нужно только вызвать метод дтехгаНь: гзеьнстК). Сам цвет устанавливается в соответствии с именем выбранной команды, из которого удаляется символ &, для чего вызывается метод 05Ы 1пд:: гепкхуе (). Строка с цветом записывается в переменную 8ЪгСо1ог. Показ контекстного меню выполняется из метода обработки события контек- стного меню <2и1адеь:: сопьехсмепи^епс () и должен производиться на мес- те (координатах) указателя мыши, при нажатии правой кнопки. Для этого нужно передать в метод ехес () значение, возвращаемое методом д1оЬа1Роз() объекта события контекстного меню. Этот метод возвращает объекты класса орохпъ, содержащие координаты указателя мыши относи- тельно верхнего левого угла экрана. Резюме Большинство программ поддерживают меню, которое предоставляет пользо- вателю разнообразные возможности выбора команд, управляющих различно- го рода действиями. Меню можно разделить на четыре основных типа: меню верхнего уровня, всплывающие, отрывные и контекстные меню. Для всех пунктов меню должны быть определены клавиши для быстрого вызо- ва, символы которых обозначаются знаком подчеркивания в названии команды меню, а для наиболее важных или часто используемых команд — "горячие" клавиши, представляющие собой комбинации клавиш, которые интерпрети- руются программой как команды меню. Если вызов команды отображает диа- логовое окно, то рекомендуется добавлять в конце имени команды три точки. Недоступные команды меню отображаются, как правило, серым цветом, сооб- щая пользователю о том. что данная команда не может быть выполнена.
Глава 33 Диалоговые окна Диалог с новой версией УУтс1о\у$ с ис- кусственным интеллектом: ХУ1П(1о^8: Вы действительно хотите удалить этот файл? Пользователь: Да! 5У1пс1о>у5: А почему? Диалоговое окно — это центральный элемент, обеспечивающий взаимодей- ствие между пользователем и приложением. Этот виджет может содержать ряд опций, изменение которых в ходе работы влечет за собой изменение в работе самой программы. Диалоговые окна всегда являются виджетами верхнего уровня и имеют свой заголовок. Их можно разбить на три основные категории: □ собственные; □ стандартные; □ окна сообщений. Правила создания диалоговых окон Диалоговые окна важны в любом приложении, и их создание — это рутина, которую часто приходится выполнять разработчику. Создание диалогового окна, на самом деле, включает в себя гораздо больше, чем просто размеще- ние нужных элементов. Важно обеспечить пользователю возможность ин- туитивной работы с диалоговым окном, чтобы ему не пришлось тратить вре- мя на изучение, а можно было сразу начать работать. Для обеспечения интуитивной работы необходимо учитывать следующие правила: □ стремитесь к тому, чтобы диалоговое окно не содержало ничего лиш- него и было как можно проще. В диалоговом окне настроек программы
желательны только основные кнопки, например: ОК, Сапсе! (Отмена) и Арр1у (Применить); □ объединяйте виджеты в логические группы, снабжая их прямоугольной рамкой и подписью (см. главу 8). Используйте горизонтальные и верти- кальные линии для разделения; □ никогда не делайте содержимое диалогового окна прокручивающимся. Если окно содержит много элементов, то постарайтесь разбить их на группы и разместить их с помощью закладок (см. главу 77); □ нежелательно, чтобы закладки в диалоговом окне занимали более одного ряда, это усложняет поиск; □ избегайте создания диалоговых окон с неизменяемыми размерами. Поль- зователь всегда должен иметь возможность увеличить или уменьшить размеры по своему усмотрению; □ сложные диалоговые окна лучше снабжать дополнительной кнопкой Не1р (Помощь), при нажатии на которую должно открываться окно контекст- ной помощи; □ команды меню, вызывающие диалоговые окна, должны оканчиваться ' многоточием, например Орел... (Открыть...). Это делается для того, что- бы пользователь заранее знал, что нажатие команды меню приведет к от- крытию диалогового окна; □ старайтесь не добавлять меню в диалоговые окна. Меню должны исполь- зоваться в окне основной программы; □ по возможности используйте стандартные виджеты, хорошо знакомые пользователям. Не забывайте, чтЪ для освоения новых элементов управле- ния может понадобиться дополнительное время; □ для показа настроек избегайте использования цвета. В большинстве слу- - чаев текст — лучшая альтернатива. Ведь один и тот же цвет может иметь, в разных странах, разные смысловые значения. Также не следует исклю- чать пользователей, неспособных различать цветовые оттенки; □ не забывайте, что пользователь должен работать с диалоговым окном не только с помощью мыши, но и с помощью клавиатуры. Для этого необхо- димо снабдить все элементы окна клавишами быстрого вызова, которые позволят, нажав букву совместно с клавишей <Ак>, установить фокус на нужном элементе. Класс СЮ/а/од Класс 0О1а1од является базовым для всех диалоговых окон, представленных в классовой иерархии (см. рис. 5.1). Хотя диалоговое окно можно созда-
вать при помощи любого виджета, сделав его виджетом верхнего уровня, тем не менее, удобнее воспользоваться классом оо1а1од, который предоставляет ряд возможностей, необходимых всем диалоговым окнам. Диалоговые окна подразделяются на две группы: □ модальные; □ немодальные. Режим модальности и немодальности можно установить и узнать при помо- щи методов дШаХод:: зепмойа! () и <2п1а1од:: тзмода! () соответственно. Значение егие означает модальный режим, а еа1зе — немодальный. Модальные диалоговые окна Такие окна обычно используются для вывода важных сообщений. Например, иногда возникают ошибки, на которые пользователь должен отреагировать, прежде чем продолжить работать с приложением. Модальные окна прерыва- ют работу приложения, и для продолжения его работы такое окно должно быть закрыто. В этих случаях модальный диалог — идеальное средство для привлечения внимания пользователя. Для блокировки приложения запускается цикл события только для диалого- вого окна, а остальные события клавиатуры, мыши и других элементов при- ложения просто игнорируются. Этот цикл запускается вызовом слота ехес (), который возвращает значение целого типа после закрытия диалогового окна. Это значение информирует о* нажатой кнопке и может равняться 0О1а1од: :АссерСеа или 0Ога 1од: :КедесЬеа, ЧТО соответствует кнопкам ОК и Сапсе! (Отмена). Типичным примером модального окна является напоми- нание пользователю о необходимости сохранения документа перед закрыти- ем приложения. В момент отображения этого окна возможность работы с са- мим приложением должна быть заблокирована. Принцип вызова модального диалогового окна примерно следующий: МуЭ1а1од* рд1д = пем МуЭ1а1од (ЬсЗаЪа) ; И: (р31д->ехес () == 0Э1а1од: :АссерЁе<Я) { // Пользователь выбрал Ассер^ед // Получить данные для дальнейшего анализа и обработки ПаЪа да!:а = р<Д1д->де!:БаЬа (); } <3е1е1:е р61д;
Немодальные диалоговые окна -А Немодальные диалоговые окна ведут себя как нормальные виджеты, не пре- рывая при своем появлении работу приложения. На первый взгляд может по- казаться, что применение немодальных диалоговых окон имеет больше смысла, так как в этом случае пользователь обладает большей свободой в своих действиях. Но, на самом деле, большинство приложений часто нуж- дается в приостановке до принятия решения пользователем перед возобнов- лением дальнейших действий. Однако и немодальные диалоговые окна ис- пользуются не реже. Немодальное окно может быть отображено с помощью метода вЬо»(), как и обычный виджет. Метод зЪошО не возвращает никаких значений и не ос- танавливает выполнение всей программы. Метод Ыде () позволяет сделать окно невидимым. Этой возможностью можно воспользоваться, чтобы не соз- давать каждый раз объект диалогового окна и не удалять его из памяти при закрытии. Если ограничиться вызовом методов зЪо«() и ыае(), это даст возможность отображать диалоговое окно на том же месте, на котором оно было скрыто. Немодальные диалоговые окна необходимо снабжать кнопкой С1о$е (Закрыть) для того, чтобы дать возможность пользователю закрыть его. Осторожно! На Мас 08 X используется совсем другой подход при отображении немодаль- ных диалогов. Для того чтобы немодальный диалог стал виден, одного вызова метода зИои () будет недостаточно, поскольку диалог будет скрыт окном ос- новного приложения. Поэтому после вызова метода зИо^о должны всегда следовать вызовы гахзе () и асЬгуаСеИхпскэ» (). Создание собственного диалогового окна Программа, показанная на рис. 33.1, иллюстрирует создание собственного диалогового окна. При запуске на экране появляется окно с кнопкой Рте» Ме (Нажми меня), нажатие на которую отображает диалоговое окно ввода имени Г1Г81 Мате (Имя) и фамилии Мате (Фамилия). .. ; Мб .. .'приЮн!сд Рис. 33.1. Собственное диалоговое окно
#хпс1и<1е <0Арр1хсаЪхоп> #хпс1иёе •’ 31:агЪВха1од. к" хпе тахп(хп1: агдс, сИаг** агду) ( ОАррИсаЪхоп арр(агдс, агду); 5!:агШ1а1од зЪагЪВхаХод; з^агЫЭхаХод. зИою (); геЪит арр. ехес (); } В листинге 33.1 создается виджет класса 5ъагепха1од, предназначенный для запуска диалогового окна. _5Ьагиэ1а1од_И__ #де^хпе _5^агОЭ1а1од_И_ #хпс1и6е <0«зи1> #1пс1иде "1приЫЯа1од.Ии // ======г=============================г===г===Ж====;===================;====г= с1азз 51:агЪОха1од : риЫхс 0Риз11ВиЁ1:оп { 0_ОВСГЕСТ риЬНс: 3^агЬО1а1од(0М1адеЁ* рюдЪ = 0) : ОРизЪВиПЪоп("Ргезз Ме", рюдЁ) { соппес!: (ЪЫз, ЗТСМАЬ (с1±скев ()), ЗЬОТ (з1оЪВиЪЬэпС1 хскес! ())) ; } риЬНс з1о1:з: уохб 81оЮи(±опС1 хскед () { 1прии>ха1од* р1приевха!од = пезд 1приЁОха1од;
(р1прииЯа1од->ехес () == ОШаХод: :АссерГеЗ) { ОМеззадеВох:: хп^огма^хоп (0, "Тп^огта^хоп", "РХгзС Кате: ” + р1приШ1аХод-->^1гзйЯате() + "ХпЬазЪ Ыате: " + р!приШ)1аХод->ХазЪЫате () ); } деХеСе рХприЪОХаХод; } }; #епс11 % / /_ЗПаг ЪБ1а1од_Т1_ Класс зьагыЯа1од унаследован от класса кнопки дриаьвисьоп. Сигнал сИскеао методом соппесСО Соединяется СО СЛОТОМ зХоЬВиЪЬопСИскеаО (листинг 33.2). В этом слоте создается объект диалогового окна хприыЯаХод, который не имеет предка. Примечание Диалоговые окна, не имеющее предка, будет центрироваться на экране. Окна с предками будут отцентрированы относительно предка. В условии оператора и производится запуск диалогового окна. После его закрытия управление передается основной программе, и метод ехес о воз- вращает значение нажатой пользователем кнопки. В том случае, если пользо- вателем была нажата кнопка ОК, будет отображено информационное окно с введенными в диалоговом окне данными. По завершении метода диалого- вое окно нужно удалить самому, так как у него нет предка, который позабо- тится об этом. #1^пде^ _1приЪБ1аХод_Ъ._ МеНпе _1приЪВ1а1од_Ъ_ #1пс1ис1е <0Э1аХод> сХазз фЬхпеЕсНЪ; / / ^============;г===========гг=ггг=======;=;=;==; с 1а53 1приЪБ1а1од : риЬИс ОПхаХод {
0-ОВОЕСТ ргдлгаСе: ОЫпеЕсИС* пирСхСГХгзСЫате; ОЫпеЕсНЬ* гп_рЬхСЬазСЫате; риЬИс: ХприСОхаХод(ОШддеС* р«дС = 0); ОВСгХпд НгзШатеО сопзС; ОЗСгхпд ХазСЫахпе () сопзС; }; #епс11С //_ХприСОХаХод_к_ Для создания своего собственного диалогового окна нужно унаследовать класс овхаХод, как это и сделано в листинге 33.3. Класс хлриспХаХод содер- жит два атрибута — указатели т_рСхСЕ1гзсыате и т_рсхсьаз скате на видже- ты однострочных текстовых полей и два метода, возвращающие содержимое ЭТИХ полей: ХХгзСЫахпе () И Хаз СКате (). г. •: ж: ХприССхаХод: :ХприСБхаХод(ОМХйдеС* рюдС/*= О*/) : ОЭхаХод(рмдС, ОС: :ШпдоадТХСХеН1пС [ ОС: :КХпдо\л75узСегпМепиН1пС) { т_рСхСГ1гзСЫате = лею ОЫпеЕсИС; псрСхСЬазСКате = пеад ОЫпеЕсИС; ОЬаЬеХ* рХЬХГхгзШате = лею ОЬаЬеХ(“&Е1гзС Кате”); ОЬаЬеХ* рХЬХЬазСЫате = лею ОЬаЪеХ (”&ЬазС Ыате"); рХЬХЕ±гзСЫате->зеСВид<Ху (псрСхСКХгзСЫате) ; рХЬХЬазСЫате->зеСВи<3<Ху (т_рСхСЬазСЫате) ; ОРизЪВиССоп* рстдОк = лею ОРизкВиССол ("&Ок” ) ; ОРизИВиССоп* рстйСапсеХ = лею ОРизИВиССол(''йсСалсеХ”) ; соллесС(рсшХОк, 51ОИАЬ(сХ1скед()), 5Ъ0Т{ассерС())); соллесС(рстгк5СапсеХ, 516НАЬ(сХ1ске<Х()), ЗЬОТ(ге^есС ())) ; //ЬауоиС зеСир ОСгХсЗЬауоиЬ* рСорЬауоиС - пем ООгХдЬауоиЬ;
рСорЬауоиС->ааан1ддеС(р1ЫН'1гзШап1е, О, О); рЬорЬауоиЪ->авдК1ддеЬ(р1ЫЬазШаше, 1, О); рСорЬауоиС->ааан1адеЬ(п\_рСхСГ1г8СЫате, О, 1); рСорЬауоиС->аааи1адеС (щ_рСхСЬазСЫате, 1, 1); рСорЬауоиС->ааан16деС (рстаЗОк, 2,0); рСорЬауоиС ->аааи1адеС (рстйСапсе!, 2, 1) ; зеСЬауоиС(рСорЬауоиС); По умолчанию область заголовка диалогового окна содержит кнопку ?, предназначенную для получения подробной информации о назначении вид- жетов. В нашем примере я решил пренебречь ею — для этого необходимо установить флаги окна, поэтому вторым параметром передаются флаги Ос: :И1п<Зо«т1ь1ен1пь и ос.: :и1п<Зо№узсе1пМепиН1пъ. Первый устанавливает заголовок окна, а второй добавляет системное меню с возможностью закры- тия окна (листинг 33.4). Модальное диалоговое окно всегда должно содержать кнопку Сапсе! (Отмена). Сигналы сИскеао кнопок ОК и Сапсе! (Отмена) соединяются со слотами ассерсо и геэесьеао, соответственно. Это делается для того, чтобы метод ехес о возвращал, при нажатии КНОПКИ ОК, значение 0О1а1од::АссерГеа, а при нажатии кнопку Сапсе! (Отмена) — значение оп1а1од: гне^ессеа. ОЗСгхпд ХприСО1а1од:: НггСИатеО сопзе { геСигп щ_рГхСР1гзСКате->сехс () ; ) Метод Нгзсыате () возвращает введенное пользователем имя (листинг 33.5). ОЗСгхпд 1приСБ1а1од::1азСИаше() сопзС { геСигп щ_рСхСЕазСЫате->СехС(); } Метод Е1гзсыаше() возвращает введенную пользователем фамилию (лис- тинг 33.61
Стандартные диалоговые окна Использование стандартных окон значительно ускоряет разработку тех при- ложений, в которых необходимо использовать диалоговые окна выбора фай- лов, шрифта, цвета и т. д. Вместо того, чтобы тратить время на разработку своих собственных классов, можно воспользоваться готовыми классами биб- лиотеки <2<. К достоинствам стандартных диалоговых окон можно отнести и целостность пользовательского интерфейса, так как вид окон во всех при- ложениях, их использующих, будет один и тот же. Диалоговое окно выбора файлов Диалоговое окно выбора файлов предназначено для выбора одного или нескольких файлов, а также файлов, находящихся на удаленном компьютере, и поддерживает возможность переименования файлов и создания директо- рий. Класс огиео1а1од предоставляет реализацию диалогового окна выбора файлов (рис. 33.2) и отвечает за создание и работоспособность сразу трех диалоговых окон. Одно из них позволяет осуществлять выбор файла для от- крытия, второе предназначено для выбора пути и имени файла для его сохра- нения, а третье — для выбора директории. Класс 0РЫеп1а1од унаследован от класса оШаХод. Его определение находится в файле <2РПеП1а1о§. Этот класс предоставляет следующие статические методы: □ депорепР11енате() создает диалоговое окно выбора одного файла. Этот метод возвращает значение типа озегХпд, содержащее имя и путь выбран- ного файла (рис. 33.2); □ деъорепРИенашеа () создает диалоговое окно выбора нескольких файлов. Возвращает список строк типа озсгХпдЫае, содержащих пути и имена файлов; □ де&ЗаVеЕ^1е^ате () создает диалоговое окно сохранения файла. Возвра- щает имя и путь файла в строковой переменной типа озегХпд; □ деЬЕхХзътпдШгессогу () создает окно выбора директории. Этот метод возвращает значение типа озьгтпд, содержащее имя и путь выбранной ди- ректории. Первым параметром этих методов является указатель на объект-предок, вто- рым передается текст заголовка окна, третьим — строка, представляющая собой рабочую директорию. Вызов метода дееорепРИенаше () запустит диалоговое окно открытия файла (рис. 33.2). Четвертый параметр, передаваемый в этот метод, пред-
ставляет собой фильтр (или маску), задающий расширение файлов. На- пример: ОЗегхпд зСг = ОШеОхаТод::деЮрепРИеЫате(0, "Ореп ВхаХод", и", • *.срр * .Ъ”); Орел Оге1сд « 1сса( 0с4 < > 01 > 4.1.2 » 3 С * У. > .11'1. -Г. И|О1м^ии11111111И * -2-2-г •. |..АН Е‘та115 Ос итеп!5 Рк!ше; Ми5!С 0 Нвте 0з1е тсс!|^ес1 Г. ре 5> СЁ . •. * 6-’Г?'2006 3:45 -4 5' , 4ег’сгп':ей--й .|е.С1."Г 0.-15 2006 ' 45 и* 1 ,М< Н€ 6 12 2‘лч: 2-45 г-’.! и ?--!* р111ес6а|С.д,<ор с ’1& 2й*5б 2'45 -и21 оРР Не сИМ^вх-д.!'- с 12 2к>5 2 45 -М :Г!«йе‘с:.\п.И .5 245 «\1 1||Ш111111111811111 - л; > ; (;Ыёгйз1од_;> т.грр б 1У 2’>36 5:к м.1 СР - ну к_дкпт^1з1сд.срр 6 15 30(1- 3 -15 ДМ СМ'Гй€ П*^'пТ'41з1сд.Н 6 13'2006 :-м дм 111111111111111111 - фприимкд.срр •5 12• 2006 3-45 ДМ <₽4 Ак ~ ‘ ". * ; , =:;трь1д'.акд.Ь 6 О '200с 315 «М Н Г Мгагпе' ж ' гс-о ’ г= ▼ : ^Ореп !▼ ; :.г- ге Рис. 33.2. Диалоговое окно выбора файлов Бгол$е Рсг ЕоШег I Я. Ое:Ис₽ -I* 1|||||||Г ьл Аорр; СкИ Сги-е & Соса! С)5к .С;) СОСте-О-.НЕЛ г 1 И 2 ‘Д 3 -/ 4 » Маке Неи* Ройсег } | ОК Сзпсе!
При помощи метода де^Еххзнхпдпхгесеогу () можно предоставить пользова- телю возможность выбора директории (рис. 33.3). Например: ОЗЁгхпд зСг = 0РИеЭ1а1од: :дееЕх1зЫпдВ1гес(:огу(0, "ОхгесЪогу О1а1од'“, Диалоговое окно настройки принтера Это окно позволяет выбрать принтер, изменить его параметры и задать диа- пазон страниц для печати (рис. 33.4). Диалоговое окно настройки принтера реализовано в классе 0₽г1пго1а1од, но вызывать его в отрыве от объекта принтера класса дргхпъег (см. главу 24) не имеет смысла, так как главная за- дача состоит в настройке этого объекта для вывода на печать. Например: ОРгхпЪег рг1п1:ег; 0Рг1п1:П1а1од* рРг±пЁВ1а1од = пет/ ОРгхпЫЯаХодС&ргхпЁег); (рРг1пЁП1а1од->ехес () == 0О1а1од: :АссерЪе<1) { // Печать } <Хе1еЁе рРг1пЫЯа1од; Рис. 33.4. Диалоговое окно настройки принтера
Диалоговое окно выбора цвета Класс осоХотхаХод реализует диалоговое окно выбора цвета (рис. 33.5). Для того чтобы показать это окно, вызывается статический метод деьсоХогО. Первым параметром в метод можно передать цветовое значение для инициа- лизации. Вторым параметром является указатель на виджет предка. После закрытия диалогового окна метод возвращает объект класса осоХог. Чтобы узнать, какой кнопкой было закрыто окно — ОК или Сапсе! (Отмена), необ- ходимо вызвать метод ХзУаИао возвращенного этим методом объекта типа ОСоХог. Значение егие указывает, что была нажата кнопка ОК, в противном случае — Сапсе! (Отмена). Например: ОСоХог соХог = ОСоХогОхаХод: :деССо!ог (Ыие) ; х€ (!соХог.1зУаХ1<Х()) { // СапсеХ } Рис. 33.5. Диалоговое окно выбора цвета Диалоговое окно выбора шрифта Окно выбора шрифта предназначено для выбора одного из зарегистрирован- ных в системе шрифтов, а также для задания его стиля и размера (рис. 33.6). Реализация этого диалогового окна содержится в классе ОРопьсхаХод, опре- деленном в заголовочном файле ОРопЮ1а1ой.
Для того чтобы показать диалоговое окно, в большинстве случаев можно обойтись методом 0Ропьп1а1од:: десРопъ (). Первый параметр этого мётода является указателем на переменную булевого типа. Метод записывает в эту переменную значение сгие в том случае, если диалоговое окно было закрыто нажатием кнопки ОК, в противном случае — !а1зе. Во втором параметре можно передать объект класса оропс, который будет использоваться для инициализации диалогового окна. После завершения выбора шрифта и за- крытия окна статический метод деЪРопьо возвращает шрифт, выбранный пользователем. Например: Ьоо1 ЬОк; ОРопЪ €пЪ = ОРопЬШаХод: :деЬРопЪ(&ЬОк) ; II (!ЬОк) { // Была нажата кнопка Сапсе! } Диалоговое окно ввода Диалоговое окно ввода данных можно использовать для предоставления пользователю возможности ввода строки или числа. Это окно реализовано в классе 01приы>1а1од. Конечно, можно и самому написать нечто подобное,
разместив в диалоговом окне виджет класса оЫпеЕсНе, но зачем это делать, когда есть готовый класс? Для более сложных диалоговых окон, имеющих более одного поля ввода, этот класс уже не подойдет, и придется реализовы- вать свой собственный. Для отображения диалогового окна ввода класс отприсШаХод предоставляет четыре статических метода: □ деътехь () — для текста или пароля; □ деЫп&едег () — для ввода целых чисел; □ десооиЫе () — для ввода чисел с плавающей точкой двойной точности; □ деыьетЦ) — для выбора элемента из списка строк. В эти методы первым параметром передается указатель на виджет предка, вто- рым— заголовок диалогового окна, третьим — поясняющий текст, который будет отображен рядом с полем ввода. Начиная с четвертого параметры этих методов отличаются друг ОТ друга. В методах деЫпЪедегО И деСВоиЬХеО четвертым передается значение для инициализации (по умолчанию равное нулю), а пятым и шестым — минимально возможное (по умолчанию -2 147 483 647) и максимально возможное (по умолчанию 2 147 483 647) значения. В методе дестехсо четвертый параметр управляет режимом ввода паролей, а пятым параметром передается текст для инициализации (по умолчанию это пустая строка). В методе деыееш(} четвертым парамет- ром передается список строк (озъгХпдЫзс), пятый параметр делает одну из переданных строк текущей (по умолчанию — первая строка), шестой пара- метр управляет режимом возможности редактирования строк (по умол- чанию включен). Два последних параметра у всех этих методов — аналогичные: это указатель на переменную булевого типа, информирую- щую о кнопке, нажатой при закрытии окна — ОК или Сапсе! (Отмена), и флаги окна (см. главу 5). ___1г ри! ; Пате: 1^2 I ОК ; Сапсе; Рис. 33.7. Диалоговое окно ввода текста
Например, отобразить диалоговое окно ввода текста (рис. 33.7) можно сле- дующим образом: Ьоо1 ЪОк; 051:г1пд з1г = С1приШга1од: :деЪТех1:(О, "ХприЁ", "Нате:”, ОЫпеЕсИЪ: : Коппа 1, "Тагда”, &ЬОк ); (’ЬОк) { // Была нажата кнопка Сапсе1 } Изменив четвертый параметр с оЫпеЕадЛ: :Ыогта1 на дгЛпеЕадЛ: :Раззж>га, мы переведем виджет однострочного текстового поля в режим ввода пароля. Диалоговое окно прогресса Для отображения прогресса какой-либо операции в диалоговом окне ()1 пре- доставляет класс 0Ргодгегв01а1од, унаследованный от класса оШаХод. Это окно информирует пользователя о начале продолжительной операции и дает возможность визуально оценить время работы. Окно может содержать кноп- ку Сапсе! (Отмена) для прерывания начатой операции. При нажатии на нее высылается сигнал сапсеХей(), который следует соединить со слотом, ответ- ственным за прекращение проводимой операции. Диалоговое окно открывается в том случае, если длительность всей опера- ции будет составлять более трех секунд, гарантируя то, что диалоговое окно прогресса не будет появляться на короткий промежуток времени и вво- дить пользователя в заблуждение. Время можно изменить, передав в ме- тод зеемХпхтшпЕигаЫоп () целочисленное значение в миллисекундах. Задать количество шагов от начала до конца операции можно в третьем параметре конструктора при создании или же с помощью метода зеьтоьахзъерз (). В процессе выполнения операции должен вызываться метод зесргодезз (). Диалоговое окно прогресса, показанное на рис. 33.8, можно создать следую- щим образом: 1П<: п = 100000; ОРгодгеззВхаХод* рргд = пею 0РгодгеззР1а1од("Ргосеззхпд ЪИе дайа...", "&Сапсе1", 0, п); рргд->зеШ1п2лилпВигаЪ1оп (0);
рргй->зеШ1пйо«Т1(:1е( *Р1еазе На1Х") ; 1ог (1пЬ 1=0; 1 < п; ++1) { рргй->зеСУа1ие(1); дАрр->ргосеззЕуепЬз(); (рргс1->«азСапсе1ес1()) { Ьгеак; } } ррг<3->зеЬУа1ие (п) ; де1еЬе рргд; Рис. 33.8. Диалоговое окно прогресса Первым параметром в конструктор класса ОРгодгеззШаТод передается текст поясняющей надписи проводимой операции. Второй параметр представляет собой надпись на кнопке Сапсе! (Отмена). Третий и четвертый параметры задают минимальное и максимальное значение для индикатора прогресса, определяя тем самым количество шагов для проводимой операции. Пятый, опциональный параметр является указателем на виджет предка. Шестой па- раметр — также опциональный, он задает флаги окна (см. главу 5). В метод зесм1п1шип1Оигас1оп () передается значение 0, говорящее о том, что диалого- вое окно будет показано без задержек. Вызов метода зе«Япао«т1с1е () уста- навливает текст в заголовке диалогового окна. В цикле, производящем дли- тельную обработку, в нашем случае в цикле 1ог, вызывается метод зег7а1ие(), обновляющий состояние индикатора прогресса. Вызов метода ргосеззЕуепсз () из объекта приложения заставляет, перед проведением ка- ждой итерации, обрабатывать события, что позволяет разблокировать графи- ческий интерфейс программы в момент проведения интенсивных операций (см. главу 14). В операторе И вызовом метода казсапсе1еао проверяется, не была ли нажата кнопка Сапсе! (Отмена), и если она была нажата, то вы- полнение цикла прерывается.
По окончании операции можно вызвать слот гезес(), для того чтобы устано- вить индикатор прогресса в начало. Можно сделать так, чтобы операция уста- новки в начало проводилась автоматически, для чего в метод зеИАиьокезеи () передается значение ъгие. Для автоматического закрытия окна по окончании операции вызывается метод зеелиеоС1озе (>, в который передается сгие. Диалог мастера Диалог мастера был придуман для сопровождения пользователя через труд- ные операции. Для навигации по страницам диалога используются две кноп- ки №х1 (Вперед) и Васк (Назад). Пользователь не имеет возможности сразу отобразить интересующую его страницу диалога, не пройдя все предшест- вующие страницы, что гарантирует выполнение всех пунктов, содержащихся в этих диалогах. Для создания класса мастера нужно унаследовать класс оиГгага и добавить каждую новую страницу Диалога вызовом метода ааарадеО. В этот метод нужно передать указатель на виджет <2Н1гагараде, в котором вызовом метода з^етд-Ые () можно установить строку заголовка, а при помощи класса ком- поновки разместить все необходимые виджеты. О кнопках для продвижения вперед и назад беспокоиться не нужно, они уже сконфигурированы для сме- ны страниц. В листинге 33.7 реализован класс мастера с тремя страницами, содержащими виджеты надписи (см. рис. 33.9). к Листинг ЗЗ.7. Диалог мастера ^ : Г '' -К . г : ..................................... ..............Г сХазз Мхгагд : риЬИс ОЮхгагс! { ргЗ^аЪе: 0ЮХгагс1Раде* сгеаЪеРаде (ОЮХддеЪ* рюдЪ, ОЗЬгхпд зЪгНЬХе) { ОМхгагбРаде* рраде = лею ОМхгагдРаде; рраде->зеЁТ1Ё1е(з1:гТ1Ъ1е) ; ОТВохЬауоиЪ* рХауоиП = пею ОТВохЬауоиЪ; рХауоиЪ->айс1МХ<Хде1: (рюдЪ) ; рраде->зеиЬауои1: (рХауоиЁ) ; геЪит рраде; ) риЬИс: М1гагс1: :М12агд(даХйде!:* рмдЬ = 0) : ОЮхгагсЦрюдЪ)
асИРаде(сгеаЪеРаде(пей; фЬаЬе! ("<Н1>ЬаЬе1 1</Н1>"), "Опе”)) ; аскЯРаде (сгеаЪеРаде (пете ОЬаЬе! (" <Н1>ЬаЬе1 2< /Н1 > *'), 11 Тадо")); асМРаде (сгеаСеРаде (пею дЬаЬе! (" <Н1>ЬаЬе1 3< /Н1 > ’*), " ТИгее ")); Тшо 1_аЬе1 2 Рис. 33.9. Диалог мастера Окна сообщений Не стоит создавать собственные диалоговые окна для вывода сообщений на экран, ведь для этого можно воспользоваться уже готовыми окнами, пре- доставляемыми классом омезгадевох. Окно сообщения —это самое простое диалоговое окно, которое отображает текстовое сообщение и ожидает реак- ции со стороны пользователя. Его основное назначение состоит в информи- ровании об определенном событии. Все окна, предоставляемые классом Омеззадевох — модальные. Они могут содержать кнопки, заголовок и текст сообщения. Класс омеззадевох предоставляет целую серию статических методов, с по- мощью которых можно создавать окна сообщений. Эти методы предостав- ляют поддержку сообщений трех уровней важности: информационного, пре- дупреждающего и критического, которые выбираются в зависимости от обстоятельств. Окна могут содержать до трех кнопок.
Это очень удобно, так как не нужно писать дополнительного кода для реали- зации вывода сообщения. Можно применять такие окна для отладочных це- лей— вывести необходимую информацию и приостановить выполнение программы. Рис. 33.10. Окно сообщения Окно сообщения, показанное на рис. 33.10, можно реализовать следующим образом: ОМеззадеВох* ртЬх = пею ОМеззадеВох (ОМеззадеВох:: Тп^оипаСхоп, "МеззадеВох”, "<Ъ>А</Ь> <1>5±пор1е</1> <и>Меззаде</и>", ОМеззадеВох: :Уез | ОМеззадеВох: :Ыо | ОМеззадеВох: :Сапсе1 ); и = ршЬх->ехес () ; (Яе1е(:е рпЬх; (п == ОМеззадеВох: :Уез) { //Нажата кнопка Уез } Это окно создается динамически. Первый параметр задает предопределенное растровое изображение, которое будет отображено слева (табл. 33.1). Во вто- ром параметре конструктора передается текст заголовка окна, во втором — текст сообщения, в котором можно использовать теги НТМЬ. Третий пара- метр задает кнопки, которые будут размещены в окне диалога (табл. 33.2). Этот параметр необязателен, и если его не указать, то диалоговое окно будет содержать только лишь одну кнопку ОК. В нашем примере мы определили три кнопки: Уе$, Ко и Сапсе1. Обратите внимание: всегда определяйте кноп- ку Сапсе! (Отмена). Клавиша <Езсаре> часто нажимается пользователями для отмены действия, и диалог в этом случае возвратит результат нажатия
кнопки Сапсе!. Четвертый и пятый параметр также необязательны, но в них можно передать указатель на виджет предка и флаги, управляющие внешним видом окна диалога. Вызов метода ехес () объекта класса омеззадевох при- водит к остановке приема событий основной программы и ожиданию нажа- тия на одну из кнопок окна сообщения. После нажатия метод ехес () возвра- щает идентификатор кнопки, который сохраняется в переменной п. Оператор ае1есе удаляет из памяти виджет окна сообщения. В операторе 1€ проверяет- ся значение нажатой кнопки на равенство значению кнопки Уез (Да). Параметры, передаваемые в конструктор, можно установить и с помощью методов, определенных в классе омеззадевох. Текст сообщения устанавлива- ется методом зеьтехе (), а текст кнопки — методом зеьвиъсоптехс (). Пер- вым параметром в метод зеевиььопТехс () необходимо передать один из це- лочисленных идентификаторов, приведенных в табл. 33.2, вторым — текст. При помощи метода Беен1пйо«т1ъ1е() устанавливается текст заголовка окна. Метод зеысопО устанавливает растровое изображение, для этого нужно передать в него одну из констант, указанных в табл. 33.1. Если указанных в этой таблице растровых изображений недостаточно, то можно создать и установить свое собственное, передав объект класса орххтар в метод зеЫсопРххшар (). Таблица 33.1. Растровые изображения Константа Значение Вид ЫоТсоп 0 1п^огтаЪ1оп 1 V Юагп1пд 2 т\ Сг1Ё1са1 3 о ОиезЫоп 4 1. Таблица 33.2. Перечисление класса омезвадеВох Константа Константа Константа ЫоВиЪЪоп Ыо Уез АН Ок АЬогС ЫоАН Сапсе! КеЁгу Езсаре Уез Хдпоге Пе^аи1С
Окно информационного сообщения Это окно используется для отображения сообщений после успешного испол- нения операции. Вызов статического метода хпСоипаез-опО отображает на экране окно информационного сообщения, показанное на рис. 33.11: ОМеззадеВох:: хпГогтаЪхоп {0, 11 ТпСоппаЫоп", "ОрегаПхоп Сотр1еЪе"); Как только окно будет закрыто, метод вернет значение нажатой кнопки (табл. 33.2). __(п/сгтаЪсп ОрегаИсл СстрНе ОК Рис. 33.11. Окно информационного сообщения Окно предупреждающего сообщения Для отображения предупреждающего сообщения (рис. 33.12) вызывается статический метод «агп1пд () класса ОМеззадеВох. Вывод окна предупреждающего сообщения может выглядеть так: хпС п = ОМеззадеВох: :\лгагп1пд(0, "Юагпхпд", "ТИе ЁехЬ хп Ы1е Н1е Иаз сЬапдед” "\п ЕЮ уои теапЪ Ъо заVе СИе сЪапдез?", ОМеззадеВох: :Уез | ОМеззадеВох: :Ыо, ОМеззадеВох: :Уез ); 1Е (и == ОМеззадеВох: :Уез) { // 5аV^пд Ъке скапдез! } В метод магптпдО первым параметром передается указатель на предка (в нашем случае нулевой), вторым — строка заголовка окна, третьим — текст сообщения. Четвертый параметр задает кнопки (в нашем примере две: Уея и №>). Пятый указывает на то, какая из кнопок будет кнопкой по умолча- нию (в нашем случае это кнопка Уея). Если пользователь нажмет клави-
шу <Е8саре>, то метод адагЫпдО возвратит значение омевзадевох: :Евсаре. В нашем примере мы проверяем нажатие кнопки Уез (Да). ТЬе 1П Л1е Каз с1тапде< Ос уси лап! 1с 1Не сНапдез? — — Рис. 33.12. Окно предупреждающего сообщения Окно критического сообщения Это окно следует показывать только в тех случаях, когда произошло что-то очень серьезное (рис. 33.13). Для его отображения нужно вызвать статиче- ский метод сгШса! (), передав ему в первом параметре указатель на виджет предка, во втором — заголовок, а в третьем — само сообщение. В четвертом задаются кнопки: 1пЪ п = ОМеввадеВох::сг1Ъ1са1(О, •,АЪЪепЪ1оп,‘, "ТЫв орегаЫоп адШ шаке уоиг " " согприЬег ипиваЫе, сопЫпие ? ", ОМезвадеВох: :Уез | ОМеввадеВох: :Ыо | ОМезвадеВох::Сапсе1 1 ); (п == ОМеввадеВох::Уез) { // Во 1С! } Рис. 33.13. Окно коитического сообщения
Окно сообщения о программе Пожалуй, самое простое окно отображается при вызове статического метода аЬоис () класса смензадевох. Например, его можно использовать для предос- тавления пользователю общей информации о программе: версии, контактной информации, информации об авторских правах и т. д. (рис. 33.14). В этот ме- тод передаются три параметра. Первый параметр — это указатель на виджет предка, второй — заголовок окна, третий представляет собой само сообщение. даеззадеВох::аЬоиЬ(0, "АЬоиЬ", "Му Ргодгаш Уег. 1.0"); -пел I— Ргсуат ‘.'ег. 1.0 □К Рис. 33.14. Информация о программе Окно сообщения АЬоиТ 01 Для отображения окна АЬои! 01 (О 01) класс фМеззадевох предоставляет стати- ческий метод аьоиъос (). В этот метод передаются два параметра. Первый слу- жит для установки предка, а второй — заголовка окна. За отображаемое сообще- ние отвечает сама библиотека 01. Как видно из рис. 33.15, в этом окне находится информация о библиотеке. Вызов окна производится следующим образом: ОМеззадеВох:: аЬоиЪ0<: (0); ___> АЬоИ; О* ИАЬои! ТН12 р«сд|ат игез $1 Среп ЕЛбсп >. егззсп 4.2,2 О» С'реп ЕгМюп и -ньепсЫ (с» с1е-, екртеп! с< Среп се арр||СвЪспз, »си пее.1 а -:стгпе"6а1 Сосете (ог с1е.е1оргг»ел{ ргсрлеГзг. »с«с5ес1 гсмгсе аррЬсаЪсш ₽1еазе : уху.гго21зес1. :сзг; , г.зг,1 (сг зп с.ег . 1е.-.-с( С^Нкеп^пд. ’<1 а С** сгс55-р1аНо"т врр!маЬсп с!е е!сртет, 01 ргс- 1с1« 51пд1е-<сигсе рсНеЫЙ) аохз5 Ш Лтжзз 05 \ Ьпцх Э!К| а11 та)с« ссттегсга!1 Ут* „алели. О* «1Г-С а-.аъеЫв Гсг еглЬесИес! с!е--к« и СЛор!5 С сие. О» 15 а ТгсНисЬ ргос!ус1. $ее ‘““с:. . згзПзесл. з:г. дз ‘ ?сг тсге т^сгтаЬсп. Рис. 33.15. Окно сообщения АЬои101Ю СИ)
Окно сообщения об ошибке Окно сообщения об ошибке реализуется классом ОЕггогмеззаде, а не клас- сом ОМеззадеВох, как все остальные окна сообщений. Оно представляет со- бой немодальное диалоговое окно. Для отображения окна сообщения об ошибке создается объект этого класса и вызывается метод зИошеззадеО, в который передается текст сообщения. Например: (пею ОЕггогМеззаде (ЬЫз)) ->з1юШе88аде ("ЮгзХе Еггог") ; Как видно из рис. 33.16, окно содержит флаг, который может деактивиро- ваться пользователем, для того чтобы не показывать данное сообщение снова при повторении данной ошибки. Не следует злоупотреблять окнами сообще- ния об ошибке; применять их следует только в тех случаях, когда это не по- вредит пользователю. Этим окном имеет смысл сообщать о критических ошибках, так как после перезапуска программы состояние флага будет сбро- шено. Рис. 33.16. Окно сообщения об ошибке Резюме Диалоговые окна — это виджеты верхнего уровня, открывающиеся поверх окна основного приложения. Для создания таких окон можно воспользовать- ся любым классом виджетов, но удобнее использовать класс ооха1од. Диалоговые окна являются ключевыми элементами для обмена информацией с пользователем, и при их создании нужен правильный подход. Необходимо учитывать ряд правил, помогающих создавать такие окна, работу с которыми пользователь мог бы начать сразу, не затрачивая усилий на изучение. Диалоговые окна подразделяются на две группы: модальные и немодальные. Модальные диалоги блокируют работу пользователя с основной программой
и ожидают действий со стороны пользователя. Разблокировка происходит в момент закрытия окна. Немодальные диалоговые окна могут быть открыты, не препятствуя параллельной работе пользователя с основной программой. Решение об использовании модального или немодального диалогового окна зависит от назначения. В тех ситуациях, когда нельзя продолжать работу приложения без решения пользователя, нужно использовать модальный диа- лог. Стандартное диалоговое окно выбора файлов является типичным приме- ром использования модального диалога. А, скажем, диалоговое окно для по- иска лучше делать немодальным, чтобы обеспечить пользователю возможность выделения текста, не закрывая само окно поиска. Применение стандартных диалоговых окон позволяет сэкономить время на разработку, так как.можно воспользоваться уже готовыми окнами для откры- тия файлов, выбора цвета, настройки принтера и т. д. Наиболее распространенные в приложениях диалоговые окна — это окна со- общений, которые используются для оповещения пользователя о важном со- бытии или для того, чтобы задать ему вопрос, требующий ответа "Да" или "Нет", например, "Вы действительно хотите удалить этот файл?"
Глава 34 Предоставление помощи "Сова приложила ухо к груди Буратино. — Пациент скорее мертв, чем жив, — прошептала она. Жаба прошлепала большим ртом: — Пациент скорее жив, чем мертв... — Одно из двух, — прошелестел На- родный лекарь Богомол, — или пациент жив, или он умер. Если он жив — он останется жив или он не останется жив. Если он мертв — его можно оживить или нельзя оживить". Алексей Толстой. "Приключения Буратино" Главная задача помощи состоит в'обеспечении пользователя всей необходи- мой информацией о приложении и его элементах для того, чтобы сделать ра- боту пользователя более удобной. Различают три типа помощи: □ воздушная подсказка; □ подсказка "Что это’’; □ система помощи (ОпПпе Неф). Воздушная подсказка Работая с различными программами, вы, наверное, заметили, что при за- держке указателя мыши над кнопками панелей инструментов автоматически появляется небольшое текстовое окошко, поясняющее назначение кнопки. Такое окно называется воздушной подсказкой (ЮоШр) и, как правило, содержит только одну строку текста (рис. 34.1). Можно использовать и подсказки, имеющие больше одной строки, но все же их следует делать как можно короче.
Присутствие воздушной подсказки в приложении является необязательным, но лучше все-таки предоставлять ее, поскольку она помогает пользователям быстрее сориентироваться среди множества кнопок. Несомненный плюс это- го типа подсказки в том, что, не останавливая своей работы, пользователь получает информацию об элементах приложения. Чаще всего такие подсказки применяются для кнопок панелей инструментов, но их можно с успехом использовать для любых виджетов. При этом не сле- дует забывать, что воздушные подсказки не следует применять в тех случаях, когда объяснение излишне. Например, будет нелогично для кнопки Сапсе! (Отмена) повторить ее надпись в воздушной подсказке. Для того чтобы уста- новить подсказку в виджете, нужно вызвать метод зеетоо1т1р (): ОРизЬВиССоп* рстс1 = пем ОРизЪВиЬСоп("&Ок"); рстс1->зеЬТоо1Т1р ("ВиГПоп") ; Рис. 34.1. Воздушная подсказка Чтобы удалить воздушную подсказку, просто передайте в метод зеьтоо1т1р () пустую строку. Подсказка "Что это" Иногда требуется отобразить больше информации о виджете, чем способна вместить воздушная подсказка. Подсказка Шя (Что это) является промежуточной между воздушной подсказкой и системой помощи. По своей функциональности эта подсказка очень похожа на воздушную, но с той раз- ницей, что она не появляется автоматически, при задержке указателя мыши. Для ее отображения нужно войти в специальный режим, нажав на кнопку ? на панели инструментов или в области заголовка окна. Также можно вос- пользоваться стандартной комбинацией клавиш <8ЫГг>+<Р1>. Пример, показанный на рис. 34.2, отображает виджет надписи. Нажатие на кнопку ? изменит указатель мыши, после чего при щелчке по элементу надписи будет выведена подсказка.
□а» Рис. 34.2. Подсказка УУЬаГз 1Ыа (Что это) #1пс1ис1е <2«3и1> 1пС и1а1п(1пЪ агдс, сЪаг** агду) ( ОАррИсаЫоп арр(агдс, агду); <2ЪаЬе1 1Ы("Р1еаае ргезз ЪЪе 7-ВиЪеоп", О, ОЪ: :Мпс1оадТ1Ъ1еН1пЪ | Оё: :^1пдоэд8узЬетМепиН1пЪ | ОЬ: :Шпйся«СопеехеНе1рВи1:ЪопН1пЬ ); 1Ы. зеШЬаПгТЪхз ("<1>ТЫз 13 Ъке ЬаЬе1</1><ВВ><В>С1а83 0ЬаЬе1</В>”) ; 1Ы.5коиК) ; геЪит арр. ехес (); В листинге 34.1 создается виджет надписи. В третьем параметре конструкто- ра передаются атрибуты внешнего вида окна, и присоединенный логической операцией | (ИЛИ) флаг <эс: :и1пао«сопеехъне1рвиъеопн1пс добавит в об- ласть заголовка окна кнопку ? (см. главу 5). После создания виджета надписи к нему добавляется контекстная помощь УУйаГв Ш$ (Что это) вызовом мето- да зеснИасзтЫз (). Как видно из листинга 34.1, текст подсказки, устанавли- ваемый методом зеьиЬаъзтЫз (), может использовать теги НТМЬ.
Система помощи (ОпНпе Не1р) Большие приложения нуждаются в объемной системе помощи, подробно описывающей все функциональные возможности программы. Самый простой вариант — это предоставление пользователю специального навигатора, кото- рый будет открываться при выборе соответствующего пункта меню Не1р (Справка) или при нажатии на клавишу <Р1>. Текст помощи может нахо- диться в формате НТМЬ, в котором, помимо текстовой, предоставляется воз- можность использовать и графическую информацию, ссылаться на другие документы, а также воспользоваться форматированием шрифтов. Большой плюс такого решения состоит в том, что для создания файлов в формате НТМЬ существует множество редакторов. При острой необходимости и на- личии навыков можно написать или подправить такой файл и "от руки" в простом текстовом редакторе. Класс отехъвгошзег располагает всем нужным для реализации навигатора, который способен показывать текст в формате НТМЬ. Пример, показанный на рис. 34.3, демонстрирует применение этого класса. РПе тепи соттапдв 0^51-55 з ге-л- ОйСиП'егГ. Оре?- 5 а1-, вк соситеп! ' С.:зс . -л 5с>-ез аг орег.ег з: зиЛ-е'г :-з а зрелее Рис. 34.3. Система помощи #1пс1иде <0«Зи1> # хпсХиде ’’ Не1рВгоюзег. И" хпс тахп (хп!: агдс, сИаг** а^дV)
{ ОАррИсаЫоп арр(агдс, агду); Не1рВгоюзег ЬеЬрВгоюзег (” дос ”, " хпдех. кСт") ; Ье1рВгоюзег.гезхзе(400,300); НеТрВгсмзег. зИоад () ; геСигп арр.ехес(); } В основной программе, показанной в листинге 34.2, создается виджет нави- гатора помощи — ке1рвго^зег. В первом параметре конструктора передается путь, а во втором — имя файла. #1Ь1деГ _Не1рВго^зег_Н_ #деГ1пе _Не1рВгоюзег_И_ #1пс1иде <0С(зи1> // ====================================================================== с1азз Не1рВгоюзег : риЬИс ОШддеС { О ОВЛЕСТ риЬИс: Не1рВгоюзег (сопзС 03Сг1пд& зЬгРаЛ, сопзС ОЗСгхпдЬ зСгРИеЫате, * ОМхддеС* раддС = 0 ) : 0Ю16деС(р«дС) { ОРизЬВиССоп* рспкЭВаск = нею дРизЪВиССоп ("«”) ; ОРизЪВиССоп* рстдНоше = пею ОРизкВиСЬоп("Ноше") ; рРизИВиССоп* рстдРотеагд = пем ОРизЬВиССоп ("»"); ОТехСВгоюзег* рСхСВго^зег = пеад ОТехСВгоюзег; соппесС(рсшдВаск, 51СЫАЬ(с1хскед()), рСхЬВгоюзег, ЗЬОТ(Ьаскюагд())
соппес11 (рсшйНоте, ЗХСЫАЬ (с1 хскед ()), р^хЬВготзег, ЗЬОТ(Иоте()) ); соппесЬ (рстйКог^агд, ЗХСЫАЬ (сИскед ()), рйхПВгоюз ег, ЗЬОТ (Гогюагс! ()) ); соппесЪ (рЬхЬВготезег, ЗХСЫАЬ (ЬаскюагаЛуаИаЫе (Ьоо1)), рсжЗВаск, ЗЬОТ(зеСЕпаЫед(Ьоо1)) ) ; соппесЪ (рЪх^го^зег, ЗХОЫАЬ (СогюагаАуаИаЫе (Ьоо1)), рстсЗРогиуагд, ЗЬОТ (зеЪЕпаЫей (ЬооХ)) ); рЬхИВго^зег->зе^Зеагс11РаЪ11з (ОЗЪгхпдЬХзЁО « зЁгРаЪЬ); рГ:хеВгоюзег->8ееЗоигсе(ОЗСг1пд(зЪгР11еЫа1пе)); //ЬауоиЬ зеЪир ОУВохЬауоиЬ* рVЬx^ауои^ = пею ОУВохЬауоиИ; ОНВохЬауоиИ* рИЬхЪауоиС = лею ОНВохЬауоиС; рНЬхЬауоиГ:->аааю1аде1: (рстдВаск); рЪЬхЬауоиГ:->аааю1адеЪ (рстйНоте) ; рЪЬхЬауоиГ:’>а(±ЗМ1с1деЪ (рспкЗРогюагсО ; р^ЬхЬауоиИ->ас1дЬауоиГ: (рНЬхЬауоиЪ); рVЪx^ауоиЪ->а(^йМ^<^де^: (р^хЪВгоюзег) ; зеЪЬауоиЬ (рт-ЬхЬауоиЪ) ; } }; # еп61С //_Не1рВго№ег_И_ В листинге 34.3, в конструкторе класса не1рвгоюзег создаются виджеты кнопок для дополнительной навигации: указатели рсшаваск, рстаноте и рстдРог^ага. После этого с помощью метода соппесе () сигналы сИскеао кнопок подсоединяются к соответствующим слотам виджета клас- са ОТехЪВгоюзег: ЬаскигагсЦ), Иоте() И ГогтеагдО. Это необходимо ДЛЯ перемещения по документам справочной системы. Последние два ме- тода соппесЬО соединяют сигналы Ьаскюага^аИаЫе (Ьоо1) И Гоий7ага?^а11аЫе (Ьоо1) виджета класса отехъвгоюзег со слотами зеиЕпаЫеа(Ъоо1) соответствующих кнопок (указатели рставаск и рстарог^ага). Это позволяет делать кнопки, в зависимости от ситуации,
активными или неактивными. Метод веезеагсърасъ () устанавливает список путей для поиска документов, в нашем случае путь только один. Метод зеЬЗоигсеО считывает переданный документ. Далее все виджеты размеща- ются при помощи вертикальной (ртЬхъауоие) и горизонтальной (рЬЪхьауоие) компоновок. Резюме Использование помощи — верный шаг для повышения интуитивности рабо- ты пользователя с приложением. Помощь необходима для снабжения пользо- вателя интересующей его информацией и подразделяется на три категории: воздушная подсказка, подсказка "Что это" и система помощи (Оп1те Неф). Воздушная подсказка представляет собой небольшую по объему, часто огра- ниченную одной строкой, информацию. Она появляется возле указателя мы- ши, если его удержать на виджете. Подсказка "Что это" очень похожа на воздушную подсказку, но она предос- тавляет большую по объему информацию и отображается только после за- пуска специального режима. Система помощи снабжает пользователя исчерпывающей информацией о функциях приложения. Чтобы воспользоваться ею, совсем не обязательно запускать само приложение, так как информация находится в формате НТМЬ и ее можно прочитать при помощи любого доступного в системе браузера. Для реализации своего собственного навигатора можно воспользоваться классом ОТехЪВгошзег.
Глава 35 Создание 801- и М01-приложений "Приступай к решению любой зада- чи, имея в виду решить ее наилучшим образом". Одна из трех заповедей 1ВМ Многие приложения имеют сходный интерфейс — окно содержит меню, ра- бочую область, строку состояния и т. д. Не удивительно, что библиотека содержит классы, помогающие создавать такие приложения. Класс главного окна ОМатУУтдош 0Ма1пН1пао»— это очень важный класс, который реализует главное окно (рис. 35.1), содержащее в себе типовые виджеты, необходимые большинству приложений, такие как меню (см. главу 32), секции для панелей инструмен- тов, рабочую область, строки состояния. В этом классе внешний вид уже подготовлен, и его виджеты не нуждаются в дополнительном размещении, так как они уже находятся в нужных местах. Окно приложения, изображенное на рис. 35.1, имеет рамку, область заголов- ка для отображения имени и три кнопки, управляющие окном. Кроме этого, окно приложения имеет меню, которое располагается ниже области заголов- ка окна. Панель инструментов расположена под меню. Под рабочей областью расположена строка состояния. Указатель на виджет меню можно получить вызовом метода <ЭМа1пИ1пао«: :шепиваг () и установить в нем нужные всплывающие меню: ОМепи* рппиРИе = пе» ОМепиС&РИе") ; ртпиРИе->а<а<ЗАс1:1оп( "&3ауе"); тепиВаг () ->а<±ЗМепи (оттиРИе);
I текстовый Заголовок. Осситем - ЛсгсРад Не Е4й Шей 1п5сг1 Еогта* Не1р ; о в а а й строка состояния ^Рог Нф,. ргеи Р1 ( кнопка ^прокрутки / рабочая А^бпасть-------- / полоса V прокрутки /кнопка изменения к.Еа™°Р°°..°.™*_ Рис. 35.1. Внешний вид главного окна приложения Как правило, устанавливаются следующие всплывающие меню: □ Гйе (Файл) — содержит основные операции для работы с файлами: (Создать), Ореп (Открыть), 8ауе (Сохранить), РппС (Печать) и 0ш1 (Вы- ход); □ Е<Ш (Правка) — включает в себя команды общего редактирования: Си1 (Вырезать), Сору (Копировать), Ра§1е (Вставить), (1ш1о (Отменить), Кейо (Повторить), Е1ПЙ (Найти), Пер1асе (Заменить) и Ве1е1е (Очистить); □ ЛТел* (Вид) — содержит команды, изменяющие представление данных в рабочей области. Например, команда 2оош (Масштаб) масштабирует ото- бражение документа. В это меню можно включать и те команды, которые управляют отображением элементов интерфейса приложения, например, панелей инструментов и строки состояния; □ Неф (Справка) — необходима для предоставления помощи пользователю при освоении приложения. А также, обычно, используя команды этого меню, можно отобразить информацию об авторских правах приложения. Например, при выборе команды АЬои1 (О программе) обычно появляется окно, отображающее имя приложения, его версию, информацию об автор- ских поавах.
Чтобы получить указатель на рабочую область, следует вызвать метод 0Ма1пИ1паоу?:: сепсга1и1адеь (), который вернет указатель на оихадес. Для установки виджета рабочей области потребуется вызвать метод 0Ма1пи1пао»а:: аессепсгаХихадес. () и передать в него указатель на этот виджет. Метод ома1пИ1паои: :зеаеизваг () возвращает указатель на виджет строки состояния. Кнопка изменения размеров окна, расположенная в нижнем пра- вом углу строки состояния (рис. 35.1), является всего лишь подсказкой для пользователя, сообщающей о том, что размеры главного окна могут быть из- менены. Этот виджет реализован в классе озХгеСгхр. Получить указатель на него из класса главного окна (омаХпкХпао») невозможно, так как он находит- ся под контролем виджета строки состояния. Класс действия ОАсНоп Действие оасьХоп предоставляет очень мощный механизм, ускоряющий раз- работку приложений. Если бы его не было, то вам нужно было бы создавать команды меню, соединять их со слотами, затем создавать панели инструмен- тов и соединять их с теми же слотами и т. д. Эго приводило бы к дублирова- нию программного кода и вызывало бы проблемы при синхронизации эле- ментов пользовательского интерфейса. Например, при необходимости сделать одну из команд меню недоступной, вам нужно было бы сделать ее недоступной в меню, а потом проделать то же самое и с кнопкой панели ин- струментов этой команды. Объекты класса оасЫоп предоставляют решение этой проблемы. Эта кон- цепция значительно упрощает программирование. Например, если команда меню Гйе | Иемг (Файл | Создать) дублируется кнопкой на панели инструмен- тов, для них можно создать один объект действия. Тем самым, если вдруг команду потребуется сделать недоступной, мы будем иметь дело только с одним объектом. ОАсЫоп объединяет следующие элементы интерфейса пользователя: □ текст для всплывающего меню; □ текст для всплывающей подсказки; □ текст подсказки "Что это"; □ "горячие" клавиши; □ ассоциированные пиктограммы; □ шрифт; □ текст строки состояния.
Для установки каждого из вышеперечисленных элементов в объекте оасЫоп существует свой метод. Например: ОАсЫоп* рас!:5аVе = пею ОАсСхопС^Ие зауе асЫоп", 0) ; расЪ5аVе->зеI^Теx^ ("&8ауе") ; расЪЗате-^еЬЗЬогЪсиЁ (ОКеуЗедиепсе ("СТКЪ+З")) ; рас^ЗаVе->5еЁТоо1Т^р(“Зауе БосшпепЪ") ; расС5аVе->8е^:31:а^:изТ^рр5аVе 1Ле €Ие Ьо ддзк”); расеЗаVе->зеШ1аЬ^Т11^5(’’ЗаVе ЬИе Ше По д!зк“); расС5аVе->зе^Iсоп (0Р1хтар (ипд4_хрт)) ; соппесЁ (расЁЗауе, ЗКЗЫАЬ (Ёг1ддегес1 ()), ЗЪОТ (зХоЪЗате ())); ОМепи* ртпиГИе = пею 0Мепи( ) ; ртпи->аддАс1:1оп(расЬЗауе); 0Тоо1Ваг* рГ:Ь = пем 0Тоо1Ваг("Ыпкег Тоо1Ваг"); р(:Ь->аск1Ас1:1оп(рас1:5ауе); Метод адддсЫопо позволяет внести объект действия в нужный виджет. В нашем примере это виджет всплывающего меню смели (указатель ршпиРИе) и панель инструментов отоохваг (указатель рсь). Панель инструментов Основная цель панели инструментов (Тоо1 Ваг) — предоставить пользовате- лю быстрый доступ к командам программы одним нажатием кнопки мыши. Это делает панель инструментов более удобной, чем меню, в котором нужно сделать, по меньшей мере, два нажатия. Еще одно достоинство состоит в том, что панель инструментов всегда видима, и не нужно тратить время на поиски необ- ходимой команды в меню или вспоминать комбинацию клавиш ускорителя. Панель инструментов представляет собой область, в которой расположены кнопки, дублирующие часто используемые команды меню. Для панелей ин- струментов библиотека предоставляет класс отоохваг, который определен в заголовочном файле ОТоо1Ваг. Процесс создания панели инструментов не- сложен, и, со временем, вы сможете формировать панели, имеющие довольно сложную структуру и удовлетворяющую любым требованиям. Для того чтобы поместить кнопку на панель инструментов, необходимо вы- звать метод адбАсеХопО, который неявно создаст объект действия. В этот метод можно передать растровое изображение, поясняющий текст и соеди- нение со слотом (листинг 35.1). Примечание Старайтесь не игнорировать и всегда передавать поясняющий текст. Ввиду от- сутствия текста на кнопках инструментов дополнительные небольшие тексте-
вые пояснения (см. главу 34} играют важную информационную роль. Эти пояс- нения всплывают в тот момент, когда пользователь задержит указатель мыши в области кнопки на небольшой промежуток времени. Наряду с кнопками, в панели инструментов могут быть размещены и любые другие виджеты. Для этого в классе отоохваг определен метод аааюхаде*: (). Программа, окно которой показано на рис. 35.2, демонстрирует использова- ние панелей инструментов. Панели инструментов могут перемещаться с од- ного места на другое. В нашем случае был использован класс омаХптехпаош. Рис. 35.2. Панели инструментов #х^пае€ _Ма1п^1пасж_11_ #аеЕхпе _МахпНхпаслО1_ #1пс1иае <ОЬОих> #хпс1иае и1шд1.хрти #хпс1иае "1год2.хрпГ #хпс1иае "хтдЗ.хрп" #хпс1иае "хтд4.хрт" с!азз МахпМхпасж : риЫхс ОМахпКхпао» { О_ОВОЕСТ
риЬХхс: Ма1пШпаом(0Ю1<1де1:* раддЬ = 0) : ОМа1пШпдоу?(радд1:) { асйТооХВаг (2Ъ:: ТорТооХВагАгеа, сгеа^еТооХВаг ()) ; асИТооХВаг (€> Ь:: ВоЬЪ<хпТооХВагАгеа, сгеаЪеТооХВаг ()); аск1Тоо1Ваг (2Ъ:: Ье^ЪТооХВагАгеа, сгеаЁеТооХВаг ()) ; асМТооХВаг(фЬ::ВХдЪЬТооХВагАгеа, сгеаЪеТооХВаг()); } ОТооХВаг* сгеаЪеТооХВаг() { ОТооХВаг* рСЬ = пеот ОТооХВаг ("Ыпкег ТооХВаг"); рЪЬ->а<ХдАс1:1Ъп (ОРХхшар (1тдХ_хрт) , " X ”, ЪЫз, 31Ш (зХоШоХтрХ ()) ) ; рЪЪ~>асЭс1АсЪХоп (ОРххтар {1тд2_^рт), "2й, ЪЫз, ЗЬОТ (зХоШэХпрХ ())) ; рйЬ->аааЗерагаЪог(); рЁЬ->а6сДАс1:1оп (ОРХхшар (1тдЗ_хрт), " 3", еЫз, ЗЬОТ (зХоЪЫоТтрХ ())) ; рЪЬ->ас1с1Ас1:Хоп (ОРХхтар (1тд4_^срт), " 4", Ы1хз, ЗЬОТ (зХоШэТтрХ ()) ) ; геЁигп рСЬ; риЬХ1с зХо^з: зХоШоХтрХ () { ОМеззадеВох:: ХпГогтоаЫои (0, "Меззаде", "ЫоЪ ипрХетепЪес!") ; } }; # епсИ / /_Ма1пМ1П<ЗоУ7_И— Класс ма1пН1пдо«, приведенный в листинге 35.1, унаследован от класса ОМаХпихпбо». С помощью метода асМтоо1ваг () в конструкторе класса к глав- ному окну добавляются четыре панели инструментов, создаваемые методом сгеаьетоо1ваг () класса Ма1пи1п<3о«. Первый параметр метода ааатоотваг () задает расположение панели инструментов в главном окне приложения. В самом методе сгеаЬеТоо1Ваг () производится неявное создание четырех объектов действий метода ааадсЫоп (), в который передаются: □ растровое изображение — массив растровых данных, хранящихся в файле формата ХРМ (см. главу 19). Из этих данных создается объект класса ОРххтар;
□ поясняющая надпись (в нашем случае это номер); □ два последних параметра определяют соединение со слотом з1осыо1тр1 (), который при вызове отображает диалоговое окно с надписью Ко1 1тр1ешеп1еб (не реализовано). Разделитель между кнопками вставляется с помощью метода ааазерагаеог (). Доки Панель инструментов не предназначена для работы со сложными виджетами. Представьте себе, что вы создали виджет, составленный из целой серии вид- жетов, и вам бы хотелось использовать его подобно панелям инструментов, то есть иметь возможность помещать их с нужной стороны окна и давать пользователю возможность перетаскиванием изменять их местоположение, а также отрывать от основного окна приложения. Для этой цели и существует класс оооскихадеъ. На рис. 35.1 этот виджет показан с левой стороны, но на самом деле он может располагаться в любой из четырех сторон. Создание и использование дока в унаследованном от ома1пи1пао» классе может выгля- деть следующим образом: ОБоскИхйдеЪ* рдоск = пего ("МуОоск", Шз); 0ЬаЪе1* р1Ы = пею рЪаЬе1 ("ЬаЬе1 1п Боек", рдос) ; рдоск->зеШ1с1де1: (р1Ы); аскЗБоскЮ1с1деЬ (ОС: : ЬеГЫЭоскШддеЬАгеа, рдоск); В приведенном примере мы сначала создаем виджет дока (указатель раоск) и при создании указываем его. название, которое будет отображаться в его верхней части, — МуОоск. Затем создаем виджет надписи (указатель р1Ы) и устанавливаем в виджете дока при помощи метода зеевдхадее (). Затем док добавляется в основное окно приложения вызовом метода <2Ма1пИ1паои:: айсШоскихадее (). В этом методе первым параметром указы- вается место, где должен быть расположен док, в нашем случае это ОЪ:: Ье1Ы>оскШ1адеЬАгеа, ТО есть слева. Строка состояния Этот виджет располагается в нижней части главного окна и отображает, как правило, текстовые сообщения с информацией о состоянии приложения или короткой справкой о командах меню или кнопках панелей инструментов. Строку состояния реализует класс озсаеизваг, определенный в заголовочном файле 0$1аШ$Ваг. Различают следующие типы сообщений: □ промежуточный — вызывается методом зПоэдМеззаде (). Для очистки строки состояния следует вызвать метод с1еагмеззаде (). Если во втором
параметре метода зЬоадмеззаде () задан временной интервал, то строка со- стояния будет очищаться автоматически по его истечении. Примером промежуточного отображения является вывод поясняющего текста для команд меню; □ нормальный — используется для отображения часто изменяющейся ин- формации (например, для отображения позиции указателя мыши). Для этого лучше всего поместить в строку состояния отдельный виджет. На- пример, в случае, если приложение должно отображать прогресс работы какой-либо операции, то лучше разместить в строке состояния индикатор прогресса выполнения. Чтобы разместить виджет в строке состояния, нужно передать его указатель в метод ааамхддеь (). Виджеты можно так- же удалять из строки состояния с помощью метода гепкууеихадеъ (); □ постоянный — отображает информацию, необходимую для работы с при- ложением. Например, для отображения состояния клавиатуры в строке со- стояния могут быть внесены виджеты надписей, отображающие состояние клавиш <Сар§ Ьоск>, <Ыит Ьоск>, <1п8еЛ> и т. д. Это достигается по- средством вызова метода ааарегтапепъихадеъ {), принимающего указа- тель на виджет в качестве аргумента. На рис. 35.3 показано окно, в котором, в строку состояния, помещено два виджета надписи для отображения актуальных координат указателя мыши. Рис. 35.3. Строка состояния, отображающая актуальную позицию указателя мыши #1€пс1еС ЦМа1пИ1пс1ои_Д_ #<ЗеЕгпе _>!а1пЫ1пс1ои!_К_
#1пс1ийе <0ЪОи1> // ====================================================================== с1азз Ма1пШпаою : риЬИс 0Ма1пИ1пао« { 0.ОВ1ЕСТ ргзллаСе: ОЬаЬе1* ш_р1Ы Х; ОЬаЬе1* щ_р1ЫУ; ргоСесСед: ухгЬиа! то1а тоизёМотеЕтепЬ (ОМоизеЕЬгепЪ* ре) { пир1ЫХ->8еЪТехЬ ("Х=" + ОЗЪгхпд () . зеШит (ре->х ())) ; т_р1ЫУ->зеЬТехЪ (”У= ” + ОЗЪгхпд () . зеШит(ре->у ())); } риЬИс: Ма1п&ЯпаоУ7(0Ю1адеП* рмде = 0) : ОМахШхпаоМридН { зеЁМоизеТгаскхпд ( Ъгие) ; щ_р1ЫХ = пезд 0ЬаЬе1 (ёЫв) ; щ_р1ЫУ = пею рЬаЬеИЪЫз); зЁаПизВаг {) ->ааам1адеЬ (пир1ЫУ) ; зЪаЪизВаг {) ->адсйЯс1деЬ (т_р1ЫХ) ; } }; #епШ^ //_^4а1пМпд<лОх_ Класс махпЮхпЗою наследуется от класса ома1пю1пдош. Этот класс содержит атрибуты, хранящие указатели на виджеты надписи пир1Ых и пир1ЫУ. Сами виджеты надписей создаются в конструкторе и помещаются в строку состоя- ния методом ааашадее (). Для отображения актуальных координат указателя мыши, текст виджетам надписи присваивается в методе обработки события тоизеМсг^^^епЁ (). Зна- чения координат указателя мыши возвращаются методами х() иу{) объекта события (указатель ре).
Окно заставки При запуске многие приложения показывают так называемое окно заставки (8р1азЬ 8сгееп). Это окно отображается на время, необходимое для инициали- зации приложения, и информирует о ходе запуска приложения. Зачастую такое окно используют для маскировки длительного процесса старта программы. В библиотеке 01 это окно реализовано в классе озрИавквсгееп. Объект этого класса создается в функции тахп () до вызова метода ехес () объекта прило- жения. Программа, приведенная в листинге 35.3, отображает перед запуском окно заставки, в котором производится отсчет прогресса инициализации в процентах (рис. 35.4). Рис. 35.4. Предшествующее окно #1пс1иде <0Ё(Зи1> //------------------------------------------------------------------------ ’Т'отс! 1оасЗМос1и1ез (08р1азк5сгееп* рзр1аз!1) { ОТипе 11 те; Ъипе.еЪагЪО ; Сог (1пе 1=0; 1 < 100; ) { 1С (ехте.еТарзеаО > 40) (
Ъдлпе. зЪагЬ () ; ++1; } рзр1азк->зкслл?Меззаде ("ЬоасИпд тоди1ез: " + ОЗЪг 1пд::питЬег (А.) + ОЬ: :А11дпНСепСег | ОН: :А11дпВои:ат, ОЪ: :Ыаск ); } } //---------------------------------------------------------------------------- 1пП шахи (1пП агдс, сИаг** агду) { 0Арр11саС1оп арр(агдс, агду)/ 05р1азк8сгееп зр1азИ (ОРххтар("зр1азИ.рпдп)) ; зр1азН. зИсж () ; ОЬаЪе! 1Ъ1 (''<Н1><СЕЫТЕК>Му АррИса^1оп<ВН>" ”1з КеасЗу !</СЕ№ГЕК></Н1>" ); 1оа<ЗМсх1и1ез (&зр1азк) ; зр1азк. Г1П13И (&1Ь1) ; 1Ы.гез1хе(250, 250); 1Ы. зЪсялг () ; ге^игп арр.ехес(); } В листинге 35.3 объект окна заставки создается после объекта приложения. В конструктор передается растровое изображение, которое будет отображаться после вызова метода зИо^(). Виджет дьаЬе1 представляет в данном примере само приложение, которое должно быть запущено. Функция 1оаамоЗи1ез () является эмуляцией загрузки модулей программы, в нее передается адрес
объекта окна заставки, чтобы функция могла отображать информацию про- гресса загрузки. Объект класса опте (см. главу 38) используется для того, чтобы значение переменной 1 увеличивалось только по истечении 40 милли- секунд. Отображение информации производится при помощи метода зЬоиМеззаде (), в который первым параметром передается текст, вторым — расположение текста (см. табл. 7.1), а третьим — цвет текста (см. табл. 17.1). Вызов метода {1п1зЪ() закрывает окно заставки. В этот метод передается указатель на главное окно приложения, и появление этого окна приводит к закрытию окна заставки. Если окно заставки не закрывать, то оно останется видимым до тех пор, пока пользователь не щелкнет на нем мышью. 801- и М 01-приложения Существуют два типа приложений, базирующихся на документах. Первый тип — это 8В1 (8т§1е ЭоситегП ЬНегГасе, однодокументный интерфейс), вто- рой — МГ>1 (МиШр1е ОоситегП ТгПегЕасе, многодокументный интерфейс). В $Б1-приложениях рабочая область одновременно является окном приложе- ния, а это значит, что невозможно открыть в одном и том же приложении сразу два документа. М1>1-приложение предоставляет рабочую область (класса омшдгеа), способную размещать в себе окна виджетов, что дает воз- можность одновременной работы с большим количеством документов. 801-приложение Типичным примером ЗДЛ-приложения является программа ОС Мо(ерас1 (Блокнот). Пример, приведенный в листингах 35.4—35.9, реализует упрощенный вариант этой программы, представляющий собой простой тек- стовый редактор. Результат показан на рис. 35.5. __501 Ргсдгаш РПе Не1р Это простое 501 приложение Рис. 35.5. ЗОЬпоиложение
_ПосМ1п<Зоъ01— МеСхпе __Вос771пдо^_И_ #1пс1ийе <ОТехЪЕсН±> // ===================================================:==================== с1азз ПосШпаою: риЬНс 0ТёхЬЕд1Ь { 0_ОВ7ЕСТ ргалгаЬе: ОЗЪгхпд щ_з^гР11еЫаше; риЬНс: ПосШпдоууСОМхддеЬ* рмдЬ = 0) ; зхдпаХз: тога сЪапдеШпЦо«ТзЛ1е (сопзЪ 05Ъг1пд&) ; риЬНс з1оГ:з: той! з16ПЪоаа (); тога з1оЪЗаVе (); \гоха з1о^5ауеАз (); }; #епсИ€ / /_ОосМ1пс1о^7__11_ Класс посюхпаою, унаследованный от класса дтехьваН:, представляет собой окно для редактирования (листинг 35.4). В его определении содержится ат- рибут ш-зЪгБ’ПеЫате, в котором хранится имя изменяемого файла. Сигнал скапдем1пао«Т1Ъ1е () предназначен для информирования о том, что тексто- вая область заголовка должна быть изменена. Слоты з1оГ:Ъоаа(), з1оезаVе () и зХоезауеАз () необходимы для проведения операций чтения и записи файлов. ИосШпаою: :ОосРЯпаою(ОЮ1адеГ:* рзддЬ/*=0*/) : ОТех^ЕааХ (рюдЬ)
В конструктор класса, приведенный в листинге 35.5, передается указатель на виджет предка. .................... ^Листинг 35.6.Файл Оос№пс)ош.срр. Методз1оъъоа<1()........................ .... , . '.<| тоМ Еос^Ш1доуп :з1оШэаЩ) { СЗЪгтпд зЬг = <ЭЕИеО1а1од: гдеСОрепКНеКагпе () ; (зЪг.хзЕтрЬуО ) { геЪигп; } ОКИе Н1е(з1г); 1С (1е. ореп (ОГОЭеухсе: : кеас1Оп1у)) { ОТех^ЗЁгеат зЁгеат(&М1е) ; зеЬРХахпТехЪ (зСгеали геадАИ ()) ; И1е.с1озе() ; ггизегЕИеЫате = з^г; еттЬ сИапдеШпдомТх^1е(1ц_ЗЁгР11еЫате); } } Метод 81оыюаао, приведенный в листинге 35.6, отображает диало- говое окно открытия файла вызовом статического метода 0РИеВ1а1од: : деСОрепРИеЫате <), С ПОМОЩЬЮ КОТОРОГО пользователь выби- рает файл для чтения. В том случае, если пользователь отменит выбор, нажав на кнопку Сапсе! (Отмена), этот метод вернет пустую строку. В нашем при- мере это проверяется с помощью метода озегхпд: гхзЕтрьуО. Если метод депорепРПеИаше () возвратит непустую строку, то будет создан объект клас- са огне, который будет проинициализирован этой строкой. Передача отопеухсе: :Неааоп1у в метод орИе: :ореп() говорит о том, что файл откры- вается только для чтения. В случае успешного открытия файла создается объект потока зегеат, который в нашем примере используется для чтения текста из файла. Чтение всего содержимого файла выполняется при помощи метода дтехезъгеаш: :геааА11(), который возвращает его в объекте строко- вого типа озсгхпд. Текст устанавливается в виджете методом зесР1а1птехс(). После этого файл закрывается методом с1озе(), а об изме- нении его местонахождения и имени оповещается высылкой сигнала скапдеихпдоадтхЫе (), для того чтобы использующее виджет Оосихпао» при- ложение могло бы отобразить эту информацию, изменив заголовок окна.
уохс! БосМхидоад:: з1оЪ5ауеАз () { ОЗЪгтпд б Иг = 0РИеО1а1од: :деИ8ауеРх1ёЫате(0, т_зЪгРЫеЫате); (’зЪгЛзЕгорНуО ) { пивПгРНеЫате = зСг; 51ое^аVе(); } } Приведенный в листинге 35.7 слот з1оI13аVеА8() отображает диалоговое окно сохранения файла с помощью статического метода 2гИеО1а1од: :деизаVеР^1е^ап^е (). Если пользователь не нажал в этом окне кнопку Сапсе! (Отмена) и метод вернул непустую строку, то в атрибут т_знгрИеЫате записывается имя файла, указанное пользователем в диалого- вом окне, и вызывается слот з1ое5аVе (). тохд Эос^хпЦо^:: зТоНЗате () { (т_зНгШеЫате. хзЕтрИу ()) { 51о^8аVеАз(); геНит; } ОРИе €Ие (т_5НгР11еПате) ; (Ше.ореп(ОЮЕЮТхсе: :Юг1НеОп1у)) { 0ТехН5Нгеат(&^11е) « ИоРХахпТехИ {); СИе.с1озе(); ели.И с!1апдеШпдоъ/Т1Ь1е (пк_з^гР11еМа1пе); } } Запись в файл представляет собой более серьезный процесс, чем считывание, так как связана с рядом обстоятельств, которые могут сделать ее невозмож- ной. Например, на диске не хватит места или он будет не доступен для запи- си. Для записи в файл нужно создать объект класса огПе и передать в него строку с именем файла (листинг35.8). Затем нужно вызвать метод ореп(),
передав в него значение ^IО^еV^се: :Иг1^еоп1у (флаг, говорящий о том, что будет выполняться запись в файл). В том случав, если файл с таким именем на диске не существует, он будет создан, если существует — то он будет от- крыт для записи. Если файл открыт успешно, то создается промежуточный объект потока, в который при помощи оператора « передается текст видже- та, возвращаемый методом еоРХахптехъ (). После этого файл закрывается методом окне: :с1озе() и высылается сигнал с новым именем и местонахо- ждением файла. Это делается для того, чтобы эту информацию могло отобра- зить приложение, использующее наш виджет посЮхпаою. #1€пс1е^ _50ХРгодгат_11_ #деГ1пе _5О1Ргодгат_й._ #1пс1ис1е <0ЁОи1> #1пс1иае "Пос^1пао\л7.11и #1пс1ис1е "ЗОТРгодгат.Н" // =======:г=====================^==^===========:==============:============ с1азз ЗОТРгодгат : риЬНс ОМахпШпаою { О-ОВСГЕСТ риЬНс: 5О1Ргодгаш(0М1дде1:* рмд1: = 0) : 0Ма1пЮ1п<1ою(рюдН { ОМепи* ртпиРИе = пею 0Мепи( ; ОМепи* ршпиНеТр - пете 0Мепи( "&Не1р") ; ПосМхпаоад* рдос = лею ПосЮ1пс1ою; ртпиГ11е->аскаАсе1оп (”&Ореп.. . ", рс1ос, 5ЬОТ(з1оЁЬоаа()), фКеуЗедиепсе ("СТКЬ+О") ); ртпиР11е->аас1АсЪ1оп (, раос, З^ОТ(з1оЪЗаVе()), ОКеуЗедиепсе("СТНЬ+3") ); ртпиР11е->аааАсС1оп (’,5&аVе Аз...",
р<1ос, 5ЬОТ(з1о113атеАз()) ); рпйпиЕ11е->айд5ерага^ог () ; ртпиР11е->ад(ЗАс1:1оп ("йОихЬ”, дАрр, 5ЬОТ(оиаЛ()), ОКеуЗедиепсе("СТВЬ+О") ); ргппиНе1р->а<ДбАсС1оп( я&АЬоиЪ", Шз, ЗЬОТ(з1оЪАЬои1:()), ОН: :Кеу_Г1 ); тепиВаг () ->аскЗМепи (ртпиЕНе) ; тепиВаг () ->а<5кЗМепи (ртпиНе1р); зеЪСеп<:гаШ1аде^ (рс1ос) ; сопнес1:(рс1ос, ЗТСМАЬ (сЪапдеШпс1о*/ГзЛ1е (сопзё 03^г1пд&)), ЗЬОТ (зЬоЪСЪапдеШпаоадТзЛЬе (сопзП 03Пг1пд&)) ); зСайизВаг () ->зЬоШеззаде ("Кеабу", 2000) ; } риЬИс 310113: то±а з1оСАЬоиС() { ОМеззадеВох:: аЪоиЪ (1Ыз, " Арр11саЁ1оп ”, ” 5Б1 Ехалпр1е } VО^а з1оеС11апдеМ1пао^/Г11:1е(сопз11 ОЗСгхпдй зёг) { зеПШпао^хЪХе (зег) ; } }; #еп<±1Г / /_ЗШРгодгаш_И_
Класс зшргодгаш унаследован от класса &па1пи1пдо« (листинг 35.9). В его конструкторе создаются три виджета—всплывающие меню ЕПе (Файл) (указатель ртпиРНе), Не!р (Помощь) (указатель ртпине1р) и виджет создан- ного нами окна редактирования (указатель рйос). Затем несколькими вызо- вами метода асМАсЫоп () неявно создаются объекты действий и добавляются в качестве команд меню. Третьим параметром указывают слот, с которым должна быть соединена команда, во втором параметре указан сам объект, который содержит этот слот. Таким образом команда Ореп... (Открыть...) соединяется со слотом з1осъоад(), команда Зауе (Сохранить) — со слотом з1оС5ауе(), а команда ЗауеАз... (Сохранить как...) — со слотом з1оьзаVеАз (). Все эти слоты реализованы в классе поси1пйо«. Команда АЬои1 (О программе) соединяется со слотом з1оельоис (), предоставляемым классом зшргодгат. Метод шепиваг () возвращает указатель на виджет меню верхнего уровня, а вызов методов ааамепио добавляет созданные всплы- вающие меню ЕНе (Файл) и Нс1р (Помощь). Вызов метода зес.Сепьга1и1адеь () делает окно редактирования центральным виджетом, то есть рабочей областью нашей программы. Для изменения текстового заго- ловка программы после загрузки файла или сохранения его под новым име- нем, сигнал скапдеи1пао«т1ъ1е (), высылаемый виджетом окна редактирова- ния, соединяется СО слотом 81оССПапдеИ1пДо»ЛхЪ1е (). Метод зИоздМеззаде (), вызываемый из виджета строки состояния, отображает надпись "Веаду" на время, установленное во втором параметре (в нашем примере это две секунды). М01-приложение МП1-приложение позволяет пользователю работать с несколькими открыты- ми документами. По своей сути оно очень напоминает обычный рабочий стол, только в виртуальном исполнении. Пользователь может разложить в его области несколько окон документов или свернуть их. Окна документов могут перекрывать друг друга, а могут быть развернуты во всю рабочую область. Рабочая область, внутри которой размещаются окна документов, реализуется классом омахАгеа (рис. 35.6). Виджет этого класса производит "закулисное" управление динамически создаваемыми окнами документов. При помощи слотов Ы1еЗиЬИ1паоу7з () и сазсааеЗиЬИгпйомз (), определенных в этом классе, произ- водится упорядочивание окон. Метод дмахАгеа: :зиЬи1паоиЫзь() возвра- щает список всех содержащихся в нем окон, произведенных от класса 0МД15иЬМ1пс1о™. Программа, окно которой показано на рис. 35.7, реализует основные функ- ции, присущие МЛЬприложению. В качестве класса окна документа приме- няется класс оосИхпаош, использованный при реализации 8В1-приложения (см. листинги 35.4—35.8).
__М31 Ргсдгат Я1е ^Ыо<л5 Не1р Рис. 35.7. МОЬприложение
ряда'Ж’П । »мьоа пен} #1Гпс1е^ _№1РгодгапЦ1_ Ме^тпе „М01Ргодгат_И_ #1пс1ис1е <0Ма1п^1пдсэтл7> с1азз ОМепи; с1азз фДсИАгеа; с1азз 051дпа1Маррег; с!азз ОосШпйою; / / ===================================;= с1азз МОТРгодгат : риЬИс 0Ма±пЮ1пс1ою { 0_ОВСГЕСТ ргзлгаЪе: ОМйхАгеа * т_рта ; ОМепи* 1гц_рттиМ1пдо^з ; ОЗхдпаТМаррег* т_рзхдМаррег; ОосМпс1ою* МОТРгодгат:: сгеаЪеМемОос (); риЬИс: М01Ргодгат(0И1с1де11* р«дС - 0) ; ргхтайе з!оГ1з: з1оЪСЪапдеУШ1<1оэдТдЛ1е (сопзЪ 05Ъг1пд&); рггуа^е з1о1:з: той! з!оЬЫе«Г)ос ( ); з1оЪЬоас1 ( ); тохд з1оЪ8аVе ( )г з1о^5аVеАз ( ); \лох<1 з1о1-АЬоиЪ ( ); VО^С1 31ОШХП(1ОУ75 ( ); Vо^д з 1 оЁЗеЬАсЪ1 Vе8иЬМ^пс^о^ (О^хбдеС*); }; #епсШ: //_М01РгодгапЦ1_
Определение класса мшргодгат, приведенное в листинге 35.10, содержит атрибуты, хранящие указатели на виджет рабочей области (перша), на всплы- вающее меню (Окна) (першпи^хпбо^з) и на сопоставителя сигналов (ш_рз хдМаррег). Примечание Объекты класса ОАсЪхоп высылают сигналы сгхддегед() только с булевыми значениями. Нам этого недостаточно, так как мы намереваемся высылать ука- затели виджетов окон редактирования. Поэтому мы и прибегли к использова- нию класса сопоставления сигналов озхдпаХМаррег. В классе млтргодгат определен слот, предназначенный для работы с окнами документов зХопихпаотез(), загрузки и сохранения файлов (з1осьоад(), з1оСЗауе() И з1оЪЗауеАз ()), СОЗДанИЯ НОВЫХ документов (зХоЁЫетеПос ()), а также предоставления информации о самом приложении. МШРгодгаш: :М01Ргодгаш(0М1<1деС* рмдЁ/*=0*/) : 0Ма1пМхпдо^(раддЪ) Г ОАсЪхоп* расЪЫею = пвм ОАсЁхоп("Ыею ЕПе", 0); рас^ею->зеЁТехЁ ("&Ыею") ; рас№ею->зеЪ5Ьог1:сиЪ (ОКеуЗедиепсе ("СТКЬ+Ы*')) ; расЪЫеад->зе1:Тоо1Тхр ("Ыею БоситепЁ”); рас^ею->зеЁ8ЪаЁизТ1р("СгеаЁе а пею ^х1е"); расЪЫеи^зеШЬаЪзТИхз ("СгеаЪе а пею ^х1е“); расЁЫею->зеЪ1соп (ОРххтар (^Непеш)) ; соппесЪ (расСЫею, ЗЮЫАЬ (Ъгхддегед ()) , ЗЬОТ (з1о^ете0ос ()) ) ; ОАсёхоп* расСОреп = лею 0АсЪхоп( "Ореп ЕПе", 0); расЁ0реп->зеСТехЪ("&Ореп..."); расЁОреп->зеС31югЁси1: (ОКеуЗедиепсе ("СТНЬ+О")) ; расЁ0реп->зеСТоо1Тхр("Ореп Восишеп!:") ; рас1:0реп-’>зеСЗЪаЁизТ1р( "Ореп ап еххз^хпд Сх1е"); расЁ0реп->зеЁМИаЁзТ111з ("Ореп ап ехтзЬхпд СИе"); расЁОреп->зеС1соп(0Р1хшар(Сх1еореп)); соппесЁ (рас!10реп, ЗТСЖАЬ (Ъгхддегед ()), ЗЬОТ (з1оеЬоад О)); ОАсёхоп* рас1:3ауе = пею ОАсЪхоп("ЗаVе Рх1е", 0); расСЗауе->зеЁТехЁ("&ЗаVе"); расЁЗауе->зе!:811огСси1: (ОКеуЗедиепсе (ИСТКЬ+5"));
расЪЗауе->зеЪТоо1Т1р( "Зауе Носителе") ; рас1:ЗаVе->зе^3^аЪизТ^р("ЗаVе Ле ^Ие Ъо <Нзк"); рас^15аVе->зеШЬа^зТИ^з (’’ЗаVе Ле ^Ие Л с11зк"); расЪЗауе->зе!:1соп(0Р1хтар(€11езауе)) ; соппесЁ (рас1:5ауе, ЗПЗЫАЬ (^гхддегеб ()), ЗЬОТ (з1о^ЗаVе ())) ; ОТооХВаг* рЛРхХе = лею ОТооХВаг("Р1Хе ОрегаЪтолз"); рЛРИе->а<Дс1Ас1:1оп (расЛею) ; рЛР11е->аа<ЗАсЁ1оп(расЬОреп) ; рЛР^1е->ас^<^АсI:^оп(рас1:ЗаVе); авйТооХВаг (ОЪ:: ТорТооХВагАгеа, рЛРх Хе) ; ОМепи* ртпиРИе » пею ОМепи ( "ЬРхХе"); рппиРх1е->аддАсЁхоп (расЛею) ; ртиРхХе->адЗАсЪхоп(рас€Ореп); р^ппиР^1е->ас^дАс^1^оп(расЪЗаVе); рппиРхХе->ас1<2Ас1:хоп (" 5аVе &Аз_", Л1з, ЗЬОТ (з1оЁЗаVеАз ())) ; ртпиРИе->а<Хс13ерагаЬог (); ртпиРИе->а<1дАсЁ1оп ("&ОиИ;", дАрр, ЗЬОТ(с1озеА11И1пс1о>73 ()), ОКеуЗедиепсе("СТВЬ+О") ); шепиВаг () ->асЗкЭМепи (ртпиРИе); пиртпиМХпдоитз = пею ОМепи (" &Ш.п(1оюз ”); тпепиВаг () ->аскЗМепи (пиртпиМхпаоадз) ; соппесЁ (пиртпиМ1пс1ои73, ЗВЗЫАЬ (аЪои^ТоЗЪоот ()), ЗЬОТ (з1оШ1п<Хо№ ())) ; шепиВаг()->аййЗерагаЁог(); ОМепи* ртпиНеХр = пеиг 0Мепи("&Не1р"); ртпиНе!р->ад<ЗАсЬ1оп (" &АЬоиЁ", Л1з, ЗЬОТ (зХоЛЬоиЁ ()), 0*1:: Кеу_Р1) ; шепиВаг () ->аддМепи (ртпиНеХр) ; щ_рта = пею ОМвхАгеа; щ_рша->зеЪНог12опЁа13сго11ВагРо11су(011:: Зсго11ВагАзЫеедед); т_^жпа->зеЬУегЁ1са18сго11ВагРоИсу(0Ё: :Зсго11ВагАзЫеес1ед); зеЪСепЪгаХУ/хбдеЁ (пс_рта);
щ_ре1дМаррег = пеад 051дпа1Маррег (ЬЫз) ; согтесЪ (щ_р5хдМаррег, 5ЮЫАЬ(тарред(0^1адее*)), Шз, ЗЬОТ (з1оЪ5еЪАсЁ^е5иШ1пао^л7 (ОМхадеС*)) ); з!:а1:изВаг () ->зИогоМеззаде ("Кеаду", 3000); } В конструкторе класса мыргодгат (листинг 35.11) создаются три объекта действий для команд создания, открытия и сохранения документов — указатели расъыёад, расьореп и расьзаVе, соответственно. Сигнал объектов Ег1ддегеа () соединяется со слотами класса мотргодгат. Вызовами методов ааадсЫоп () объекты действий добавляются к панели инструментов и в меню. Команда меню Г11е | Ош! (Файл | Выход) соединяется со слотом объекта приложения с1озеА11к1паошз (), который закрывает все окна приложения. Для создания рабочей области МЕИ-приложения необходимо создать вид- жет омахАгеа. Чтобы содержимое рабочей области можно было прокру- чивать, вызываются методы зеСНогхаопЕаТЗсгоИВагРоИсуО И зеЁ7егЫса13сго11вагРоЦсу (), в которые передается значение оь:: зсгоИВагАзыееаеа, для того чтобы горизонтальная и вертикальная по- лоса прокрутки не были постоянно видны, а возникали лишь при необходи- мости. Установка рабочей области в главном окне виджета производится ме- ТОДОМ зеЁСепСга1ГО1(1деЪ (). После создания объекта сопоставителя сигналов (указатель т_рз1дмаррег) его сигнал тарресЦ) соединяется со слотом 81оезеЪАсЪгуе5иЬи1п<ао»(), реализованным в нашем классе мшргодгат. Это позволит нам высылать вместе с сигналами указатели на виджеты, которые будет обрабатывать слот з1оьзеСАсе1уе5иЬИ1пао«(). Об этом слоте будет рассказано позже (см. листинг 35.20). Метод зЪошеззаде (), вызываемый из виджета строки состояния, отображает надпись "Кеаду" в течение трех секунд. то!с1 МОТРгодгат:: зТо^ЫемОос () { сгеаСеЫеюОос () ->з11ою() ;
Слот зТоъыемэос(), приведенный в листинге 35.12, создает новое окно доку- мента и делает его видимым. \ УЗВ» Л&ъ V............................................ ЦосРЛп(5о\л7* МОТРгодгат:: сгеаЪеЫеадОос () { ТХхЛЯпдою* рйос = лею СосМхпйо^; т__Ехпа->а<1<13иЬМ1пгЗо^(р<^ос) ; р<Зос->зе^АЁ1:г1Ьи1:е(Се: :И?<_Ре1еее0лС1озе) ; рдос->зеШхпс1о5лД’1Ё1е ("Пллатед ОоситепЪ”) ; рдос->зеШхп(Зо^1соп (ОРххтар (М1епею)); соппесЬ (рс1ос, ЗХОЫАЬ (сИалдеИхпдоюТхЁ1е (солзЪ 03Ъгхлд&)), ЗЬОТ (з1о<:СкалдеМхпс1оадТхЪ1е (сопзТ: 05^гхлд&)) ); *• геЪигл р<1ос ; } В листинге 35.13 в методе сгеа^еыеъюос () создается виджет класса посихпдою, который вызовом метода адазиШхлДоМ) добавляется в рабочую область приложения. В метод зеедеегхЬиее () передается значение ОН: :МАвпе1еъеОлС1озе, говорящее виджету о том, что он должен быть унич- тожен при закрытии своего окна. Метод зеш1паоадт1Ё1е() устанавливает заголовок окна. Небольшое растро- вое изображение в области заголовка устанавливается методом зешхпаоуутсоп (). Для изменения заголовка окна виджета, а также и окна программы, если виджет развернут, сигнал скалдемхлдо^тхетеО соединяет- ся СО СЛОТОМ 81оЪСЪапдеЮ1паоУ7Т1Ь1е() . ^^1а^иТ1йа ($ ’ч' уохд МОТРгодгат: :з1оЁСкапде^1паоадТ11:1е(сопзЬ 03Ъг1пд& зЪг) { доВдесЪ—сазЪ<ОосЮхпс1оУ7*> (зепдег ()) ->зеШ1пскл//Г1е1е (зёг) ; } Слот з1огсъапдеи1пдо»тхс.1е () определен как рг^аее и не может быть вы- зван извне. Поэтому мы не проверяем успешность приведения к типу
0осн1п4ок, а сразу вызываем метод зеси1пао«т1ъ1е() виджета окна редакти- рования, установив в нем имя и местонахождение ассоциированного с ним файла (листинг 35.14). уо1с1 МЛТРгодгат:: з1о(1Ьоас1 () { ОосИхпйои* рдос = сгеаЬеЫеиЕюс(); р<1ос->з1оЫюас1() ; р<1ос->зЬош() ; } Внутри слота зХоеьоаа () вызывается метод сгеаеемешоос (), который созда- ет новый виджет документа и возвращает его указатель, после чего операция считывания делегируется далее, к виджету созданного документа (лис- тинг 35.15). то1<а ЮХРгодгага:: з1о^:ЗаVе () { ОосИ1пдом* рйос - до^есЬ_сазЪ<ОосКхпаои*>(псрвпа->асС^еЗиЪИ1паои()) ; х€ (рдос) { рс1ос->з1оС5а-уе (); ) Слот з1оъ^аVе () получает указатель на текущее окно документа при помощи виджета рабочей области и, если приведение к типу восихпао» было успеш- ным, делегирует операцию сохранения (листинг 35.16). тоха МОХРгодгат: :з!оЬЗауеАз () { Оосйхпаои* рс1ос = доЬзесЪ_сазЪ<1ЭосИ1пао»*>(щ_р«па->асЬ1уеЗиЮТхпао»()) ; хГ (рйос) { рдос->з1оЬ5ауеАз(); } }
Действия слота 81оезаVеАз() аналогичны действиям з1оъз^е() (лис- тинг 35.16), только с делегированием метода ^1оъзаVеА^ () (листинг 35.17). »•»:....;...... ,г- ' - з то:1д МЛТРгодгат:: з1оеАЬоие () { ОМеззадеВох:: аЬоие (Шз, • Арр11сае1оп ”, ” М01 ЕхащрЬе ") ; } Слот г1оЬАЪоиь () отображает окно сообщения с информацией о приложении (листинг 35.18). То1с1 МОТРгодгат: : з1оШ1пс1оюз () ( т_ртпийШкЯоодз->с1еаг (); ОАсехоп* расе = т_ртпиМ1пдо^з->ас1дАсе1оп ("ЛСазсаде", щ_рта, ЗЬОТ (сазсааеЗиШхпдо^з ()) расе->зееЕпаЫе<1( ’1гирта->з1ЛзМ1паоздЬ1зе () . 1зЕтреу ()) ; расе = щ_ртпи^1пдоадз->а<ас1Асе1оп( и&Т11е", т_рта, ЗЬОТ (е11еЗиШ1пс1оюз ()) ); расе->зееЕпаЫес!( !п^^даа->зиШ1псЗо™Ь1зе (). хзЕгпреу ()) ; т_ртпиМпдо>гз->ас1дЗерагаеог () ; 0Ь1зе<0Ш15и№1пЦою*> НзеОос п^^ата->зиЪМ1п(Яо^Ь1зе () ; йэг (хпе 1 = 0; 1 < НзеОос.зхге() ; ++1) { расе = пь_1ХппиМ1п<Яо^7з->а<5с1Асе1оп (1 хзеПос. ае (1) ->^1Пс1о^Т1е1е ()) ; расе->зееСЬескаЫе (егие) ; расе->зееСИескесЯ (т_рла->асе^еЗиЬМ1пскж () == НзеОос. ае (1));
со.ппесС(расе, ЗИЗЫАЬ(егхддегеа()), псрзхдМаррег, ЗЬОТ(шар())); т_рз1дМаррег->зеемарр1пд(расС, ИзС1)ос.ас(1)); Одним из отличий МО1- от 801-приложения является наличие всплывающего меню ^У!пс1омг$ (Окна), назначение которого — управление окнами докумен- тов, находящимися в рабочей области. Для отображения актуальной инфор- мации, перед показом это меню необходимо очистить методом с1еаг() и за- тем заполнить списком команд (листинг 35.19). Первыми в меню (Окна) добавляются команды меню СазсаДе (Каскад) и ТПе (Мозаика). В за- висимости от наличия в рабочей области окон (опрашивается методом зиЬИтпйоздЫзе ()) эти две команды, при помощи вызова метода зесвпаЫеа (), делаются доступными или нет. Затем цикле €ог в меню добавляются коман- ды с названиями окон документов. Каждая команда меню вызовом метода зеьсиескаЫе () с параметром сгие получает возможность оснащения флаж- ком, который устанавливается только у команды, ассоциирующейся с актив- ным окном. Является'ли окно активным, выясняется с помощью сравнения, результат которого передается в метод зессьескеао для установки статуса активности. Далее, сигнал сг!ддегеа<) объекта действия (указатель расе) соединяется со слотом шар () сопоставителя сигналов ш_рз1дмаррег. Благодаря этому, при активации команды меню будет выслан сигнал шаррес1() (см. листинг. 35.11) с указателем на виджет окна редактирования, возвращаемым методом ас (), который устанавливается методом зесмаррхпд (). Метод з1осзесАсс^езиЬ«1пао«() делает активным окно, в соответствии с переданным в него указателем виджета (листинг 35.20). Для начала мы убеждаемся в том, что значение указателя не нулевое, а затем приводим этот указатель к типу ома1зиьи1пао» и передаем его в метод зеСАсСз^еЗиЬИхпЦо’Ю (). зго1а МШРгодгаш: :з1оСЗеСАсС1УеЗиЬИ1пао»(0К1адеС* ридС) { И (рмдС) { т_рта->зеСАсС^еЗиШ1паом(доЬэесС_сазС<(^1а13иЬИ1паои*> (рмдС)) ; } } 19 Зак. 3983
Резюме Существует два типа приложений — поддерживающие 8В1 (8т§1е Г)оситеп( ЬПегГасе, однодокументный интерфейс) и поддерживающие МГ)1 (МиШр1е Восшпеп( 1п1егГасе, многодокументный интерфейс). Главное отличие МО1- от 801-приложения состоит в том, что 801-приложение содержит только одно окно документа, а МОЬприложение способно содержать несколько таких окон, что дает пользователю возможность параллельной работы с несколь- кими документами. Класс с>ма1пи±пао« предоставляет уже готовое окно приложения, в котором находятся виджеты, необходимые большинству программ. В центре разме- щена рабочая область, которая может содержать только один виджет. При помощи класса омшлгеа в этой области можно размещать сразу несколько виджетов, что позволяет реализовывать МТЛ-приложения. Виджеты находят- ся в рабочей области в виде отдельных окон, которые можно перемещать, изменять размеры, сворачивать, разворачивать, упорядочивать и т. д. Класс действия ОАсЫоп предоставляет возможность централизации всех элементов интерфейса, связанных с конкретной командой, в одном объекте. Это позволяет значительно сократить время разработки программы, а также уменьшить объем исходного кода. Панель инструментов используется для предоставления быстрого доступа к часто используемым командам меню. Этот виджет представляет собой до- чернее окно, в котором помещены кнопки, позволяющие запускать команды одним нажатием левой кнопки мыщи. Виджет строки состояния располагается в нижней части главного окна про- граммы и используется для отображения информационных сообщений трех типов: промежуточного, нормального и постоянного. При запуске программы можно отображать окно заставки, реализованное в классе 03р1азЪЗсгееп. Это позволяет скрыть длительный процесс инициа- лизации поиложения.
Глава 36 Рабочий стол (Оезк1ор) "Все следует делать настолько простым, насколько это возможно, но не проще”. Альберт Эйнштейн До настоящего времени все наши программы не выходили за пределы графиче- ской области своего окна, но в некоторых случаях в этом есть необходимость, так как преследуются особые цели. В этой главе мы рассмотрим программу, распо- ложенную в области оповещений, которая представляет собой пример такого приложения. Кроме того, мы расскажем о работе с виджетом экрана. Область оповещений Практически все мы с этой областью хорошо знакомы. В ХУ1пс1о\у8 (см. рис. 36.1) она называется областью уведомлений (ЫоНДсайоп агеа), хотя часто использу- ется термин 8у$1еш Тгау, и находится в нижнем правом углу (то же месторас- положение принято в КОЕ (рис. 36.2) и (ЖОМЕ (рис. Зб.З)), а в Мас 08 X она называется Мепи БхПаз и располагается в верхнем правом углу (см. рис. 36.4). Вот такие разные названия, а по своей сути одинаковые вещи. Основное назна- чение этой области — индикация состояния программ и системы. Рис. 36.1. Мпдоюз \Лз1а Рис. 36.2. КЭЕ/Опих Рис. 36.4. Мас 08 X 1_еорагс1 Рис. 36.3. (ЗМОМЕ/Опих
В области оповещения обычно расположены индикатор часов, индикатор заряда батареи (для ноутбуков), иконка для регулирования громкости звука, отображение сетевого подключения и многое другое. Такие приложения как, например, 8куре и 1С<2 загружают в ^шЛохуз свою собственную иконку в эту область. Это позволяет быстро оповещать пользователей о наступающих со- бытиях, например, получении нового сообщения, а также дает возможность быстро открыть основное окно этих программ двойным нажатием на их иконку. Этот прием позволяет также не загромождать панель задач. Посред- ством смены самой иконки и вывода сообщений можно оповестить пользова- теля о текущем состоянии программы или об ошибках. Итак, вы хотите воспользоваться этими возможностями и в ваших програм- мах? Тогда перейдем поскорее к классу озузьеттгаутсоп. Именно он и реа- лизует все эти замечательные возможности. Его использование довольно просто. Для того чтобы установить иконку, нужно вызвать метод зеысоп (), а для ее отображения в области уведомления нужно вызвать метод зИо» (). Если вам нужно отобразить сообщение для пользователя, то следует вызвать метод зйомМеззаде (). Желательно также установить подсказку для пользователя при помощи мето- да зегтоо1т±р (), для того чтобы он мог увидеть имя вашей программы и, возможно, информацию о ее состоянии, наведя указатель мыши на иконку вашей программы. И, конечно же, в большинстве случаев вам может потре- боваться установить контекстное меню, для того чтобы взаимодействовать с вашей программой из области уведомления. Для этого предусмотрен метод зеССопЬехСМепи(). Проиллюстрируем все выше изложенные возможности написанием простой программы. Эта программа имеет контекстное меню, из которого пользова- тель может выбрать следующие команды (см. рис. 36.5): □ 8йо5У/НШе АррПсаНоп ЮТшкп? (Показать/скрыть окно приложения) — отображает показанное на рис. 36.6 окно. Если же окно до этого было ви- димо, то оно будет скрыто; □ 8Ьоху Меввадо (Выдать сообщение)— выводит сообщение, показанное на рис. 36.7; □ СЬавде 1соп (Сменить иконку)— изменяет изображение снежинки на звездочку и наоборот; □ (}ш< (Выход). Кроме того, наше приложение будет обладать подсказкой (см. рис. 36.8).
СНС . ик1с -рр к’ЪО 1 /.'!ГС:С.. 5Исл Менаде СЬапде коп С-.М кчЙБт г < О С?ф> 12:22 АМ Рис. 36.5. Контекстное меню программы 0 гог уС'мГ ’пЬгтатсл Уои Паме $е1ес1ес11Ье "ЗНомгМеззадеГ орйоп Рис. 36.6. Основное окно программы Рис. 36.8. Подсказка Рис. 36.7. Сообщение #1пс1иае <2С<Зи1> # 1пс1и<5е • ЗузСетТгау. 11 ” 1пС 1па1п(1пС агдс, скаг** агду) ( ОАррИсаЫоп арр(агдс, а^дV) ; ЗузЬетТгау зС; рАррИсаСхоп:: зеС0и1СОпЕазСШп<ЗоюС1озед (1а1зе) ; геСигп арр.ехес(); } В основной программе, приведенной в листинге 36.1, мы только созда- ем виджет. Так как закрытие окна приложения ни в коем случае не должно завершать нашу программу, мы вызываем статический метод ОАррИсасхоп:: зеьои1еопЬазсм1п<Яо^() и передаем в него значение ^а1зе. Окна виджетов 0( по умолчанию невидимы, и это замечательно, так как, в основном, приложения, предназначенные для области отображения, пока- зывают после запуска только свою иконку в этой области и никаких окон. По этой причине в основной программе нет привычного вызова метода зПо«().
_5уз^етТгау_Ии_ #де^1пе _5узЪетТгау_Ъи_ #1пс1иде <0ЬаЬе1> с1азз 08уз€етТгау1соп; с!азз ОМепи; // ^====^======^=^======^=^=^====^============^=====^=^====^=====^=== с1азз ЗузЪетТгау : риЬНс ОЬаЬе1 { 0-ОВ7ЕСТ рггтаЪе: О5уз ЪеяпТгауТсоп* пцЛгауЮоп ; ОМепи* пи_рЪгау1сопМепи; Ьоо1 пЦэ1соп8ш11:с11ег; ргоЪесЪед; то.гЪиа1 то!Д с1озеЕуепЪ(0С1озеЕуепЪ*); риЬНс: ЗузЁешТгау(ОЮхйдеЪ* раддЪ = 0) ; риЬНс з1оЪз: тохс! з!о!:5]1о^Н1(Яе (); то1с! зТо^ЗЬошМеззаде () ; тохс! з1оЪСкапде1соп {); }; #епд1 € / /_5узЪетТгау_?1_ В заголовочном файле, приведенном в листинге 36.2, мы определяем класс окна нашего приложения. Он наследуется от дьаЬе!, для того чтобы могли отобразить надпись. Наш класс содержит три атрибута: □ объект <23узеетТгау1соп (указатель щ_рыау1соп), который и предоставит нам возможность использования области оповещений; □ виджет омепи (указатель псръгауюопмепи), который необходим для реа- лизации контекстного меню;
□ переменная булевого типа т^этсолЗюхъсИег, нужна нам для управления сменой растровых изображений. Мы перегрузили метод события закрытия окна с1о8еЕVепс(), так как наше приложение должно реагировать на него иначе. Три слота зТоСЗИоюШсЯе (), зТо&ЗИоюМеззаде () и зХо^СИапдеТсоп () реализу- ют команды контекстного меню. ЗузЪетТгау:: ЗузСетТгау (ОМхбдеЁ* рюдС /*=0*/) : 0ЬаЬе1("<Н1>Арр1хса1:хоп М1п<5ою</Н1>", риде) , пЦэХсолЗюхЪсЪег (1а1зе) { зеЪШпДоюТхЪЬе ("ЗузСет Тгау") ОАсЬхол* рас^ЗИо^Иде = лею 0Ас^хоп("&ЗИош/Нхс1е АррИсаЪхол Мхлдою”, еЫз); соппесЁ (расЪЗИоюНхДе, 51СЫАЬ (Сгхддегес! (}), Шз, ЗЬОТ {зХоСЗкоадНМе ()) ); ОАсЬхоп* расЁЗИошМеззаде = лею 0АсЪ1оп( "З&Иою Меззаде", 1Ыз); соппес!: (рас^ЗИоюМеззаде, ЗКЖАЬ (Ыхддегес! ()), ЬЬхз, ЗЬОТ (зХоЪЗЪоиМеззаде ()) ); ОАс^хоп* рас±СЪалде!сол = ле» ОАсЪхоп( "&СИалде 1соп", еЫз); соплесС (расЪСЬапдеХсоп, ЗЮЫАЬ (егхддегесЯ () ), сЫз, ЗЬОТ (зХоЪСИалдеТсоп ()) ); ОАсЪхоп* расЪОиэХ = лею 0АсС1оп("&0иИ:и, 1:Ыз) ; соплесЁ (расЪОихЪ, ЗЮТАЬ (1:г1ддеге<1 ()), дАрр, ЗЬОТ (дихС ())); щ_рЪгау1солМели = лею ОМели(ЪЫз); щ_р!:гау1сопМепи->адс1Ас1:1оп (расСЗкоюНхДе) ; п^цэЪгау1сопМели->а<МАсЪ1оп (рас^ЗкоюМеззаде) ; щ_рЪгау1сопМепи->а<а<ЗАсЪ1оп (рас€СЪалде1соп);
1ц_рЬгауХсопМепи->а<ЗДАсЪ1оп(расС0и1Ь) ; щ_рСгау!соп = пе» 08узЬетТгауХсоп(Ы11з); щ_рЬгау1соп->зеССопСехШепи (т_р5гау1сопМепи); к\_регау1соп->зеЬТоо1Т1р( "ЗузСет Тгау"); зХоЬСЪапдеХсоп(); т_рСгауХсоп->зИо«(); ) В конструкторе (листинг 36.3) мы создаем объекты действий (одсЫоп) для наших команд и соединяем их соответствующими их назначению слотами. Затем мы создаем меню (омепи) и добавляем в него наши объекты действий. Последним мы создаем объект области оповещения (озузсеттгаухсоп), кото- рый в качестве предка получает указатель ьЫз. 'Метод зеьсопсехсмепи () устанавливает только что созданное нами контекстное меню. Метод зеьтоо1т1р () устанавливает надпись подсказки "ЗузХет Тгау". Установка растрового изображения происходит в слоте зХоссиапдехсопо, поэтому мы просто вызываем его. Наконец, метод зИош() добавит растровое изображение программы к области оповещений. /*У1гЪиа1*Лго1с1 ЗузЪетТгау: :с1озеЕуепЬ(0С1озеЕХгепЬ* ре) { 15 (щ_р5гау1соп->1зУ1з1Ые()) { Ыде () ; ) } Большинство программ, рассчитанных на область оповещения, скрывают свое окно, а не закрывают его. Для этого мы перегрузили метод обработки события закрытия окна виджета (листинг 36.4). В нем мы проверяем, являет- ся ли иконка в области оповещений видимой, и если да, то просто вызываем метод мае (), чтобы спрятать окно. то1а ЗузЬетТгау::зХоЬЗИоиНШе() { зеСУ1з1Ые(! 1зУ1з1Ые());
Слот з1оезъо»нхае (), приведенный в листинге 36.5, закреплен за командой ЗЬо'й'/НШе АррИсайоп УУвпдо^. Всякий раз при его вызове он меняет режим видимости на противоположный. То есть если виджет был видимым, то он станет невидимым, и наоборот. тохс! ЗуаЁетТгау:: з1оЬ5Ио\Жез5аде () { ш_регау1соп->з1юШез5аде ("Рог уоиг 1пСохтпаЪ1оп", "Уои 1юте зе1есЁе<1 Ъ11е ’’ "\"5Иою Меззаде!\" орС1оп", ОЗузСешТгауЮоп: :1п^огтпаЪ1оп, 3000 ); Слот 81оезйо«ме8заде() закреплен за командой 8Ьомг Меввадо. Вызовом ме- тода зкоимезваде () он отображает сообщение (см. листинг 36.6). Первый параметр — это заголовок сообщения, второй — само сообщение, третий за- дает иконку, которая будут отображаться с информацией, и четвертый — промежуток времени, отведенный для отображения сообщения на экране, в нашем случае это три секунды. уохй ЗузСетТгау::з1оЬСИапде1соп() { п\_ЫсопЗ»1Ьс!1ег = !щ_Ысоп5«1СсИег; ОЗЫхпд зЪгРхяпарЫате = гц_ЫсопЗт;1ес11ег ? ’ :/йпадез/йпд1.Ътр" : ":/йпадез/йпдЗ .Ьпр" ; 1Ц_рЬгау1соп->зеЫсоп(0₽1хтар(зЬгР1хтарНате)) ; Слот з1о1:СЬапде1соп() закреплен за командой СЬап§е 1соп и предназна- чен для установки растрового изображения в области уведомления (лис- тинг 36.7). При его вызове мы всякий раз меняем значение булевой перемен- ной щ_Ысопзм1есЪег и, в зависимости от ее состояния, используем одно из двух растровых изображений 1т§1.Ьтр либо 1т§2.Ьтр. Затем мы устанавли- ваем его в области уведомления вызовом метода зеысоп ().
Виджет экрана Класс ооевксориМдее отвечает за доступ к содержимому графической информа- ции экрана. Кроме того, этот класс может оказаться очень полезен для правиль- ного позиционирования окна вашего приложения, ведь пользователь может ис- пользовать различные разрешения экрана. Этот виджет существует только в одном экземпляре, и его нельзя создать, но зато можно получить указатель на него. Это достигается вызовом статического метода ЗезкЪорО, определенного в классе ОАррПсаЫоп. Таким образом, для того чтобы отобразить ваше окно строго по середине экрана, можно поступить следующим образом: 1. Получить указатель на виджет экрана, вызвав ОАррЫсаЫоп: :аезкеор(). 2. Определить текущее разрешение экрана при помощи методов и Ъе1дкС(). 3. Вычислить позицию окна посередине в соответствии с текущим разреше- нием и размерами окна приложения (ш1аеь () и Ье1дЬе () самого виджета окна). 4. Установить ее вызовом метода тоуе (). Все это можно реализовать одной программной строкой: ридС->пюуе( (ОАррНсаЫоп: :<1езкЪор()->«71(101 () - риде-жМЬЬ()) / 2, (ОАррИсаЫоп: :<1езкЬор()->Ие1дЫ:{) - ридЪ->Ье1дЫ: ()) / 2 ); Теперь давайте вернемся к возможности доступа к графической информации экрана. Ее можно проиллюстрировать на примере программы (рис. 36.9), ко- торая делает снимки с экрана компьютера при нажатии на кнопку "СарШге Зсгееп" и тут же показывает сделанный снимок. #1пс1ис1е <ОЪСих> #1пс1ис1е "СгаШЫдеС .И" 1пС та1п(1пе агдс, сИаг** агду) { ОАррИсаШоп арр(агдс, агду) ; СгаШхддеГ «дС; юдЬ.зЪоюО ; геЪигп арр.ехес();
Рис. 36.9. Программа, делающая снимки экрана В основной программе (листинг 36.8) мы создаем виджет определенного в листингах 36.9—36.11 класса сг^ьихадеъ, в котором реализуется возмож- ность снятия снимка экрана. #1€пЗе€ _0га1Жс1деЬ_Ь_ Ме^тпе _СгаШ1аде1:_Ь__ #1пс1иде <0И1с1деЪ> с1азз 0ЬаЬе1; / / ====================================================================== с1азз ЗгаЬМддеЪ : риЬИс ОММдеЪ { 0__ОВЛЗСТ ргдоаСе: 0ЬаЬе1* т_р1Ы;
риЬНс: СгаШхддеГ (О&НЗдеЪ* рмд!: = 0) ; риЬНс з1оЪз: тохд з1оЬОгаЪЗсгееп(); }; #еп<И / /_<ЗгаШ1адее_Ъ_ В самом классе СгаЬИхдде^ (листинг 36.9) мы определяем указатель на виджет надписи (п1_р1ы), который нам понадобится для отображения только что сфото- графированной области экрана. Класс также содержит слот з1оъсгаЬЗсгееп(), который будет реализовывать собственно процедуру фотографирования экрана. СгаЬМхйдеС:: ОгаЬйМдеЪ (ОЮхйдеЁ * ридЪ / *=0 * /) : ОМхбдеЬ (раддЪ) { гез1хе(640, 480); щ_р1Ы = пем 01»аЬе1 () ; ОРизЪВиЪХоп* рспкЯ = пею ОРизЪВиГХоп ("СарЪиге Зсгееп"); соппесЬ (рспкЯ, ЗЮЫАЬ (с1 хскед ()) , ЗЬОТ (зХоЬСгаЬЗсгееп <))); //ЬауоиЪ зеЪир ОТВохЬауоиЁ* рVЬx^ауоиЪ = пезд. ОУВохЬауоиЬ; р^хЬауои^->а<Яс[М1с1де11 (рстс1) ; руЬхЬауоиЬ->а<1<ЗМ1ддеЁ (пир1Ы) ; зеЪЬауоиС(руЬхЪауои1:); В конструкторе, приведенном в листинге 36.10, мы создаем кнопку с надпи- сью СарШге 8сгееп (Захватить экран) и соединяем ее сигнал нажатия (сНскеао) со слотом фотографирования экрана (в1оьсгаьзсгееп<)). Затем кнопка и надпись добавляются при помощи вертикальной компоновки р^хЬауоиЪ. СгаШМде!::: з!оШгаЬЗсгееп() { РВезкЪор^МдеЁ* р«дС = ОАррИсаЪхоп: гйезкЪорО ;
ОРгхтар рхс = 2₽ххтар::дгаЬИхп<1ои(р«дС->зсгееп()->»хп1<а()); щ_р1Ы->зеЬ.Рххтар(рхс.зса1ес1(1ц_р1Ы->5Х2е())); } В листинге 36.11 приведена самая интересная, хотя и несложная, часть про- граммы, позволяющая получить содержимое экрана. Сделать это можно с помощью указателя на виджет экрана, который мы получаем вызовом ста- тического метода аезкъор (). Класс овххтар предоставляет возможность по- лучать графическое содержимое виджета в виде растрового изображения, что достигается вызовом метода дгаьш1паок() и передачей в него идентифика- ционного номера окна виджета, который, в свою очередь, возвращается ме- тодом «хшао. В метод дгаЬИхпйоиО можно передать также вторым аргументом и прямо- угольную область, в пределах которой нам нужен снимок. Если второй аргу- мент отсутствует, то фотографируется сразу вся область виджета. Но сначала нам понадобится вызвать метод зсгееп (), чтобы получить указатель на вид- жет конкретного экрана, ведь пользователь может иметь не обязательно только одну, а две, три и более экранных областей, отображаемых на разных мониторах. Вызывая этот метод без аргументов, мы получаем указатель на виджет экранной области по умолчанию. Примечание Сколько именно пользователь имеет экранных областей, можно узнать вызо- ВОМ метода : :цшп5сгеепз (). Остался последний штрих: мы приводим размеры полученного изображения в соответствие с размером виджета надписи (вызывая метода зса1ебо) и ус- танавливаем его вызовом метода зесРххтар (}. Резюме Область оповещения информируют пользователей о заряде батареи, статусе приложения, позволяет производить некоторые изменения, например, регу- лировать громкость. Работа с областью оповещения реализована классом СЗузЪетТгауТсоп. За доступ к содержимому экрана отвечает класс опезкъорюхбдег:. Его можно также использовать в целях правильного позиционирования окон приложе- ния на экране компьютера.
Часть VI Особые возможности 01
Глава 37 Работа с файлами, директориями и потоками ввода-вывода "Цивилизация движется вперед путем увеличения чисел операций, которые мы можем осуществлять, не раздумы- вая над ними". Альфред Норт Уайтхед Редко встречается приложение, которое не обращается к файлам. Работа с директориями (папками, в терминологии ОС ^тдохуз) и файлами — это та область, в которой не все операции являются платформонезависимыми, по- этому 0( предоставляет свою собственную поддержку этих операций, кото- рая базируется на следующих классах: □ оШг — для работы с директориями; □ ОШе — для работы с файлами; □ ОРПехпЕо — для получения файловой информации; □ оюэеухсе — абстрактный класс для ввода-вывода; □ Ови^Гег — для эмуляции файлов в памяти компьютера. Ввод-вывод. Класс ОЮОеу/се Класс <2ЮОеу1се — это абстрактный класс, обобщающий устройство ввода- вывода, который содержит виртуальные методы для открытия и закрытия устройства ввода-вывода, а также для чтения и записи блоков данных или отдельных символов. Реализация конкретного устройства происходит в унаследованных классах. В ($ есть четыре класса, наследующих класс оюоеу±се (рис. 37.1): □ ОРИе — класс для проведения операций с файлами;
□ ОВи^Еег — класс буфера, который позволяет записывать и считывать дан- ные в массив овуЪеАггау, как будто бы это устройство или файл; □ САЬзггасезоскее — базовый класс для сетевой коммуникации посредст- вом сокетов (см. главу 40)-, □ ОРгосегг — класс, предоставляющий возможность запуска процессов с дополнительными аргументами (см. главу 39) и позволяющий обмени- ваться информацией с этими процессами посредством методов, опреде- ленных В ОХОПеухсе.' |аидр$Рйке|^.г,здЧ'| Рис. 37.1. Иерархия классов ввода-вывода Для работы с устройством его необходимо открыть в одном из режимов, оп- ределенных в заголовочном файле класса отовеухсе: □ 01опеу1се: :Лосореп — устройство не открыто (это значение не имеет смысла передавать в метод ореп ()); □ ^IО^еV^се: ,:Кеааоп1у — открытие устройства только для чтения данных; □ оюоеу1се:: игЮеОпХу — открытие устройства только для записи данных; □ ^IО^еV^се: :кеааигК:е — открытие устройства для чтения и записи дан- ных (то же, ЧТО Кеад0п1у I Иг1СеОп1у); □ оюоеухсе:: Аррепа — открытие устройства для добавления данных; □ <2юоеу1се: ;цпЬи«егеа — открытие для непосредственного доступа к дан- ным, в обход промежуточных буферов чтения и записи;
□ <210оеу1се: :Техе — применяется для преобразования символов переноса строки в зависимости от платформы. В ОС ХУтсктль используется комби- нация "\г\п", а в МасОЗ X и Йшх — н\г"; □ ^IО^еV^се: гтгипсаье — все данные устройства, по возможности, должны быть удалены при открытии. Для -того чтобы в любой момент времени исполнения программы узнать, в каком из режимов было открыто устройство, нужно вызвать метод орепмоде (). Считывать и записывать данные можно с помощью методов геад () и иг1ъе (). Для чтения всех данных сразу определен метод геаадпо, который возвра- щает их в объекте типа овуееАггау. Строку или символ можно прочитать ме- тодами геайЫпе () И деЬСИаг (), соответственно. В классе оюпеухсе определен метод для смены текущего положения зеек(). Получить текущее положение можно вызовом метода роз (). Но не забывай- те, что эти методы применимы только для прямого доступа к данным. При последовательном доступе, каким является сетевое соединение, они теряюз смысл. Более того, в этом случае теряет смысл и метод з±ге (), возвращаю- щий размер данных устройства. Все эти операции применимы только для ОРИе, ОВи^ег И ©ТетрогагуГИе. Для создания собственного класса устройства ввода/вывода, для которого 91 не предоставляет поддержки, необходимо унаследовать класс ^IО^еV^се и реализовать в нем методы геасЮаЪа () и ^ггеепа^а (). В большинстве случа- ев может потребоваться переопределить методы ореп (), с1озе () и аЬЕпа (). Благодаря интерфейсу класса дюоеухсе можно работать со всеми устройст- вами одинаково, и при работе обычно не имеет значения, является ли устрой- ство файлом, буфером или другим устройством. Например, с помощью тако- го метода можно выводить на консоль данные из любого устройства: рг1п1:(0100^1 се* рс^еV) < сИаг сЪ; ОЗЬгхпд зСг; р<^еV->ореп(^I0^еV^се: :Кеа<ЗОп1у) ; Сог (; !рс^->аЪЕп<3() ;) { рдеV->деЪС11а^ (&сИ) ; зЪг +- сЪ; } р(^еV->с1о8е () ; феЬид() « зЬг;
Класс ^IО^еV^се предоставляет ряд методов, с помощью которых можно по- лучить информацию об устройстве ввода-вывода. Например, одни устройства могут только записывать информацию, другие — только считывать, а третьи способны делать и то, и другое. Чтобы проверить, какие операции доступны при работе с устройством, следует воспользоваться методами тзнеадаЫе () И 1зИг1(:еаЫе (). Работа с файлами. Класс ОЕПе Класс ОРПе унаследован от класса оюс^Хсе (см. рис. 37.1). В нем содер- жатся методы для работы с файлами: открытия, закрытия, чтения и записи данных. Создать объект можно, передав в конструкторе строку, содержащую имя файла. Можно ничего не передавать в конструкторе, а сделать это после создания объекта, вызовом метода зесыаше (). Например: ОГНе ЕНе; Н1е.зеСМате("Ше.даЬ") ; В процессе работы с файлами иногда требуется узнать, открыт файл или нет. Для этого вызывается метод отооеухсе: -ЛзОрепО, который вернет сгие, ес- ли файл открыт, иначе — ^а1зе. Чтобы закрыть файл, нужно вызвать метод с1озе (). С закрытием производится запись всех данных буфера. Если требу- ется произвести запись данных буфера в файл без его закрытия, то вызывает- ся метод ОГПе: : г1изЬ (). Проверить, существует ли нужный вам файл, можно статическим методом ОРПе: :ех1зьз(). Этот метод принимает строку, содержащую полный или относительный путь к файлу. Если файл найден, то метод возвратит егие, в противном случае — 1а1зе. Для проведения этой операции существует и нестатический метод ОРПе: :ех1зсз(), который проверяет существование файла, возвращаемого методом ШеыатеО. Методы оюпеухсе:: геай () и С1ооет1се: :«г1ъе() позволяют считывать и записывать файлы блоками. Продемонстрируем применение некоторых методов работы с файлами: 0Рх1е Ше1("€11е1.<1а1:*); 0Рх1е €х1е2 (и^х1е2.йаЁ: "); х^ (^х1е2.еххзЪэ()) { //Файл уже существует. Перезаписать? } х^ (! ^х1е1. ореп (ОЮЕ^хсе: :Кеа<З.Оп1у)) { дИеЬидО « "Ошибка открытия для чтения"; хС (’ Н1е2 .ореп(0101^хсе: :ЮгхЪе0п1у)) (
дБеЬидО « "Ошибка открытия для записи"; } сЪаг а[1024]; «Ы1е(! Н1е1 .аЬЕпд()) { хпС пВХоскзхге = СИе1.геасЦа, зхзеоЩа)); €Ие2.юг1Ъе(а, пВТоскзхге) ; } Н1е1. с1озе () ; €Пе2 .с1озе(); Если требуется считать или записать данные за один раз, то используют ме- тоды :^г1Ёе() и отогютхсе: :геавА11(). Все данные можно счи- тать в объект класса овуееАггау, а потом записать из него в другой файл: ОЕИе ^Ие1 (" ^11е1 .даЪ") ; ОРИе ^Ие2 ("Ше2 . йаЬ") ; (Н1е2 .еххзЁз ()) { //Файл уже существует. Перезаписать? } (I И1е1.ореп (^IО^еV^се: :Кеас1Оп1у)) { дПеЬцдО « "Ошибка открытия для чтения"; } 1Г (! ЕПе2. ореп (01О1)е'ц1се: :У?г1ЪеОп1у)) { дВеЬидО « "Ошибка открытия для записи"; } ОВу^еАггау а = ^Не1.геас1А11 () ; Ше2 .югхЁеСа); Н1е1.с1о8е(); €Ие2. с1озе (); Примечание Операция считывания всех данных сразу при большом размере файла может занять много оперативной памяти, а значит, к этому следует прибегать только в случаях острой необходимости или в том случае, когда файлы занимают ма- ло места. Расход памяти при считывании сразу всего файла можно значитель- но сократить при условии, что файл содержит избыточную информацию. Тогда можно воспользоваться функциями сжатия дСошргеззО и дПпсотргезз (), которые работают с классом ОВуЬеАггау. Эти функции получает в качестве аргумента объект класса овуЪеАггау и возвращают в качестве результата но- вый объект класса овуъедггау.
Для удаления файла класс ОРИе содержит статический метод гепклгеО. В этот метод необходимо передать строку, содержащую полный или относи- тельный путь удаляемого файла. Класс ОВиНег Класс ови«ег унаследован от огопеухсе (см. рис. 37.1) и представляет собой эмуляцию файлов в памяти компьютера (тепюгу тарреН Г11е$). Это позволяет записывать информацию в оперативную память и использовать объекты как обычные файлы (открывать при помощи метода ореп () и закрывать методом с1оге ()). При помощи методов мг1се () и геад () можно считывать и записы- вать блоки данных. Можно это также сделать при помощи потоков, которые будут рассмотрены далее, например: ОВуЬеАггау агг; ОВиЕСег ЬиССег(&агг); ЬиЕЕег,ореп(0101)еУ1се::Иг1ЬеОп1у); ОЭаЬаЗЬгеаш оиЬ(&ЬиГ^ег); оиС « 03Сг1пд("Меззаде"); Как видно из этого примера, сами данные сохраняются внутри объекта клас- са овуьеАггау. При помощи метода ЬиЕ^ег () можно получить константную ссылку на внутренний объект овуееАггау, а при помощи метода зеьвиССег () можно устанавливать другой объект овуеедггау для его использования в ка- честве внутреннего. Метод йапа () идентичен методу ЬиГ^ег (), но метод зеЬОаЬа () отличается от зеевиЕГегО тем, что получает не указатель на объект овусеАггау, а кон- стантную ссылку на него для копирования его данных. Класс ови^Еег полезен для проведения операций кэширования. Например, можно считывать файлы растровых изображений в объекты класса овиНег, а затем, по необходимости, получать данные из них. Класс СПетрогагуЕПе Иногда приложению может потребоваться создать временный файл. Это мо- жет быть связано, например, с промежуточным хранением большого объема данных или передачей этих данных какой-либо другой программе. Класс отешрогагурИе предоставляет удобный способ работы с временными файлами. Этот класс самостоятельно создает имя файла, гарантируя его уни- кальность, для того чтобы не возникало конфликтов, в результате которых могли бы пострадать уже существующие файлы. Сам файл будет расположен
в каталоге для промежуточных данных, местонахождение которого можно получить вызовом метода оо1г:: сетрРасы). С уничтожением объекта будет уничтожен и сам временный файл. Работа с директориями. Класс ОО/г Разные платформы имеют разные способы представления путей. ОС УУт- доАУ8 содержит буквы дисков, например: С:\\Упк1о\У8\8у81ет. 1ЛЧ1Х исполь- зует корневой каталог /, например: /изг/Ып. Обратите внимание, что для раз- деления имен директорий в обоих представлениях используются разные знаки. Для представления директорий в плаформонезависимом виде пре- доставляет класс ош г. Для этих целей класс содержит целый ряд статических методов, которые по- зволяют определить: □ оШг:: сиггепс () — путь текущей директории приложения; □ оохг: :гоос()—корневую директорию; □ :: бг 1 уез ()—указатель на объект класса окиешсо, содержащий список с узловыми директориями (для ХУ1пс1о\У8 это будут С:\, Э:\ и т. д.); □ 0О1г: :Ьоте() — персональную директорию пользователя. Примечание Класс оШг не предоставляет методов для определения каталога, из которого было запущено приложение. Если нужно узнать этот путь, то следует восполь- зоваться либо методом ОАррИсаЫоп: :аррИсаР1опО1гРаЪЪ(), либо мето- дом ОАррИсаЫоп: :арр11саЬ1опРИеРаЩ1(), возвращающим еще и имя приложения. Существование директории можно проверить с помощью метода ехтзезО. Чтобы перемещаться по директориям, можно использовать метод са (), пере- дав в качестве параметра путь директории, и метод сайр (). Вызов са (•.. •) эквивалентен вызову метода сапр (). Оба метода возвращают булево значе- ние, сигнализирующее об успехе операции. Для конвертирования относительного пути директории в абсолютный можно вызвать метод такеАЬзоХиСе (). Для создания директории нужно вызвать метод ткагг (). При успешном про- ведении операции метод вернет значение сгие, в случае неудачи — Еа1зе. Если вам потребуется переименовать файл или директорию, то воспользуй- тесь методом гепатео. В этот метод первым параметром нужно передать
старый путь, а вторым — новый. Если операция проведена успешно, то ме- тод вернет егие, иначе — 1а1ве. Удаление пустых директорий производится методом ипйхг (), который полу- чает путь и в случае успеха возвращает егие, а в случае неудачи -— €а1ве. Просмотр содержимого директории При помощи класса оохг можно получить содержимое указанной дирек- тории. При этом допускается применять различные фильтры, чтобы исклю- чить из списка не интересующие вас файлы. Для этих целей в классе опре- делены методы епегуЫгео и епегушГоЫзе (). Первый возвращает список имен элементов (езсгхпдьхзе), а второй — информационный список (ОШетпГогЛзе). Если вам нужно лишь узнать количество элементов, нахо- дящихся в директории, то просто вызовите метод соипс (). Программа, показанная на рис. 37.2, осуществляет рекурсивный поиск фай- лов в директории, указанной в текстовом поле В1гес1огу (Директория). Нажатие кнопки с растровым изображением откроет диалоговое окно выбора нужной директории. В текстовом поле Маек (Маска) задается фильтр для отображаемых файлов. Например, для отображения исходных файлов на языке С++ нужно задать в поле Маек (Маска) *.срр и *.Н. После нажатия кнопки Ипй (Поиск) производится поиск и отображение файлов в соответст- вии с заданными параметрами. Результаты отображаются в виджете много- строчного текстового поля. ОгсДог/ С:.,М|пЗ‘Л' гахй > Маек ’.сйэ * К ’ _Р,п<з ; • *С..-'Мп-><5УУ.--1по1ий1в. с-—.’З/ 2.Ьзскиагс!. гсое.И > ; ’ 1С МитЭЙ.'|пг!иЗе. с—. 3 - 2. Ьаз<г.-а:0 ; |с М|пЗ‘Л;/1Г13^-зе. с--. 3.- 2.Ъаск,.,.,а<^.. : !С .'МгЗй. с--.. 3 ^-2.ЪасК',-.-31'±'Яа:к.Ь ! р - ’/г'З’Л'. —.'Зл.^Ьаок’Л'агд/йгегт.Ь | - . ’ЛпЗЙ шсЬйе, с—. ЗЛ 2. Ъаск'л'агО. йгеатЬиГК ?? рС . Мг.Зи-сЬае, с—. 'ЗАг-Ъагк'л’ап! 1еФрЬь<Н р ; Цр&'Л'. 1гс<иае. о--. 3* 2,-Ьазк'Л'агдЛгее Р, " !• |С . Мшб'.'Л 11’с1иде. з—. 3 > 2. Ьаек’л'агс! уес1сг\ |С'. М|п37А |г<1иде. с—.'З * 2,Ьм. а!юса:сг Ь ЗА2. ЬйЬ! а1ип1ак.Ь____________________ _ . \\- ' : : ' Рис. 37.2. Программа отображения файлов директории по маске
Рх1еРхпс1ег: :Рх1еРхлс1ег (ОМхбдеЬ* радд1:/*= 0*/) : ОИхДдеЪ (рюдЬ) { ш_рЬхЪОхг = лею ОЫпеЕсШ: (<ЭЫг:: сиггепк () . аЬзо1иСеРаЪЪ. ()) ; т_р1зхЪМазк = лете ОЬхлеЕсИЪ (” *.срр * .И") ; п\_рЁхЪНези111 = лете ОТехЪЕсИЪ; ОЬаЪе1* р1ЬЮхг = лете ОЬаЬе! {”&Г>1 геологу ”); ОЬаЪе1* р1ЫМазк = лете ОЬаЬе1("&Мазк") ; ОРизЪВиЪЁоп* рстсЮхг = лете ОРизкВиЪСол (ОРххшар (СИеорел), ОРизЪВиЪЪоп* рстйРхлб. = лете ОРизЪВиЪЁол ("&РхлсР‘); соппес!: (рстсЮхг, 51(ЗЫАЬ (сИскед ()) , ЗЬОТ (зТоЪВготезе ())) ; соппесЁ (рсткЗРхпс!, ЗЮЫАЬ (сИскед ()), ЗЬОТ (зТо^Ехлс! ())); р!ЫПхг->зеЪВискЗу (щ_рЪхи?хг) ; р1ЫМазк->зе^Ви<1с1у (псрЪх134азк) ; //ЬауоиН зеСир ОСгхсЗЬауоиЬ* рдгс!ЬауоиЬ = лею ОСгхсЯЬауоиЬ; рдгсЗЬауоиЁ->зеШагд1п (5) ; рдгс1Ьауоие~>зееЗрас1лд (15); рдг6ЬауоиГ:->аддМ1с1деЪ(р1ЫП1г/ 0,0); рдгдЬауоиЪ->ад(ЗЮ1аде!1 (р1ЫМазк, 1, 0) ; рдгбЬауоиЁ->а(3<ам1<1деС (щ_рЪхСВхг, 0, 1) ; рдг5ЬауоиЬ->айс1^16де11 (щ_рЪхГМазк, 1, 1) ; рдг<ЗЬауои!:->адс1МхйдеП (рсжЮхг, 0, 2); рдгдЬауоиЁ->ад<ЗМхс1деС(рстйРхлб, 1, 2); рдгдЬауоиЪ->аД<ЗЮхс1деГ(т_р^хЬКези11:, 2, 0, 1, 3); зе!1Ьауои1: (рдгдЬауоиЪ); } В конструкторе класса рПеРхлбег, который приведен в листинге 37.1, соз- даются виджеты однострочного и многострочного текстовых полей (указате- ли т^рсхньхг, т__рехемазк и т^рсхгкезиИ:). Первый виджет инициализирует- ся абсолютным путем, возвращаемым методом олхг;; аЬзоХи^еРаъи (), который, в свою очередь, инициализируется значением текущей директории, возвращаемой методом дпхг:: сиггель (). Второй виджет инициализируется
строкой, предназначенной для фильтрации найденных файлов. Для открытия диалогового окна выбора директории и запуска операции поиска создаются две кнопки (указатели рсшагхпа и рст<Ю1г), которые соединяются со слотами з1оьГ1па() и з1оевго«зе (). В конструкторе создаются два виджета надписей и с помощью метода зеевиаауо ассоциируются с виджетами однострочных текстовых полей. Созданные виджеты размещаются в виде таблицы при по- мощи объекта класса оогЮъауоиЬ (см. главу 6). той} ЕНеПпдег: : з1оСВго»зе () { ОЗСгхпд 511 г = 0РИеП1а1од: :де1:Ех18Ъ1пдВ1гес^огуСО, "Зе1ес1: а БхгесЬогу", т_рЪхЬЛ1г->1:ех1: () ); ' ( !зПг.1зЕЙрСу() ) { псрЬхЪЛ1г->зе1:ТехЪ(зЪг) ; } } В листинге 37.2 приведен метод а1оевгошзе(), который открывает диалого- вое окно для выбора директории поиска. После закрытия этого окна выбран- ная директория записывается в текстовое поле В1гес1огу (Директория) мето- дом зеЬТехЬ(). уо1а Р11еР1п<1ег: :з1оЪР1пс1() { зЬагЬ(0О1г(щ_рЬхи)1г->ЬехЁ())); } Метод з1осР1па() запускает операцию поиска, для чего передает в метод зпагЁ () выбранную пользователем директорию (листинг 37.3). •уоШ РИеР1пдег: :зЪахХ(сопа<: 0В1Г& <Нг) { ОАррИсаЫоп: :ргосеззЕуепЪз ();
ОЗСгхпдЫзС ИзЪРИез = <Иг. епегуЫдЪ (пирЪхСМазк->1:ехЪ (). зрИе (" "), ОБхг:: Шез) ; ГогеасЪ (ОЗСгхпд СНе, НзСРИез) { т_рЪх!:Кези1^->аррепд(сПг.аЬзо1иЪеРИеРа1111 (СИе)) ; } ОЗЪгхпдЫзЪ Из€С1Г = й1Г.епЪгуЬ1зЪ(0П1г: :Йхгз) ; ^огеасЪ. (05&г1пд зиЬсПг, ИзЫЭхг) { (зиЬсПг " || зиЬсИг { сопе1пие; } зГаге(0В1г (<Цг.аЬзо1иГеГ11еРа^к(зиЬс11г))); } } Метод зеагео использует рекурсию для поиска по поддиректориям (лис- тинг 37.4). Процесс поиска заданных файлов может быть длительным, что может привести к "замиранию" графического интерфейса программы. По- этому, для подавления этого нежелательного эффекта, при каждом вызове метода мы вызываем метод олррЫсаЫоп: :ргосеззЕуепъ() и даем возмож- ность обработаться накопившимся событиям. В методе зсагьо переменная ИзскИез получает список файлов текущей директории, соответствующих маске. Для этого в метод передаются два параметра. Первый представляет собой список шаблонов поиска, которые распространяются на имена и рас- ширения файлов. В нашем случае мы преобразуем строку в список, разделив ее при помощи пробелов вызовом озег!пд: :зрИс (). Второй параметр (флаг 0О1г: :РИез) говорит о том, что список должен содержать только файлы. Элементы полученного списка добавляются в виджет многострочного тек- стового поля с помощью метода аррепас). По завершении работы цикла до- бавления в метод епъгуЫзьО передается флаг 0П1г::п1гз для получения списка директорий. Для каждого из элементов списка, кроме •. • и •..", вы- зывается метод зСагС (). Примечание Использование в листинге 37.4 флага оШг:: поооЪАпаооСБоь избавило бы нас от необходимости сравнения имени директории со строками *. ° и "..". Также можно использовать метод епсгуЫзьО без указания параметров. В этом случае сами критерии фильтрации можно задать при помощи метода зесШьег (). Дополнительно можно так же задать критерий сортировки спи-
ска, при помощи метода зе1;5огс1пд(). В следующем примере мы получаем список скрытых файлов, отсортированных по их величине: сНг.зеЬГНЬег(0О1г: :РИез | Орхг: :Н1ск1еп) ; (Нг. зеЬЗогЫпд(дШг::31ге) ; □5Ьг1пдЬ1з1: сопЬепЪ = сНг. епЬгуЫзЬ () ; Информация о файлах. Класс ОРПе!пТо Задача класса оШеХпЕо состоит в предоставлении информации о свойствах файла: его имени, размере, времени последнего изменения, правах доступа и т. д. Объект этого класса создается передачей в его конструктор пути к файлу или объекта класса о?11е. Файл или каталог? Иногда необходимо убедиться, что исследуемый объект является каталогом, а не файлом, и наоборот. Для этой цели существуют методы хзгНеО и хзохг (). Если объект является файлом, метод хзШе () возвращает значение егие, иначе — еа1ее. Если объект является директорией, то метод хзохг () возвращает егие, иначе — еа1зе. Кроме этих методов, класс ОРПехпЕо со- держит метод хезушъхпк (), возвращающий егие, если объект является сим- вольной ссылкой (термин зутЬоНс 1тк применяется в ЦЬПХ, в ОС принято название ярлык (зЬойсШ)). Примечание Символьные ссылки применяются в 1ЛЧ1Х для обеспечения связи с файлами или каталогами. Создаются они при помощи команды 1п с ключом -з. Путь и имя файла Чтобы получить абсолютный путь к файлу, нужно воспользоваться методом аЪзо1иееРИе₽аеи (), а для получения относительного пути следует исполь- зовать метод еНеРаеъо. Для получения имени файла нужно вызвать метод енеыашео, который возвращает имя файла вместе с его расширением. Если нужно только имя файла, то следует вызвать метод ЬазеМате (). Для получе- ния расширения используется метод сошрХеееЗи^Их (). Информация о дате и времени Иногда нужно узнать время создания файла, время его последнего изменения или чтения. Для этого класс ОЕИеХпЕо предоставляет методы сгеасеао, хазьмосШхеао и 1азькеаа(), соответственно. Эти методы возвращают объ-
екты класса роаъетхте (см. главу 38), которые можно преобразовать в строку методом созыз-пд (). Например: //Дата и время создания файла ^НеТпГо. сгеаЪес! () . ИоЗИгхпд () ; //Дата и время последнего изменения файла ШеТпСо.ТазШосИ^хесК) .ЪоЗЬгхпдО; //Дата и время последнего чтения файла ^НеТп^о. 1аз«<еад () . ЬоЗЪгхпд (); Получение атрибутов файла Атрибуты файла дают информацию о том, какие операции можно проводить с файлом. Для их получения в классе дШетп^о существуют следующие ме- тоды: □ хзКеадаЫе () — возвращает сгие, если из указанного файла можно читать информацию; □ 18Нг1ьеаЫе () — возвращает сгие, если в указанный файл можно записы- вать информацию; □ 18Н1ааеп() — возвращает ьгие, если указанный файл является скрытым; □ 1зЕхесипаЫе () — возвращает ъгие, если указанный файл можно испол- нять. В ОС 1Л41Х это определяется не на основании расширения файла, как принято в ЭО8 и ОС ХУшйохуб, а из свойств самого файла. Определение размера файла Метод з1ге() класса огПехпЕо возвращает размер файла в байтах. Размер файлов редко отображается в байтах, чаще используются специальные буквен- ные обозначения, сообщающие о его размере. Например, для килобайта — это буква К, для мегабайта — М, для гигабайта—О, а для терабайта — Т. Сле- дующая функция позволяет сопровождать буквенными обозначениями раз- меры, лежащие даже в терабайтном диапазоне (вполне возможно, что через несколько лет это будет обычным размером некоторых типов файлов): РЗЪгхпд СИе312е(д1п^64 п312е) { 1пС 1 = 0; €ог (; п81зе > 1023; п81ге /= 1024. ++1) {
Шх >= 4) { Ьгеак; } } гекигп 05Ьгхпд().зеС№лп(п31ге) + "ВКМЗГ"[1]; } Наблюдение за файлами и директориями Поскольку файлы и директории используются не только вашей программой, бывает очень полезно получать уведомления в случае их изменений. Напри- мер, вы написали файловый браузер и показываете содержимое текущей ди- ректории. Но пользователь может воспользоваться другими программами и скопировать новые файлы в эту директорию, после чего отображаемая ин- формация перестанет отвечать действительности. Не имея специального ме- ханизма слежения за изменением текущей директории, вы не сможете узнать о необходимости обновить его содержимое. Такой механизм реализует класс огИеЗузсетиаьсЬег. Его применение сво- дится к тому, что нужно добавить методом ааараеъо нужный вам путь к файлу либо к директории, а когда необходимость в наблюдении за ним от- падет, удалить при помощи метода гепкууеРаьь (). При изменениях файла вы- сылается сигнал ЕЫесъапдеа (), а если изменена директория— сигнал ахгессогусьапдеа (). Оба сигнала.передают в качестве параметра путь, по которому произошли изменения. Потоки ввода-вывода Объекты файлов сами по себе обладают только элементарными методами для чтения и записи информации. Использование потоков делает запись и считы- вание файлов более простым и гибким. Для файлов, содержащих текстовую информацию, следует использовать класс отехьзыеат, а для двоичных фай- лов — класс ОВаЪаЗСгеат. Применяются классы отехсзыеат и оваьазсгеат так же, как и стандартный поток ввода-вывода в языке С++ (хозсгеаш), с той лишь разницей, что они могут работать с объектами класса ^IО^еV^се. Благодаря этому потоки мож- но использовать и для своих собственных классов, унаследованных от ^IО^еV^се. Для записи данных в поток используется оператор «, а для чте- ния данных из потока — ».
Класс 0Тех181геат Класс отех^зъгеаш предназначен для чтения текстовых данных. В качестве текстовых данных могут выступать не только объекты, произведенные клас- сами, унаследованными от отопеухсе, но и переменные типов сНаг, осиаг, сИаг*, ОЗЬгхпд, ОВуЪеАггау, зкогЪ, хпЬ, 1опд, ^ХоаЪ И ДоиЬХе. Числовые данные, передаваемые в поток, автоматически преобразуются в текст. Можно управлять форматом их преобразования, например, метод отехъзъгеат: :зеъкеа1ншпЬегРгес1з1оп() задает количество знаков после запятой. Этот класс следует использовать для считывания и записи текстовых данных в формате Ипхсоде. Чтобы считать текстовый файл, необходимо создать объект типа рРхХе и считать данные методом отехсзегеаш:: геаДЫпе (). Например: ОРхХе Н1е(иН1е.ехе*); И (€хХе. ореп (рГОТЭетхсе:: НеаДОпХу)) { ОТехЁЗЪгеат зЪгеат(&^хХе); 05Ьг1пд з€г; юкх1е (! зЬгеат. а^ЕпД ()) { зСг = зЪгеат.геаДЫпеО ; дОеЬидО « зЪг; } х€ (зЪгеат.зЪаЪиз () != ОТехСЗСгеат: :Ок) { дПеЬидО « "Ошибка чтения файла"; } ^хХе.рХозеО ; } Методом стехьзегеаш: хгеадлп () можно считать сразу весь текстовый файл в строку. Например: ОРхХе ^хХе ("гпуИХе. ЬхЬ"); 1 ± (€1Хе. ореп (ЩООеуХсе:: КеаДОпХу)) { ОТехЪЗЪгеат зЬгеашС&ИХе) ; ОЗЁгхпд зЪг = зСгеаш.геабАХХ(); НХе.сХозеО ; } Чтобы записать текстовую информацию в файл, необходимо создать объект класса овне и воспользоваться оператором «. Перед записью можно про- вести необходимые преобразования строки. Например: ОРхХе ^хХе( "^хХе.ЬхЪ"); ОЗЬгхпд зёг = "ТЫз хз а ЪезС"; х^ (НХе.ореп(01О1^хсе: :Юг1ЪеОпХу)) {
ОТехСЗСгеат зЁгеат(&€11е); зЬгеат « зЬг.ЪоПррег (); //Запишет ТН15 13 А ТЕЗТ ЕНе.сХозеО ; 1Г (зГгеат.зЪаГпдз () != ОТехЪЗЪгеат: :Ок) { дВеЬидО « "Ошибка записи файла"; } > Класс отехсзьгеат создавался для записи и чтения только текстовых данных, поэтому двоичные данные при записи будут искажены. Для чтения и записи двоичных данных без искажений следует пользоваться классом ооаеазыеаш. Класс СЮа1а81геат Класс опасезьгеат является гарантом того, что формат, в котором будут за- писаны данные, останется платформонезависимым и его можно будет счи- тать и обработать на других платформах. Это делает класс незаменимым для обмена данными по сети с использованием сокетных соединений (см. гла- ву 40). Формат данных, используемый ооасазсгеат, в процессе разработ- ки версии (X претерпел множество изменений и продолжает изменяться. По этой причине этот класс содержит информацию о версии, и для того что- бы заставить его использовать формат обмена, соответствующий определен- ной версии <Х нужно вызвать метод зееУегзхоп (), передав ему идентифика- тор версии. Текущая версия имеет идентификатор ое_4_5. Класс поддерживает большое количество типов данных, к которым относят- ся, например,ОВуЬеАггау, ОРопС, СХшаде, ОМар, ОРгхшар, ОЗСгхпд, 0Уа1иеЬ1зЪ и Уаг1апе. Следующий пример записывает в файл объект точки (орохпср), задающей позицию растрового изображения, вместе с самим объектом рас- трового изображения (оьпаде): ОРИе Н1е(,Н1е.Ып'); 1С({Ие.ореп(010ОеУ1се: :Кг1ЬеОп1у)) { ОБаЬаЗЬгеат гЬгеаш(&111е); зЬгеат.зеЬУегз1оп(01>аЕ.аЗегеап1: :0Ь_4_5); зЪгеаш « ОРохпЬР (30, 30) « ОХшаде (" йпаде. рпд") ; 1Е (зСгеаш.зЬаЬиз () != ОРаЬаЗСгеат: :Ок) { дОеЬидО « "Ошибка записи"; } } {Ие.с1озе();
Для чтения этих данных из файла, нужно сделать следующее: ОРохпЪР рЪ; ОТтаде 1тд; ОРИе ШеСШе.Ып"); И: (€ 11е. ореп (дювеухсе:: КеасЯОп1у)) { ОПаЬаЗСгеат з^геат(&^Ие) ; з Сгеат. зеЪУегз топ (ОБаЪаЗСгеат:: ОЪ_4_5) ; зСгеат » рЪ » ипд; ' 1Г (зЬгеат. зЬаЬиз () ’ = ОБаЪаЗЪгеат:: Ок) { дРеЬидО « "Ошибка чтения файла"; } ) СИе.с1озе() ; Резюме В 01 имеется абстрактный класс оюоеухсе, который представляет собой уст- ройство ввода-вывода. Классы, унаследованные от охооет1се, предоставляют возможность записи и чтения данных в файл байтами или блоками. ОРПе и 0Ви«ег наследуют этот класс. Класс орИе содержит методы, необходимые для работы с файлами, такие как открытие, закрытие, чтение и запись. Класс овиЕ€ег предназначен для эмуляции файлов в оперативной памяти. Такие файлы хранятся не на диске, а в памяти компьютера, что существенно ускоряет процесс обращения к ним. Класс оо1г предоставляет платформонезависимый подход для работы с ди- ректориями. Этот класс содержит методы для создания и уничтожения ди- ректорий, а также для получения главной, текущей и персональной директо- рии пользователя. При помощи класса ОРИехп^о можно получить всю необходимую информа- цию о файлах и директориях. <21 также предоставляет классы потоков, которые делают удобной работу с данными файлов: ооаеазсгеат для двоичных данных и отехезьгеаш для текстовых. Использование этих потоков очень похоже на использование обычного потока в языке С++. Как и в стандартных потоках, для чтения и записи перегружены операторы « и ». Так как потоки принимают объекты классов, унаследованных от охооеухсе, то, вместо объекта класса окне, дан- ные можно перенаправить, скажем, в объект класса овиИег. 20 Зак. 3983
Глава 38 Дата, время и таймер "Что часто повторяется, то вскоре ста- новится доказанным”. О. Минье В этой главе речь пойдет о часто необходимых при программировании объ- ектах для манипулирования датами и временем, а также о реализации повто- ряющихся событий или задержек при помощи таймеров. Дата и время Приложениям часто требуется информация о дате и времени, например, для выдачи отчетной информации или для реализации часов. (^1 предоставляет для работы с датой и временем три класса ооаъе, опте и опасетйпе, опреде- ленных в заголовочных файлах РБа1е, ОТппе и 0Оа1еТ1те. Класс даты ООа1е Класс <2Оасе представляет собой структуру данных для хранения дат и прове- дения с ними разного рода операций. В конструктор класса опаье передаются три целочисленных параметра. Первым передается год, вторым — месяц а третьим — день. Например, создадим объект, который будет содержать да- ту 25 октября 2009 года: ОПаЬе баЪе(2009, 10, 25); Эти значения можно установить и после создания объекта, с помощью мето- да ееьоасе (). Например: ООаЪе даЪе,- <ааСе.зеЫЗаЬе(2009, 10, 25);
Для получения значений года, месяца -и дня, установленных в объекте даты, следует воспользоваться следующими методами: □ уеаг () — возвращает год в диапазоне от 1752 до 8000; □ шопеи () — возвращает целое значение месяца в диапазоне от 1 до 12 (с января по декабрь); □ аау() — возвращает день месяца в диапазоне от 1 до 31. С помощью метода аауз1пмопей() можно узнать количество дней в месяце, а с помощью метода ЗауаТпУеаг () — количество дней в году. Для получения дня недели следует вызвать метод аауо^иеек (). Для получе- ния порядкового номера дня в году служит метод аауосуеаг (). Можно также узнать номер недели, ДЛЯ Чего НУЖНО ВЫЗВаТЬ МеТОД шеекЫишЬег (). Метод Созыхпд () позволяет получить текстовое представление даты, а в ка- честве параметра можно передать одно из значений, указанных в табл. 38.1. Таблица 38.1. Перечисления оанеРохтае пространства имен ос Константа Значение Описание ТехеВаее 0x0000 Специальный формат 01 (определен по умолча- нию) 18ООаее 0x0001 Расширенный формат 180 8601 (УУУУ-ММ-ОЭ) 5узСетЬоса1еВаЪе 0x0002 Формат, зависящий от установленного в опера- ционной системе языка страны Ьоса1еПаЪе 0x0003 Формат, использующий локализацию приложе- ния, которая устанавливается вызовом метода ОЪосаХе:: зеыэе!аи1Ъ (). Если он не установ- лен, то используется формат 5уз еетЬоса1еВаСе Если в таблице не приведен нужный вам формат, то можно определить свой собственный, передав в метод ъозсгхпдО строку-шаблон, описывающую его. Например: ОБаСе <Заее(2009, 7, 3); рЗСгхпд зЪг; зГг = сааЪе.Ъо5Сг1пд("сЬМ.уу”); //зег = "3.7.09” зСг « даЪе.ео5Сг1пд(”дд/ММ/уу"); //зЪг = "03/07/09” зСг = да ее. ео8ег!пд( "уууу.МММ.ддд") ; //зёг - ”2009.июл .Пт" з^г = даее.ео8ег1пд("уууу.ММММ.дддд");//зег = "2009.Июль .пятница"
При помощи метода аааоауз () можно получить измененную дату, добавив или отняв от нее указанное количество дней. Действия методов ааамопьиз () и адбУеаггО аналогичны, но разница в том, что они оперируют месяцами и годами. Например: ООаЪе 6аЬе(2009, 1, 3); ОВаСе с1аЬе2 = <ЭаЪе. аскЗОауз (-7); ОЗСгХпд зСг = <ЗаЬе2.ЪоЗЬгхпд("дй/ММ/уу"); //зСг = "27/06/09" Класс опаъе предоставляет статический метод сготзсгхпд(), позволяющий проводить обратное преобразование из строкового типа к типу ооаье. Для этого первым параметром метода нужно передать строку с датой, а вторым можно передать формат в виде флага из табл. 38.1 или строки. Одна из самых частых операций — получение текущей даты. Для этого нуж- но вызвать статический метод сиггепсласе (), возвращающий объект класса ОБаЬе. При помощи метода йаузто () можно узнать разницу в днях между двумя да- тами. Следующий пример определяет количество дней от текущей даты до Нового года: 01>аЪе даЬеТойау = 01ЭаЬе:: СиггепЫЭаЬе () ; ООаСе даЬеМеыУеаг(даЬеТодау.уеаг(), 12, 31); дВеЬидО « "Осталось " « <ЯаЬеТо<1ау.с1аузТо(с1аСеЫе1Лгеаг) « " дней до Нового года"; Объекты дат можно сравнивать друг с другом, для чего, в классе ооаее, оп- ределены операторы ==, > -, <, <=, > и >=. Например: 00а€е <ЗаСе1(2009, 1, 3);- ООаСе йаСе2(2009, 1, 5); Ъоо1 Ь = (<1аСе1 == <1аСе2); //Ь = 1а1зе Класс времени СГПте Контроль над временем — очень важная задача, с помощью которой можно вычислять задержки в работе программы, отображать на экране текущее время, проверять время создания файлов и т. д. Для работы со временем, библиотека предоставляет класс отйпе. Как и в случае с объектами даты, с объектами времени можно проводить опера- ции сравнения ==, !=, <, <=, > или >=. Объекты времени способны хранить время с точностью до миллисекунд. В конструктор класса отйпе передаются четыре параметра. Первый параметр задает часы, второй — минуты, тре-
тай — секунды, а четвертый — миллисекунды. Третий и четвертый парамет- ры можно опустить, по умолчанию они равны нулю. Например: ОТйпе Ъыпе(20, 4); Эти значения можно устанавливать и после создания объекта времени, с по- мощью метода зеенмз (). Например: ОТйпе Ъйпе; Сйпе.зеЬНМЗ (20, 4, 23, 3); Для получения значений часов, минут, секунд и миллисекунд, установленных в объекте времени, в классе отмпе определены следующие методы: □ Ноиг () — значение часа в диапазоне от 0 до 23; □ пи-пиье () — минуты, в диапазоне от 0 до 59; □ зесопа () — секунды, в диапазоне от 0 до 59; □ шзес () — миллисекунды в диапазоне от 0 до 999. Класс отхте предоставляет метод ъозеггпд () для передачи данных объекта времени в виде строки. В этот метод, в качестве параметра, можно передать одно из значений, указанных в табл. 38.1, или задать свой собственный формат. Например: ОТйпе Сипе(20, 4, 23, 3); ОЗЬгхпд ЗИГ; зЪг = Ыте.ЬоЗСг1пд("Ы1:гшп:зз.222"); //зСг = "20:04:23.003" зЬг = Ь.йпе.Ио8Ъг1пд("1х:1п:8 ар"); //зЪг = “8:4:23 рт" При помощи статического метода Гготзиггпд () можно произвести преобра- зование из строкового типа в тип отйпе. Для этого первым параметром мето- да передается строка, представляющая время. Вторым параметром можно передать одно из значений форматов, приведенных в табл. 38.1, или строку с ожидаемым форматом. Изменить объект времени можно, добавив или отняв значения секунд (или миллисекунд) от существующего объекта. Эти значения передаются в мето- ды ааазесз () и ааамзесз (). Для получения текущего времени в классе отгте содержится статический метод сиггепстйпе (). При помощи метода зъагь () можно начать отсчет времени, а для того чтобы узнать, сколько времени прошло с момента начала отсчета, следует вызвать метод е1арзеа (). Например, на базе этих методов можно сделать небольшой профайлер, позволяющий оценить эффективность кода. Следующий пример вычисляет время работы функции сезг (): ОТипе Ыте; Ъхше.зЪагп();
СезЪ(); дреЬидО « "Время работы функции Реас() равно * « ъйпе.еХарвесЦ) « • миллисекунд" « еп<11; Недостаток класса отйпе состоит в ограничении 24-часовым интервалом, по истечении которого отсчет будет производиться с нуля. Для решения этой проблемы можно воспользоваться классом овасетйпе. Класс даты и времени Ойа1еТ1те Объекты класса опдъет1ше содержат в себе дату и время. Вызовом метода васе () можно получить объект даты (ооаее), а вызов с1ше () возвращает объ- ект времени (от!те). Этот класс также содержит методы еозы1пд() для представления данных в виде строки. Для этого можно воспользоваться одним из форматов, указанных в табл. 38.1, или собственной строкой с форматом. Таймер В программах часто возникает потребность в периодическом повторении оп- ределенных действий через заданные промежутки времени. Конечно, в неко- торых случаях для задания промежутка времени вызова функции можно вос- пользоваться и объектом класса <2Т1ше и сделать примерно следующее: ОТхше Ыше; Ъ1ше. зЪагЪ () ; Гог(;ЪХте.еХарзед(} < 1000;) { } ГипсЫоп (); Но такой подход обладает огромным недостатком. Исполнение цикла на се- кунду приостанавливает выполнение всей программы. Из-за этого события интерфейса пользователя не будут обрабатываться и, скажем, если одно из окон перекроет окно приложения, то оно все это время перерисовываться не будет, то есть приложение на это время "замрет". В подобных ситуациях можно, вызывая метод ргосеаздуепез () класса при- ложения одррИсаСхоп, приостанавливать исполнение цикла, для того что- бы программа получала возможность обработки поступивших событий. Например: ОТипе Гзше; Ыте.зГагГО ; Гог (; Ыше. еХарзей {) < 1000;) {
дАрр->ргосеззЕ^лепЬз () ; } Но и данный подход обладает недостатком: он не асинхронен. То есть наша программа будет обрабатывать поступающие события, но не сможет испол- няться дальше, пока цикл не завершится до конца. Таймер представляет собой решение этой проблемы. События таймера про- исходят асинхронно и не прерывают обработку других событий, выполняе- мых в том же потоке. Таймер — это гарант, обеспечивающий передачу управления программе. Однако длительная обработка событий влечет за со- бой задержки выполнения события таймера, то есть таймер ждет своего вре- мени, как и остальные события. Период между событиями таймера носит на- звание интервал запуска (Г1пп§ пМегуа!). Таймер переходит в сигнальное состояние по истечении интервала запуска, который указывается в миллисе- кундах. Точность интервала запуска ограничивается, к сожалению, точно- стью системных часов, а это значит, что на таймер нельзя полагаться как на секундомер. ОС 5Ушс1о>У8 98 отличается самой плохой точностью — она со- ставляет 55 миллисекунд, а во всех остальных ОС, поддерживаемых 01, точ- ность лежит в пределах одной миллисекунды. Следовательно, при написании программы имитации часов будет нелишним, после каждого сообщения тай- мера, проверять текущее время (см. листинг 38.4). Так как временной интер- вал, задаваемый в таймере, представляет собой целое число, то самый боль- шой интервал запуска, который можно установить — 24 дня. Эту проблему можно решить введением дополнительного счетчика для таймера. Существует много областей для применения таймера. Например, в текстовом редакторе его используют для автоматического сохранения файлов или в ка- честве альтернативы многопоточности (см. главу 39), разбив программу на части, каждая из которых будет выполняться при наступлении события тай- мера. Также таймер используется для отображения информации о состоянии данных, изменяющихся с течением времени. Таймер незаменим для устране- ния различий, связанных с мощностью и возможностями разных компьюте- ров, то есть для исполнения программ в режиме реального времени. События таймера можно использовать и в мультипоточном программирова- нии (см. главу 39) для каждого потока, имеющего цикл сообщений (еуеп! 1оор). Для запуска цикла сообщений в потоке нужно вызвать метод ОТИгеаЗ::ехес(). Событие таймера Каждый класс, унаследованный от ооьдесъ, содержит свои собственные встроенные таймеры. Вызов метода ооьэесь: :аьагътхшег() запускает тай- мер. В качестве параметра ему передается интервал запуска в миллисекун-
дах. Метод зеагьт1тег() возвращает идентификатор, необходимый для рас- познавания таймеров, используемых в объекте. По истечении установленного интервала запуска генерируется событие оПтегЕуепс, которое передается в метод ь^Iпе^ЕVепь (). Вызвав метод оИтегЕуепъ:: с1тег1<1() объекта собы- тия отхтегЕуепь, можно узнать идентификатор таймера, инициировавшего это событие. Идентификатор можно использовать для уничтожения таймера, передав его в метод ооьзесь: :кх11т1шег(). В программе, показанной на рис. 38.1, отображается надпись, которая появляется и исчезает через задан- ные промежутки времени. 1_ ВПпИаЬе! Рис. 38.1. Мигающая надпись 1ПЪ шахп (1пЪ агдс# сЪаг** а^дV) { ОАррЫсаЪхоп арр (агдс, а^дV) ; ВИпкЪаЬе! 1Ы("<Р0ОТ СОЬОК = КЕО><СЕОТЕВ>В11пк</СЕИТЕК></РОЫТ>и) ; 1Ы. зкою () ; ге!:ит арр. ехес (); В функции тахпо создается виджет класса вИпкьаЬе!, в конструктор кото- рого первым параметром передается отображаемый текст в формате НТМЬ (листинг 38.1). с1азе ВИпкЪаЬе! : риЬНс 0ЬаЪе1 { рг^аЪе: Ъоо1 пЦэВИпк; 05Ъгз.пд щ_зЬгТехЪ;
ргоСесЁед: V^^Xиа1 тохд Ъхтег1^епЪ(0ТхтегЕуепС*) { 1Л_ЬВИпк = !пСЬВИпк; зеСТехЪ(ш_ЬВ11пк ? щ_8СгТехЬ : } риЫхс: В1хпкЬаЬе1(сопзЪ ОЗЁгхпдб зЪгТехЪ, хпЪ п1пПегуа1 = 200, ОЮЫдее* рмдП = 0 ). : рЬаЬе! (зЬгТехЪ, раддЁ) , т_ЬВ1хпк (Ъгие) , т_зЪгТехе (з^гТехЪ) { зЁагЬТхшег (п1п^егуа1); } }; Класс вИпкьаЬе1, приведенный в листинге 38.2, содержит атрибут булевого типа ш_ьвИпк, управляющий отображением надписи, и атрибут т_зегтехе, содержащий текст надписи. В конструктор класса вИпкьаЬе! передается ин- тервал мигания п!пьегуа1. По умолчанию он равен 200 миллисекундам. Вы- зов метода зьагьтхтег () запускает таймер со значением переданного интер- вала запуска. По истечении этого интервала создается событие отйпег^епь, которое передается в метод Ышегвуепс (), где значение атрибута т_ьвипк меняется на противоположное. В соответствии с установленным значением метод зеетехе () выполняет одно из действий: □ Еа1зе — вся область надписи очищается установкой пустой строки; □ сгие — текст надписи устанавливается заново. Класс СПнпег, Использование объекта класса от±тег гораздо проще, чем использование со- бытия таймера, определенного в классе ооЬзесь. К недостаткам работы с со- бытием таймера относится необходимость наследования одного из классов, наследующих ооъзесс. Затем в унаследованном классе нужно реализовать метод, принимающий объекты события таймера. А если в объекте создается
более одного таймера, то возникает необходимость различать таймеры, что- бы узнать, который из них явился инициатором события. Для ликвидации этих неудобств 01 предоставляет класс таймера отхтег, яв- ляющийся непосредственным наследником класса ООЬэесс. Чтобы запустить таймер, нужно создать объект класса отйпег, а затем вызвать метод зьагп (). В параметре метода передается значение интервала запуска в миллисекундах. Класс отйпег также содержит статический метод 81пд1езъоъ () для однора- зового режима отработки таймера (режима 81п§1е8ко1). С его помощью можно запустить одноразовый таймер без создания объекта класса с*т1тег. Первый параметр метода задает интервал запуска, а второй является указате- лем на объект, с которым должно осуществляться соединение. Слот для со- единения передается в третьем параметре. Этим можно воспользоваться, на- пример, для прекращения работы демо-версии программы через 5 минут после ее запуска (листинг 38.3). 1пЪ та1п(1пЁ агдс, сИаг** а^дV) { 0АррИсае1оп арр(агдс, агду') ; МуРгодгат туРгодгат; ОТипег: :81пд1еЗЪоЁ(5 * 60 * 1000, &арр, 5ЬОТ(ди1Ъ())) ; туРгодгат. ; геЪит арр. ехес (); По истечении интервала запуска таймера высылается сигнал е1шеоиь (), ко- торый нужно соединить со слотом, выполняющим нужные действия. При помощи метода зеыпеегуа! () можно изменить интервал запуска таймера. Если таймер был активен, он будет остановлен и запущен с новым интерва- лом, и ему будет присвоен новый идентификационный номер. При помощи метода 1зАссз^е() можно проверить, находится ли таймер в активном со- стоянии. Вызовом метода знор () можно остановить таймер. Программа, окно которой изображено на рис. 38.2, реализует часы, отобра- жающие дату и время. Отображаемая информация актуализируется в соот- ветствии с установленным полусекундным интервалом запуска таймера.
- С1оск 12/4/2006 2:42:33 РМ ’ Рис. 38.2. Электронные часы #1Ь13е€ _С1оск_К_ #с!еНпе _С1оск_К_ #1пс1ис1е <0«Зи1> // ============□:=======================================================:= с1азз С1оск : риЬНс 0ЬаЬе1 { 0.0В0ЕСТ риЬНс: С1оск(0М1адеЬ* рмдП = 0) : 0ЬаЬе1 (родЪ) { ОТХтег* рЫтег = пею ОТипег (СЫз) ; соппесЪ (рЫтег, ЗТСЯМАЬ (СхтеоиС ()), ЗЬОТ (зХоШрдаЬеОа^еТХте ())); рЁ1тег->зЪагЬ(500); з1оШрдаЪеОаСеТ±ше (); } риЫ1С 8101X8: тоЫ зХоЩрааПеОаЪеТипе () { ©ЗЪгхпд зСг = ОЬа^еТипе:: сиггепеОаЪеТзше (). ЪоЗЪг1пд(0Ъ: :Зу8ЬетЬосаХеВаЪе) ; зееТехИ(н<Н2><СЕОТЕК>” 4- зЪг + И</СЕЫТЕН></Н2>") ; } }; #епсН С / /_С1оск_Ьи В листинге 38.4, в конструкторе класса С1оск создается объект таймера (указатель рЫтег). Его сигнал Ытеоиео соединяется СО СЛОТОМ з1оГОр<1аГеПаееТ1те(),
ответственным за обновление отображаемой информации. Вызов метода зЕагсо запускает таймер. В него передается интервал запуска. Слот зХоЕОрйасеОаСеТхте () получает актуальную дату и время с помощью метода сиггепьоаЕеТхше <). Затем он, используя параметр локализации ОЕ:: ЗузЕетъоса1еПаЕе (см. табл. 38.1), преобразует дату и время к строково- му типу и передает в метод зеьтехъ () для отображения. Класс 0Ваз1сТ1тег В качестве еще одной альтернативы, в 01 предусмотрено минималистское решение для таймера — это класс овазхстхтег, который предоставляет толь- ко четыре метода: хзАсъгуе (), зеаге (), зеор () и еЬпегта (), которые по сво- ей функциональности аналогичны методам класса отйпег. Исключение со- ставляет только метод зсагсо. Помимо первого параметра, задающего интервал, в этот метод, вторым параметром, передается указатель на объект Ооь^есЕ, который будет получать события таймера. То есть вам нужно будет реализовать в классе, унаследованном от ооьэесе, метод обработки события Таймера ООЬэесС: : ЕхшегЕуепЕ (). Резюме Классы ораье, отйпе и <2ВаЕет1те предназначены для хранения дат и времени и проведения с ними различных операций. Чаще всего требуется получение текущей даты и времени. Эти классы предоставляют методы для преобразо- вания даты и времени в строку определенного формата. Также есть методы для проведения обратного преобразования — из строки. Таймер уведомляет приложение об истечении заданного промежутка време- ни. События таймера относятся к разряду внешних прерываний. Внешние прерывания — это прерывания, вызываемые асинхронными событиями, на- пример, устройствами ввода-вывода или самим устройством таймера. Интер- валы запуска таймера устанавливаются в миллисекундах. Недостаток состоит в том, что если программа занята интенсивными вычислениями, то’ события таймера могут быть обработаны по окончании процесса вычисления. При выходе из приложения таймеры автоматически уничтожаются.
Глава 39 Процессы и потоки "...каждый крупный результат является конечным продуктом длинной последо- вательности маленьких действий". Кристофер Александер В современных компьютерах одновременно выполняется множество про- грамм. Некоторые из них запускаются при старте системы, некоторые — пользователем, а некоторые — другими программами. Как это можно реали- зовать в 0<, будет рассказано в этой главе. Но и многие приложения сами по себе часто выполняют несколько действий одновременно. Например, текстовый редактор проверяет орфографию, со- храняет резервные копии, и все это не прекращая реагировать на действия пользователя. Для организации подобного поведения служат потоки. Процессы В том случае, когда пользователь или программа производят запуск другой программы, операционная система всегда создает новый процесс. Процесс — это экземпляр программы, загруженной в память компьютера для выпол-< нения. По своей сути, процессы — это независимые друг от друга программы, обла- дающие своими собственными данными. Коротко процесс можно охарак- теризовать как совокупность кода, данных и ресурсов, необходимых для его работы. Под ресурсами подразумеваются объекты, запрашиваемые и ис- пользуемые процессами в период их работы. Любая прикладная програм- ма, запущенная на вашем компьютере, представляет собой не что иное, как поонесс.
Создание процесса может оказаться полезным для использования функцио- нальных возможностей программ, не имеющих графического интерфейса и работающих с командной строкой. Особенно это полезно для запуска ко- манд или программ, время работы которых не продолжительно. Запускать другие программы из текущей 01-программы довольно просто. Процессы можно создавать с помощью класса <2Ргосезз, который определен в заголовочном файле ОРгосезз. Благодаря тому, что этот класс унаследован от класса юоеу1се (см. главу 37), объекты этого класса позволяют считывать информацию, выводимую запущенными процессами, и даже реагировать на их запросы ввода данных. Кроме того, класс ОРгосезз содержит методы для манипулирования системными переменными процесса. Работа с объектами этого класса производится в асинхронном режиме, что позволяет сохранять работоспособность графического интерфейса программы в моменты, когда запущенные процессы находятся в работе. При появлении данных или других событий, объекты класса ОРгосезз высылают сигналы. Например, при воз- никновении ошибок объект процесса вышлет сигнал еггог () с кодом этой ошибки. Для создания процесса его нужно запустить. Запуск процесса выполняется методом зеаге (), в который необходимо передать имя команды и список ее аргументов, либо все вместе — команду и аргументы одной строкой. Как только процесс будет запущен, высылается сигнал зъагсеао, а после завер- шения его работы высылается сигнал ИпхзИеао. Сигнал СхпхзИеао сооб- щает код и статус завершения работы процесса. Для получения статуса вы- хода можно вызвать метод ех1ьзьаьиз (), который возвращает только два значения: цоппа1Ех1с (нормальное завершение) и сгазЪЕхК: (аварийное за- вершение). Для чтения данных запущенного процесса класс ОРгосезз предоставляет два разделенных канала: канал стандартного вывода (зьаоие) и канал оши- бок (зсаегг). Эти каналы можно переключать с помощью метода зеънеааскаппе1 (). Бели процесс готов предоставить данные по установлен- ному текущему каналу, то высылается сигнал геайукеаа(). Также высылают- ся сигналы для каждого канала в отдельности: геааукеаагъапаагаоиърие () И геадуКеайЗ^апйагйЕггог() Считывать и записывать данные в процесс можно с помощью методов клас- са ОЮЦеухсе: шг1Ъе(), геад(), геадЫпеО И деЪСкаг(). Также ДЛЯ чтения можно воспользоваться методами, привязанными к конкретным каналам: геайА113<:апс1аг<аОиЬриЪ () И геадАИЗСапдагдЕггог (). Эти методы считыва- ют данные в объекты класса овуееАггау. Приложение, изображенное на рис. 39.1, иллюстрирует применение некото- рых методов класса ОРгосезз. В текстовом поле Соттапс! (Команда) может
быть введена любая команда, понимаемая в операционной системе. Если за- пущенная команда или программа осуществляют вывод на консоль, то ото- бражение будет производиться в виджете многострочного тестового поля. 1 Ссттапд бмВб-ЁмВ'З :: 101. 21-2227 С-Е 15 РМ X С1.-21/20С- 06-15-РМ 1С1.21/2К7 К.45РМ 7 |:-1.2Г22"7 СЕ-45 •; Е1.21.-23Г 5б<5 РМ • 101-22. 222^ 07.01 =М !01. 21/20-27 Й:4Е РМ 1С1.-22-Ж7 07:16 РМ ’ -2-1.21 2027 С6-45 РМ , <21-12 200? С»:С4АМ -21.21/2027 06.45 ₽М сО1Р.> 5.259 Макете 4917 МакеЫе.Оеоид Л С*М М>1г1«»Ыв Са.к|лед 80 Ргасезз.рго 2.565.252 ЗЬеН.еже 2.333 ЗШЛ да:- 7ЙеЖ’ 2,554132 ЬуШ •зеЬид 703 тат.срр ге!еазе Рис. 39Л. Простейшая командная оболочка -ШРЬ с1азз 5Ъе11 : риЬИс О^ЫдеЁ. { 0_ОВСГЕСТ рггтаЬе: ОРгосезз * пиргосез з ; ОЫпеЕсНС* пирЬхЪСсвптапй; ОТехЬЕсШ:* псрСхЫИзрХау; риЬИс: //------------------------------------------------------------------- 5Ие11 (ОМ1ддеГ* ридЪ = 0) : ОМхйдее(рмдС) { псргосезз = пею ОРгосезз (ЬЫз); т_рЪхЫЭ1зр1ау = пею ОТехЬЕсИЪ; 0ЬаЬе1* р1Ы = пем ОЬаЬеИ "ЬСспгаапа: ");
т^рЪхПСсхттапд = пею ОЫпеЕаХЬ ("ЗХг"); р1Ы->8еЫВидду (щ^р^жЪСоттапд); ОРизЪВиЬЁоп* рспк! = пею ОРизЪВиЪЪоп (’'&ЕпЪег"); соппесЬ (щ_ргосезз, ЗЮЫАЬ (геас1уКеад5Ъапдагс1ОиСри1: ()), ЗЬОТ(зХоЪОаЪаОпЗЬЗоиЬ()) ); соппесЪ (пирЪхЪСаттапсЬ ЗГСМАЬ (геЬитРгеззед()), ЗЬОТ (зХоеНеЪитРгеззес! ()) ); соппесЪ (рстс!, ЗХОМАЬ (сХХскеб()), ЗЬОТ (зХоЪКеЬигпРгеззеа ())) ; //ЬауоиП зеЪир ОНВохЬауоиЪ* рЪЪхЬауоиЬ = пеад фНВохЬауоиЬ; рЪЬхЬауоиЬ->ас1<Ж1с1дее (р1Ы); рЪЬхЬауоиС->а<3<ЗМ1бде1: (т^р^хЬСоттап^); рИЬхЬауоиЁ.->а<Хс3^1дде^ (рстс1) ; (ЭУВохЬауоиЬ* р^хЬауоиЬ = пем ОУВохЬауоиП; руЪхЬауоиЁ.->аа<ЗМхддеЁ. (пцрЪхЁСхзрХау); рVЬx^ауои^->аск^^ауоиЪ (рЪЪхЬауоиЪ); зе!:ЬауоиС (рл/ЪхЬауоиЬ); } риЬХхс зХоГз: //---------------------------------------------------------------------------- тохй зХоиЭаЬаОпЗЫоиС () ( т__рЬхЫ>18рХау->аррепд(пк_ргосез5->геадАХХ51:ап(Загаои1:ри11 ()) ; } //------------------------------------------------------------------------ ’уохс! зХоЪКеЬигпРгеззеа () ' { ОЗЁгХпд зЪгОоттапД = "" ; #1€ае€ о_мз_мхы
зЬгСсотпапд = "стой /С #еп<НЕ зЬгСсямтапд += т_рСхЬСогяпапд->ЬехЬ () ; щ_ргосезз->8С.агЬ(8СгСогатап<а) ; } }; В конструкторе класса 5Ие11 создается объект класса ОРгосезз (лис- тинг 39.1). Его сигнал геайукеайзсапйагдоисриь() соединяется со слотом зТоЬОаЬаОпЗЬдоиЬ (), В котором вызывается метод геадАИЗЬапдагйОиСриЪО для считывания всего содержимого стандартного потока. После считывания эти данные добавляются в виджет многострочного , текстового поля т_рЬхЪО1зр1ау ВЫЗОВОМ метода аррепй (). Слот з1оскееигпРгеззеа() соединен с сигналом кнопки сИскеЗО (указа- тель рста) и с сигналом однострочного текстового поля геъигпРгеззеао (указатель пиьхьсопппапа). Некоторые команды ОС \У1П(1о\у8, например, а1г, не являются отдельными программами, поэтому они должны быть исполне- ны посредством командного интерпретатора ста. Поэтому для ОС \У1пскж8 в командную строку сначала добавляется строка "спк! /С". Во всех остальных операционных системах введенная в однострочном текстовом поле строка передается как есть, без дополнений. Для запуска процесса вызывается метод зСагС(). Потоки Потоки становятся все более популярным средством программирования. Для их реализации ()1 предоставляет класс отъгеаа. Но давайте сначала разбе- ремся, что же они собой представляют. Поток — это независимая задача, которая выполняется внутри процесса и разделяет с ним общее адресное пространство, код и глобальные данные. Процесс сам по себе не исполняется, поэтому для выполнения программного кода он должен иметь хотя бы один поток (далее — основной поток). Конеч- но, можно создавать и более одного потока. Вновь созданные потоки начи- нают выполняться сразу же, параллельно с главным потоком, при этом их количество может изменяться — одни создаются, другие завершаются. Завершение основного потока приводит к завершению процесса, независимо от того, существуют другие потоки или нет. Создание нескольких потоков в процессе получило название многопоточность.
Многопоточность требуется для выполнения действий в фоновом режиме, параллельно с действиями основной программы, и позволяет разбить выпол- нение задач на параллельные потоки, которые могут быть абсолютно незави- симы друг от друга. А если приложение выполняется на компьютере с не- сколькими процессорами, то разделение на потоки может значительно ускорить работу всей программы, так как каждый из процессоров получит отдельный поток для выполнения. В последнее время используется все больше компьютеров, оснащенных двухъядерными процессорами (<1иа1 соге), что делает многопоточное программирование еще более популярным. Приложения, имеющие один поток, могут выполнять только одну опреде- ленную операцию в каждый момент времени, а все остальные операции ждут ее окончания. Например, такие операции, как вывод на печать, считывание большого файла, ожидание ответа на посланный запрос или выполнение сложных математических вычислений могут привести к блокировке или за- висанию всей программы. При помощи многопоточности можно решить эту проблему, запустив подобные операции в отдельно созданных потоках. Тем самым, при зависании одного из потоков, функционирование основной про- граммы не будет нарушено. Среди разработчиков встречается разное отношение к многопоточному про- граммированию. Некоторые стараются сделать все свои программы многопо- точными, а других многопоточность пугает. Важно учитывать и то обстоя- тельство, что использование потоков существенно усложняет процесс отладки, а также написание самого приложения. Но, при правильном и обос- нованном применении многопоточности, можно существенно увеличить ско- рость приложения. Неправильное же ее применение, наоборот, может при- вести даже к снижению скорости. Поэтому использование потоков должно быть обоснованным. А это значит, что если вы не можете сформулировать причину, по которой следует сделать приложение многопоточным, то лучше отказаться от этой идеи. Если вы сомневаетесь, то постарайтесь, на началь- ных стадиях, создавать два прототипа для тестирования — один многопоточ- ный, а другой нет. Тем самым, прежде чем тратить время на разработку про- граммы, можно определить, является ли многопоточность решением поставленной задачи или нет. В том случае, если достижения того же резуль- тата можно добиться без использования многопоточности, то стоит отдать предпочтение этому варианту. Но перед тем как начать написание програм- мы с использованием многопоточности, очень важно разобраться и понять фундаментальные принципы и подходы программирования потоков, приме- няемые в 01. Именно этому и посвящена данная глава. Так с чего же все-таки начинается многопоточное программирование? Оно начинается с наследования класса отьгеаа и перезаписи в нем чисто вирту-
ального метода гип (), в котором должен быть реализован код, который будет исполняться в потоке. Например: с1азз МуТИгеасЗ. : риЬИс ОТИгеад { риЬИс: уо1с1 гип() { //Код, исполняемый в потоке } } Второй шаг заключается в создании объекта класса потока и вызове его ме- тода зьаге (), который запустит, в свою очередь, реализованный нами метод гип (). Например: МуТЬгеад ЪЪгеаЦ; Ыггеаа.з^агЪ(); Приоритеты У каждого потока есть приоритет, указывающий процессору, как должно протекать выполнение потока по отношению к другим. Приоритеты разде- ляются по группам: □ в первую входят четыре наиболее часто применяемых приоритета. Их зна- чимость распределяется по возрастанию — татеРгхогхпу, ъоиевсРгхогхЪу, ьошРгхогхьу, ыогтаХРгхогхсу. Они подходят для решения задач, которым процессор требуется только время от времени, например, для фоновой пе- чати или для каких-нибудь несрочных действий; □ во вторую группу входят два приоритета — нхдЪРгхогхеу, н1дИе8СРгхог1ьу. Пользуйтесь такими приоритетами с большой осторож- ностью. Обычно такие потоки большую часть времени ожидают какие- либо события; □ в третью входит лишь один приоритет — ТхшеСгхСхса1РгхогхСу. Потоки с этим приоритетом нужно создавать только в случаях крайней необходи- мости. Эти приоритеты нужны для программ, напрямую общающихся с аппаратурой или выполняющих операции, которые ни в коем случае не должны прерваться. Для того, чтобы запустить поток с нужным приоритетом, необходимо пере- дать одно из приведенных выше значений в метод зьаге (}. Например: мутЬгеаЦ ЬЪгеай; ЬЪгеас1.81:агс(0ТЬгеа<1:: ТсПеРгхогхсу);
А для того чтобы узнать, с каким приоритетом был запущен поток, нужно вызвать метод рг1ог1еу (). Приоритет можно предварительно установить при помощи метода зеСРгхогхьу (). Осторожно В Ыпих ядро системы запустит поток даже в том случае, если в нем уже запу- щены более 100 потоков с максимальным приоритетом. Ядро системы не поте- ряет работоспособность, а вы сможете запускать и другие потоки. Но в \ЛЛп- дочув все обстоит иначе: если вы запустите поток с самым высоким приоритетом, то поток с самым низким приоритетом, возможно, будет просто проигнорирован, и это может быть плачевно для тех, кто использует потоки с самым низким приоритетом для проведения каких-либо вспомогательных операций, например, загрузки данных с диска. Обмен сообщениями Один из важнейших вопросов при многопоточном программировании — это обмен сообщениями. Действительно, если вы, например, в одном потоке соз- даете растровое изображение и хотите переслать его объекту другого потока, то каким образом вы можете это сделать? каждый поток может иметь свой собственный цикл событий (рис. 39.2). Бла- годаря этому можно осуществлять связь между объектами. Такая связь мо- жет производиться двумя способами: при помощи соединения сигналов и слотов или обмена событиями. Примечание Если сигнально-слотовое соединение осуществляется между объектами раз- ных потоков, то внутри оно преобразуется в событие. Рис. 39.2. Потоки с объектами и собственными циклами обоаботки событий
Использование в потоках собственных циклов обработки событий позволяет снять ограничение, связанное с применением классов, способных работать только в одном цикле событий, и параллельно использовать необходимое количество объектов этих классов. К таким классам относятся класс отйпег (см. главу 38) и сетевые классы (см. главу 40). Для того чтобы запустить собственный цикл обработки событий в потоке, нужно вызвать метод ехес (} в методе тип (}. Цикл обработки событий потока можно завершать посредством слота ди 1.е () или метода ех1с (). Это очень похоже на то, как мы обычно поступаем с объектом приложения в функции ша±п(). Класс ооьзесъ реализован так, что обладает близостью к потокам. Каждый объект, произведенный от унаследованного от ооьзесь класса, располагает ссылкой на поток, в котором он был создан. Элу ссылку можно получить вы- зовом метода ооьэесь:: ъъгеаас). Потоки осведомляют свои объекты. Благо- даря этому каждый объект знает, к какому потоку он принадлежит. Обработка событий производится из контекста принадлежности объекта к потоку, то есть обработка его событий будет производиться в том потоке, которому объект принадлежит. Объекты можно перемещать из одного пото- ка В другой С ПОМОЩЬЮ метода ООЬэесС: :июуеТоТИгеа<1 (). Сигнально-слотовые соединения Итак, мы можем взять сигнал объекта одного потока и соединить его со сло- том объекта другого потока. Мы уже знаем из главы 2, что соединение при помощи метода соппесе () предоставляет дополнительный параметр режима обработки. Этот параметр по умолчанию имеет значение ое: :Аиъосоппесе1оп, что соответствует автоматическому режиму. Как только происходит высылка сигнала, проверяет, происходит связь в одном и том же или в разных по- токах. Если это один и тот же поток, то высылка сигнала приведет к прямому вызову метода. В том случае, если это разные потоки, сигнал будет преобра- зован в событие и доставлен нужному объекту. Реализация сигналов и слотов в <31 содержит механизм, обеспечивающий надежность при работе в потоках, а это означает, что вы можете высылать сигналы и получать их, не заботясь о блокировке ресурсов. Вы можете перемещать объект, созданный в одном потоке, в другой. А если вдруг обнаружится, что высылающий объект нахо- дится в одном потоке с принимающим, то высылка сигнала будет сведена к прямой обработке соединения. Программа, показанная на рис. 39.3, демонстрирует использование сигналов и слотов в потоке. После ее запуска производится отсчет таймера от 10 к О, после чего программа завершает свое выполнение.
Рис. 39.3. Сигнально-слотовые соединения в потоках с1аз5 МуТЪгеад : риЫхс ОТИгеад { 0_ОВСГЕСТ ргд^аЪе: хпЪ щ_пУа1ие; риЫхс: МуТИгеасЦ) : т_пУа!ие(10) { } тохс1 гип () { ОТхтег 1ицпег; соппесС (&1:хтег, ЗТОЫАЬ (СхшеоШ: ()), ЗЬОТ (з1оЬЫех!:Уа1ие ())) ; Ъхтег.зЪагЪ(1000); ехес(); ) зхдпа1з: тохс1 ИпхзЪей ( ) ; лгохс! сиггепЪУа1ие(хпЪ) ; риЫхс з1оЪз: тох<1 з1оЬЫехЪУа1ие() { ели. сиггепЪУа1ие (—пспУа1ие) ;
епйе ЛпХекед () ; } } }; Класс мутИгеаб представляет собой класс для управления потоком. Он унас- ледован от класса отъгеаа, и в нем переопределен метод гип(). Обратите внимание, что в определении нашего класса потока указан макрос о_оваЕСт, что необходимо для использования сигналов и слотов. Конструктор класса пуст и служит только для инициализации атрибута т_пУа1ие (листинг 39.2). Одна из самых интересных частей нашего класса — это метод гип (), кото- рый должен быть, переопределен — в него помещается код, выполняющийся в потоке. В этом методе мы создаем объект таймера и соединяем его сигнал сйпеоис () со слотом з1осыехпУа1ие (), запускаем таймер с интервалом в од- ну секунду и приводим в действие цикл обработки событий вызовом метода ехес(). Вас не должно смущать, что объект таймера (объект Ытег) был создан ста- тически, а не динамически, при помощи оператора пей: так как метод гип () является методом для исполнения кода в потоке, разрушение объекта тайме- ра произойдет только при завершении работы потока. После вызова метода ехес () произойдет запуск цикла событий, который заблокировал бы испол- нение всех дальнейших команд метода гип (), если бы таковые имелись. Этот метод можно сравнить с функцией тахпо, ведь в ней мы поступаем ана- логичным образом, когда запускаем методом 0Арр1Хсае1оп:: ехес о цикл обработки событий основного потока приложения, без которого не была бы возможна работа ни одного (^-приложения с пользовательским интер- фейсом. Слот з1оьыехеуа1ие () уменьшает значение атрибута т_пУа1ие на единицу и высылает сигнал сиггепеуаХие () с его актуальным значением. Если значе- ние станет нулевым, то будет выслан сигнал ^ХпХзЪеао, информирующий о конце работы. Г™:-................................................................ [ЛиРт^нг.39.3.Файлгпа1п.срр.Функция1па1п() ; 'V''/-V У 1п1: та!п(1п1: агдс, скаг** а^дV) { ОАррИса^хоп арр(агдс, агду) ; ОЬСЛЫитЬег 1 сд; МуТЪгеад Ыггеад;
ООЬэесС: :соппесС (&Ыхгеас1, 31СЖАЬ(сиггепЬУа1ие(хпС)), &1сд, ЗЬ0Т(<ахзр1ау(хпС)) ); <2ОЬ]есЬ: :соппесЪ(&ЪЪгеас1, 31ШАЬ(С1пхзкед()), &арр, ЗЬОТ(дихИ()) ); 1сд.зе1:ЗедтепЪЗЁу1е(0ЬС№ип1Ьег: :ГИ1ед) ; 1сд.дхзр1ау(10); 1сд.гезхге(220, 90); 1сд.з11о»() ; Ъкгеад. зЪагС () ; геЬит арр. ехес () ; ) #хпс1ис1е "тахп.тос" В листинге 39.3 приводится основная программа, в которой мы создаем вид- жет электронного индикатора 1са и объект потока пьгеаа. Для отображения значений, высылаемых потоком, мы соединяем сигнал сиггепъуа1ие () со слотом виджета индикатора ахзр1ау(). Также мы соединяем сигнал €хп1зИеа() объекта потока со слотом приложения дихсо для того, чтобы приложение завершило свою работу. После некоторых настроек, влияющих на показ виджета электронного индикатора, мы запускаем поток методом зСагЕ(). Ввиду того, что весь исходный код у нас реализован в одном файле тат.срр, нам необходимо включить метаинформацию, которая будет сгенерирована МОС при помощи директивы хпс1иае, что делается в последней строке лис- тинга 39.3. Теперь давайте изменим наш пример таким образом, чтобы слот нашего по- тока был соединен с сигналом из другого созданного нами потока. Для этого просто уберите три строки, связанные с таймером, из метода гип о (лис- тинг 39.2), и перенесите их в реализацию основного потока (в функцию шахп()). Метод гип () должен выглядеть следующим образом: то:1с1 гип () { ехес () :
А в функцию тагп (), показанную в листинге 39.3, добавьте строки: ОТйпег Сйпег; ООЬдесЬ: хсоппесЬ (&Ытег, ЗКЗЛАЬ (ЬыпеоиЬ ()), &Ы1геад, ЗЬОТ (81оЬКехЬУа1ие ()) ); С1тег.аСагС(1000); Теперь наш поток соединен с объектом таймера, созданным в основном по- токе, и отсылка сигналов должна приводить к внутренней генерации собы- тий. Откомпилируйте и запустите пример. Вы убедитесь в том, что он рабо- тает так же, как и его предшественник. Если поток должен только высылать сигналы, то запуск цикла обработки событий не нужен. Теперь давайте создадим более простой пример потока, без цикла сообщений. В приложении, показанном на рис. 39.4, используется поток, высылающий сигналы со значениями для индикатора прогресса. "ТЬгеасЫдпЛ Рис. 39.4. Поток, высылающий сигналы с1азз МуТЬгеас! : риЬИс ОПтгеад { О-ОВСГЕСТ риЬИс: тоз.с1 гип () { Ьэг (1пЪ 1 = 0; 1 <= 100; ++1) { из1еер(100000); епйЬ ргодгезз (1) ; } } зхдпаХз: ргодгезз (хпе);
В основном методе нашего потока гип (), показанном в листинге 39.4, мы запускаем цикл от 0 до 100, в котором через каждые 10 миллисекунд высы- лается сигнал ргодгезз () с актуальным значением переменной цикла. Оста- новка выполнения цикла на десять миллисекунд производится при помощи метода отигеай: :иг1еер(), который принимает в качестве аргумента время задержки в микросекундах. Функция аа1п( ) Л-г\;-| ---- —I 1пЪ та1п(1пЬ агдс, сИаг** а^дV) { САррИсаЬхоп арр(агдс, агду); ОРгодгеззВаг ргЬ; МуТИгеаЗ Ёкгеад; ООЬз есЁ:: соппесЪ (йЬИгеад, ЗЮЫАЬ (ргодгезз (1пЪ)) , &ргЬ, ЗЬОТ(зеЬУаЬие(ЬпЬ)) ); ргЬ.зЬоюО; 1й1геас1. зЪагЪ () ; геЁигп арр.ехес(); } #1пс1ис1е "тахп.тос” В листинге 39.5 после создания виджета индикатора прогресса производится создание одного потока — Охгеаб, а его сигнал ргодгезз () соединяется, для отображения высылаемых им значений, со слотом зеЪУа1ие () виджета инди- катора прогресса. Запуск потока приводится в действие методом зьагс (). Высылка событий Высылка событий—это еще одна из возможностей для осуществления связи между объектами. Как мы знаем из главы 16, есть два метода для высылки со- бытий: 0СогеАрр11саС1оп: срозЬЕуепЬО И ОСогеАррИсаЁхоп: : зепйЕуепЬ (). Здесь есть небольшой нюанс, который нужно знать: высылка событий ме- тодом розе&уепьо обладает надежностью в потоках, а методом зепб^епъ () — нет. Поэтому при работе с разными потоками всегда исполь- зуйте метод розсЕуепсо. На рис. 39.5 показано, как с помощью механизма
обмена событиями разных потоков можно осуществлять коммуникацию ме- жду двумя потоками. Поток может высылать события другому потоку, кото- рый, в свою очередь, может ответить другим событием и т. д. Сами же собы- тия, обрабатываемые циклами событий потоков, будут принадлежать тем потокам, в которых они были созданы. Рис. 39.5. Обмен событиями Для того чтобы объект потока был в состоянии обрабатывать получаемые события, в классе потока нужно реализовать метод ооьэ есь:: еуепе (). Если поток предназначен исключительно для высылки событий, а не для их получения, то реализацию методов обработки событий и запуск цикла обра- ботки событий можно опустить. Для сравнения высылки событий с высыл- кой сигналов, давайте реализуем программу высылки событий из потока, аналогичную программе, показанной шгрис. 39.4. с1азз РгодгеззЕуепС : риЬИс О&гепС { ргхуаСе: , 1пС ш_пУа1ие; риЬИс: епиш {РгодгеззТуре = Пзег + 1}; Ргодгезз&лепЬ () : СЕуепМ(Туре)РгодгеззТуре) { } уо!<3 зеСУа1ие(1п11 п) { т_пУа!ие = п;
1пЬ Vа1ие() сопзС { геСигп щ_пУа!ие; } Первое, что нам нужно сделать, это создать класс для нашего события, кото- рое мы будем высылать из потока (листинг 39.6). Наш класс наследуется от класса ОЕуепь и определяет атрибут целого типа т_пУа1ие. Для получения и установки значений этого атрибута класс содержит метод уа1ие() и зесуа1ие(). В конструкторе мы задаем тип нашего события, передавая его целочисленный идентификатор в конструктор класса сдогепс (). с1азз МуТИгеад : риЫхс ОТИгеай { рг^аГе: риЫхс: МуТЪгеаб (ООЬэесЪ* рсЬ^Несегуег) : п^^юЪ^Кесехтег (роЬдКесе^ег) { } тоха гип () { Гог (хпП х = 0; х <= 100; ++х) { из1еер(100000); РгодгеззЕуепЪ* ре = пею РгодгеззБХгеп^; ре->зеЬУа1ие(х); ОАррИсаЫоп:: розГЕуепЬ (щ_роЬзНесе^ег , ре); В листинге 39.7 приведена реализация класса мутьгеаа нашего класса пото- ка. которая очень похожа на реализацию из листинга 39.4. Разница заключа-
ется в том, что, во-первых, мы определяем в классе атрибут, указывающий на объект-получатель нашего события, и, во-вторых, вместо высылки сигнала мы создаем объект нашего класса события и отправляем его объекту- получателю с помощью метода роз^^епъ (). Создание нашего события в ме- тоде гип () без его уничтожения очень похоже на утечку памяти (тешогу 1еак), но ею не является, так как после обработки все объекты событий уда- ляются. Примечание Очевидно, что если бы нам понадобилось выслать событие еще одному объек- ту, то нам пришлось бы создать второй объект класса и повторить действия, проделанные для первого события. Для третьего пришлось бы еще раз повто- рить код и т> А- При высылке сигналов нам этого делать не нужно, так как объект-получатель задается методом доЬдесЁ: :соппесп(). Поэтому реше- ние с использованием сигналов, в данном случае, смотрится более элегантно. Мы всего лишь один раз высылаем сигнал независимо от числа его получате- лей (листинг 39.4). сХазз МуРгодгеззВаг : риЬИс ОРгодгеззВаг { риЬИс: МуРгодгеззВаг (ОЮхбдеЁ* рмде: = 0) : ОРгодгеззВаг(рмдЪ) { } Vо^с^ сизЬстЕиепЪ (ОЕХгепЁ* ре) { И ((1пЁ)ре->Ъуре() == РгодгеззЕХгепе::: РгодгеззТуре) { зе^УаХие (((РгодгеззЕуепЬ*) (ре)) -^а1ие ()); ОЮХбдее:: :сизЁотЕ\леп1: (ре) ; } Для того чтобы виджет мог получать и правильно интерпретировать высы- лаемые потоком события, у нас есть две возможности. Первая заключается в реализации класса фильтра событий и установки его в виджете (см. главу 15). Вторая — в наследовании подходящего класса виджета и переопределении в нем метода сиаеотЕуепъ (). Это решение представлено в листинге 39.8. В методе сиаЪотЕ^епь () мы проверяем тип полученного события и, если он
соответствует типу нашего события Ргодгеззтуре, приводим его к классу РгодгеззЕуепь, чтобы вызвать метод уа1ие (). Возвращаемое этим методом значение передается методу ОРгодгеззВаг: :зеСУа1ие() для отображения виджетом хода процесса. .мм............мм.................................... 1пЬ та1п(1п1: агдс, сИаг** а^дV) { дАррИсаехоп арр(агдс, ; МуРгодгеззВаг ргЬ; МуТЪгеад еЪгеа<1(&ргЬ) ; ргЬ.зкоМ) ; ЪИгеасЬ з^агХ (); геЬигп арр.ехес(); } В основной программе, приведенной в листинге 39.9, мы создаем виджет ин- дикации прогресса и объект потока, после чего производим запуск потока вызовом метода зъаге (). Если сравнить реализации программы при помощи сигналов (листин- ги 39.4, 39.5) и при помощи событий (листинги 39.6—39.9), то заметно, что подход с использованием сигналов более компактный. Но, в целом, оба под- хода равнозначны и имеют право на существование. Какой из подходов ис- пользовать, зависит от вас. В конкретных ситуациях следует оценить, на- сколько удобным будет применение каждого из них. Синхронизация Основные сложности возникают тогда, когда потокам нужно совместно использовать одни и те же данные. С одной Стороны, это просто, так как несколько потоков могут одновременно обращаться и записывать данные в одну область. Но, с другой стороны, это может привести к нежелательным последствиям. Представьте себе такую ситуацию: один поток занимается вы- числениями, используя значения какой-нибудь глобальной переменной, а в это время другой поток вдруг изменяет значение этой переменной. Поток, занимающийся вычислениями, продолжает свою работу, ничего не подозре-
вая и используя уже измененное значение, и результат вычислений может оказаться совершенно бессмысленным. Для предотвращения подобных си- туаций требуется механизм, позволяющий блокировать данные, когда один из потоков намеревается их изменить. Этот механизм получил название син- хронизация. Синхронизация позволяет создавать мьютексы (тшех), которые позволяют в определенный момент времени предоставлять доступ к данным только од- ному из потоков. Это гарантирует, что данные ресурса, контролируемые мьютексом, будут невидимы для других потоков и не будут непредвиденно изменены. И только после того, как поток выполнит всю необходимую рабо- ту, он освобождает ресурс, после чего доступ к этому ресурсу может полу- чить любой другой поток. Например, если один поток записывает информа- цию в файл, то все другие не смогут использовать этот файл до тех пор, пока поток не освободит его. Мьютексы Мьютексы (щщех) обеспечивают взаимоисключающий доступ к ресурсам, гарантирующий, что критическая секция будет обрабатываться только одним потоком. Поток, владеющий мьютексом, обладает эксклюзивным правом на использование ресурса, защищенного мьютексом, и другой поток не может завладеть уже занятым мьютексом. Мютексы можно образно сравнить с дверью душевой кабинки, способной разместить только одного человека. Как только в нее заходит один человек, он закрывает дверь. И если после этого кто-то еще захочет воспользоваться этой душевой кабинкой, то он просто не сможет попасть в нее, так как дверь кабинки будет заперта. Когда кабинка освободится, ее дверь откроется, и в нее сможет войти следующий желающий. Войдя в нее, он закроет за со- бой дверь, сделав ее недоступной для других и т. д. Примечание Я не беру во внимание совместное принятие душа, ибо в реальном мире могут встретиться исключения из этого правила, которые на программирование не распространяются. По этому принципу работает и мьютекс, только вместо людей выступают по- токи, а вместо кабинок ресурсы. Этот механизм реализован классом омисех. Метод 1оск () класса омисех производит блокировку ресурса. Для обратной операции существует метод ипХоск (), который открывает закрытый ресурс для других потоков. Класс омисех также содержит метод сгуьоск(). Его можно использовать для того, чтобы проверить, заблокирован ресурс или нет. Этот метод не приоста-
навливает исполнение потока и возвращается немедленно, со значением ^аХзе, если ресурс уже захвачен другим потоком, не ожидая его освобожде- ния. В случае успешного захвата ресурса этот метод вернет сгие, и это зна- чит, что ресурс принадлежит потоку и он вправе распоряжаться им по своему усмотрению. Давайте воспользуемся мьютексом для того, чтобы реализовать класс с меха- низмом надежности использования в потоках (1Ьгеа<1 заГеГу). сХазз ТЪгеа<аза1е5^гхпд5еаск { ргхуаЪе: СМиЪех щ_тиЬех; 05Ъаск<05Ъг1пд> щ_зСаскЗЁгхпд; риЬХхс: \гохс1 ризй(сопзЬ 03^гхпд& з^г) { пцпиСех. 1оск (); т_зЁаскЗЁгхпд.ризЪ. (зЪг); щ_тиЬех,ипХоск(); } ОЗЪгхпд рор(сопзС 03Сгхпд& зСг) { *2МиЬехЬоскег Хоскег (&т_щцЪех); геЪит т_зЪаскЗЬгхпд.етрЪу() ? рЗЪгхпдО : щ_з ЪаскЗ Ьгхпд. рор (); В классе тъгеаазаГезсгхпдзсаск, приведенном в листинге 39.10, мы опреде- ляем два метода, один из которых, ризъ (), служит для помещения строки в стек, а другой, рор (), — для извлечения из стека. В секции рг злгасе опреде- лены два атрибута: атрибут мьютекса т_тиьех и атрибут стека строк. Не за- бывайте, что класс озьаск<т> не обладает механизмом надежности использо- вания в потоках. Единственная возможность синхронизировать доступ к данным объекта класса озсаск<т> — это блокировать их каждый раз, как только кто-то получит к ним доступ, и разблокировать после завершения
операции. В методе ризъо самой первой строкой вызывается метод 1оск() объекта мьютекса, который блокирует доступ к ресурсу, а после помещения строкового значения в стек вызов метода ип1оск() разблокирует его. Теперь этот метод могут вызвать несколько потоков одновременно, и это не приве- дет к порче данных. В методе рор () мы используем объект класса омиъехъоскег. Иногда очень удобно использовать именно этот класс. Для создания объекта этого класса в его конструктор необходимо передать указатель на объект мьютекса. В конструкторе этого класса ресурс сразу же блокируется, а деструкторе — разблокируется. Это означает, что не нужно явно вызывать метод для раз- блокирования ресурса, как мы это делали в методе ризИ (), потому что за- вершение метода приведет к разрушению этого объекта, и будет вызван его деструктор. Примечание Золотое правило: никогда не используйте мьютексы, если вы не уверены в том, что это действительно необходимо. Бытует мнение, что классы, не обладаю- щие надежностью для потока — это плохо. Проблема, связанная с реализаци- ей надежности использования в потоках, заключается в том, что ее невозможно осуществить без механизма блокировки. Однако применение блокировки ре- сурсов может снизить эффективность работы класса, а значит и приложения в целом. ,Представьте себе, что каждый класс вашего приложения должен бло- кировать и разблокировать все ресурсы: это бы резко снизило быстродействие вашей программы. Именно по этой причине не все классы 01 реализованы с механизмом надежности. Ожидание условий Библиотека 01 предоставляет класс оиаИхсопсНЫоп, обеспечивающий возмож- ность координации потоков. Если поток намеревается дождаться разблокировки ресурса, то он вызывает метод о»аИ:сопв1ц1оп: :ма1с() и, тем самым, входит в режим ожидания. Выводится он из этого режима в том случае, если поток, ко- торый заблокировал ресурс, вызовет метод 0Иа1есоп31ъ1оп::иакеопе() или ОИахъсопдхЫоп: :«акеА11(). Разница этих двух^методов в том, что первый выводит из состояния ожидания только один поток, а второй— все сразу. Также можно установить время, в течение которого поток будет ожидать разблокировки данных. Для этого нужно передать в метод «а±е () целочис- ленное значение, обозначающее временной интервал в миллисекундах. Возникновение тупиковых ситуаций Работая с многопоточностью, нужно помнить о возможном возникновении тупиковых ситуаций, когда потоки могут заблокировать друг друга. Пред- ставьте себе такую ситуацию, когда поток заблокировал ресурс А, а после 21 Зак. 3983
работы над ним собирается работать с ресурсом В. Другой же поток заблоки- ровал ресурс В и по окончании намеревается работать с ресурсом А. И вот один из потоков, закончив работу, обнаружил, что нужный ему ресурс забло- кирован другим потоком. Он переходит в режим ожидания, надеясь дождать- ся разблокировки ресурса, но то же самое делает и другой поток. В итоге — оба ждут друг друга. Если ни один из этих потоков не освободит занятый им ресурс, то оба "зависнут" и не смогут продолжать свою работу дальше. Рис. 39.6. Взаимная блокировка (Оеа<11оск) Это явление получило название взаимной блокировки (беасПоск) (рис. 39.6). Существует множество решений такой проблемы. Например, можно органи- зовать работу потока так, чтобы в случае, когда он не может получить доступ к необходимому ресурсу, он просто освободил бы все занятые им ресурсы, а позже повторил попытку их захвата. Семафоры Семафоры являются обобщением мьютексов. Как и мьютексы, они служат для защиты критических секций, чтобы доступ к ним одновременно могло иметь определенное число потоков. Все другие потоки обязаны ждать. Пред- положим, что программа поддерживает пять ресурсов одного и того же типа, одновременный доступ к которым может быть предоставлен только пяти по- токам. Как только все пять ресурсов будут заблокированы, следующий поток, запрашивающий ресурс данного типа, будет приостановлен до освобождения
одного из них. Принцип действия семафоров очень прост. Они начинают действовать с установленного значения счетчика. Каждый раз, когда поток получает право на владение ресурсом, значение этого счетчика уменьшается на единицу. И наоборот, когда поток уступает право владения этим ресурсом, счетчик увеличивается на единицу. При значении счетчика равном нулю се- мафор становится недоступным. Механизм семафоров реализует класс ОЗетарИоге. Счетчик устанавливается в конструкторе при создании объекта этого класса. Резюме Процессы представляют собой программы, независимые друг от друга и за- груженные для исполнения. Каждый процесс должен создавать хотя бы один поток, называемый основным. Основной поток процессд создается в момент запуска программы. Однако сам процесс может создавать несколько потоков одновременно. Многопоточность позволяет разделять задачи и независимо работать над ка- ждой из них для того, чтобы максимально эффективно задействовать процес- сор. Написание многопоточных приложений требует больше времени и ус- ложняет процесс отладки, поэтому многопоточность нужно применять тогда, когда это действительно необходимо. Многопоточность удобно использовать для того, чтобы блокировка или зависание одного из методов не стали при- чиной нарушения функционирования основной программы. Для реализации многопоточности' нужно унаследовать класс от отьгеад и переопределить метод гип (), который должен содержать код для исполне- ния в потоке. Чтобы запустить поток, нужно вызвать метод зеагь (). Каждый поток может иметь свой собственный цикл событий, который запускается вызовом метода ехес () в коде метода гип (). Связь между объектами из разных потоков можно осуществлять при помощи сигналов и слотов или посредством обмена объектами событий. При работе с потоками нередко требуется синхронизировать функциониро- вание потоков. Причиной синхронизации является необходимость обеспече- ния доступа нескольких потоков к одним и тем же данным. Для этого биб- лиотека предоставляет классы ОМиСех, ОИахЬСопЫоп И ОЗетарИоге. В целях эффективности не все классы (}1 обладают механизмом надежности использования в потоках.
Глава 40 Программирование поддержки сети "Информация есть информация, а не энергия и не материя". И. Винер. "Кибернетика" Сокетное соединение Сбкет (от англ. зоске1 — гнездо, разъем) — это устройство пересылки данных с одного конца линии связи на другой. Другой конец может принадлежать про- цессу, работающему на локальном компьютере, а может располагаться и на удаленном компьютере, подключенном к Интернету и расположенном в дру- гом полушарии Земли. Сокетное соединение — это соединение типа точка- точка (рогпс (о ропл), которое производится между двумя процессами. Сокеты разделяют на дейтаграммные (даОДгаш) и поточные. Дейтаграмм- ные сокеты осуществляют обмен пакетами данных. Поточные сокеты уста- навливают связь и производят потоковый обмен данными через установлен- ную ими линию связи. На практике, поточные сокеты используются гораздо чаще, чем дейтаграммные, из-за того что они предоставляют дополнительные механизмы, направленные против искажения и потери данных. Поточные сокеты работают в обоих направлениях, другими словами, то, что один из процессов записывает в поток, может быть считано процессом на другом конце связи и наоборот. Для дейтаграмных сокетов (^1 предоставляет класс оиарЗоскее, а для поточ- ных — класс отсрзоскеъ. В этой главе мы ограничимся рассмотрением толь- ко поточных сокетов. Класс отсрзоскее содержит набор методов для работы с ТСР (Тгапзтйюп Соп1го1 Рго1осо1, протокол управления передачей данных) — сетевым прото- колом низкого уровня, который является одним из основных протоколов в Интернете. С его помощью можно реализовать поддержку для стандартных
сетевых протоколов, таких как НТТР, РТР, РОРЗ, 8МТР, и даже для своих собственных протоколов. Этот класс унаследован от класса ОАЪзыасьзоскее, который, в свою очередь, наследует класс ^Iо^еV^се. А это значит, что для доступа (чтения и записи) к его объектам можно применять все методы клас- са 01ООеу1се И использовать классы потоков ООаЬаЗЫеат ИЛИ ОТехГЗСгеат (см. главу 37). Работа класса стсрЗоскее асинхронна, что дает возможность избежать бло- кировки приложения в процессе его работы. Но если вам это не нужно, то можете воспользоваться серией методов, начинающихся со слова шахегог. Вызов этих методов приведет к ожиданию выполнения операции и заблоки- рует, на определенное время, исполнение вашей программы. Не рекоменду- ется вызывать эти методы в потоке графического интерфейса. Модель клиент-сервер Сценарий модели клиент-сервер выглядит очень просто: сервер предлагает услуги, а клиент ими пользуется. Программа, использующая сокеты, может выполнять либо роль сервера, либо роль клиента. Для того чтобы клиент мог взаимодействовать с сервером, ему нужно знать 1Р-адрес сервера и номер порта, через который клиент должен сообщить о себе. Когда клиент начинает соединение с сервером, его система назначает данному соединению отдель- ный сокет, а когда сервер принимает соединение, сокет назначается со сто- роны сервера. После этого устанавливается связь между двумя этими* сокета- ми, по которой высылаются данные запроса к серверу. А сервер высылает клиенту, по тому же соединению, готовые результаты согласно его запросу. Сервер не ограничен связью только с одним клиентом, на самом деле он мо- жет обслуживать многих клиентов. V КГМЛЕНТ СЕРВЕР^ Рис. 40.1. Модель клиент-сервер
Каждому клиентскому сокету соответствует уникальный номер порта. Неко- торые номера зарезервированы для так называемых стандартных служб (табл. 40.1). Таблица 40.1. Некоторые зарезервированные номера портов Порт Сервис Описание 11 зузТа! Показ зарегистрированных в системе пользователей 21 РТР Доступ к файлам по сети 22 88Н Зашифрованное соединение к удаленному компьютеру 23 Те1пе1 Удаленное соединение с компьютером 25 8МТР . Отсылка электронной почты 80 НТТР Веб-сервер (иногда применяются порты 8080 или 8000) 110 РОРЗ Получение электронной почты 139 №(Ыов-88Ы Разделение сетевых ресурсов 143 1МАР Пересылка электронной почты 194 1НС 1п1ете1 Не1ау СЬа1 442 НТТР8 Зашифрованный НТТР Реализация сервера Для реализации сервера СИ предоставляет удобный класс отсрзегуег, кото- рый предназначен для управления входящими ТСР-соединениями. Програм- ма, показанная на рис. 40.2, является реализацией простого сервера, который принимает и подтверждает получение запросов клиентов.
#1пс1ис1е <0еСи1> #1пс1ис1е "МуЗегуег.к" хпё та1п(1п1: агдс, скат** агду) { ОАррИсаЫоп арр(агдс, агду) ; МуЗегуег зегуег(2323); зегуег.зкошО; ге^игп арр.ехес(); В листинге 40.1 создается объект сервера. Чтобы запустить сервер, мы созда- ем объект определенного нами в листингах 40.2—40.6 класса МуЗегуег, пе- редав в конструктор номер порта, по которому должен осуществляться нуж- ный сервис. В нашем случае передается номер порта, равный 2323. #1Ь16еГ _Му5егуег_Ъ Ме^тпе _МуЗегуеГ—И #1пс1иде <да1<ЗдеГ> с1азз ОТсрЗегуег; с1азз ОТехЁЕсИЪ; с!азз ОТсрЗоскеС; / / =======================================: с!азз МуЗегуег : риЬИс ОЮхйдеЬ { 0-СШЕСТ ргхуаЪе: ОТсрЗегуег* пирЪсрЗегуег; ОТехСЕсНЬ* щ_рЪх1:; дихпЫ 6 щ_пЫехЬВ1оск31 хе ; ргхуаЪе: уохсЗ. зепдТоСИепЪ (ОТсрЗоскеЪ* рЗоскеС, сопзЪ ОЗЪгхпдй зЬг);
риЬНс: МуЗегуег(1пС пРогС, ОМхддеС* раддЪ = 0); риЬНс зХоСз: V^^Сиа1 тохсй з1оСЫеадСоппесС1оп () ; лло1с1 з1оСКеадС11епС () ; }; #епд1Г / /_Му5е1Гуег„11„ В классе музегуег, определяемом в листинге 40.2, мы объявляем атрибут т_рссрЗе^ег, который и является основой управления нашим сервером. Ат- рибут т_пыехпв1оскз12е служит для хранения длины следующего получен- ного от сокета блока.-Многострочное текстовое поле т__рсхс предназначено для информирования о происходящих соединениях. МуЗегуег: хМуЗехпгег (1пС пРогС, 0М1йдеС* рюдЪ /*=0*/) : ОйЦсЗдеС (ридС) , т_пЫехЪВ1оск312е(0) { псрЪсрЗегуег = пем ОТсрЗегуег (Л1з) ; (’т_рСсрЗег^ег->11зСеп(0НозСА!Йс1ге5з: :Апу, пРогС)) { ОМеззадеВох::сг1Ь1са!(0, "Зегуег Еггог”, "ПпаЫе Со зСагС Ске зегуег:" + т_рСср5егллег->еггогЗСг1пд () ); т_рСсрЗе]плег->с1озе () ; геСит; } соппесС (щ_рСср5егуег, ЗТСЫАЪ(пеюСоппесСхоп()), Ы113, ЗЬОТ (з 1оШе«СоппесС1оп ()) ); пцрЪхС = пезд ОТехСЕсИС; т__рСхС->зеСКеад0п1у (Сгие); //ЬауоиС зеСир ОТВохЬауоиС* р^ЬхЬауоиС = пею ОУВохЬауоиС;
руЬхЬауои€->а<ИЫ1<1де1: (пе« 0ЬаЬе1("<Н1>Зегиег</Н1>")) ; руЪхЬауоие->аааи1адеЕ(щ_р1эЛ) ; зеСЬауоиМрл/ЪхЬауоиЪ) ; } Для запуска сервера нам необходимо вызвать в конструкторе метод Пзсеп () (листинг 40.3). В этот метод необходимо передать номер порта, который мы получили в конструкторе. При возникновении ошибочных ситуаций, напри- мер, невозможности захвата порта, этот метод возвратит значение €а1зе, на которое мы отреагируем показом окна сообщения об ошибке. Если ошибки не произошло, мы соединяем определенный нами (листинг 40.4) слот 81оьне«соппесЫоп () с сигналом пеюСоппесЫоп (), который высылается при каждом присоединении нового клиента. Для отображения информации мы создаем виджет многострочного текстово- го поля (указатель ш-рехе) и устанавливаем в нем режим, в котором возмо- жен только просмотр информации, вызовом метода зескеааоп1у (). / ^1г±иа1 ♦ / то1с1 МуЗеигу'ег:: з1оШемСоппесС 1оп () { ОТсрЗоскеС* рСИепЪЗоске!: = пи^срЗе^ег->пехЪРепд1пдСоппес1:1оп () ; соппесЬ (рСЫепЪЗоскеЪ, ЗКЗЫАЬ (сНзсоппес^ед ()) , рСХхепИЗоскеИ, ЗЬОТ (ЗеХеИеЬаПег ()) соппесI: (рС1 ХепЪЗоскеЪ, ЗКЗЫАЬ (геайуКеасЗ. ()), Ыпз, ЗЬОТ (81оЪНеа<асИеп<: ()) ); зепсТГоСИепЪ (рСИепЪЗоскеЪ, • Зегуег Кезропзе: СоппесНей! "); } Метод з1оъые«соппесь1оп(), показанный в листинге 40.4, вызывается каж- дый раз при соединении с новым клиентом. Для подтверждения соединения с клиентом необходимо вызвать метод пехсрепа1пдсоппесс1оп (), который возвратит сокет, посредством которого можно осуществлять дальнейшую связь с клиентом. Дальнейшая работа сокета обеспечивается сигнально- слотовыми связями. Мы соединяем сигнал а1зсогтесъеа (), высылаемый соке- том при отсоединении клиента, со стандартным слотом ооьэ есе:: ае1есеьасег (), предназначенным для его последующего уничтожения. При поступлении
запросов от клиентов высылается сигнал геааутокеаао, который мы соеди- няем СО СЛОТОМ З1оспеаас11епе <). тохд МуЗегуег:: з1оСКеа<ЗСИепС () { ОТсрБоскеС* рСИегПхЗоске!: = (ОТсрЗоскеЪ*) еепбег () ; ОПаСаЗЪгеат 1п(рС11епПЗоскеГ) ; 1П. зе^Уегзтоп (ОПаЛаЗСгеат:: 011_4__5) ; еог (;;) { 1Г (!пцзМехЪВ1оск31те) { 1Г (рС11епЪЗоскеЪ->Ьу1:езАуа11аЬ1е () < зтгеоГ(ди!пе16)) { Ьгеак; } хп » 1ц_пМех^В1оск31ге; } (рСИепСЗоскеС->ЬуЪезАуа11аЫе () < пслЫехЪВТоскЗхзе) { Ьгеак; } ОТхте Ыте; ОЗЪгхпд зЪг; 1п » <:1те » зЪг; ОЗСгхпд зЁгМеззаде = е1те.ЪоЗ€х1пд() + " " + "СИепЬ Ъаз зепЁ - " + зСг; т_рСхЪ“>аррепа(зГгМеззаде) ; пспЫехПВ1оск51ге = 0; зепдТоСХхепъ (рСИепСЗоскеЪ, "Зегуег Кезропзе: КесегуесЗ. V" + зЪг + }; } В листинге 40.5 сначала производится преобразование указателя, возвращае- мого методом зепйег (), к типу ртсрзоскеъ. Цикл Гог нам нужен потому, что
не все высланные клиентом данные могут прийти одновременно. Поэтому сервер должен "уметь" получать как весь блок целиком, так и только часть блока, а также и все блоки сразу. Каждый переданный сокетом блок начина- ется полем размера блока. Размер блока считывается при условии, что размер полученных данных не меньше двух байт и атрибут т_пнехсв1оскз1ге равен нулю (то есть размер блока неизвестен). Если размер доступных для чтения данных больше или равен размеру блока, тогда данные считываются из пото- ка в переменные Ыше и вы. Затем значение переменной Ыте преобразуется вызовом метода созсгхпдО в строку и вместе со строкой зсг записывается в строку сообщения зсгмеззаде, которая добавляется в виджет текстового поля вызовом метода аррепб (). Анализ блока данных завершается присваи- ванием атрибуту щ_п_нехьв1оскз1ге значения 0, которое говорит о том, что размер очередного блока данных неизвестен. Вызовом метода зепатосИепс () мы сообщаем клиенту о том, что нам успешно удалось прочитать высланные им данные. МуЗе1плег:: зепатоСИепС (ОТсрЗоскеС* рЗоскеС, сопзС 05Сг1пд& зег) { ОВуЕеАхгау аггВ1оск; ООаСаЗСгеапг оиС(&агхВ1оск, 010Пеу1се::ИгдХеОп1у); оиС.зеСУегз1оп(0ПаеаЗСгеат::0С_4_5); оиС « ди1пС16(0) « ОТыпе::сиггепСТйпе() « зСг; оиС.Зеу1се()->зеек(0) ; оиС « ди1пС1б(аггВ1оск.з1ге() - 81гео€ (дихпЫб)); рЗоскеЬ->иг1Ье(аггВ1оск); } В методе зепатоС11епЬ() мы формируем данные, которые будут отосланы клиенту (листинг 40.6). Есть небольшая деталь, которая заключается в том, что нам заранее не известен размер блока, а следовательно, мы не можем за- писывать данные сразу в сокет, так как размер блока должен быть выслан в первую очередь. Поэтому мы прибегаем к следующему трюку. Сначала мы создаем объект аггвхоск класса овуееАггау. На его основе мы создаем объ- ект класса ОРасазсгеаго, в который записываем все данные блока, причем вместо реального размера записываем 0. После этого мы перемещаем указа- тель на начало блока вызовом метода зеек(), вычисляем размер блока как
размер аггвхоск, уменьшенный на з12еоПои1п1:1б), и записываем его впо- ток (оиъ) с текущей позиции, которая уже перемещена в начало блока. После этого созданный блок записывается в сокет вызовом метода юг1ее (). Примечание Для пересылки обычных строк мы могли бы использовать класс потока ввода ртехъзсгеат. Класс опапаз^геат используется, поскольку пересылка бинар- ных данных представляет собой общий случай. Нам бинарные данные также необходимы, чтобы переслать объект класса <2т1те. Используя ОБаЪазьгеаш, вы можете отправлять не только строки, но и растровые изображения, объекты палитры и т. д. Реализация клиента Для реализации клиента нужно создать объект класса отсрзоскеь, а затем вызвать метод соппесътонозп(), передав в него, первым параметром, имя компьютера (или его 1Р-адрес), а вторым — номер порта сервера. Объект класса отсрзоскее сам попытается установить связь с сервером и, в случае успеха, вышлет сигнал соппессёа (). В противном случае будет выслан сигнал еггог(1пь) с кодом ошибки, определенным в перечислении ОАЬзЬгас^зоскес:: зоскеСЕггог. Это может произойти, например, в том слу- чае, если на указанном компьютере не запущен сервер или не соответствует номер порта. После установления соединения, объект класса отсрзоскеь мо- жет высылать или считывать данные сервера. Рисунок 40.3 иллюстрирует взаимодействие клиента с сервером. Высылка на сервер информации, введенной в однострочном текстовом поле окна клиента, производится после нажатия кнопки 8еп<1 (Выслать). .... МуСЬепт СКеп1 кесе|уеа (Не соппесЫ() 51дпа1 18:20:02 5ег*ег ке$рап5е: Соппес^ед! 18:20:57 Зегмег йезройее: Йесв1*ей "Те*ЛГ’ : 18:21:06 Зегмег Йе$роп5е: йесекей а1е512*' ' 18:21:16 Зегмег йе$роп$е: Яесегмес! "ТеЯЗ" I Г М:.5ег,гг Зегмег 18:20:57 Оепе М5 5ег^-Те5и 18:21:06 ОегЛ Ьж 5егЛ -ТезК 18:21:16 Пеп! Ьа5 - Те«ЛЭ 1 Рис. 40.3. Взаимодействие клиента с сеовеоом
#хпс1иде <0Арр1хсаЪхоп> #хпс1и<1е ”МуС1хепЪ.1Г' хпе тахп(хпЪ агдс, сЪаг** агду) 2Арр1хсаЬхоп арр(агдс, агду); МуСИепЬ с1^еп^;("1оса1]1оз^:’,, 2323); сИепЬ,8110717() ; гееигп арр.ехес ()•; } В функции шахп (), приведенной в листинге 40.7, мы создаем объект клиента (см. листинги 40.8—40.13). Если сервер и клиент запускаются на одном ком- пьютере, то в качестве имени компьютера можно передать строку "1оса1Ьо81". Номер порта в нашем случае равен 2323, так как это тот порт, который ис- пользуется нашим сервером. #Ипс1е^ _МуС1хеп1:_к_ #де^хпе _МуС11еп1:_И_ #хпс1иде <ОИх<ЗдеЪ> #хпс1иде <0Тср5оскеС> с1азз 0Тех1:Ес11Ъ; С1а55 ОЫпеЕсШ:; / / =====^=============;=========== с1азз МуСИепС : риЫхс ОММдеС { О.ОВ1ЕСТ рггуа^е: ОТсрЗоскеС* щ_рТср5оскеЬ ; ОТехЪЕсЯх Ё * пирСхЪТп^о ; ОЬхпеЕахЪ* псрЪхСХприЪ;
дихпЫб 1Я_лЫехСВ1оск512е; риЬИс: МуСЫепС(сопзС ОЗСгЬпдб зСгНозС, хпС пРогС, О^НддеС* р«дС = 0) ; ргалгаСе з1оСз: уо!д з!оСКеа<1уКеад ( уожсЗ. з!оСЕггог (ОАЬзСгасСЗоскеС::ЗоскеСЕггог); \го1<3. з1оС5епдТо5егллег ( зЬоССоппесСед ( }; #епЛС //_МуС11епЬ_И_ В классе мусИепс, определяемом в листинге 40.8, мы объявляем атрибут т_рТсрЗоскес, который необходим для управления нашим клиентом, и атри- бут т_пыехсв1оск31ге, необходимый нам для хранения длины следующего по- лученного от сокета блока. Остальные два атрибута т_ехЫпЕо и т_ехЫприе используются для отображения и ввода информации соответственно. МуСЫепС: :МуС11епС(сопзС 03Сг1пд& зСгНозС, 1ПС пРогС, ОМхйдеС* р^дс /*=0*/ ) : ОЮ1сЯдеС (р«дС) , т_пИехСВ1оск512е(0) { пирТсрЗоскеС = пе« ОТсрЗоскеС (СЫз) ; пк_рТсрЗоскеС->соппесСТоНозС(зСгНозС, пРогС); соппесС (псрТсрЗоскеС, 51СЫАЬ {соппесСев ()), ЗЬОТ (з1оССоппесСед ())); соппесС (иирТсрбоскеС, ЗТОЫАЬ (геадуКеад ()), ЗЬОТ (з1оСКеас!уКеас10 )) ; соппесС (псрТсрЗоскеС г ЗТСЯЯАЬ (еггог (ОАЬзСгасСЗоскеС:: ЗоскеСЕггог)), СЫз, ЗЬОТ (з1оЬЕггог (ОАЬзСгасСЗоскеС:: ЗоскеСЕггог)) ); щ_рЬхЫп^о = пею ОТехСЕйЬС; пцрЪхСТприС = пе>7 ОЫпеЕсИС; щ_рСхС1пСо->зеСКеа<50п1у (Сгие);
ОРизЪВиЪЪоп* рстй = пею 0РизЪВи1:Соп( "&ЗепсГ) ; соппесе (рслкЗ, 51(ЯЯАЬ(с11скед()) , 51ХЭТ (з1ое5епсЯТо8е:гуег ())) ; соппесЪ (ш_р^х1:1при1; , 31СЫАЬ (ге1:игпРгеззесЗ. ()), Ы118, ЗЬОТ(зХоЁЗепдТоЗег^ег()) ); //ЬауоиЁ зеСир ОУВохЬауоиС* руЬхЬауоиЁ = пего ОУВохЬауоиС; руЬхЬауоиС->аасЗЮ1ддеЪ (пеш ОЬаЬе! ("<Н1>С11еп1:</Н1>И)) ; руЬхЬауоиС->ад<ЗМ16де1: (т_рех!:1п^о) ; ртЬхЬауоиГ->ад<ЗЮ1(ЗдеЁ (т_р1^сЫприС) ; р^хЬауои1з->аск1Ю1(1деГ: (рстс!) ; зе!:ЬауоиГ (р^хЬауои!:); В листинге 40.9 приведен конструктор, в котором прежде всего создается объект сокета (указатель т_ртсрзоскес). Затем вызывается метод соппесстонозь () этого объекта, устанавливающий связь с сервером. Первым параметром в этот метод передается имя компьютера, а вторым — номер порта. Коммуникация сокетов асинхронна. Сокет высылает сигнал соппесееа (), как только будет произведено соединение, а также сигнал геадукеаао при готовности предоставить данные для чтения. Мы соединяем эти сигналы со слотами з1оесоппесееа() и з1оскеааукеаа(). В случаях возникновения ошибок, сокет высылает сигнал еггог (), который мы соединяем со слотом з1осЕггог (), предназначенным для отображения ошибок. Затем создается пользовательский интерфейс программы, состоящий из надписи, кнопки, однострочного и многострочного текстовых полей. Сигнал сИскеао виджета кнопки нажатия соединяется со слотом з1ос5епато5егуег() класса мусИепс, ответственным за отправку данных на сервер. Для того чтобы к аналогичному действию приводило и нажатие кла- виши <Еп1ег>, мы соединяем сигнал геьигпргеззеа<) виджета текстового поля (т—рьхЫприс.) с тем же слотом з1резепатоЗегуег (). тоха МуСИепС::з1оСКеаауКеаа() { ОПаСаЗЬгеат хп(пирТсрЗоскеЬ); 1п.зеСУегзхоп(0ОаЬаЗСгеат::0С_4_5); Еог (;;) {
хС (!щ_пМех1:В1оск5х2е) { хС (гц_рТсрЗоске1:->ЬуеезАуах1аЫе () < зхзеоС(дихпЫб)) { Ьгеак; } 1п » ш_дЫехСВ1оскЗхге; ) И (т_рТсрЗоскеС->ЬуЬезА'7ах1аЫе () < пцпЦехЬВ1оскЗхге) { Ьгеак; } ОТхте Сипе; ОЗЬгхпд зСг; хп » Сипе >> зСг; псрЁхЫпСо->аррепа(Ьипе.ЬоЗЬгхпд() + " " + зСг) ; 1Ц_пКехСВ1оскЗх2е =0; В листинге 40.10 приведен слот з1оскеааутовеас1 (), который вызывается при поступлении данных от сервера. Цикл Еог нужен потому, что с сервера не все данные могут прийти одновременно. Поэтому клиент, как и сервер, должен быть в состоянии получать как весь блок целиком, так и только часть блока или даже все блоки сразу. Каждый принятый блок начинается полем размера блока. Когда мы будем уверены, что блок получен целиком, можно без опасения использовать оператор » объекта потока ооаеазсгеаш (переменная хп). Чтение данных из сокета осуществляется при помощи объекта потока дан- ных. Полученная информация добавляется в виджет многострочного тексто- вого поля (указатель ш_рсхыпГо) с помощью метода аррепа(). В завершение анализа блока данных мы присваиваем атрибуту т_пЫехьв1оскЗх2в значение 0, которое говорит о том, что размер очередного блока данных неизвестен. КЗЕШ .Ум -У • • тохд МуСИепС: :з1оёЕггог (ОАЬзЁгасСЗоске!:: : ЗоскеЬЕггог егг) { ОЗСгхпд зЪгЕггог =
"Еггог: ” + (егг == ОАЬзЪгасЪЗоске!:: :Ноз№оЪРоипдЕггог ? "Тке коз!: здаз пок 1оипск " : егг == ОАЬзкгаскЗоскеС: :КетоЪеНозСС1озедЕггог ? "Тке гетоке коз к 1з с1озес1. * : егг == ОАЬзкгаскЗоскек: :СоппесЫопР.е^изес1Еггог ? "Тке согтесЁ1оп юаз гекизеск " : ОЗкгхпд (щй-рТср5оскек->еггог5кг1пд ()) ); т_ркхк1п^о->арреп(3 (зкгЕггог) ; } Слот з1оьеггог (), приведенный в листинге 40.11„ вызывается при возникно- вении ошибок. В нем мы преобразуем код ошибки в текст, а затем отобража- ем его в виджете многострочного текстового поля. хготсЗ. МуСНепЬ: : з1оЪ5еп<1ТоЗегуег () { ОВуСеАггау аггВ1оск; СВакаЗкгеат оик (&аггВ1оск, : Мг1 кеОпХу); оик. зек\7егз1оп (ОВакаЗкгеаш: :0е_4_5) ; оиЪ « ди.1пк1б(0) « <2Т1те::сигг^пкТхше() « пцрехе1приГ->еехГ () ; оик. с^еV^се () ~>зеек (0) ; оик « дихпкТб (аггВТоск.зхгеО - зхаеок (дихпк1б)) ; пирТсрЗоскек->шг1ке (аггВ1оск) ; щ_ркхк1прик->зекТехЪ (""); В листинге 40.12 приведен метод отсылки запроса серверу. В нем использу- ется тот же трюк, что и в аналогичном методе сервера (см. листинг 40.6), ко- торый был уже подробно описан. Единственное отличие от метода муЗегуег.-: зепатосИепь () заключается в том, что после отсылки данных мы дополнительно стираем текст в поле ввода сообщения, вызывая метод зеьтехс <) с пустой строкой в качестве параметра (можно было бы также ис- пользовать метод с1еаг ()).
Напомним, что согласно установленным сигнально-слотовым связям слот з1опзепатозегуег () вызывается после нажатия кнопки 8епс1 (Послать) или клавиши <Еп1ег> в однострочном текстовом поле (указатель т-рьхыприе ()). Листинг40.13.Файл МуС)!еп1.срр. Метод •^^ойпее^вад)^. тоМ МуСИеп!::: 51о11СоппесЁес1 () { п\_рЬхЫпЕо->аррепа( "Кесе^ед СЬе соппесЬесЦ) з!дпа1"); } Как только связь с сервером установлена, вызывается метод з1оесоппеспеа(). Этот слот добавляет в виджет текстового поля строку сообщения (лис- тинг. 40.13). Высокоуровневые классы Для облегчения работы 91 предоставляет специализированные классы ОРкр и онсьр, базирующиеся на классе ооьдесс. Работа этих классов асинхронна, поэтому приложение не будет заблокировано на время отправки сообщений или получения данных. Для того чтобы получать информацию о процессе выполнения команд, эти классы предоставляют сигналы, которые можно со- единить, например, для визуального отображения, со слотом зеъргодгезз () виджета индикатора прогресса. В конце выполнения, операций высылается сигнал аопе (). Класс ОР1р Передача файлов является часто выполняемой операцией практически во всех сетях. ЕГР (РПе ТгапзГег Ргоюсо!, протокол передачи файлов) — наиболее известный из старых протоколов, предназначенный для поддержки одной из первых сетевых служб. Целью создания этого протокола было пре- доставление пользователям доступа к файлам на удаленном компьютере. Протокол поддерживает целый ряд команд, с помощью которых можно управлять удаленным компьютером для передачи данных. Например: □ дес — скопировать файл с удаленного сервера; □ риъ — копировать файл на удаленный сервер; □ гпкИг — удалить каталог на удаленном сервере; □ тксИг — создать каталог на удаленном сервере;
□ сб — открыть каталог на удаленном сервере; □ гепаше — переименовать файл или каталог на удаленном сервере; □ с1озе — закрыть соединение с удаленным сервером. Класс огер реализует сторону клиента РТР-протокола и определяет методы для наиболее часто используемых операций с РТР. Названия этих методов соответствует названиям РТР-команд: две о соответствует команде две, риьо — команде рис и т. д. Также класс окср предоставляет возможность исполнения произвольных РТР-команд. Для этого в метод гаисоттапао нужно передать строку, содержащую нужную команду, например: ГЬр.гаиСогптагкЖ "МКЛ1К МуО1г") ; Каждый из методов возвращает идентификационный номер, который исполь- зуется в сигналах класса огьр. Этим можно воспользоваться для оповещения пользователя о проводимых операциях. Например, в начале исполнения од- ной из команд объект класса орер высылает сигнал сопппапазьагсеаЦпь), а по завершении команды высылается сигнал соштапаПпхзЪеа (хпе, Ьоо1). При этом идентификационный номер проводимой операции содержится в параметре типа 1пе. Класс орср содержит и методы для осуществления соединения с РТР- сервером: соппесьтонозс() и 1од1п(). Листинг40.14 демонстрирует считы- вание файла 1пдех.(х1, размещенного на удаленном компьютере, посредством команд РТР и запись его в текущей директории под именем шдех.ш. Для этого сначала открывается файл для записи, а затем запускается серия из пя- ти РТР-команд. Обратите внимание на второй аргумент метода дес (), с по- мощью которого задается устройство ввода/вывода. ОРИе й1е (’ 1пдех. ЬхЬ ") ; ОРЪр ЕЬр; И (€Ие.ореп(0ЮОеу1се: :Иг1ЬеОп1у)) { €(:р. соппесЬТоНозС (" €Ър .ий.сго8о . сот”) ; ^Ср.1одд.п() ; ЕСр.сд ("Зо^ЫхЬ"); ЕСр.деС("1пдех.Ьхе", &И1е);
Класс ОНПр НТТР (НурегТехС ТгапяГег Рпйосо!, протокол передачи гипертекста) является стандартным и самым известным протоколом для обмена данными в сетях. Его использование проще, чем использование рассмотренного протокола ЕГР. В нем используется только одно соединение, в то время как РТР ис- пользует два: одно для отсылки команд, другое — для перекачивания дан- ных. (^С предоставляет класс онььр для реализации стороны клиента протокола НТТР. Использование этого класса очень похоже на использование класса О?ьр. Листинг 40.15 демонстрирует запись растрового изображения, находя- щегося на мг^к.§еос111е$.сот/т51епп/йпа§е5, в текущую директорию под именем ц<Ьоок.§И. ОЕИе Ше("Ьоок.дИ") ; ОНЁЁр Исер; 1 (М 1е. ореп (дтаоетхсе:: Юг! СеОп1у)} { ЫХр. зе^НозС ( "улйгю.деосхЁхез,сот") ; ИЪЁр. деС (" /тз1егт/ хтадез/дйхюк. д1^", Ш1е) ; } Резюме Сокетные соединения — это стандартный механизм обмена данными через сеть в обоих направлениях. Каждому сокету соответствует пара значений: сетевой адрес и номер порта. Сценарий клиент-сервер выглядит следующим образом: сервер занимает оп- ределенный порт, по которому он предоставляет свои услуги, и начинает ожидать поступления запросов от клиентов через этот порт. Чтобы подклю- читься к серверу, клиент должен знать его адрес и номер порта. Для соедине- ния с сервером клиент должен создать сокет. Для упрощения стандартных операций при работе с сетью Ос предоставляет специализированные классы огср и онеьр, представляющие собой реализа- ции стороны клиента одноименных протоколов. .
Глава 41 Работа с ХМЬ "Все воистину мудрые мысли были об- думаны уже тысячи раз, но чтобы они стали по-настоящему вашими, нужно честно обдумывать их еще и еще, пока они не укоренятся в вашем мозгу". Ггте В настоящее время формат ХМЬ (ЕхСепщЫе Магкир Ьап§иа§е, расширяемый язык разметки) — одна из самых активно используемых технологий. Зайдя в книжный магазин, вы, наверное, поразитесь количеству книг, посвященных ХМЬ. С распространением Интернета обмен данными между разными плат- формами стал необходимостью для программ, работающих с данными. Это и послужило причиной создания ХМЬ. Разработка ХМЬ началась в 1996 году, и в 1998 году организацией ХУЗС (ХУогШ \УИе ХУеЬ СопзоПшт) был принят первый стандарт. На самом деле, ХМЬ не представляет собой ничего нового и просто является упрощенной версией языка 8ОМЬ, разработанного в начале 80-х. Разработчики ХМЬ пе- реняли из него все лучшее, с учетом опыта языка НТМЬ, и создали нечто, по своей мощности не уступающее 8ОМЬ, но, вместе с тем, гораздо удобнее и проще как для понимания, так и для использования. ХМЬ не имеет лицензии, что позволяет бесплатно использовать этот формат в своих программах. Всеобщая поддержка ХМЬ исключает зависимость от отдельной фирмы или платформы, оказывающей поддержку для ХМЬ. Так как ХМЬ-документ представляет собой текст в формате А8СП или ЬгйсоНе, то он является читабельным и для человека. ХМЬ-документ может быть из- менен при помощи простых текстовых редакторов, например, программой ЛоСераД (Блокнот) из стандартного набора ОС ХУшдохуз.
Основные понятия и структура ХМЬ-документа ХМЬ — это средство хранения структурированных данных в текстовом фай- ле. Примером структурированных данных являются: адресная книга, генеа- логическое дерево, информация о продуктах и т. д. ХМЬ очень похож на НТМЬ. Язык ХМЬ описывает структуру самого документа без детализации его отображения. Также следует заметить, что ХМЬ гораздо строже, чем НТМЬ, например, при ошибке спецификация ХМЬ запрещает приложению, работающему с ХМЬ-документом, предпринимать попытку корректировки. В подобных случаях приложение должно прекратить считывание дефектного файла и сообщить об ошибке. Например, упрощенный вариант ХМЬ-документа, который представляет со- бой структуру адресной книги, предназначенную для хранения имен, телефо- нов и адресов электронной почты, мог бы выглядеть следующим образом: <?хт1 Vе^8^оп - <!— Му АдсЬгезз Воок —> <аск!гез зЬоок> <сопЪасС пшпЬег = "1"> <пате>Р1дду< /пате> <р1юпе>+49 631322187</рЬопе> <етаН>р1дду@теда .<Зе</етаИ> </сопПасЬ> ссопГасЬ пшпЬег = и2”> <пате>Кегт111< /пате> <рЬопе>+49 631322181</р1юпе> <ета11>кегт1Ь@теда. бе</етаИ> </сопЬасЬ> < / аскЗгеззЬоок> Любой ХМЬ-документ начинается с заголовка, говорящего о принадлежно- сти к этому формату и указывающего номер версии. Сам ХМЬ-документ со- стоит из множества элементов (ектпепй). Имена элементов заключаются между символами < и >, образуя теги. Теги нужны для обозначения границ элемента. Начало элемента обозначается открывающим тегом, например <паше>, а конец — закрывающим, в данном случае </пате>. Между этими тегами может находиться содержание, например, <пате>Р1дду</паше>, но это необязательно. Иногда встречаются элементы, не имеющие содержания, например —<етреу></етреу>. Для подобных случаев спецификация ХМЬ
предусматривает сокращенную форму — например, предыдущую надпись можно заменить на <етр1:у/>. Может показаться, что подобные теги не могут содержать информацию, но это не так — информацию можно сохранить при помощи атрибутов, следующих за именем элемента, например, <етрьу пит- Ьег = "1"></етрЬу> ИЛИ <етрЬу питЬег = п1*/>. Кроме ТОГО, информация содержится в самом наличии или отсутствии пустого тега. В ХМЬ-документ можно вставлять комментарии, которые представляют со- бой от одной до нескольких строк текста, ограниченных тегами <! — и —>. Основные отличия тегов НТМЬ от тегов ХМЬ заключаются в следующем: □ теги используются только для сохранения информации, а интерпрета- ция этих данных целиком лежит на приложении, которое их считывает. Например, в ХМЬ тег <р> необязательно является тегом параграфа, как у НТМЬ, а может обозначать, например, ргорегЫез (свойства); □ для описания документов можно использовать теги с любыми подходя- щими названиями. Следует учитывать, что строчные и прописные буквы в именах различаются, так, например, <тад></тад> и <тадх/тад> являют- ся разными тегами. На рис. 41.1 показана часть графического эквивалента структуры. Обратите внимание, что элементы ХМЬ-документа задают иерархическую структуру в виде дерева. УЙГ810П ="1.0" Му АССгезэ Воок _____> (питЬег =“1") -----»^Р1дду _____>.+49631322187 _____> р1дду@теда.<1е Рис. 41.1. ХМЬ-документ ХМЬ и 01 Библиотека 01 очень активно использует формат ХМЬ. Например, 0( Пе81^пег (см. главу 45) сохраняет файлы пользовательского интерфейса именно в этом формате. Также он используется утилитами 01, предназначен- ными для интернационализации приложений (см. главу 31).
Поддержка ХМЬ в СМ— это отдельный модуль (^Хш!, для использования которого необходимо указать его имя в проектном файле. Сделать это не- сложно: нужно просто добавить следующую строку: ОТ += хт1 А для того чтобы работать с классами этого модуля, необходимо включить заголовочный метафайл СМХт1. #1пс1ис1е <0СХт1> предоставляет две возможности использования ХМЬ. Первая называется ПОМ, вторая — ЗАХ. Нельзя однозначно сказать, что одна из них лучше другой. Каждая имеет свои преимущества. Работа с ООМ ПОМ (ЛосшпеШ ОЬ)ес1 Моде1, объектная модель документа) — это стан- дартный АР1 для анализа ХМЬ-документов, разработанный ^УЗС. рс поддер- живает второй уровень реализации, следующий рекомендациям \УЗС и включающий в себя поддержку пространства имен (паше зрасез). рротИоа» 3 1йР0фСРА^5*сЩ>п |йр₽т5ртал.ш 1ДР!0<пНоЦ11оп Рис. 41.2. Иерархия классов для работы с ООМ
Самое большое преимущество БОМ состоит в возможности представления ХМЬ-документа в виде древовидной структуры в памяти компьютера. Цена этого удобства очевидна — большой расход памяти. Но если на компьютере, где запускается ваша программа, нет недостатка в оперативной памяти, то использование ПОМ будет наиболее подходящим решением. На рис. 41.2 отображена иерархия классов дпотыоае, предоставляемого р! для работы с БОМ. Доступ ко всем классам БОМ можно получить включе- нием заголовочного файла 0сХш1. Самые используемые из этих классов — ЭТО ОБотЫоде, ООошЕХешепЬ, ООотАССг И ОЕЮтТехЪ. Чтение ХМЬ-документа Класс <2ОотЕ1етепе создан для представления элементов. Иерархия ООМ со- держит узлы различного типа. Например, узел элемента соответствует от- крытому и закрытому тегу. Данные, находящиеся между этими тегами, пред- ставляют собой узлы потомков, также имеющие тип оротЕ1етепп. Все узлы иерархии 1ЮМ являются объектами класса дпотмойе, которые способны со- держать в себе любые типы узлов. Для проведения операций над узлом, его, прежде всего, необходимо преобразовать к нужному типу. Для преобразова- ния объектов доошЫойе в 00отЕ1етепе следует воспользоваться методом ООотиоае:: ъоЕ1етепс (). Нужно всегда проверять возвращаемое этим мето- дом значение, ведь в случае ошибки будет возвращено нулевое значение, что можно проверить методом 1зЫи11 (). Программа, приведенная в листингах 41.1—41.3, осуществляет чтение ХМЬ- документа и выводит его данные на экран в следующем виде: АЬЫ: "1" ТадЫате: ТадЫате: "пате" "рЪопе" ТехЬ: ТехС: "Рхдду” ”+49 631322187” ТадЫате: "егааН" ТехС: "р1дду@шеда.Зе" АЬЫ: "2" ТадЫате: " пате" ТехЬ: ” Кегли. Ь" ТадЫате: "рИопе" ТехЬ: ”+49 631322181” ТадЫате: "етаИ" ТехЬ: "кегли Ь@теда.ае” ТЕМРЬАТЕ = арр ОТ += хт1 ^1п32: СОЫРЮ += сопзо1е 5ОЦКСЕ5 = таги. срр ТАКСЕТ = . . /ХтТВОМКеаа
Для поддержки ХМЬ (X предоставляет отдельный модуль, для включения которого его нужно указать в секции от рго-файла (листинг 41.1). Обратите внимание на секцию соырхо, в которой добавилось значение сопзоХе. Это нужно для того, чтобы приложение могло осуществлять вывод на консоль в ОС 5ЛЧтк1о\У8. #хпсХиде <ОЪХтХ> //------------------------------------------------------------------ уохд С^аVе^зе^оде (сопзЬ ОПошЫоде& поде) ' { ОВслпЫоде дотЫоде = поде.ИгзЬСЪхХдО ; юкхХе (! догпЫоде. 1зЫи11 ()) { И (догпЫоде. хзЕХетеп!: ()) { ОБатЕХетеШ: дотЕХетепЪ = дотЫоде. ЁоЕХетепЪ () ; хС(IдатЕХетепЁ.хзЫиХХ()) { х^(датЕХетепЁ.еадЫате() == "соп^асъ") { среЬидО « "АЪЁг: ” « датЕХетепЪ. аЁЪгхЬиЪе ("пшпЬег", " ”) ; } еХзе { ш дОеЬид О « "ТадНате: ’* « дотЕХетепЁ.ЪадЫате() « м\ЬТехЪ: “ « дстЕХетепЪ.^ехЪ(); } } ) 11^аVе^зе^оде (дотЫоде); дотЫоде = дотЫоде.пехЪ51ЬХ 1пд{) ; } } хпЬ тахг^О { ОРотОоситепЪ дотВос; ОРхХе Iх Хе (" аддгеззЬоок. хтХ •) ; хЕ (ЕхХе. ореп (ОХОБеухсе::КеадОпХу)) {
1С (склпЛос. веЪСопЬепе (&И1е)) ( ОВотЕХяпепЁ дотЕ1етепЪ= дотОос. <ЗоситепЬЕ1етепЬ(); СгауегзеЫоде(датЕ1етепЬ); } €Ие.с1озе() ; } геСигп 0; } В функции та1п(), показанной в листинге41.2, создаются объекты классов ООошлосишепс и ОРИе. Объект класса осошгюситепс представляет собой ХМЬ-документ. Для считывания ХМЬ-документа в метод зессопсехс () объ- екта класса опотоосшпепс необходимо передать объект, произведенный от класса, унаследованного от отооеухсе. В данном случае это класс огне. Да- лее, для получения объекта класса опотЕ1ешепс, производится вызов метода йосишепсЕХешепс () объекта класса ооопФосшпепс, который возвращает кор- невой элемент ХМЬ-документа. Полученный объект передается в функцию сгауегзеыойе (), которая обеспечивает прохождение по всем элементам ХМЬ-документа. Для прохождения по иерархии 1ЮМ удобно применить рекурсию. Из рекур- сивной функции егауегзеыоае () вызываются методы ЕХгзьсъыао и пехЕЗхЬИпд () объекта класса опошноде. Если метод пехъзхЫхпдО возвра- щает нулевое значение, то это значит, что узлов больше нет. Все получаемые узлы проверяются в цикле, с помощью метода 1зЕ1етепь (). Если получен- ный узел является элементом, то он преобразуется к типу ооошЕ1етепс с по- мощью метода соЕ1етепг () и результат присваивается объекту класса 0ОошЕ1етепг. При обнаружении элемента с именем (получаемым методом саднатеО) соп1ас1, его метод асьгхЬиЪеО применяется для отображения значения его атрибута пшпЬег. В остальных случаях вызываются методы ЬадЫаше () и еехе () для отображения имени и данных элемента. Создание и запись ХМЬ-документа При создании ХМЬ-документа необходимо иметь в своем распоряжении ме- ханизм создания элементов. Для этого класс опотОосишепь содержит серию методов, например, сгеаЬеЕ1етепЬ (), сгеаЬеТехЬЫоде (), сгеаЬеАЬСгХЬиСе (). Каждый из этих методов возвращает объект узла. Программа, приведенная в листинге 41.3, демонстрирует процесс создания ХМЬ-документа.
хпё шахпО { ОВопЮоситепе дос (" аддгеззЬоок") ; 01)атЕ1етепЁ дстЕТетепЪ = дос. сгеаЪеЕ1етепЁ ("аддгеззЬоок") ; дос. аррепдСЫ 1д (дотЕ1етепЪ); 0ВотЕ1етепГ: соп1:асЪ1 = сопЬасС (дос, ”Р1дду", "+49 631322187”, "р1дду@теда.де"); 0ПотЕ1етепе сопЪас1:2 = сопЪасЪ(дос, "КегтхЁ", ”+49 631322181”, "кегпиХ@теда.деи); СОотЕ1етепе соп^асЪЗ = сопЪасС(дос, ”Ооп2О", "+49 631322186”, "доп2о@теда.де"); дотЕ1етепЁ .аррепдСЫ1д(сопЪас!:1) ; дотЕХетепЬ. аррепдСкИд (сопЁас!:2) ; дотЕ1етепС. аррепдСИИд (сопЪасЪЗ) ; ОРИе зЕ Не (" аддгеззЬоок. хт1") ; И (НХе.ореп(<21(Х^1се: гИгдХеОпХу)) { ОТехЁЗЁгеат (&Н1е) « дос. ЪоЗЪгхпд () ; Н1е.с1озе() ; } геСигп 0; После создания объекта класса дпошпосишепс необходимо создать начальный элемент, который представляет собой начальный узел создаваемой иерархии (листинг 41.3). В нашем случае этот элемент имеет имя айдгеззЬоок. Вызов метода аррепдсиндо добавляет созданный элемент. Вызов функции сопъасио создает элемент, содержащий контактную информацию (лис- тинг 41.4). Эти элементы добавляются, методом аррепдсЫ1д(), в начальный элемент документа дотЕ1етепс. Для записи ХМЬ-документа в файл необхо- димо вызвать метод позсгхпд (), который возвратит текстовое представление ХМЬ-документа, и после этого произвести запись в файл.
0ВотЕ1етеп1: сопГ:асЪ( 0ВотОосип1епС& «ЗотВос, СОПЗЁ ОЗёг1пд& з егЫате, СОПЗЁ 1пд& з егРкопе, СОПЗЁ 08Сг1пд& зегЕтаИ ) { зЁаЁхс 1пС пЛишЬег « 1; 01)атЕ1етепЪ <1атЕ1етепЁ = такеЕ1етепЬ (ЗохпОос, "сопЪасЁ", ОЗЪгхпд () . зеШит (пЫитЬег) ); с!отЕ1етеп11.аррепбСЬИд(такеЕ1етепЬ(«ЗотЛос, "пате", "", зЬгЫате)) ; <1атЕ1етеп11.аррепс1СЫ1с1 (такеЕ1етепЪ ((ЗотВос, "ркопе", " ” , з^гРЪопе)) ; аотЕ1етепЪ. аррепдСЫМ (такеЕ1етепЪ (датЛос, " етаИ", "", зЪгЕтаИ)); пЫитЬег++; геЬит с1отЕ1етеп(:; В листинге41.4 приведена функция сопеассо. Она содержит статическую переменную, увеличивающую свое значение после каждого вызова функции. Это необходимо для присвоения контактным адресам определенного номера. Из этой функции вызывается функция такеЕ1ешепс (), которая создает эле- менты, представляющие собой составные части контактного адреса. 0ОотЕ1етепЪ такеЕ1етеп^( ОВатВоситепЪ& дотЛос, СОПЗЁ 05^г1пд& зЪгЫате, сопзЪ 05Ьг1пд& зЪгАЪЪг = ОБЪгхпд(), СОПЗЪ □5Ьг1пд& зЁгТех!: = ОБЬгхпдО ) { <2Е>отЕ1етепЬ дотЕ1етепС = «ЗопЮос.сгеаЬеЕХетепС (гСгИате); (! зСгАССг. хзЕтрЪу (>) {
ОБотАЬСг аошпАЬЬг = <1оп11>эс.сгеаЬеАГЪг1ЬиЁе(нпитЬег"); скяпАСЪг. зеЬУа1ие (з ЪгАЪЪг) ; йотЕХетепП. зеСАЁЬгхЬиЬеЫойе (склпАЪЪг) ; } (1зЬгТехЬ. хзЕтрЬуО ) { ООотТехЪ склпТехЁ = йотЛос. сгеа^еТехСЫобе (зегТех!;) ; «ХошЕХегпепС. аррепдСЫ Х<Х (дотТехЪ) ; } геЬигп дотЕХетепС; ) В листинге 41.5 приводится функция шакеЕХетепь (), которая создает элемент С ПОМОЩЬЮ метода сгеаЬеЕХешепЬ () объекта класса ОВошВоситепЬ. Если третьим параметром было передано значение атрибута, то к элементу будет добавлен атрибут пшпЬег. Его создание производится вызовом метода сгеаЬеАЬЪгХЬиЬе () объекта класса ОЛошВоситепЬ. Вызов метода зеЬУаХие () присвоит этому атрибуту переданное в метод значение. Если в четвертом па- раметре функции была передана строка текста, то методом сгеапетехьыоде () объекта класса рвошыоае будет создан текстовый узел. Вызов метода аррепасьна () внесет текстовую информацию в объект элемента. Работа с ЗАХ Ввиду большого расхода памяти, работа с моделью ЭОМ не всегда жела- тельна или возможна. Существует принципиально другой способ для анализа ХМЬ-документов — это ЗАХ. ЗАХ (81тр1е АР1 Гог ХМЬ, простой АР1 для ХМЬ) является стандартом 1ауа АР1 для считывания ХМЬ-документов. ЗАХ применяется для последова- тельного считывания ХМЬ-данных, что позволяет без проблем работать с очень большими файлами. Чтение ХМЬ-документа Класс охт1з1шр1екеааег представляет собой ХМЬ-анализатор, базирующий- ся на ЗАХ. Он читает ХМЬ-документ блоками и сообщает о том, что было найдено, с помощью соответствующих методов. В этом и состоит его основ- ное преимущество: в память помещаются только фрагменты, а не весь ХМЬ- документ. Но это и недостаток, так как информация не считывается целиком, и невозможно получить иерархию ХМЬ-документа.
Иерархия классов для работы с ЗАХ показана на рис. 41.3. Класс 0Хт1Сопиепснапа1ег должен использоваться для соединения с объектом класса охт1з1тр1екеааег. Другие классы, такие как 0Хт1ЕпЫсуКезо1уег, ОХтИУГОНапсНег, ОХтХЕггогНапаХег, ОХтХВесХНапаХег И 0хт1Ьех1са1Напд1ег просто содержат определения виртуальных методов, соответствующих раз- личным событиям анализа ХМЬ-документа. 1ОХтЮТРНа^1ег |ОХт1рес1Напа1ег..- .. 1 |ДХгоШ№огНдй1ад —^,1 Рис. 41.3. Классы для работы с ЗАХ В большинстве случаев, для считывания ХМЬ-документа можно прекрасно обойтись двумя классами: охтхсопсепьнапахег и 0Хт1Еггогнапа1ег. Интер- фейс класса охтхсопеепснапахег содержит методы, связанные с отслежива- нием структуры документа. Вызов этих методов происходит в следующем порядке: 1. Метод зсагспосишепс () вызывается при начале чтения ХМЬ-документа. 2. Метод веагСЕ1етепс () вызывается при начале чтения элемента. 3. Метод сИагасСегз () вызывается при чтении данных элемента. 4. Метод епаЕХетепс () вызывается при завершении обработки элемента. 5. Метод епаоосишепс () вызывается при завершении обработки документа. Для удобства существует класс охтхое&шхснапахег, который унаследован от всех шести классов (см. рис. 41.3). Он содержит пустые реализации виртуальных методов этих классов. Для чтения ХМЬ-документа нужно унаследовать его и пе- реопределить следующие методы: зеагЫЭосшпепЬ (), зеагСЕХетепСО, сЪагасСегз (), епаЕХетепС(), епаооситепС() и СаСаХЕггог(). Определение по- следнего метода унаследовано от класса охтХЕггогнапахег. Если эти методы возвращают значение ыие, то это говорит объекту класса охшхзХтрХекеааег
о том, что он должен продолжить анализ файла. Значение Еа1зе говорит об ошибке, а чтобы отобразить соответствующее сообщение о ней, необходимо переопределить метод еггогзыХпд (). Программа, приведенная в листингах 41.6 и 41.7, производит чтение ХМЬ- документа. Вывод на консоль аналогичен выводу программы, рассмотренной в листингах 41.1 и 41.2. *• ' 1ПЁ. хпахпО АсМгеззВоокРагзег ЪапсИег; фЕ11е € 11е (” аскЗгеззЬоок. хш1 ") ; 0Хт11приЪ8оигсе зоигсе (&Н1е); 0Хпй81пр1еКеайег геадег; геадег. зе^СопЬепСНапй1ег (&Ъапс11ег) ; геадег.рагзе(зоигсе); геЬит 0; } В основной программе, приведенной в листинге 41.6, создаются объекты классов даагеазвоокРагзег (см. листинг 41.7), окне (для чтения файла) и охт1з1тр1екеааег (для анализа'файла). Чтобы поместить ХМЬ-документ в 8АХ-анализатор, нужно создать объект класса охтИприьзоигсе, передав ему указатель на оюоеухсе. Созданный объект нужно передать в метод рагзе () объекта класса охщхзйтрхекеааег. Этот метод запустит процесс ана- лиза ХМЬ-документа. До его вызова необходимо установить в методе зеЪСопСепЬНапаХег () объект, унаследованный ОТ ОХтХСопЬепСНапаХег, кото- рый производит анализ и отображение ХМЬ-документа. сХазз даагеззВоокРагзег : риЬХхс ОХтХое€аиХСНапаХег { рггуаЪе: ОЗСгХпд щ_зЫТехЬ; риЬХХс: //---------------------------------- ЬооХ зЬагСЕХетепЫсопзЬ 03ЬгХпд&,
сопзЬ 03Ьг1пд&, сопзе С5Ъг1пд&, сопзе 0Хт1АЬЬг1Ьиеез& аЬЬгз ) { €ог(1пе 1 = 0; 1 < аЬЬгз.соипЬ() ; 1++) { 1€(аЬЬгз.1оса1Ыаше(1) == ”пшпЬег") { дПеЬидО « "АЬег:" « аЬЬгз.уа!ие(1); } } геЬигп егие; } И-------------------------------------------------------------------- Ьоо1 сЬагасЬегз (сопзЬ 03Ьг1пд& зЫТехЬ) { т_зЬгТехЬ = зЬгТехЬ; геСигп егие; } //------------------------------------------------------------------- Ьоо1 епдЕХетепЬ (сопзЬ 03Сгд.пд&, сопзС ОЗСгхпдй, сопзС ОЗСгхпдй зег) { хС (вег ' = "сопЬасе" && зЬг != "аскЯгеззЬоок") { дЦеЬидО « "ТадЫате:" « зЫ « “ХеТехе:” « ш_зегТехе; } геЬигп егие; } //------------------------------------------------------------------- Ьоо1 еаеа1Еггог (сопзЬ СХт1РагзеЕхсерЫоп& ехсерЬГоп) { дВеЬид () « "Ьхпе:" « ехсерЫоп. ИпеЫитЬег {) « ", Со1игоп:" « ехсерЫоп. соТитппЫшпЬег () « ", Мез заде:" « ехсерехоп.теззадеО ; геЬит ^а1зе; } };
Приведенный в листинге 41.7 класс даагеззвоокРагзег реализует виртуаль- ные методы, которые вызываются при прохождении по ХМЬ-документу. При нахождении тегов вызываются соответствующие методы зсагьвхетепс () и епОЕ1етепе (), которые должны быть переопределены для того, чтобы реа- гировать надлежащим образом. Метод зсагсЕ1етепъ() вызывается, когда при считывании встречается открытие тега. Первые два параметра, переда- ваемые в этот метод, относятся к пространствам имен ХМЬ, которые мы здесь не рассматриваем, третий параметр, также нами не используемый, — это имя тега, а четвертый — список атрибутов — нами просматривается. Ес- ли название атрибута совпадает со строкой питЬег, то выводится его значе- ние. Метод сЪагассегз () записывает содержимое текущего элемента в пере- менную ш_зьгтехе. Это необходимо для того, чтобы можно было получить доступ к этому содержимому из любого метода класса даагеззвоокРагзег. Метод епав1ешепс ()' вызывается всегда, когда при чтении встречается за- крытие тега. Третий параметр, передаваемый в этот метод — имя тега. Если он не совпадает со строками сопеасъ и ааагеззЬоок, то данные элемента вы- водятся на экран. Метод 1аеа1Еггог () вызывается в том случае, если не уда- ется проанализировать ХМЬ-документ. В этом случае метод производит отображение предупреждающего сообщения с указанием номера строки и столбца ХМЬ-документа, в котором произошел сбой, и текст ошибки ана- лизатора. Резюме В этой главе мы познакомились с очень популярным форматом хранения и обмена данными — ХМЬ. В настоящее время ХМЬ очень распространен и привлекает на свою сторону все больше и больше разработчиков прикладно- го программного обеспечения. 01 предоставляет два способа работы с ХМЬ-документами: ПОМ и ЗАХ. Первый представляет данные ХМЬ-документа в виде иерархии (древовидной структуры), что очень удобно для работы. Второй способ считывает данные из ХМЬ-документа блоками и сообщает о результатах в определенные вирту- альные методы. При выборе одного из них следует учитывать, что ЗАХ — это низкоуровне- вый способ, поэтому он работает быстрее. Его лучше всего применять в тех случаях, когда не требуется проведение очень сложных операций. Кроме то- го, его следует применять для считывания больших ХМЬ-документов, так как он более экономно расходует ресурсы памяти.
Глава 42 □ЙР Программирование баз данных "Распознавание проблемы, которая мо- жет быть решена и достойна решения, есть... тоже своего рода открытие". Макс Поланьи База данных представляет собой систему хранения записей, организованных в виде таблиц. База данных может содержать от одной до нескольких сотен таблиц, которые бывают связаны между собой. Таблица состоит из набора строк и столбцов (рис. 42.1). Столбцы таблицы имеют имена, и за каждым столбцом закреплен тип и/или область значений. Строки таблицы баз данных называются записями, а ячейки, на которые делится запись — полями. Первич- ный ключ — это уникальный идентификатор записи, который может пред- ставлять собой не только один столбец, но и целую комбинацию столбцов. Пользователь может выполнять множество разных операций с таблицами: добавлять, изменять и удалять записи, вести поиск и т. д. Для составления подобного рода запросов был разработан язык 8РЬ (ЗТгисТигЫ Сиегу Ьап§иа§е, язык структурированных запросов), который дает возможность не только осу- ществлять запросы и изменять данные, но и создавать новые базы данных. ПЕРВИЧНЫЙ ключ_____ имя. ТЕЛЕФОН АДРЕС ЭЛЕКТРОННОЙ ПОЧТЫ___ столбец (атрибут) строка (запись) ячейка (поле) питЬег л____ 2 пате РЩЙУ—_ КегтН рЬопе етаП +49 6313221871.р|дду@теда.с1е ^М9Ъз1^2181[^..кегт1(@теда^е' м-49 6313221361 допго@теда.<1а количество строк (кардинальное число) количество столбцов (степень) Рис. 42.1. Таблица (отношение)
Основные положения 8О1_ Основными действиями, производимыми с базой данных, являются создание новой таблицы, чтение (выборка и проекция), вставка, изменение и удаление данных. Чтобы сохранить цельность изложения, мы опишем базовые коман- ды 80Ь, позволяющие это сделать. Стандарт 80Ь поддерживает более раз- вернутый синтаксис для этих команд, чем описывается здесь, не говоря уже о конкретных СУБД (системах управления базами данных), производители которых обычно предоставляют расширенный синтаксис для наилучшего использования их особенностей. Этому предмету посвящаются целые книги, но и изложенных далее основ достаточно для написания простых программ, работающих с базами данных. Сразу следует сказать, что язык 8<2Ь нечувствителен к регистру, то есть бук- вы, набранные в разных регистрах, не различаются. Например, 8ЕЕЕСТ, &е- 1ес1, 8е1ес( и т. п. означают одно и то же. В дальнейшем, для выделения клю- чевых слов мы будем использовать верхний регистр. Создание таблицы Для создания таблицы, показанной на рис. 42.1, используется команда скеате тавье, в которой указываются имена столбцов таблицы, их тип, а также задается первичный ключ: СНЕАТЕ ТАВЬЕ асИгеззЬоок ( питЬег ЮТЕЗЕК РН1МАКУ КЕУ ЙОТ ЙПЬЬ, пате УАКСНАК(15), рЬопе УАКСНАК(12), етаИ УАКСНАК(15) ); Операция вставки После создания таблицы можно добавлять данные. Для этого 8(}Ь предос- тавляет команду вставки хйзект тйто. Сразу после названия таблицы нужно указать в скобках имена столбцов, в которые будут заноситься данные. Сами данные указываются после ключевого слова уаьнез: 1Ы8ЕКТ ЮТО аДДгеззЪоок (питЬег, пате, рИопе, ета!1) УАШЕ8(1, ‘Рхдду*, 1+49 631322187*, ’ р!дду@теда. Де ’) ; 1Ы5ЕКТ ЮТО аДДгеззЬоок (питЬег, пате, рЬопе, етаИ) таШЕ5(2, 'КегпйЬ’, '+49 631322181’, ’кегли.С@теда.Де');
Чтение данных Составная команда зеьест ... рвом ... инеке осуществляет операции выборки и проекции. Выборка соответствует выбору строк, а проекция — выбору столбцов. Эта команда возвращает таблицу с частью исходных данных, созданную согласно заданным критериям. В этой команде три составных части: 1. Ключевое слово зеьест является командой для проведения проекции, то есть после него указываются столбцы, которые должны стать ответом на запрос. Если указать после зеьест знак *, то результирующая таблица бу- дет содержать все столбцы таблицы, к которой был адресован запрос. Указание конкретных имен столбцов оставляет в проекции только эти столбцы. 2. После ключевого слова рком задается таблица, к которой адресован за- прос. 3. Ключевое слово инеке является оператором выборки. Выборка осуществ- ляется согласно условиям, указанным сразу после оператора. Эту часть команды можно опускать, что приведет к включению в результат всех строк исходной таблицы. Например, для получения адреса электронной почты мисс Р1&;у, нужно сде- лать следующее: ЗЕЬЕСТ етаИ РКОМ аддгеззЬоок ИНЕКЕ пате = 'Рхдду1; Изменение данных Для изменения данных таблицы используется составная команда прпате ... зет инеке. После названия таблицы в операторе зет указывается название столбца, в который будет заноситься нужное значение, и через знак = само это значение. Указав несколько таких выражений через запятую, можно об- новить сразу несколько столбцов. Изменение данных производится в стро- ках, удовлетворяющих условию, указанному в ключевом слове инеке. В по- казанном ниже примере производится замена адреса электронной почты мисс с р18§у@те§а.(1е на р1^§у@$ирегте§а^е: ЦРЛАТЕ айбгеззЬоок ЗЕТ етаИ = ' рхдду^зирегтеда. с1е ’ МНЕР.Е пате = Рхдду';
Удаление Удаление строк из таблицы производится при помощи команды пеьете рком Киеве. После ключевого слова инеке следует критерий, согласно которому производится удаление строк. Например, удалить адрес мисс из табли- цы можно следующим образом: ПЕСЕТЕ ЕКОМ асЗдгеззЬоок КИЕНЕ паше = ’Рхдду’; Использование 8ОЬ в библиотеке СК Для использования баз данных, (^1 предоставляет отдельный модуль Для его использования необходимо сообщить об этом — просто добавьте в проектный файл следующую строку: ©Т += зд1 1 А для того чтобы быть в состоянии работать с классами этого модуля, необ- ходимо включить заголовочный метафайл ♦ХпсХийе <ОЪЗдХ> Классы этого модуля разделяются на три уровня: 1. Уровень драйверов. 2. Программный уровень. 3. Уровень пользовательского интерфейса. К первому уровню относятся классы для получения данных на физи- ческом уровне. Эго такие классы, как ОЗдХОгХуег, 03дХОгХуегСгеаСог<Т*>, ОЗдХПг^егСгеаЪогВазе, ОбдХПгз^егРХидХп И ОЗдХЯезиХе. Классы второго уровня предоставляют программный интерфейс для обраще- ния к базе данных. К классам этого уровня относятся следующие классы: ОЗдХВаЪаЬазе, ОЗдХОиегу, ОЗдХЕггог, ОЗдХРХеХсЦ ОЗдХХпйех И ОЗдХКесогй. Третий уровень представляет модели для отображения результатов запросов в представлениях интервью. К этим классам относятся: оздхоиегумоаех, ОЗдХТаЬХеМсхХеХ И ОЗдХКеХаСХопаХТаЬХеМсхХеХ. Классы первого уровня вам не придется использовать, если вы не собирае- тесь писать свой собственный драйвер для менеджера базы данных. В боль- шинстве случаев, все ограничивается использованием конкретной СУБД, поддерживаемой 01. В настоящее время существует несколько десятков СУБД, а поддерживаемые Ос приведены в табл. 42.1.
Таблица 42.1. Идентификаторы менеджеров баз данных Идентификатор Описание ООС1 Доступ к базам данных Огас1е через Огас1е Са111п1егТасе (ОС1). Поддерживаются версии 7,8 и 9 ОООВС ООВС (Ореп ОаТаЬазе Соппес1м1у, открытый интерфейс досту- па к базе данных) — стандартный ООВС-драйвер для Меговой ВОк 8еп/ег, 1ВМ ОВ2, ЗуЬаве 8О1_, ЮОВС и других баз данных ОМУЗОк МуЗОк — самый популярный в настоящее время бесплатный менеджер базы данных. Всю необходимую информацию можно получить на странице штт.туад1.сот ОТОЗ ЗуЬаве АОарНуе Зеп/ег ОРЗОЬ Базы данных РоздгеЗОк с поддержкой 80к92/80кЗ О5ОЦТЕ2 8О1Ле версии 2 ОЗОкЛГЕ ЗОкЙе версии 3 О1ВА8Е ВоНапб 1п*егВазе СЮВ2 ОВ/2 — платформонезависимая база данных, разработанная 1ВМ Если в таблице нет поддержки нужной вам базы данных, то вам потребуется самому написать для нее драйвер. Соединение с базой данных (второй уровень) Для соединения с базой данных прежде всего нужно активизировать драйвер. Для ЭТОГО вызывается статический метод ОЗдЮаСаЬазе::айаоаЕаЪазе() (листинг 42.1). В него нужно передать строку, обозначающую идентификатор драйвера СУБД (см. табл. 42.1). зЪаЁхс Ъоо1 сгеаЪеСоппесЁхоп() { ОЗдЮаЪаЬазе сЗЬ = 08д1ВаСаЬазе: :аскЗВаеаЬазе("080ЫТЕ") ; <ЗЬ. зеЁВаСаЬазёКате ("аскЗгеззЪоок"); сЗЬ. зеСПзегЫате("еЗХоп"); (ЗЬ. зееНозеЫате (" ерхса"); <ЗЬ. зеЪРаззюогб ("раззюогсГ);
(’сЗЬ.орепО ) { дЭеЬидО « ”СаппоЪ ореп сЯаеаЬазе:" « (ЗЬ.ХазЪЕггог () ; геЪигп ^а1зе; } ге1:игп Ъгие; } Для того чтобы подключиться к базе данных, потребуется четыре следующих параметра: □ ИМЯ базы данных — передается В метод СЗдЮаСаЬазе:: зеЫЭаЪаЬазеЫате (); □ имя пользователя, желающего к ней подключиться — передается в метод ОЗдШаеаЪазе: : зеЪЛзегЫаше (); □ имя компьютера, на котором размещена база данных — передается в ме- ТОД 08дЮа<:аЬазе: : зе^Ноз^Ыате (); □ пароль — передается в метод 03д1ПаЬаЬазе:: зеСРаззшога (). Методы должны вызываться из объекта, созданного с помощью статического метода ОЗдШаеаЬазе: :аааоаСаЬазе() (ЛИСТИНГ 42.1). Само соединение осуществляется методом оздЮаЬаЬазе: :ореп(). Значе- ние, возвращаемое им, рекомендуется проверять. В случае возникнове- ния ошибки, информацию о ней можно получить с помощью метода ОЗдШаЪаЪазе: :ХазЬЕггог(), который возвращает объект Класса ОЗдХЕггог. Его содержимое можно вывести на экран с помощью доеЬид (). Если вы хо- тите получить строку с ошибкой, то можно вызвать метод ъехъо объекта Класса ОЗдХЕггог . Исполнение команд $01. (второй уровень) Для исполнения команд 8()Ь после установления соединения можно исполь- зовать класс оздхоиегу. Запросы (команды) оформляются в виде обычной СТРОКИ, Которая передается В КОНСТруКТОр ИЛИ В МеТОД ОЗдХОиегу: :ехес(), При передаче запроса в конструктор запуск команды будет производиться автоматически при создании объекта. Класс оздхоиегу предоставляет возможность навигации. Например, после выполнения запроса зеьест можно перемещаться по отобранным данным при помощи методов пехЪ (), ргеуХоиз (), ГхгзЬ (), ХазЬ () И веек (). С ПОМО- ЩЬЮ метода пехе () мы перемещаемся на следующую строку данных, а вызов метода р^еV^оиз () перемещает нас на предыдущую строку данных. При по- мощи методов ЕХгзЦ) и Хазе о можно установить первую и последнюю
строку данных, соответственно. Метод зеек () устанавливает текущей строку данных, целочисленный индекс которой указан в параметре. Количество строк данных можно получить вызовом метода зхге (). Дополнительные сложности возникают с запросом тнзект. Дело в том, что в запрос нужно внедрять данные. Для достижения этого можно воспользовать- ся двумя методами ргераге () и ЫпДУа1ие (). В методе ргераге () мы задаем шаблон, данные в который подставляются методами ЫпДУа1ие (). Например: диегу.ргераге(*1ЫЗЕКТ ЮТО аДДгеззЬоок (пшпЬег, пате, рЬопе, етаИ) " "УА1ДЛЕЗ(:пшпЬег, :пате, :рЬопе, :ета!1);"); диегу.ЫпДУа1ие (":пшпЬег", 1) ; диегу. ЫпДУа1ие (" :пате", "Р1дду" ) ; диегу .ЫпДУа1ие (" :рЬопе", "+49 631322187"); диегу. ЫпДУа1ие (" :ета11", "рхддубтеда.Де") ; Также можно воспользоваться известным из ОЭВС методом использования безымянных параметров: диегу.ргераге("1ЫЗЕКТ ЮТО аДДгеззЬоок (пшпЬег, паше, рЬопе, етаИ)" "УАШЕЗ(?, ?, ?, ?);”); диегу. ЫпДУа1ие (1) ; диегу. ЫпДУа1ие (" Рхдду") ; диегу .ЫпДУа1ие ( "+49 631322187") ; диегу. ЫпДУа1ие ("р!дду@теда. Де") ; Есть и третий вариант — можно воспользоваться классом озъгапд, в частно- сти методом озсгапд: :агд(), с помощью которого можно произвести под- становку значений данных. Этот способ показан в листинге 42.2. Разумеется, в рассмотренном случае можно было бы сразу вставить данные в текст запроса, поскольку они известны заранее, но в реальных приложениях данные чаще всего представляют собой значения выражений, поэтому такой метод не работает. Программа, приведенная в листинге 42.2, демонстрирует исполнение команд Производится создание базы, запись данных и их опрос. В результате, на консоль будут выведены следующие данные: 1 "Рхдду" ; "+49 631322187" ; "рхддубтеда.Де" 2 "Кегтд.Ь" ; "+49 631322181" ; "кеппхЬбтеда.Де" .........------------------------------------------------Т”Я хпП тахпЦпЪ агдс, скаг** агд^) { ОСогеАррИсаЪхоп арр(агдс, а^дV);
//Соединяемся с менеджером баз данных хГ (! сгеаСеСоппесСхоп ()) { геСит -1; } //Создаем базу 05д10иегу диегу; ОЗСгхпд зСг = "СКЕАТЕ ТАВЬЕ аййгеззЬоок ( " "питЬег ЮТЕОЕК РНТМАКУ КЕУ ЫОТ ЫЦЬЬ, " "паше УАКСНАКЦБ), • "рПопе УАКСНАК(12), " "етаП УАКСНАВ(15) " н);И. хС (’диегу.ехес(зСг)) { дЕеЬидО « "ПпаЫе Со сгеаСе а СаЫе"; } //Добавляем данные в базу ОЗСгхпд зСгР = "1Ы5ЕКТ ЮТО аййгеззЬоок (питЬег, пате, ркопе, етах1) " "УАШЕЗ(%1, *%2‘ , •’&З’, зСг = зСгР.агд("1") .агдрРхдду") ,агд("+49 631322187“) .агд("рхддуФтеда.Йе"); хС (!диегу.ехес(зСг)) { дВеЬидО « "ПпаЫе Со таке 1пзегС орегаСхоп"; } зСг = зСгР.агд("2") .агд( "КеплхС") .агд("+49 631322181") .агд ("кегтхСвтеда.Йе"); хС (’диегу.ехес(зСг)) { дПеЪидО « "ПпаЫе Со таке хпзегС орегаСхоп хС (!диегу.ехес("ЗЕЬЕСТ * РКОМ аййгеззЬоок;")) {
дПеЬидО « "ПпаЫе Хо ехесиСе диегу - ехлЛхпд"; геЪигп 1; //Считываем данные из базы ОЗдХКесогд гес = диегу.гесогсЦ); 1пЪ пЫитЬег = 0; ОЗЬг1пд з егЫаше; ОЗ^гХпд зегРЬопе; СЗегхпд зИгЕтаИ; юкНе (диегу.пехЬО) { пЫитЬег = диегу.уаХие (гес. ХпдехО^ ("пшпЬег*)) . ЬоХпЬ () ; зЬгКате = диегу * VаXие (гес. хпдехО^ ("пате")). ЁоЗЁгХпд() ; зЬгРкопе = диегу ^аХие (гес. 1пс1ех0^ ("ркопе")) . ЬоЗЬгхпд () ; зЬгЕтаИ = диегу ^аХие (гес. 1пс1ехОГ ("етахХ")) . ЬоЗЬгхпд () ; дПеЬидО « пЫитЬег « " " « з^гЫате « " ;\е" « з^гРИопе « ";\Ь" « зЬгЕтаИ; } геЬигп 0; В случае удачного соединения с базой данных с помощью сгеасеСоппессхоп () создается строка, содержащая команду 8<2Ь для создания таблицы. Эта стро- ка передается в метод ехес () объекта класса оздЮиегу. Если создать табли- цу не удается, то на консоль будет выведено предупреждающее сообщение. Ввиду того, что в таблицу будет внесена не одна строка, в строковой пере- менной асгР при помощи символов спецификации определяется шаблон для команды 1№ект. Вызовы методов агд () класса озы1пд подставляют нужные значения, используя шаблон. Затем, когда база данных создана и все данные были внесены в таблицу, вы- полняется запрос зеьест, помещающей все строки и столбцы таблицы в объ- ект диегу. Вывод значений таблицы на консоль производится в цикле. При первом вызове метода пехе (} этот объект будет указывать на самую первую строку таблицы. Последующие вызовы приведут к перемещению указателя на следующие строки. В том случае, если записей больше нет, метод пехе () вернет Еа1зе, что приведет к выходу из цикла.
Для получения результата запроса следует вызвать метод оздХфиегу::уа1ие (), в котором необходимо передать номер столбца. Для этого мы воспользуемся методом гесогао. Этот метод возвращает объект класса озд1кесога, кото- рый содержит относящуюся к запросу зеьест информацию. Вызывая метод 05д1кесога:: Хпйехос (), мы получаем индекс столбца. Метод уа1ие () возвращает значения типа ОУагхапе. ОУагхапе — это специ- альный класс, объекты которого могут содержать в себе значения разных типов (см. главу 4). Поэтому в нашем примере полученное значение нужно преоб- разовать к требуемому типу, воспользовавшись методами ОУагхапе:: сотпъ () И ОУагхапС::ЬоЗЬгхпд(). Классы 8О1_-моделей для интервью (третий уровень) Модуль поддерживает концепцию интервью (см. главу 12), предостав- ляя целый ряд моделей для использования их в представлениях. Класс 03д1таЬ1емоае1 позволяет, например, отображать данные в табличной и ие- рархической форме. Использование интервью — это самый простой способ отобразить данные таблицы. В этом случае не потребуется цикла для прохождения по строкам таблицы. Программа, окно которой отображено на рис. 42.2, демонстрирует такую возможность. Рис. 42.2. Использование интервью для отображения данных хпе шахпСхпЪ агдс, сйаг** агду) { ОАррИсаСхоп арр(агдс, агду) ; 1Е (! сгеаСеСоппесЫоп ()) {
геСит -1; } ОТаЬХеУхе» ухе»; 02д1ТаЫеМ<х1е1 шойеХ; люде!. зеСТаЬХе ("аскЗхеззЬоок") ; тсхХеХ. зеХесС (); гцсхХеХ.зеЬЕсНЕЗегаЕедуСОЗчХТаЬХеМосХеХ: гОпГХеХасЪапде); ухе». зеЬМсхХеХ (&тодеХ) ; ухе», эко»() ; генит арр. ехес (1; } В листинге 42.3 после соединения с базой данных, которое осуществляется функцией сгеаНеСоппесьхоп () (см. листинг 42.1), создается объект таблич- ного представления отаьхеухе» и объект табличной модели,оздхтаьхемоаех. Вызовом метода зеьтаьхео мы устанавливаем актуальную базу в модели. Вызов метода зеХесн () производит заполнение данными. Теперь настало время немного рассказать о возможностях редактирования и записи данных. Класс 03д1таЬХеМойеХ предоставляет для этого три сле- дующие стратегии редактирования, которые устанавливаются с помощью метода зеЬЕах СЗСгаСеду (): □ Опномсъапде — запись данных производится, как только пользователь пе- рейдет к другой строке таблицы; □ опРхехасьапде — запись данных происходит после того, как пользователь перейдет к другой ячейке таблицы; □ опмапиахзиЪшхс — данные записываются при вызове слота виЬтхслххо. Если вызывается слот геуегнА1Х(), то данные возвращаются в исходное состояние. Какая из стратегий вам больше подходит, должны выбрать вы сами. В лис- тинге 42.3 мы используем стратегию 05дХтаьХеМо<1еХ: :0пРхе1асЬапде, вызы- вая метод зеьЕах^зьгаьеду () с этим аргументом. Теперь данные нашей мо- дели можно изменять после двойного щелчка на ячейке. В завершение мы устанавливаем модель в представлении вызовом метода зеемоде! (). Если вам понадобится произвести отображение данных какого-либо кон- кретного запроса беьест, то для этого целесообразнее будет воспользоваться
другим классом 8<2Е-моделей— классом <эзд10иегумоае1. Листинг 42.4 ил- люстрирует отображение только электронных адресов и телефонных номеров всех контактов с именем В нашем случае найдется лишь одна соответ- ствующая строка (рис. 42.3). __$(315иегуМасН | снопе .1 1 -45 6312221»’ | "I етаН р р-ддуетеда.зе | г г Рис. 42.3. Использование интервью для отображения выборочных данных 1пС та1п(1пЪ агдс, сЬаг** агду-) { ОАррИсаЪхоп арр(агдс, агду') ; (!сгеаЪеСоппесЪхоп()) { геСигп -1; } ОТаЫеУхею VI ею; 08д10иегуМос1е1 пкх1е1; тсх5е1. зе^Оиегу ("ЗЕЬЕСТ рИопе, етаИ " "РКОМ ад<ЗгеззЬоок ” "ИНЕКЕ пате = ’Рхдду';" ); 1Г (тоае1.1азЪЕггог () . хзУаИЗ ()) { дПеЬид() « тоде1.1азЪЕггог(); } V^ею. зеШоде! (&тоде1) ; у1ем.8Ъсж(); ге^ит арр. ехес ();
В листинге 42.4 мы создаем табличное представление отаЫеУхек и модель запроса <23д1$иегуМо<ае1. Строка запроса передается в метод зееоиегуо, по- сле чего результат выполнения запроса проверяется в операторе и на нали- чие проблем исполнения, с помощью метода 1азеЕггог(). Инвалидный объ- ект класса <23д1Еггог, возвращаемый этим методом, означает, что ошибок нет и никаких проблем не произошло. Это и проверяется методом 1зУаиао. Возникновение проблем повлечет их отображение в дюеЬид (). В завершение модель устанавливается в представлении вызовом метода зеемоае1 (). Резюме Базы данных — одна из самых объемных тем информатики. Поэтому описа- ние всех возможностей работы с базой данных требует отдельной книги. Данная глава описывает лишь их малую часть, концентрируясь на основных особенностях От при работе с базами данных. В этой главе мы узнали, что база данных — это хранилище, организованное в виде таблиц, в каждой ячейке которых содержатся данные определенного типа: текст, числа и т. д. Язык запросов 8рЬ — это стандарт, который используется в большом коли- честве СУБД, что обеспечивает его платформонезависимость. Запросы (ко- манды) — это своего рода вопросы, задаваемые базе данных, на которые она должна давать ответы. 01 представляет модуль поддержки баз данных, классы которого разделены на три уровня: уровень драйверов, программный и пользовательский. Для работы с базой данных нужно сначала активизировать драйвер, а затем установить связь с базой данных. После этого, посредством запросов 80Ь, можно получать, вставлять, изменять и удалять данные. Для высылки запро- сов используется класс 03д10иегу. При помощи концепции интервью можно очень просто отображать данные 8рЬ-моделей в представлениях.
Глава 43 Динамические библиотеки и система расширений "Что едино, разъедини". Сунь-цзы Зачастую приходится реализовывать свои дополнения, предназначенные для использования в одной или нескольких программах. Это можно сделать при помощи динамических библиотек, которые реализуют специальный интер- фейс. Некоторые подобные дополнения поставляются с От и называются расширениями (р1и@-т$). Динамические библиотеки В самом деле, на практике очень часто возникают случаи, когда требуется совместное использование какой-либо функции сразу в нескольких програм- мах, работающих на одном компьютере. Не совсем экономично, если каждая из этих программ будет содержать одинаковый код, значит, необходим меха- низм для объединения общего кода в отдельных файлах (библиотеках), кото- рый позволял бы воспользоваться кодом таких функций. Такие файлы долж- ны подгружаться динамически, в процессе работы самих программ, по мере необходимости и в зависимости от потребностей. Динамическая библиотека по структуре является архивом, содержащим группу объектных файлов. Ис- пользование динамических библиотек дает следующие преимущества: □ программа занимает меньше места в оперативной памяти и на диске. Ко- гда динамическая библиотека подключается к программе, то в исполняе- мом файле присутствует лишь ссылка на нее; □ разные программы могут использовать одну и ту же библиотеку; □ после обновления динамической библиотеки, не обязательно перекомпилиро- вать использующие ее программы, что позволяет обновлять динамические
библиотеки, не затрагивая связанные с ними программы. То есть если в библиотеке будет обнаружена ошибка, то достаточно будет просто заме- нить файл. Создание динамической библиотеки Следующий пример, приведенный в листингах 43.1—43.3, демонстрирует создание динамической библиотеки, содержащей только одну функцию. Листинг 43.1. Файл ТЕМРЬАТЕ = ИЬ ОЕЗТШК = .. ОТ -= дит ЗОШСЕЗ = дупЫЬ.срр НЕАЬЕНЗ = бупИЬ.11 ТАРСЕТ = йупПЬ Обратите внимание на файл проекта, показанный в листинге 43.1. Для созда- ния динамической библиотеки нужно установить в секции темрьате значение иь. Расположена готовая библиотека будет на один уровень выше каталога с ее исходными файлами, для этого в секции оезтохк мы зададим значе- ние Ввиду того, что создаваемая библиотека не нуждается в элементах пользова- тельского интерфейса, мы сделаем так, чтобы они были изъяты из компонов- ки, для чего мы оператором -= исключаем опцию ди! в секции от. #1пс1ис1е <фЗег1пд> ехЬегп "С” { ОЗЬгхпд оддЦррег(сопзЪ ОЗЪгхпдй зЪг); } В динамическую библиотеку должны быть экспортированы прототипы функций для их дальнейшего использования. Эти функции необходимо за- ключить в спецификатор ехсегп "с" {„}. В этом случае компилятор С++ не будет прикреплять информацию о типе к символьной сигнатуре функции. Без этого спецификатора компилятор может подставить вместо имени функции оаацррег () совсем другое имя, в котором будет закодирована дополнитель-
ная информация. Этот спецификатор нужно указать, если вы хотите, чтобы вашу динамическую библиотеку можно было загружать в процессе работы основной программы, например, при помощи класса оыьгагу, о котором речь пойдет далее. #1пс1ис1е "с1уп11Ь.11и ОЗЪгхпд осЯсЮррег (сопзъ 05ег1пд& зЪг) { ОЗЪгхпд зЬгТегор; ^ог (1пС 1=0; 1 < зЪг.1епдЫ1(); ++1) { зЬгТетр += (1 % 2) ? зЪг.аШ) : зСг.аЦ1) .ЪоЦррег() ; } геГигп зПгТетр; } В листинге 43.3 показана реализация экспортируемой функции, которая пре- образует каждый нечетный символ переданной строки в заглавный и возвра- щает полученный результат. Использование динамических библиотек Существуют два способа использования динамических библиотек. При пер- вом способе связывание с динамической библиотекой производится в про- цессе компоновки самой программы. В этом случае динамическая библиоте- ка загружается автоматически при запуске использующей ее программы. Для реализации этого способа потребуется подключить динамическую библиоте- ку в рго-файле в секции ывг (ее нужно указать после префикса -1), а распо- ложение ее заголовочных файлов — в секции хысыгоератн. Например: ЫВЗ += -1Му_Оуп_ЫЬ ШС1ДГОЕРАТН += .. /.. /Му_Суп_ЫЬ/1пс1ийе Вам может понадобиться загрузка некоторого кода без явной компоновки во время работы самой программы. Для этого существует второй способ, ко- торый заключается в использовании класса оыьгагу. Этот класс заботится и о том, чтобы загруженная библиотека оставалась в памяти на протяжении всего времени использования. В коде, приведенном в листинге 43.4, реализо-
вана загрузка динамической библиотеки и исполнение функции, экспорти- руемой этой библиотекой. Эта функция изменяет каждую нечетную букву на заглавную (рис. 43.1). ___Арр11саг*сп Т- 5 3 ТпЕ Е Тех Рис. 43.1. Исполнение функции динамической библиотеки #1пс1ийе <0ЪОи1> 1пЪ та1п(1пЬ агдс, сИаг** агдгг) { ОАррИсаЫоп арр(агдс, а^дV); 0ЬаЪе1 1Ы("ЬЫз 1з ЪЪе ехатр1е ьехС"); 0Ьа.Ьгагу ИЬ( "дупИЬ") ; ЬуредеЕ 03Сг1пд (*РсЬ) (сопзЬ 05Ьг1пд&); РсЪ ГсЪ = (РсЬ)(11Ь.ге8о1уе("оскЮррег")); 1Г (Гее) { 1Ы. зеСТехС (Гее (1Ы. ЬехЬ ())); } 1Ы.зЪ<ж() ; геЬихп арр.ехес(); В листинге 43.4 создается виджет надписи хы, которому присваивается текст в конструкторе. Чтобы использовать динамическую библиотеку в программе, нужно создать объект класса оыьгагу и передать в его конструктор имя файла динамической библиотеки, но заметьте — только имя, без расшире- ния. Это связано с тем, что на разных платформах динамические библиотеки имеют различные расширения. В ОС ХУшёохуз наша библиотека будет иметь расширение Л1, в 1)п1х — зо, а на МасОЗ X — ёуНЬ. Передавая только имя, мы возлагаем на 0( ответственность на подстановку нужного расширения.
Указатели на экспортируемые функции извлекаются с помощью метода гезо1уе () (листинг 43.4). В этот метод передается символьная сигнатура, по которой будет производиться поиск нужной функции. Возвращает метод ука- затель на тип уо1<а, который представляет собой адрес найденной функции. Если метод гезо1уе () вернет нулевой указатель, это означает, что функция не найдена. Для вызова функции этот указатель необходимо привести к нуж- ному типу. В случае успешной проверки указателя мы вызываем саму функ- цию. В завершение мы вызываем метод зЬозд() для отображения виджета надписи на экране. Расширения (р1ид-1п$) Использование расширений— это неотъемлемая часть любого профессио- нального приложения. По сути, расширение — это совместно используемая динамическая библиотека, предназначенная для загрузки в процессе испол- нения основного приложения, которая обязательно должна реализовывать хотя бы один специальный интерфейс. Расширения делятся на две группы: □ расширения для Ри □ расширения для собственных приложений. Расширение для 01 Р( предоставляет различные типы расширений, предназначенные для допол- нения возможностей библиотеки, например, для поддержки новых форматов растровых файлов, новых драйверов баз данных и т. д. Всего их более 20. Вот некоторые из них: □ 0здЮг1уегР1идхп — для драйверов баз данных; □ ОРХсСигеГоипаСРХидХп, ОХшадеХОРХидХп— ДЛЯ ПОДДвржки раЗЛИЧНЫХ форматов растровых изображений (см. главу 19\, □ ОТехЪсоаесРХидхп — для реализации кодировок текста; □ <25(;у1еР1ид1п — для стилей элементов управления (см. главу 26). Все эти классы наследуют класс ооьзесп, который является базовым для всей системы расширений Свои расширения для СИ нужно строить на базе этих классов, реализуя в них нужные методы. Возьмем для примера класс 05су1еР1ид1п. Реализация расширения с применением этого класса, для соз- данного нами в главе 26 стиля сизсотзсуХе, могла бы выглядеть так, как по- казано в листингах 43.5—43.7.
с!азз Сиз1:отЗСу1еР1ид1п : риЬНс 08Ъу1еР1идхп { риЬНс: 05Ёг1пдЫзЬ кеуз ( ) сопзЪ; 28Ёу1е* сгеаЪе(сопз1: ОВ^гхпд &кеу) ; }; В классе, унаследованном от 05ьу1еР1ид1п (листинг 43.5), необходимо реа- лизовать виртуальные методы кеуз () и сгеаье (). <25Ьг1пдЬ1зе СизЬотЗЬу1еР1ид1п::кеуз() сопзЬ { геСит ОЗСгхпдЫзЪ () « ,|СизЬ<жп5Ьу1еп ; } Метод кеуз () возвращает список строк с именами реализованных в классе расширений. В нашем примере расширение одно — сизьошзьуХе (лис- тинг 43.6). СЗйуХе* Му31:у1еР1идхп: :сгеаЬе(сопзЁ ОЗЪгхпд &кеу) { И (кеу == иСиз^СIт5I:у1е‘,) геЬигп пем СизЬот5Су1е; геШт 0; } 0_ЕХР0КТ_РШС1Ы2 (СизЬат31:у1еР1идхп) Метод сгеаъе () создает запрашиваемый объект и возвращает указатель на него. Если запрашиваемый объект не найден, метод вернет нулевое значение (листинг 43.7). Класс расширений необходимо экспортировать, добавив в конец срр-файла макрос о_ЕХРокт_Рьис1Ы2, как показано в листинге 43.7.
та1п(1пЬ агдс, скат** агду) { ОАррИсаТхоп::зеЬЗЪу1е(08Ьу1еРасСогу::сгеаЪе("СизЬатЗЬуХе")); } Созданный класс расширений должен быть скомпилирован в динамическую библиотеку. Его загрузку можно произвести при помощи класса озьу1егасъогу (листинг 43.8). Для того чтобы (^1 мог воспользоваться нашим расширением, его необходимо поместить в директорию расширений соответствующего типа, расположен- ную в каталоге библиотеки. В нашем случае это будет директория «^Директо- рия 01>/р1и;рп5/8(у1е8. Поддержка собственных расширений в приложениях Связь с расширением производится с помощью интерфейса (см. далее), по- этому приложение должно предоставлять по меньшей мере один интерфейс для использования расширения. Расширения загружаются приложением при помощи класса 0Р1ид1пьоааег, который содержит несколько методов. Самый часто используемый из них — это метод хпзеапсеО, создающий и возвра- щающий указатель на объект расширения. Этот класс автоматически загру- жает расширение, чье имя файла указывается в его конструкторе. Выгрузку расширения, если в этом есть необходимость, можно осуществить с по- мощью метода ип1оаа (). Рис. 43.2. Приложение, поддерживающее расширения
Программа, показанная на рис. 43.2, демонстрирует приложение, предостав- ляющее поддержку для использования расширений (листинги 43.9—43.14). Для этого ею предоставляется интерфейс для операций над текстом. #Ипс3.еС _1пЁегГасеа_]1_ #с!е€1пе _1п11ег^ааез_Ъ_ с1азз ОВЪгхпд; с1азз ОЗ^гхпдЬхзЁ; / / ====гг=======Я=х=============:==========г======:==г=г=======; с1азз ВегхпдТпЪег^асе { риЫхс: V ^^^иа1 -ЗЁгхпдХпЪег^асе() {} V ^^^иа1 ОЗЪгхпдЫзЪ орегаЫопэ () сопзЪ = 0; V ^^^иа1 ОЗЁгхпд орегаЫоп(сопзС 081:гхпд& зйхТехЬ, сопзП 08Ьгхпд& зйгОрегаЫоп ) = 0; }; О_ЭЕСЬАКЕ_1КТЕШ?АСЕ (хпдХпйег€асе, "сот.тузо^. АррИсаЫоп. ЗЪгхпд1п1:ег^асе/1.0" ) #епдх^ 7/_хпСег^асез_И— Интерфейс — это класс, который содержит только чисто виртуальные опре- деления методов (листинг 43.9). В нашем случае, приложение предоставляет ТОЛЬКО ОДИН интерфейс — ЗЁгхпдШЁегаГсе, из названия которого ясно, что он предназначен для операций над строками. Этот интерфейс объявляет два прототипа методов: орегаЫопз () — для получения списка операций рас- ширения и орегаЫопО —для вызова операций над строками. Виртуальный деструктор нам нужен для того, чтобы С++ не выдавал предупреждающее сообщение о том, что класс, имеющий виртуальные методы, не имеет вирту- ального деструктора.
Идентификация интерфейса должна быть задана при помощи макроса 0_оесьаке_1ытекгасе (), в котором необходимо указать строку-идентификатор, для которой МОС должен сгенерировать метаинформацию. С ее помощью объект класса овтидьоааег проверяет версию расширения и другую инфор- мацию, заданную в этой строке. Строка идентификатора состоит из четырех компонент, разделенных между собой точками: □ домен создателя интерфейса; □ имя приложения; □ имя интерфейса; □ номер версии. # 1 _Р1ид1пзЮ1пдох_11_ #де^1пе _Р1идхпзМ1п6о\<_Ь— #1пс1ис1е <0Ма1пШпдоУ7.11> #1пс1и6е "хпЪег^асез.Ь" с1азз ОЬаЬеХ; с1азз ОМепи; / / ================================«======= с1азз Р1ид1пзМ1п<Зою : риЬИс 0Мад.пйЛпс1оте { 0_ОВЛЗСТ рг^аЪе: 0ЬаЬе1* ш_р1Ы; ОМепи* ш__ртпиР1ид1пз ; риЬИс: Р1ид1пзШпс1ою(0М1с1де^* раддЪ = 0); 1оадР1ид1пз( ); уо1с1 асИТоМепи (ООЬдесП* роЬэ); ргоЬесЪеб з1оПз: з1о1:ЗЬг1пдОрега1:1оп(); }; #епсН С / /_Р1ид1п5Ю1п<Зо™_
Класс основного окна приложения р1ид1пзМ1паою унаследован от класса ОМагпШпаою (листинг 43.10). Это сэкономит нам время при работе с меню и компоновками. Р1ид1пзЮ1паою:: Р1ид1пзИ1паоу7 (0Ю1адеЪ* рзддЁ/ *=0 * /) : (рюдС) { ш_р1Ы = лею 011аЬе1(”Ё111з 13 ЪЪе СезЪ ^ех^"); пс_ргапиР1ид1пз = лею ОМепи (" &Р1ид1пзОрегаЁ1опз"); 1оааР1ид1пз<); зеИСепСгаТМадеЪ (тп_р1Ъ1) ; шепиВаг () ->аскЗМепи (щ_ртпиР1ид1пз); В конструкторе класса, приведенном в листинге 43.11, создаются виджеты надписи и меню. Виджет надписи (указатель ш_р1Ы) вносится в рабочую об- ласть приложения, а меню (указатель т_ртпиР1ид1пз) добавляется вызовом метода ааамелиО к основной строке меню. Вызов метода 1оаар1ид1пз () производит поиск и загрузку расширений. Vо^а Р1ид±пзЮ1паою:: 1оааР1ид1пз () { ОЭхг а1г(0Арр11саЪ1оп:: аррИсаЁ1ОпО1гРа1:11 ()) ; (’ а1г.са(”р1ид!пз")) { ОМеззадеВох: :сг1Ыса1 (0, , ”р1ид!пз сИгеологу аоез поЪ еххзЪ"); геГигл; } ^огеасИ (ОЗЪгхпд з^хРИеМате, а1г.елЪгуЬхзЬ (ООхг: :Р11ез)) { 0Р1ид1пЬоааег 1оааег (а1г. аЬзо1иЪеР11еРа1:Ъ (зЪгРИеЫате)) ; ааатоМепи (доЬзесЪ_сазЪ<0ОЬдес1:*> (1оааег. хпзЪапсе ())); } } Мы хотим использовать в приложении все возможные расширения. При за- грузке расширений мы исходим из того, что они находятся в каталоге р1и§1П5,
в котором мы ищем все файлы расширений. Для этого мы используем класс <2с1г (листинг 43.12). Найденные файлы передаются в конструктор класса 0Р1ид1пьоадег. Затем указатель, возвращенный из объекта о₽1ид1пьоадег вызовом метода 1пзеапсе(), преобразуется к типу указателя на ооьэесе и передается в метод ааатомегш (), как возможный кандидат для добавления операций расширения к пунктам меню. Р1ид1пзИ1пдозд::аддТоМепи(ООЬэесЬ* роЬ]) { (!роЬз) .{ г геСигп; } ЗСг1пд1пЬегСасе* р! = доЬ]есС_сазС<8Сг1пд1пСегГасе*>(роЬэ); 1Г (р!) { ОЗЪгХпдЫзЪ 1зССрегаС1спз = р1->орегаЫопз (); ГогеасЬ (ОЗЬгЬпд зСг, 1зС0регаС1опз) { ОАсЬхоп* расе = пей 0АсЬ1оп(зСг, роЬЛ; соппесЬ(расе, ЗКЗЫАЬ(Сг1ддегес1 ()), еЫз, ЗЬОТ (зХоезегхпдОрегаСгоп ()) ); щ_рпшиР1ид1пз->ад<аАсе1оп(расе); } } } В методе аддтомепи () первым делом проверяется допустимость указателя, то есть его неравенство нулю (листинг 43.13). Если указатель равен нулю, мы просто выходим из метода. В противном случае мы проверяем, имеется ли нужный нашему приложению интерфейс в расширении, используя шаблон- ную функцию доЬзесе_сазе<зег1пд1пеегСасе*>. Поддерживаемых интер- фейсов может быть несколько, и при помощи доЬэесе_сазе<т> можно отли- чить один от другого. Если проверка на поддержку интерфейса прошла удачно, то мы, вызывая метод орегаЫопз (), опрашиваем список всех пре- доставляемых расширением операций и сохраняем их в переменной 1зсорегас1опз. Затем для каждой операции создается объект действия, в кото- рый передаются название операции и указатель на объект, являющийся расшире-
нием. Созданный объект действия соединяется со слотом З1оъзъг1пд0регае1оп () и добавляется в меню. \го1с1 Р1ид1пзН1пс1ои7:: з1оСЗЬг1пдОрегаЫоп () { ОАсСхоп* расе = доЬ]есС_сазС<ОАсЫоп*>(зепдег()) ; ЗСгХпдТпЬегСасе* р! = доЬ5есе_сазе<зег1пд1пЪег€асе*>(расС->рагепЪ()); т_р1Ы->зеЬТехС (р1->орегаС1оп (щ_р1Ы->еехе (), расС->Ьехе ())) ; } В слоте, приведенном в листинге 43.14, метод зепаег () возвращает указатель на объект, выславший сигнал. Этот указатель приводится к типу указателя на 0Асс1оп. Вызовом метода рагепе () из объекта действия мы получаем указа- тель на объект расширения. Воспользовавшись методом орегаЫопО, мы производим действия над текстом виджета надписи. В этот метод мы переда- ем в текст виджета надписи и название применяемой операции, которое со- ответствует названию объекта действия. Создание расширения для приложения Теперь, когда мы имеем приложение, поддерживающее систему расширений, самое время создать для него хотя бы одну компоненту расширения. ТЕМРЬАТЕ = 1хЬ СОЫГ1С += р1идхп ОТ -= дих ЛЕЗТЛ1К = . . /р1идхпз ЗОШСЕЗ = МуР1идхп. ерр НЕАЛЕНЗ = МуРЬидхп.Ъ \ ../АррХхсаЬхоп/хп^ег^асез.к таксет = шур1идхп Для создания расширения в секции соырю рго-файла необходимо добавить опцию р1ид1п, а также включить в секцию неаоейз файл интерфейсов при- ложения 1п1егГасе8.Ь (листинг 43.15).
#1Ь1с1е1: _МуР1ид1п_Ъ._ #де^1пе _МуР1ид1п_к_ #1пс1иде <ООЬэес^> #1пс1иде •../АррХхсаПхоп/хпЁег^асез.к" // ========г===============================гг=====:===========г=^============= с1азз МуР1идхп : рйЬИс ООЬдес!:, риЬНс ЗЪгхпдТпЪег^асе { ОьовггЕСт 0-1ЫТЕВГАСЕ5(ЗЪгхпдТпЪег^асе) ргллгаЪе: ОЗегтпд оДсЮррег (сопзЬ 03Ёг1пд& з!:г) ; риЬНс: V^^^иа1 ~МуР1ид1п{); V^^Ёиа1 ОЗЪгхпдЫзЬ орегаЪхопз ( ) сопз!:; V^^Сиа1 ОЗЪгхпд орегаСхоп (сопзЪ ОЗЪгхпдй, сопзЪ 03Ьг1пд&); }; #епс!1^ / О*уР1ид1п„11— Наш класс расширения муР1ид1щ показанный в листинге 43.16, наследует сразу два класса: ооьэесе и зъгхпдшеегСасе. Добавление макроса 0_1ытекрасе5 () нужно для того, чтобы МОС сгенерировал всю необходимую для расширения метаинформацию. Виртуальный деструктор нам нужен для того, чтобы компилятор С++ не жа- ловался на то, что класс, имеющий виртуальные методы, не имеет виртуаль- ного деструктора. /*ухгЬиа1*/ ОЗЬгхпдЫзЬ МуРХидхп: :орегаЫопз () сопзС ( геЬигп ОБСггпдЫз): () « "одсЮррег* « ”1о«егп; } Метод, приведенный в листинге 43.17, возвращает список поддерживаемых расширением операций. В нашем случае их две: оааиррег и 1о«ег.
ОЗегхпд МуР1идхп::о<±30ррег(сопзГ 05Ёг1пд& зЪг) { 05Ъг1пд зЪгТетр; Гог (1пС 1=0; 1 < зПг.1епдГИ(); ++х) { зСгТешр += (1 % 2) ? зСг.аЪ(1) : з1:г.аЪ(1) .ЬбПррег() ; } геЬигп зГгТелпр; } Метод оааиррегО, приведенный в листинге43.18, нам уже знаком. Мы ис- пользовали его для нашей динамической библиотеки (листинг 43.3), а теперь мы используем его в нашем расширении. /^1г^иа1*/ ОЗГгхпд МуР1ид1п::орегаЁ1оп(сопзЪ 05Сг1пд& зЪгТехС, сопзё 03ег1пд& зСгОрега^хоп ) { ОЗЪгхпд зСгТетр; х€ (зГгОрега^Гоп == “оаапррег”) { зЪгТетр = оаапррег (зЪгТехё) ; } е1зе И (з€г0регаС1оп == "Тсмег") ( зСгТетр = зЬгТехЪ. ЬоЬошег (), ; } е!зе { феЬидО « "ИпзиррогХеа орегаЫоп"; } геГигп зЁгТегпр; В листинге 43.19 приведен метод орегаШоп (), который получает текст и имя операции. Этого достаточно, чтобы вызвать нужную реализацию, отвечающую за проведение операции. Если имя операции будет не найдено — приложение выдаст сообщение о том, что данная операция не поддерживается. В завершение, для создания нашего расширения в конце срр-файла мы долж- ны экспортировать интерфейс при помощи макроса с_ехрокт„ршс!Ы2 ().
Макрос о_ехрокт_ршс1Ы2 () задает точку входа нашего расширения, что де- лает его доступным для Ос 0_ЕХРОКТ_РШС1Ш (5Ьг1пд1пСег€асе, МуРХидхп) Резюме Динамическая библиотека содержит код, который может использоваться сразу несколькими приложениями. В отличие от статических библиотек, код, содер- жащийся в динамической библиотеке, не включается в основной код приложе- ния, а находится в отдельном файле. Если при исполнении программы произ- водится вызов функции из динамической библиотеки, то в память компьютера загружаются только нужные функции. Это позволяет сэкономить объем диско- вого пространства и оперативной памяти, поскольку общедоступный код нахо- дится в отдельном файле, совместно используемом программами. Ос предоставляет все необходимые средства для создания и загрузки динамических библиотек. При помощи функции сезоне () можно получить адрес нужной функ- ции. После приведения адреса к нужному типу можно производить его вызов. Расширение — это динамическая библиотека, реализующая специальный ин- терфейс. Ос предоставляет возможность реализации двух типов расширений: □ расширения для самой библиотеки; □ расширения для собственных приложений. Для расширения самой библиотеки, Ос предлагает классы для реализации драйверов баз данных, создания стилей и др. Чтобы создать свое собственное расширение, нужно унаследовать один из этих классов и переопределить все необходимые виртуальные методы. Для поддержки расширений собственных приложений, Ос предоставляет класс 0Р1ид1пЪоааег, который способен как загружать, так и выгружать расширения. Приложение, поддерживающее расширения, должно предоставлять хотя бы один интерфейс, посредством которого будет производиться коммуникация с компо- нентой расширения. Созданный интерфейс должен быть зарегистрирован при помощи макроса о_оесьаке_1мтекгасе. В приложении, основываясь на метадан- ных, можно проверить, реализует ли расширение нужный нам интерфейс, ис- пользуя шаблонную функцию приведения типа доЬзесь_сазь<т>. Классы расширений должны наследоваться от интерфейсов, предоставляе- мых приложением, и реализовывать их. Поэтому при реализации расширения его необходимо унаследовать сразу от двух классов: ооьэесе и класса интер- фейса, предоставляемого приложением. В классе определения расширения необходимо разместить макрос о_1Мтекеасее, чтобы препроцессор МОС смог сгенеоиоовать метаданные.
Глава 44 Совместное использование 01 с платформозависимыми АР1 "Алиса рассмеялась. — Это не поможет!,— сказала она. — Нельзя поверить в невозможное! — Просто у тебя мало опыта, — заметила королева. — В твоем возрасте я уделяла этому полчаса каждый день. В иные дни я успевала поверить в десяток невоз- можностей до завтрака". Льюис Кэрролл. "Алиса в Зазеркалье" Несмотря на то, что (^1 предоставляет практически весь инструментарий, необходимый для реализации программ, иногда возникает необходимость в использовании технологий, связанных с конкретной платформой, или реа- лизации кода низкого уровня. Прибегать к написанию платформозависимого кода нужно только в случаях острой необходимости. Помните, что если про- грамма рассчитана на несколько платформ, то эти функции придется реали- зовывать для каждой поддерживаемой платформы. По возможности, реко- мендуется весь платформозависимый код объединять в динамических библиотеках (см. главу 43) для отделения его от платформонезависимого. Но можно производить и смешивание, а чтобы на каждой из систем была задей- ствована только нужная часть кода, следует воспользоваться директивами препроцессора, определенными специально для каждой из них. Участок про- граммы, поддерживающей несколько операционных систем, может выгля- деть следующим образом: ае11пеа(0_ИЗ_Н1Н) //Реализация для Ихпйошз #е11Е йеЕ1пе<й(0_ИЗ_Х11) //Реализация для Цпхх #еН€ деЕ1пед(0_ИЗ_МАС) //Реализация для МасОЗ
#е1зе //Не поддерживается #епд1^ Также позволяет распознавать и саму платформу, в табл. 44Л указаны некоторые из препроцессорных определений. Таблица 44.1. Препроцессорные идентификаторы платформ Константа Описание 0_08_А1Х А1Х (Аскапсес! 1п(егас1ке еХесШке) — операционная система, базирующаяся на 0Ы1Х 8уз1ет V, разработанная фирмой 1ВМ 0_ОЗ_РКЕЕВЗО ГгееВ80 — 11Ы1Х-подобная операционная система, унаследо- ванная от ММIX разработки фирмы АТ&Т 0_08_1К1Х 1А1Х — операционная система 1ЛМ1Х от фирмы 861 (8Шсоп СгарЫсз) 0_05_ЫШХ □пих — платфомонезависимая многопользовательская операци- онная система, похожая на (ЛЧ1Х 0_08_МАС Мас 08 X — операционная система от фирмы Арр1е для компью- теров Мас1п1озй <2_03_30ЬАК13 8О1_АН18 — сертифицированная операционная система 1ЛЧ1Х, разработанная фирмой 8ип М|сгозуз1етз 0_03_И1Ы32 32-битная операционная система \АЛпс1оууз от фирмы МюгозоЙ 0_ОЗ_И1Ы64 64-битная операционная система У71Пбо«/з от фирмы МюгозоЙ Совместное использование с УУ1пс1оуу8 АР1 Программа, окно которой показано на рис. 44.1, демонстрирует возможность использования функций СЯЭ1 (СгарЫса! Оеу1се 1п1егГасе, интерфейс графиче- ского устройства) для графического вывода в ОС \У1пс1о\У8. При иажатии правой кнопки мыши в области окна приложения вызывается окно сообще- ния посредством ХУтдохуз АР1.
с1азз М1ПАР1 : риЬИс ОШддеЕ { ргоСесСеск у!гСиа1 Ьоо1 юхпЕуепС (М5С* ртзд, 1опд* гези1С) { 05,Пг1пд зСг1 = "Жпскэюз Уегзхоп = " + ОЗСгхпд: :питЬег (05уз1пГо: :М1пс1ою8Уегз1оп) ; ОЗСгхпд зСг2 = "ЮхисЗоюз Меззаде"; зшхСсЪ.(ртзд->теззаде) { сазе Ш_ВВОТТОИООИН: : :МеззадеВох(^хп1б(), (сопзС ИСНАК*) 8Сг1.иСС1б () , (сопзС МСНАК*)зСг2.иС€16(), МВ-ОК | МВ_1СОМЕХСЬАМАТ1ОЫ ); Ьгеак; с1еСаи1С: 7 } геСигп ОЮхддеС: :юхп^епС (ртзд, гезиТС) ; V^^Сиа1 уо1а рахпСЕ^епС (ОРагпСЕуепС*) { ОРахпСег рахпСег (СЫз); НОС Мс = ра!пСег. рахпСЕпдхпе () ->деСОС (); НВКП5Н ЫВгизЬ = : :СгеаСе5оИ<ЗВгиз11(КСВ(255, 0, 255)); НРЕМ НРеп = : :СгеаСеРеп(РЗ_5ОЪЮ, 2, КСВ(0, 0, 128)); ОЗСгхпд зСг - “ОО1 Огаюхпд" ; ТЕХТМЕТК1С Ст; : :СеСТехСМеСг1с8 (Мс, &Ст) ; :: 5е1есСОЬ}есС (Мс, ЬВгизк) ; 23 Зак. 3983
::Зе1есЬ0Ъ] есС(Идс, ИРеп); : :Е1Ирзе (Ъдс, 0, 0, и1дЫг(), ИехдЪЬО); :: ТехСОиС (Идс, >п.дЫ1() / 2 - (ет.ЬпЛуеСНагНхдЬИ * зЪг. 1епдЫ1 ()) / 2, (Ъе1дЬе() - ип.ШНегдкЬ) / 2, (сопзС ИСНМг*)зЪг.иСЕ16(), з(?г. 1епдЬИ () ); : :Г>е1еЪеОЬэесЬ(11ВгизЪ) ; ::Ое1еЪеОЬ5есЪ(ЪРеп); рагпьег.разлЪЕпдхпе()->ге!еазеОС(Идс); } риЬИс: Н1пАР1(0И1ддеЬ* ридЪ = 0) : ОМ1ддеЪ(рмдЬ) { } }; В листинге 44.1 приведен специальный метод обработки событий для ОС ХУшдоу/х—шхпЕуепе (). Если не требуется дальнейшей обработки события с помощью 0^, то из этого метода нужно вернуть значение ъгие. Реализация этого метода, по своей сути, очень похожа на реализацию оконной функции ОС ХУшдо\У8. В нашем случае, отслеживается событие нажатия правой кноп- ки мыши и, когда оно происходит, вызывается функция меззадевохо ХУш- дои'к АР1, отображающая окно сообщения. Первым параметром в эту функ- цию в качестве родительского окна передается значение, возвращаемое методом »1п1д(). Метод поддерживается для всех платформ и, в случае ОС ХУпмклмз, возвращает идентификационный номер окна, соответствующий типу итого (указатель на окно). В методе события перерисовки ра1псЕуепь () надпись и эллипс отображают- ся при помощи функций СЮ1. Обратите внимание на вызов метода десос () объекта класса 0Ра1пеЕпд1пе (листинг 44.1). Он возвращает значение типа нос (указатель на Оеугсе Соп(ех1, контекст устройства), который нужен функциям 601, чтобы они могли проводить рисование. По окончании рисо- вания мы вызываем метод ге1еазевс () объекта класса ОРахпеЕпдхпе и пере- даем в него дескриптор контекста 601 (переменная нас), чтобы закрыть кон- текст устройства.
Совместное использование с Ыпих Так же как и в случае ОС АУтдокуз, в ЦЫ1Х 01 предоставляет возможность доступа к событиям на низком уровне. Класс ои1адеъ содержит метод хИЕ^епсо, который необходим для получения событий оконной системы X АУтдочу. Чтобы получать события, этот метод нужно переопределить. Если после завершения метода не требуется продолжать обработку события мето- дами 01, то из этого метода нужно вернуть сгие. Резюме 01 допускает возможность использования в своих программах платформоза- висимого кода. Это может быть полезно для реализации программ, исполь- зующих возможности, не предоставляемые библиотекой 01.
Гп- нП Щ Глава 45 010е81дпег. Быстрая разработка прототипов "Наш век беспокойства в значительной мере является результатом попыток выполнить сегодняшнюю работу вче- рашними средствами..." Маршал Мак-Люган Программа 0сОе81§пег— это средство быстрой разработки приложений (Каргё АррНсайоп Г)еуе1ортеп1, КАЛ). Прежде всего, этот инструмент пред- назначен для дизайнеров и принцип его работы отвечает принципу (ХУЬаС Той 8ее к ХУЬаС Той бег, "что видишь, то и получишь"). Он предоставляет возможность быстро создавать прототипы приложений, которые базируются на диалоговых окнах, а также могут иметь главное окно, меню, строку состояния и панель инструментов. Созданные в 0сГ)е81§пег файлы описания интерфейса можно конвертировать в исходный код на языке С++. Кроме виджетов, уже содержащихся в библиотеке 01, 01 Оезцщег мож- но дополнять виджетами, созданными самим разработчиком. В этой главе для демонстрации возможностей Ос Оезйдпег мы создадим про- стое приложение, позволяющее изменять значения виджета электронного индикатора оьспышпЬег. Создание новой формы в 010е81дпег Окно программы Ос Ое81§пег содержит меню и панели инструментов. Панель инструментов предоставляет общие операции для редактирования формы: скопировать, вставить и т. д., а также и режимы редактирования и размеще- ния, о которых речь пойдет далее. Эти команды также доступны и в основ- ном меню.
На рис. 45.1 показано окно <21Резц’пег. Слева расположено окно Вох (Виджеты), в котором можно найти объекты компоновки и сами виджеты, сгруппированные в отдельные категории. Именно из этого окна, посредством перетаскивания, добавляются элементы на форму. Справа расположены сразу пять следующих окон: □ ОЬ]ес1 1п$рес1ог (Объекты) — список используемых виджетов. В этом окне их можно выбирать для последующего изменения. Например, при помощц РгореСу ЕсН1ог (Редактор свойств) изменять их свойства; □ РгореНу Е<Шог (Редактор свойств) — ряд свойств выбранного виджета. Это могут быть цвет фона, шрифт, максимальный/минимальный размер виджета и т. д.; □ 81§па1/81о1 ЕйНог (Редактор сигналов и слотов) — окно редактирования соединений сигналов со слотами; □ Кеаоигсе ЕсШог (Редактор ресурсов) — с его помощью можно загрузить существующий ресурс или создать новый; □ Асбоп Е(Н1ог (Редактор действий) — предназначен для создания, удале- . ния и управления действиями команд создаваемой формы. Ги Ьу..- ф Бл:' ‘Ж.' Бл ал '(«м "3 '*•’ * < Т-^ *ч «дат л*- Ев«к "2 -с • < 1м В ь"' и Рис. 45.1. СП Оезюпег
После запуска программы в диалоговом окне Кем? Гопи (Новая форма) будет предложен на выбор один из шаблонов (рис. 45.1). Если вам не нужно ото- бражение этого окна при последующих запусках, то следует убрать флажок 8Ьом? Шя 1Иа1о8 оп 81аг<ир (Показывать это окно при старте). Впоследст- вии, для вызова этого диалогового окна можно выбрать в меню команду ЕИе | Кечт Гопп... (Файл | Новая форма) или нажать комбинацию "горячих" клавиш <С1г1>кТ>. В диалоговом окне Кем? Гопп (Новая Форма) предлагается четыре варианта создания форм. Выберите опцию ЮТ(1&е< (Виджет), после чего будет создано новое окно виджета (рис. 45.2). Рис. 45.2. Новое окно виджета По умолчанию в 01 1)еы§пег установлен режим редактирования виджетов формы. В 01 Ое81§пег этих режима четыре (см. табл. 45.1).
Таблица 45.1. Режимы редактирования Название Описание Вид ЕсЯН МГШде^в (Редактирование виджетов) В этом режиме можно изменять внешний вид формы, добавляя в нее, например, виджеты и компоновки или отредактировав свойства каждого виджета ЕбЙ 81дпа1з/51о1з (Редактирование сигналов/слотов) Этот режим позволяет соединять виджеты для об- мена сообщениями ЕсМ ВискКез (Редактирование поручений) В этом режиме виджет может быть "поручен* видже- ту надписи. Это делается для того, чтобы этот вид- жет получал фокус тогда, когда его получит элемент надписи ЕсШТаЬ Отбег (Редактирование порядка следова- ния табулятора) Этот режим служит для установки порядка, в кото- ром виджеты получают фокус клавиатуры при нажа- тии на клавишу табуляции —— Ы При помощи окна РгореНу Е<Шог (Редактор свойств), расположенного спра- ва, в поле мгнккигТШе (Заголовок окна) можно изменить заголовок окна виджета Гога, например, на "ПевцресГООДгеГ (рис. 45.3). Нелишним будет задать имя в поле оЬ|ес1Нате (Имя объекта), которое будет использовано при его соединении с сигналами и слотами других компонент. Для нашей формы укажем имя "МуЕопп". Примечание При обработке утилитой ию икфайла будет создан код на С++, в котором имя формы будет использовано для имени класса, а имена размещенных на ней элементов — для названий атрибутов этого класса. Редактор свойств отображает свойства выделенного виджета, расположенно- го на форме, а если ни один из виджетов не выделен, то отображаются свой- ства самой формы. Поэтому для редактирования нужного виджета его нужно сначала выделить. Для сохранения созданного диалогового окна выберите команду меню ЕНе | 8ате Гопп Аб... (Файл | Сохранить форму как) или нажмите комбинацию "гооячих" клавиш <С1г1>+<8>.
епаоЫ Ргорейу УаЬе МуЬп-п <+ жеРойсу |+| гпаотэтйе ® §ие1псгетеп( Ж Ьа^е ра1еИе Ш <сг+ !ме р. 0. 4К.ЗК] [Рг^ете.1 Рге<е,т Ю-01 5. 16~ 13.05 1МСГС50Й 3 сиг&ог III Ю.:: №1 том5еТгаскг.д Ьсш Ройсу сотехШепцРйсу ассерШгерв <а;ве Ог^сгосиз ОьЭеШОпш ! ЧП*-. -« гч > ( 1Л1Г | ’лтпсктюоп {13 ГозЗТр ® МгйшПр Рис. 45.3. Окно свойств. Изменение заголовка окна виджета Добавление виджетов Чтобы добавить в диалоговое окно новые виджеты, нужно воспользоваться окном виджетов МОДгеС Вох (Виджеты) (см. рис. 45.2). Выберите нужный вам виджет, нажмите на нем левую кнопку мыши и перетащите его в область формы виджета окна на нужное вам место. Итак, перетащите с закладки ВиНоп8 (Кнопки) две кнопки Ри$Ь ВиМоп (Кнопка), с закладки 1при1 (Виджеты ввода) — НопгопСа! 8П(1ег (Горизонтальный ползунок), а с закладки О1яр1ау (Виджеты отобра- жения)— ЬСОМшпЬег (Электронный индикатор) и разместите их в окне формы. Обратите внимание, что размещенные на форме виджеты стали вид- ны не только в ней, но и в окне ОЬ]есС 1п8рес1ог (Объекты), расположенном справа (рис. 45.4). В его окне виджеты отображаются в иерархической форме. Выберите, щелкнув левой кнопкой мыши, в окне ОЬ]ес11п$рес(ог (Объекты) или на самом диалоговом окне, одну из созданных кнопок. Введите в поле
1ех1 (Текст) окна РгореНу ЕбНог (Редактор свойств) текст "&Ке8е1", а в поле оЬ]ес1Кате (Имя) — "т_рстс1Ке8еГ. Повторите ту же операцию со второй кнопкой, но в поле оЬ]ес1№ате (Имя) введите "т_рспк1()ий", а в поле <ех1 (Текст) — "&Оий". Изменения, вносимые в поле оЬ]есСМате (Имя), исполь- зуются в дальнейшем для различения виджетов: это их имена, которыми вы будете пользоваться. Переименуйте также ползунок и индикатор (см. рис. 45.4). Само окно формы должно выглядеть примерно, как на рис. 45.5. |Ор!ес1 (пзреЛог С'о.«с: п'__рзй - -.|5 х Оазз О'ЛМзде! ОЮВНитЬег ОРизИБиКоп СРизпВцЦоп □Шзег Рис. 45.4. Окно ОЬ]ес11п$рес1ог (Объекты) |у. Ое51дпес1Л|с1де1- ипгл1ес! Еезе! { Си< * Рис. 45.5. Форма с добавленными виджетами
Компоновка (1ауои() После того как все нужные виджеты добавлены, можно заняться их размеще- нием. Для этого поместите указатель мыши в область диалогового окна, на- жмите левую кнопку и, не отпуская, переместите указатель. Вы увидите рам- ку. Охватите этой рамкой виджет ползунка' и виджет электронного индикатора. Отпустите левую кнопку мыши, и эти виджеты будут выделены. Нажмите на выделенных элементах правую кнопку мыши, и вы увидите кон- текстное меню, в котором выберите команду меню Ьау ои< | Ьау ои( УегйсаНу (Размещение | Разместить по вертикали) или просто нажмите комбинацию "горячих" клавиш <С1г1>+<2>. Проделайте то же самое с кнопками Ке$е< (Сброс) и ОиЙ (Выход). В места, которые должны оставаться свободными, следует поместить запол- нитель пространства ($расег). Для этого выберите в окне Вох (Вид- жеты) на закладке 8расегв (Заполнители) УегПса! 8расег (Вертикальный за- полнитель) и перетяните его на лейаут под самую нижнюю кнопку. У вас должно получиться так же, как показано на рис. 45.6. Рис. 45.6. Вертикальное размещение виджетов Последний, завершающий штрих — укажите диалоговому окну размещать свои элементы в горизонтальном порядке. Для этого щелкните на одной
из пустых областей диалогового окна указателем мыши или выберите МуВ1а1о§ в окне ОЬ]ес11п5рес(ог (Объекты). Затем нажмите правую кнопку мыши и выберите, из контекстного меню, команду Ьау ои11 Ьау оиС Ноп2оп1а11у (Размещение | Разместить по горизонтали) или нажмите комбинацию "горя- чих" клавиш <Ог1>+<1>. Окно изменится и примет вид, показанный на рис. 45.7. Теперь при изменении размеров окна размеры и позиции виджетов будут автоматически изменяться, заполняя всю рабочую площадь. Рис. 45.7. Горизонтальное размещение вертикальных групп виджетов Порядок следования табулятора Порядок следования табулятора нужен для навигации по окну при помощи клавиатуры. Нажатие на клавишу <ТаЬ> перемещает фокус от одного видже- та к другому. Для определения порядка следования табулятора нужно уста- новить соответствующий режим, для чего выберите команду меню Е<Ш| ЕШ1 ТаЬ ОгДег (Редактирование | Порядок следования) или нажмите кнопку этой команды на панели управления. Окно примет вид, показанный на рйс. 45.8. Нажимая указателем мыши на виджеты, обозначенные голубыми прямоугольниками, можно менять порядок следования табулятора.
|», Се5:дпесГЛ'1йде1 - ипШ1есГ Рис. 45.8. Порядок следования табулятора Сигналы и слоты Для соединения сигналов одного виджета со слотами другого нужно устано- вить соответствующий режим, для чего выберите команду меню Е<Ш | Едй 812па18/81оЬ (Редактирование | Редактирование сигналов/слотов) или нажмите <Р4>. Для того чтобы произвести соединение, просто задержите указатель мыши на нужном вам элементе до тех пор, пока он не будет выделен, а затем нажмите левую кнопку и переместитесь к нужному элементу, с которым должно быть осуществлено соединение. Вы увидите красную стрелку, показывающую в его сторону. Давайте задержим указатель на горизонтальном ползунке, на- жмем левую кнопку мыши и переместим его в сторону виджета электронного индикатора. После отпускания будет показано диалоговое окно СопП^иге Соппесйои (Конфигурация соединения), предлагающее выбрать сигналы и слоты для связываемых виджетов. Выберите в левой колонке сигнал уа1иесйапдеа(1пс), а в правой — а1зр1ау (1пс) (рис. 45.9). Чтобы закрывать окно виджета нажатием кнопки 0ш1 (Выход), необходимо соединить эту кнопку со слотом формы с1озе (). Выделите эту кнопку, на-
жмите левую кнопку мыши и переместите ее указатель в свободную область формы, но не отпускайте, пока не увидите большую стрелку. Ваши соедине- ния должны выглядеть так, как показано на рис. 45.10. Рис. 45.10. Соединения
Отпустите кнопку мыши и выберите в правой колонке диалогового окна Сопй§иге СоппесИоп (Конфигурация соединения) сигнал сИскейО. Для того чтобы соединить его со слотом с1озе (), нужно сперва отметить флажок 8Ьом' а11 апд $1о<$ (Показать все сигналы и слоты), в результате чего будут показаны сигналы и слоты унаследованных классов. Чтобы полюбоваться на созданный нами виджет в действии, выберите ко- манду меню Гопп | Ргетает» (Форма | Просмотр). Теперь вы можете поэкспе- риментировать с изменениями положения ползунка, которые приведут к из- менению информации, отображаемой виджетом электронного индикатора. Можете проверить установленный порядок следования табулятора и поведе- ние лейаутов при изменении размеров основного окна. Если вы остались довольны просмотром виджета, то вам остается только сохра- нить его. Для этого вам нужно выбрать в меню команду Р11е 18ауеРогт Аа... (Файл | Сохранить Форму как...) и выбрать в диалоговом окне путь для со- хранения формы и задать ее имя, например: МуРгот.ш. Использование форм в проектах Использование форм в проектах — это и есть то, ради чего мы их создаем. И для этого (21 предоставляет три способа. Итак, первый способ называют прямым (сНгес1 арргоасЬ) — он является са- мым простым. Все, что нам нужно — это создать виджет и установить в ме- тоде п1: :Му?огш: гзеЬироК) созданную нами форму так, как это показано в листинге 45.1. #1пс1ис1е ми1_МуРогп1.}1" #1пс1иде <0ЪОи1> 1пС ша1п(1пЬ агдс, сИаг* агду[]) { 0Арр11саЬ1оп арр(агдс, а^дV) ; ОИхддеЪ* Сот = пею ОМхддее,- П1: :МуГопп их; их. зе^ирП! (Сот); Сот->зЬоте () ; генита арр.ехес(); }
Но недостаток такого подхода очевиден — мы не в состоянии инициализиро- вать данными и дополнять кодом используемый класс. Если в вашей форме есть, например, виджеты, которые нуждаются в соединении со слотами, реализованными другими классами проекта, то данный способ — не для вас. Для нашего конкретного случая он не подходит, потому что нам нужно реа- лизовать слот, с которым будет соединена кнопка Ке«е1 (Сброс). Второй способ реализуется при помощи наследования (шИеп1апсе арргоасЬ). В этом случае мы наследуем класс от класса, за базу которого была взята на- ша форма, в нашем примере это оихадее (листинг 45.2). #1пс1иде "их-ЗЯуГохпцЬ" с!азз МуРогтп : риЬИс ОМхбдее { О.ОВОЕСТ рг : Их::МуРогт т_и!; риЬИс: МуКогт(0&Мдее* радд1: = 0) : ОИхддеЪ(риде) { псих. зеСирПх (Йпз) ; соппесС (щ_и1.щ_спкЗКезеЪ, ЗТСКАЬ (сИскес! () >, ЗЬОТ (зХоЪКезеЪ ())); } риЫхс з1оЬз: тохс! з1оИ<езе1:() { щ_их .т_з16->зеЬУа1ие{0); т_их .щ_1сд->сИзр1ау {0) ; } Мы используем форму их: :МуРогт в качестве атрибута нашего класса и уста- навливаем ее В конструкторе С ПОМОЩЬЮ метода Пх: -.МуРогт: : зеСирПх (). Мы соединяем кнопку КеяеС (Сброс) (указатель т_сшкезее) с реализованным на- ми слотом зХоьпезеъ (). И если бы мы вдруг пришли к выводу, что наша форма нуждается еще в паре элементов, то мы бы просто добавили их при помощи ()1 Ре81§пег, после чего реализовали бы недостающие слоты в нашем
классе. То есть дизайн графического интерфейса отделен от программной реализации. Теперь давайте перейдем к последнему — третьему способу, который реали- зуется при помощи множественного наследования (тиШр1е тЬепСапсе арр- гоасЬ). По своей сути он очень похож на второй способ, но его достоинство заключается в том, что мы можем напрямую обращаться ко всем виджетам формы. Листинг 45.3 иллюстрирует этот способ. #1пс1ис1е "и1_МуЕопп.11" с!азз МуЕогт :-риЬНс 0Ю1с1де1:, риЬНс П1:;МуЕогт { 0_ОВОЕСТ риЬНс: МуЕогт(ОМШдеЪ* радд1: = 0) : ОШйдеЪ(р«дЪ) . { зейирТН (йпз) ; соппесЪ (щ_стЖезее, ЗКЗЯАЬ (сНскей ()), ЗЬОТ (з1оЬКезеЪ ())) ; } риЬНс з1осз: уо1с! з1о№езеЁ() { т_з1а->зе1:Уа1ие {0) ; т_1сд->с11зр1ау (0); } }; #епд15. / /_ХуРогщ__Ь_ Как видно из листинга 45.3, теперь наш класс МуЕогт наследуется не только от дм1адеъ, но и от класса формы, созданной нами в Веы^пег. Компиляция Последнее, что осталось сделать — откомпилировать проект с использовани- ем формы. Первым делом надо создать проектный файл и не забыть вклю-
чить все используемые формы в секции рокмз. В нашем случае она только одна, и рго-файл должен выглядеть вот так: ТЕМРЬАТЕ = арр НЕАСЕКЗ += МуРогт.11 РОКМЗ += МуРогт.И! ЗОПКСЕЗ += тахп.срр Ю1П32 : ТАКСгЕТ = . ./МуРопп После этого в директориях, содержащих проектный файл, файл формы (МуРогт.ш) и другие исходные файлы, выполните команды создания таке- файла и компиляции: дтаке таке В процессе компиляции из ш-файла будет автоматически создан Ь-файл с префиксом ш_. По завершении компилирования будет создана исполняемая программа, отображающая окно, показанное на рис. 45.11. 1»^ - [Рг№е^] Рис. 45.11. Готовое приложение Динамическая загрузка формы Есть и четвертый способ использования форм, который в корне отличается оя трех изложенных ранее способов. Его суть заключается в том что ХМЬ репрезентация формы (ш-файл) используется в программе как есть, без до-
полнительных преобразований. При помощи специального класса оихьоааег, находящегося в модуле (^ЛЛТоок, данные репрезентации формы могут быть загружены, и в результате их интерпретации этот класс создаст соответст- вующий виджет. Рассмотрим этот класс в работе и создадим программу за- грузки созданной нами формы, функционально аналогичную программам листингов 45.1—45.3. ТЕМРЬАТЕ = арр СОЫР1С += и1Соо1з НЕАОЕНЗ = ЪоасЗМуРогт. И ЗОПКСЕЗ = таги.срр КЕЗОПКСЕЗ = гезоигсе.дгс ад!п32 :ТАКС?ЕТ - . . /ЬоабМуРопп В нашем проектном файле (листинг 45.4) должен быть подключен модуль ОЛЛТоок, это мы делаем в секции соырю. Саму форму мы располагаем в ресурсе, а ресурс подключаем к проекту в секции кезооксез. Заметьте, что секция рокмз в нашем проектном файле отсутствует, так как мы будем ис- пользовать ХМЬ-данные формы напрямую. #1пс1ибе <0АррИсаС1оп> #1пс1ис1е "ЬоасЭМуГогтп.И" хпС тахп(1ПС агдс, сЪаг** а^дV) { ОАррИсаСхоп арр(агдс, агд\г); ЬоасЗМуРогт аддЪ ; «дС. зкош () ; геСигп арр.ехес(); ) В основной программе, показанной в листинге 45.5, мы просто создаем вид- жет нашего класса ьоаамуРогт.
#х!пс1еГ _ЬоасЗМуРогпк_И_ #де^1пе _Ьоа<ЗМуРогпЦ1_ #1пс1ис1е «2Ши5.> #хпс1иае <0<1ПхТоо1з> // ====================================================================== с1азз ЬоасЗМуРопп : риЫхс ОЮхсЯде!: { 0-ОВОЕСТ ргзлгаЪе: 031хдег* щ_рз1д; ОЬСОЫитЬег* щ_р1сй; риЫхс: ЬоасЗМуРохтп(0ДО1ддеЁ* раддЪ = 0) : ОМхйдеЪ(ридН { 0ПхЬоас1ег* рих! = пею ОЛхЬоадег (Ъкхз); РРх1е Ех1е(": /МуРогт.их"); ОМхдде!:* рзддЬРопп = рих1->1оасЦ&Гх1е) ; х€ (раддЪРогт) { гезххе(рэддЬРогт->зх2е()); т_рз1й = рздд1:Гогт->€хпдС]1х1а<031х<1ег*> (г,т_з1<3") ; щ_р1с6 = рзддЬРогтп->^хпс1С1111д<0ЬСОЫи1пЬег*> (ппи1с<1п) ; ОРизИВиС^оп* рспкЗКезее = р\лгдЬРопп->^хп(ДСк11(Д<0Риз11Вие1:оп*> (''п^стЖезеС") ; согтесЪ (рстбКезе!:, 51СЫАЬ (с1хскес1 ()), ЗЬОТ (зХоЬКезеЪ ())) ; ОРиз1хВиГЪоп* рстаОихЬ = раддЪРогШ’->^хпс1Скх1д<0Ри811Ви1111оп*> ("пистдОиН:”) ; согтес!; (рстс10их<1, ЗКЭМАЬ (сЫскес! ()), дАрр, ЗЬОТ (дихЪ ())); //ЬауоиЪ зе^ир ОНВохЬауои!:* ркЬхЬауои!: = пеет ОНВохЬауои^; рЪЬхЬауои11->ас1с1М1йде(: (р^дЬРогт);
зеЬЬауоиЬСрйЬхЬауоиЬ); } } риЬНс з1оСз: уохс! аХоШегеЪО { щ_рз1д->зеЬ7а1ие(0); т_р1сб->а1зр1ау(0); } }; # еп<21 € / /_Ьоа<ЗМуРогщ_И_ Наш класс ьоаамуРогт унаследован от класса омхадеъ (листинг 45.6). Для того чтобы использовать виджеты озххаег и оъсомитЬег в отдельном слоте, мы определяем указатели в качестве атрибутов класса (ш_рз1а и т_р1са). В конструкторе мы создаем объект класса загрузки форм стНьоааег (указа- тель ри11) и передаем ему в качестве предка указатель еЫз. Далее нам необ- ходимо создать объект файла нашей формы, поэтому в конструкторе окне мы указываем имя расположенного в ресурсе файла ":/МуРопп.ш". В метод Хоаао мы передаем адрес объекта файла и получаем указатель на сконст- руированный виджет. Если попытка конструирования виджета закончилась неуспешно, а это может случиться, например, когда ХМЬ-данные содержат ошибки, то это метод возвратит нулевое значение. Поэтому мы обязаны про- верить это значение. В случае успеха мы приводим размеры виджета в соот- ветствие с размерами нашей формы рзддсропп, для чего вызываем метод гезгге (). Теперь нам нужно получить доступ к виджетам формы, и для этой цели используется шаблонный метод Е1 пасы 1 а<тип *> ("имяОбъекта"), опре- деленный в классе ооьзесъ. Таким образом мы получаем доступ к виджету ползунка (указатель пцрзха), электронному индикатору (указатель т_рса), кнопке КевеС (указатель рсшакезег) и кнопке (указатель рстаоиХь). Сиг- нал с11скеа() кнопки Кеяе1 мы соединяем со слотом зХосвезеь о, в котором устанавливаются нулевые значения для виджетов ползунка и электронного индикатора. Для завершения работы приложения при нажатии кнопки ее сигнал сИскеао мы соединяем со слотом диХе() объекта приложения дАрр. Сам сконструированный виджет ркдеРопп мы размещаем на поверхно- сти нашего виджета ьоаамугопп при помощи горизонтальной компоновки рИЬхЬауоиЪ.
Резюме Реализация пользовательского интерфейса отнимает много времени. Про- грамма Оеы^пег была создана для ускорения этого процесса. В этой главе мы создали, при помощи этой программы, приложение, базирующееся на диалоговом окне. Создав диалоговое окно, к нему можно добавлять любые виджеты и изменять их свойства. При помощи компоновок можно задавать разные способы размещения. В места, .которые должны быть свободными, нужно помещать специальный заполнитель. Эе81§Т1ег предоставляет возможность быстрого просмотра созданного диа- логового окна. Использовать созданные формы в своих проектах можно че- тырьмя разными способами: □ прямой способ; □ использование наследования; □ использование множественного наследования; □ динамическая загрузка ш-файла.
Глава 46 Проведение тестов Сидит программист глубоко в отладке. Третий день сидит. Ничего не полу- чается. Подходит к нему сынишка и говорит: — Папа, а почему солнце встает на вос- токе? — Ты это проверял? -Да- — Работает? -Да. — Каждый день работает? -Да. — Тогда, сынок, ради Бога, ничего не трогай, ничего не меняй! Тестирование — это фундамент разработки программ, позволяющий быстро продвигаться вперед. На самом деле на написание кода тратится не так уж много времени. Много времени уходит на понимание задачи и проектирование. Ну а львиную долю занимает отладка. Каждый из читателей наверняка помнит часы, а может и дни, которые ему пришлось провести в поисках закравшейся ошибки. При- чем исправить ее можно, как правило, за считанные минуты, но поиск может отнять целую вечность. Но и это еще не все. На исправлении ошибки история не заканчивается. Ис- правив ее, вы не можете дать гарантию того, что сделанное вами исправление не повлечет за собой появление других ошибок. На самом деле может ока- заться так, что исправление ошибки вызовет возникновение не одной, а сразу нескольких ошибок, еще более коварных, чем исправленная. Тесты являются решением подавляющего большинства подобных проблем. Чем чаще вы будете их выполнять, тем больше шансов быстро обнаружить
ошибку. Попробуйте выполнять тесты после каждой компиляции, и вы сами увидите, как резко возрастет производительность вашего труда. Вы переста- нете тратить много времени на отладку, связанную с поиском ошибки. И ес- ли вы вдруг при исправлении сделали другую ошибку, то вы сразу же обна- ружите ее, так как будете точно знать, что в предыдущей компиляции ее не было. В любом случае, вы без труда сможете внести необходимые исправле- ния, снова откомпилировать ваш модуль и провести следующий тест. Заметь- те, важно не только создавать классы тестов, но еще и часто запускать сами тесты. В идеале, для каждого класса должен быть написан тест. Но на практике это не всегда целесообразно. Всегда есть риск что-то пропустить, поэтому не стремитесь написать много тестов — их все равно будет недостаточно. Ваши опасения по поводу тбго, что тестирование не выявит все ошибки, не должны отваживать вас от написания тестов: они в любом случае позволят обнару- жить большинство ошибок. Чрезмерное усердие при написании тестов может вызвать обратный эффект — вы решите, что написание тестов отнимает слишком много времени, и откажетесь от них. Поэтому необходимо писать тесты только для подозрительных мест. Подумайте, например, о граничных условиях, которые могут быть неправильно обработаны, и сосредоточьте свои тесты на них. Всегда помните — тестирование приносит ощутимую пользу, даже если осуществляется в небольшом объеме. Для создания тестов 0)1 предоставляет специальный модуль ОГГезйСлЬ, кото- рый разработан для того, чтобы упростить тестирование классов вашего при- ложения. Также он включает в себя возможности проведения тестов классов графического интерфейса и позволяет "симулировать" клавиатуру и мышь. Тесты, о которых пойдет речь в этой главе, называются модульными тестами (ип111ез(з). В подобных тестах каждый класс действует в рамках одного ва- шего модуля и исходит из того, что за его пределами все работает нормально. Создание тестов Тесты полезно создавать до начала реализации кода. Это позволит вам, при написании теста, лучше осмыслить и понять задачу, задав себе вопрос — что нужно сделать для добавления реализации. Для демонстрации возьмем простой пример: предположим, нам нужно реали- зовать класс с методами для нахождения максимума и минимума двух чисел. Первая задача заключается в подготовке тестовых данных, которые будут выступать в качестве образцов для тестирования. Возьмем для этой цели че- тыре пары чисел: (25, 0), (-12, -15), (2007, 2007) и (-12, 5). Теперь, когда
тестовые данные готовы, можно начинать писать тесты. Существуют согла- шения для названия тестирующего класса и его методов, которые успели за- крепиться и зарекомендовать себя на практике с наилучшей стороны. А именно: □ называйте тестирующий класс именем тестируемого с префиксом тезм_. Например, если мы тестируем класс мус1азз, то тестирующий класс будет называться Тезе_мус1азз; □ называйте тестовое слоты (методы) именами тестируемых методов. #1пс1ис1е <ОЪТезЪ> #1пс1ис1е иМуС1азз.11"‘ // ====================================================================== с1азз ТезЪ_МуС1азз : риЬНс ООЬдесЪ { 0_ОВСГЕСТ ргздга^е з1оЪз: пап () ; 5701(1 тах (); }; //--------------------------------------------------------------------т------ То1с1 Тез1:_МуС1азз: :ш1п () { МуС1азз щуС1азз; ОСОМРАЯЕ (гпуС1азз.т1п(25, 0), 0); 0СХ)МРАКЕ(туС1аз5.т1п(-12, -5), -12); 0СОМРАЯЕ(туС1азз.т!п(2007, 2007), 2007); 0СОМРАКЕ (туСТазз.т!п(-12, 5), -12); } //--------------------------------------------------------------------------- Тез1:_МуС1азз: :тах () { МуС1азз туС1азз; 0ССМРАКЕ(1луС1азз.шах(25, 0), 25); 0С0МРАКЕ(туС1азз.тах(-12, -5), -5);
ОСОМРАКЕ (гпуС1азз.шах(2007, 2007), 2007); 0СОМРАКЕ(гпуС1азз.тах(-12, 5), 5) ; ) 0ТЕ5Т_МА1М(ТезИ_МуС1азз) #1пс1иде "ЬезЪ.тос" В листинге 46.1 показана программа, которая должна будет проводить тест методов пап о и тах() класса мус1азз. В тестовой программе необходимо включить заголовочный файл ОТез). Тестирующий класс должен быть уна- следован от класса ооьзесъ и, для создания специальной метаинформации, содержать в своем определении макро оовавст. Это позволит вызывать слоты класса при исполнении, включая его тестовые слоты в секции рггуаее. Макрос осомраке () принимает два аргумента, полученный и ожидаемый ре- зультат, и сравнивает их. Если значения не совпадают, то тогда исполнение тестового метода прерывается с сообщением о непройденном тесте. Нам нужна функция та!п(), в которой будет исполняться каждый тест. По- скольку для проведения тестов эта функция всегда выглядит одинаково, 01 предоставляет для ее замены макрос <этезт_ма!ы (). В завершение, мы должны включить метаинформацию, сгенерированную МОС. После создания теста можно приступить к реализации методов тестируемого класса. МуС1азз_Ъ_ Ме^тпе МуС1азз_к— // ===============================================================;========= с1азз МуС1азз { риЬИс: 1ПС т1п(1ПС п1, злС п2) { геСигп п1 < п2 ? п1 : п2; } 1пС тахСхпС п1, 1пС п2)
геЪит п1 > п2 ? пХ : п2; } }; #епдхС //МуС1азз_к_ Класс мусхазз реализует два метода для нахождения минимума и максимума (листинг 46.2). ЗООНСЕ5 = Ъез^.срр НЕАБЕКЗ = МуС1азз.Ъ СО№1С += дЪезПХхЬ ТАКОЕТ = ../ТезЬЬхЬ В рго-файле в секции соырхо должна быть добавлена опция дЬезьХхЬ. Имя заголовочного файла тестируемого класса указано для того, чтобы при лю- бых его изменениях можно было скомпилировать тест заново. При первом проведении теста полезно начать с проверки на отказ, то есть модифицировать проверяемый метод Чак, чтобы тест завершался неудачей. Это поможет убедиться в том, что тест действительно выполняется и прове- ряет то, что требуется. Для этого поменяйте в методах тхп () и шах () класса мусхазз знаки сравнения на противоположные. Теперь откомпилируем и за- пустим тест. На экране появится следующее: ********* зсагГ: ЪезГхпд оС ТезС_МуС1азз ********* СопСхд: Пзхпд ОТезЪ ХхЬгаху 4.2.2, ОС 4.2.2 РА35 : ТезС_МуСХазз: : хпхСТезССазе () ГАТЬ! : ТезС_деуСХазз: :тхп() Сатрагед VаXиез аге поС Мае зате АсСиаХ (туСХазз.тхп(25, 0)): 25 ЕхресСед (0) : 0 СезС.срр(25) : СахХиге ХосаСхоп ЕАХЬ! : ТезЬ—МуСХазз: :тах() Сатрагед VаXиез аге по С Ске зате АсСиаХ (туСХазз.тах(25, 0)): 0 ЕхресСед (25): 25 СезС.срр(35) : СахХиге ХосаСхоп РА55 : ТезЬ_МуСХазз:: сХеапирТезССазе () ТоСа1з: 2 раззед, 2 СаИед, 0 зкхрред ********* РхпхзЪед СезСхпд оС Тез^_МуСХазз *********
Методы 1п1етезьсазе () и сХеапиртезс () вызываются в начале и конце теста, соответственно. Эти методы не трактуются как тест-методы. Они выполняются при запуске тестов и служат для инициализации и очистки теста. Кроме того, на экране мы видим информацию о том, что тест прошел неудачно: сообщение "РАИЛ", имена тестов "Те81_МуС1а88::пйп()" и "Те81_МуС1а88::тах()", актуаль- ные значения (АсСиа1) и ожидаемые (БхресСед). Отлично! Наш тест завершил- ся неудачей, а это значит, что проверка работы теста удалась — он действи- тельно способен отслеживать ошибки. Теперь поменяем операторы сравнения в классе муС1азз так, как это показано в листинге 46.2. Скомпили- руем и запустим тест еще раз. Мы увидим на экране следующие сообщения: ********* 51:агЪ СезЁхпд Тез1:_МуС1азз ********* СопИд: Пзтпд 0Тез1: ИЬгагу 4.2.2, ОЪ 4.2.2 РА55 : ТезЪ_ДОуС1азБ: : хпХЪТезЪСазе () РА55 : ТезЬ_МуС1азз : :ш1п () РА85 : ТезЁ_МуС1азз: :шах() РА38 : ТезЁ_^МуС1азз:: сХеапирТезЪСазе () ТоСа1з: 4 раззей, 0 ^аНед, 0 зк!рре<1 ********* р1п1д]1еа ЪезЫпд оС Тез^_МуСХазз ********* Это говорит о том, что все наши тесты прошли удачно. Тесты с передачей данных До настоящего момента мы вписывали данные для проведения теста в макрос 0СОМРАНЕ(). Подобный подход вызывает нежелательный эффект дублирования кода. Для минимизации дублирования кода ОПезЙИЬ предоставляет возмож- ность проведения тестов с передачей данных. Такой подход позволяет отделить тестовый код от данных, поместив их в отдельное место. Этим местом является слот, который предоставляется для каждого тестирующего слота. Этот слот дол- жен называться так же, как и тестирующий, но с постфиксом _ааеа. с!азз ТезЪ__деуС1азз : риЬИс ООЬэесЁ { О_ОВЛЗСТ рг^аСе 51ОСЗ: то1с1 пй.п_даЪа (); то1с1 шах^даЪа () ; хго1с1 тхп () ; 'У'отс! шах О :
В тестирующий класс, показанный в листинге 46.4, мы ввели два дополни- тельных слота т1п_<ааеа () и тпах_ааеа (), которые будут обеспечивать дан- ными наши тестирующие слоты. тохД ТезЪ_ЦуС1азз: :Ш1п_Да1:а () { ОТезЁ: :аДДСо1итп<1пЪ> ("агд1") ; ОТезЬ:: аДДСо1итп<1пС> (“агд2 *); ОТезС: :аДДСо1шпп<1пС> (игези1Ъ") ; ОТезЁ: :пеюКою( ищ1п_СезЪ1") « 25 « 0 « 0; ОТезЪ: :пе^Ко^( "Ш1п_^ез1:2") « -12 « -5 « -12; ОТезЪ: : пеюКоот ("т1п_СезЪЗ ") « 2007 « 2007 « 2007; ОТезЬ: :пеюКсж( "тд.п_Ьез1:4") « -12 « 5 « -12; } В листинге 46.5 показан слот пап_Дага (), в котором необходимо создать таб- лицу тестовых данных. Для задания ее столбцов используется метод ОТези: :ааасо1ипт(). Определив столбцы, мы добавляем данные в таблицу с помощью метода отезЪ: шейкою (). Строка, переданная в этот метод, явля- ется идентификатором строки таблицы. Каждый вызов создает свою собст- венную строку, а при помощи оператора « в нее добавятся данные: два па- раметра и ожидаемый результат применения метода пап (). тохД ТезС_МуС1азз: хптахцДа^а () { ОТезЪ:: аДДСо1шпп<1пЪ> (" агд1"); ОТезЪ:: аДДСо1шпп<1п1:> ("агд2") ; ОТез!:: : аДДСо1шпп<1пЪ> (пгези11:"); ОТезЪ: :пемНсж( "таХ-СезЫ") « 25 « 0 « 25; ОТезС::пе^Ною("тах_еезе2") « -12 « -5 « -5; ОТезЪ: :пеюКою(*П!ах_ЬезЁЗя) « 2007 « 2007 « 2007; ОТезЪ: :пеШою( "гпах^ез^") « -12 « 5 « 5;
Реализация слота данных тах_ааса () (листинг 46.6) аналогична слоту т1п_ааеа () (см. листинг 46.5). Единственное отличие в идентификаторах строк таблицы и в ожидаемых результатах. ТезЬ_МуС1азз: :пц.п() { МуС1азз шуС1азз; ОРЕГОН(1пС, агд!); ОРЕГОН(1пП, агд2); ОРЕГОН(1ПС, гези1Ъ) ; 0СОМРАКЕ(туС1азз.'пй.п(агд1, агд2), гезиХС); } В методе данных создается таблица из четырех строк, поэтому слот пйп (), приведенный в листинге 46.7, будет запускаться четыре раза — по разу для каждой строки таблицы данных. Мы используем три макроса оретсн () для создания локальных переменных агд1, агд2, гези1е и внесения в них данных. Заметьте, имя должно совпадать с именем элемента тестовых данных, а если элемент данных с этим именем будет не найден, то тест завершится с сооб- щением об ошибке. Теперь для проведения тестов нам нужен только лишь один макрос осомракео. Такой подход позволяет легко добавлять новые данные для теста без модификации самого теста. тохд ТезЬ_ЦуС1азз: :шах () { МуС1азз туС1азз; ОРЕГОН(1пЬ, агд1); ОРЕГОН(1пЪ, агд2); ОРЕГОН(хпЬ. гези!С); 0СОМРАНЕ(туС1азз.шах(агд1, агд2), гезиЗХ); } Реализация тестового слота тах() приведена в листинге 46.8 и аналогична реализации слота пап (), с той разницей, что для проведения теста вызывает- ся метод муС1азз: :тах (), вместо метода МуС1азз: :т!п ().
В завершение, мы должны предоставить функцию та1п () при помощи мак- роса оте5т_ма1м( ) и включить метаданные, сгенерированные МОС: 0ТЕ5Т_МА1И(ТезЬ_МуС1азз) #1пс1ис1е "ЬезС.пюс" Создание тестов графического интерфейса <2Те$с1лЬ предоставляет механизм для тестирования графического интерфей- са. Предположим, что мы хотим протестировать поведение виджета одно- строчного текстового поля оЫпеЕбхь. Прежде всего нам потребуется создать класс, содержащий тестовый метод. #1пс1ибе <ОЬТезЬ> #1пе1ибе <0ЬОи1> // ==;==:===г:=================================:====================;===:;гг:===== с1азз ТезЬ_СЫпеЕб1Ь : риЬЫс ООЬдесЬ { 0_ОВ6ЕСТ ргдлгаЬе з1оЬз: тоге! есИь () ; }; //------------------------------------------------------------------- ТезЬ_ОЫпеЕсНЬ:: есИЬ () ( ОЫпеЕШЬ ЬхЬ; ОТез Ь: : кеуС 1 1скз (&ЬхЬ, " АВСВЕГОН ’’) ; ОСОМРАКЕ (ЬхЬ. ЬехЬ () , 0ВЬг1пд("АВСВЕГОН")); 0УЕК1РУ (ОсЬ. тзМобШеб ()) ; } 0ТЕ5Т_ЖЕЫ(ТезЬ__0ЫпеЕб1Ь) #хпс1ибе "ЬезЬ.тос" В реализации тестового метода еахъо мы создаем виджет оЫпеЕан: (лис- тинг 46.9). Затем мы имитируем ввод "АВСВЕРОН", используя метод
ОТезь: :кеусискг (), который имитирует серию нажатий на клавиши клавиа- туры. В необязательных параметрах этого метода можно передавать модифи- каторы клавиатуры, а также задержку, в миллисекундах, после каждого нажатия на клавишу. Класс отезе предоставляет методы, позволяющие си- мулировать события не только клавиатуры, но и мыши. Мы используем макрос осомране( ) для того, чтобы проверить на совпадение текст однострочного текстового поля и ожидаемый текст. После того как текст в виджете поля был изменен, вызов метода 1змоа±^1еа() должен вер- нуть значение сгие. Мы проверяем это при помощи макроса оуектру (). Этот макрос оценивает переданное выражение, и если оно истинно, то исполнение теста продолжается. В противном случае производится отображение сообще- ния об ошибке и выполнение теста прекращается. Параметры для запуска тестов Модуль рсТезсЫЬ предоставляет возможность использования параметров при запуске тестов. Это позволяет оказывать индивидуальное влияние на ка- ждый тест. Например, при запуске теста без параметров будут исполнены все тестовые методы, но если бы мы хотели убедиться в правильности работы ТОЛЬКО ОДНОГО метода МуТезС: :шах() (листинг 46.2), то нам нужно было бы запустить тест следующим образом: ТезЪЫЬ шах В табл. 46.1 указаны некоторые опции, которые могут вам пригодиться при запуске тестов. Таблица 46.1. Опции для запуска тестов Опция Объяснение -о ^Непате Записать результаты теста в файл Л 1е -зИепЁ. Ограничить сообщения показом только предупреждений и ошибок -VI Отображать информацию о входе и выходе тестовых методов ^2 Дополняет опцию -VI тем, что выводит сообщения для мак- росов ОСОМРАКЕ () ИОУЕПХРУО ^3 Отображать каждый высланный сигнал и вызванный слот -хт1 Осуществлять вывод всей информации в формате ХМЬ -еVепС<Зе1ау шз Заставляет тест остановиться и подождать определенное время (в миллисекундах). Эта опция полезна для нахождения ошибок в элементах графического интерфейса
Резюме Тесты — это мощный детектор ошибок, резко сокращающий время их поис- ка. Создав надежные тесты, можно значительно увеличить скорость про- граммирования. Запускайте тесты как можно чаще. Для создания теста нужен класс, который будет содержать тестовые слоты. Этот класс должен быть унаследован от класса ооьдесъ. Тестовая программа должна содержать макрос оте5т_иа1М(), который заменяет функцию тахпо для запуска всех тестовых методов. Макрос осомракео сравнивает результирующие значения с ожидаемыми. Если значения идентичны, то исполнение теста будет продолжено, если нет—тест будет остановлен с отображением сообщения об ошибке. Макрос отек1ру() проверяет правильность условия. Если значение равно сгие, то выполнение теста продолжается. Если нет, то тест далее не исполня- ется и отображается сообщение об ошибке. Во избежание проблем, связанных с повторением кода, (уГеаИлЬ предостав- ляет возможность создания тестов с передачей данных. Все, что нужно — это просто добавить еще один слот в секцию ртгтасе нашего класса. Слот для тестовых данных должен называться так же, как и тестовый слот, но с пост- фиксом _йаЕа. Тестовые данные имеют формат обычной таблицы. Назначе- ние макроса оретсн () состоит в создании локальных переменных и заполне- нии их данными. (уГезбЛЬ предоставляет возможность для тестирования графического ин- теоЛейса.
Глава 47 ШеЬКН "Интернет похож на золотой пустяк, который действительно является золо- том... Это означает, что он перевернет наши представления о бизнесе, образо- вании и даже развлечениях”. Билл Гейтс В наши дни Интернет приобрел невиданную популярность. Некоторые учеб- ные заведения используют его для предоставления доступа студентам к учебным материалам и методическим пособиям. А приобретение товаров, заключение сделок, оплата счетов, торговля акциями через Интернет сегодня стала обычным делом. Поэтому многие приложения нуждаются в показе \УеЬ-содержимого, и почти каждый разработчик хочет создавать приложе- ния, использующие ХУеЬ. На настоящий момент из основной массы приложений, нацеленных на А^еЬ, выделяются ХУеЬ-браузеры. Но вполне естественно, что вам может понадо- биться отображать содержимое НТМЬ-документов без необходимости запус- ка УУеЬ-браузера при каждом щелчке мыши пользователя. Поэтому ситуация все больше изменяется в сторону встраивания УУеЬ-клиентов в собственную программу. Пользуясь, например, преимуществами программирования приложений и включив в него возможность использования встроенного ХУеЬ-клиента, вы можете создать гибрид, использующий сильные стороны обоих миров, сла- женно взаимодействующих и взаимодополняющих друг друга. Один из са- мых, пожалуй, ярких примеров такого гибрида— это приложение 1Типе$ (рис. 47.1) фирмы Арр1е. Счастливые обладатели 1Род и 1Р1юпе отлично зна- ют эту программу, она есть как для Мас 05 X, так и для \У1Гк1о\У8. Скачать ее можно по адресу м'ЛУду.аррк.сот/йипез/йом'Ыоад/.
В этом приложении есть устроенный ХУеЬ-клиСнт, который позволяет поль- зователям открыть и загрузить из магазина 1Типе$ музыку. После загрузки песни показываются в приложении в виде списка. И в этой же программе их можно проиграть в медиаплеере. Скачивать можно не только музыку, но и видео, игры, программы — одним словом все, что находится в элек- тронном виде, но естественно не бесплатно. Отметим тот факт, что встро- енный в Пипез ХУеЬ-клиент базируется на ХУеЬКП, которому и посвящена эта глава. Рис. 47.1. Главное окно приложения ГГипее В создании гибридов открывается большой простор для фантазии. Так, например, вы можете встроить ХУеЬ-клиент в свое приложение для показа погоды, курсов валют, акций, предоставить вашему приложению ресурсы, находящиеся за пределами \УеЬ, например, в локальной системе, создать соб- ственные \УеЬ-сервисы, используя информацию из Ооо§1е, УаЬоо и многое другое. Так что же такое А^еЬКИ?
Путешествие к истокам ХУеЬКй — это проект с открытым исходным кодом, на базе которого можно создавать полноценные У/еЬ-браузеры. Его истоки уходят корнями в ХУеЬ- браузер Копциегог, разработанный для КЛЕ. Позже его основная часть была модифицирована и отточена фирмой Арр1е и воплотилась в библиотеку, которая начиная с 2002 года использовалась в \УеЬ-браузере ЗаГап, затем в Пипез, ЛазЬЬоап!, Май и других приложениях Мас 08 X. С появлением |РЬопе она, естественно, стала неотъемлемой частью и этой платформы. ХУеЬКй автоматически загружает и отображает большинство распространен- ных типов документов, таких как, например, НТМЬ, ХНТМЬ, 8УО, С88, ХМЬ, обычный текст, растровые изображения, 1ауа8спр1 и т. д. ХУеЬКй вы- бирает подходящую модель данных и представления, базируясь на М1МЕ- типе документа. При разработке ХУеЬКн ему была придана способность рас- ширяться, что позволяет вам создать собственную модель данных и пред- ставление, специфичные для вашего М1МЕ-типа. Это также означает, что использование ХУеЬКй вовсе не заставляет отказываться от своих собствен- ных расширений. Например, Соо§1е в своей мобильной программной плат- форме Апдгок! использует ХУеЬКй с собственной реализацией 1ауа8спр<. На настоящий момент М1сгозой тоже не остался безучастным и проявляет интерес к этому проекту. На одной из конференций разработчиков в Сиднее шеф М1СГО8ОЙ Стив Балмер заявил, что у Мгсгозой Ехр1огег, возможно, в бу- дущем будет произведена замена движка для отображения ЧУеЬ-страниц, и одна из возможных рассматриваемых альтернатив — это ЛУеЬКй. А зачем? При разработке браузеров и других приложений, отображающих ХУеЬ- страницы, до настоящего времени программисты сталкивались с целым ря- дом нелегких задач, например, связанных с загрузкой и отображением со- держимого ХУеЬ-страниц. Такие программы должны базироваться на архи- тектуре клиент-сервер, в которой клиент делает асинхронные запросы к УУеЬ- серверу для получения содержимого ^УеЬ-страницы. Во время этого процесса может возникнуть масса проблем, связанных с отправкой запросов и получе- нием ответов со стороны сервера через сеть. Например, могут возникнуть перебои в работе сети, неправильные гиперссылки, дефектное содержание и так далее. Не легче и отображать содержимое полученной информации, которое может быть весьма сложным. Оно может содержать несколько фреймов, серию М1МЕ-типов, таких, как например, растровые изображения и фильмы.
Все это требует от разработчиков программного обеспечения либо колос- сальных временных затрат, которые исчисляются месяцами, либо затрат де- нежных на приобретение библиотек от независимых производителей. С появлением в 01 \УеЬКй все изменилось в лучшую сторону. Использование этой библиотеки сократило время разработки с нескольких месяцев до счи- танных минут и сняло дополнительные затраты при реализации коммерче- ского программного обеспечения. Быстрый старт АУеЬКй предоставляет целый ряд классов, которые предназначены как для реализации простых встраиваемых в приложение \УеЬ-клиентов, так и для полнофункциональных \УеЬ-браузеров. Вместе с тем он скрывает от разра- ботчиков детали всех сложных задач. По умолчанию классы ХУеЬКй про- зрачно обращаются с запросами клиента. \УеЬКй также предоставляет все необходимые классы для отображения получаемых ХУеЬ-станиц. Как только пользователь нажал ссылку, \УеЬКй автоматически разрушает старые объек- ты и создает новые, необходимые для новой ^еЬ-страницы. Класс отображе- нйя страниц поддерживает возможность показа нескольких фреймов, каждый из которых может обладать собственными полосами прокрутки и содержать объекты различных М1МЕ-типов. Необходимо отметить, что УУеЬКй не предоставляет реализацию полнофунк- ционального браузера со всеми элементами графического интерфейса в виде одного виджета. Но он предоставляет "конструктор", содержащий все необ- ходимые компоненты, при помощй которых, используя также другие видже- ты 01, вы сможете реализовать аналог АУеЬ-браузера нужной вам модифика- ции. Использование ХУеЬКй настолько просто, что при помощи всего лишь не- скольких строк кода можно реализовать программу, отображающую содер- жимое \УеЬ-страницы (в соответствии с заданной ссылкой). Это приложение будет также обладать возможностью навигации по сети посредством нажатий на ссылки, а наличие контекстного меню предоставляет функции возврата к предыдущей, последующей странице и ее обновление. Рецепт создания простейшего веб-клиента прост, нам потребуется сделать лишь следующее: 1. Создать виджет оиеЬУ1е«. 2. Отослать запрос загрузки. 3. Показать окно виджета. Вот и все! Теперь для наглядности создадим программу, которая выполняет эти действия (рис. 47.2).
«юинсктую... : -л '-: т-'-а •• В 1, • • . —,- м... -. - -. г .........- ..........— - — -те н хнат* *:г:г:н Данилов П, Прит-мь. <р:>пи ь«*мги ЦИС'рСШСИ К,*”СРМ»1 1»ИВ^ Изменение лдррга роданцин ; •"’ 1 ;.?яжяв’!ые мнтэтеси *ёреи’кч в»^е ени-'-нл» н9 -о. чТС у реда«-_си сХ=- Петерб/сг 1«лен;чср ягоес "с-рсг-е: е *„ггй--ты «в^егс :дйт5 •;тсрйр5:п:ч:г‘ньэ!--: 0 Е -О СПИР 2 %ЛЛ1ЛИГНЫЙ ЛНг1П<Ц .РПо11МНор____ _______ _ .„___ Зо сентября !Сми ,-нцга 2 < чго-е-;••• :• О*: } ” /,'П ‘"‘г‘ Э’= гергге лемэтнее иглание на р.-со-о- язе-<е п; Л’сан.к ^гои-н а тэ-&‘е вс --0 ' . -я 2 •’ г „>‘ ‘ -'< -- .,е-. ’ 61МР2 нн-ГС!’ аня^ги-нс’'' РКсг-аГ’СГп 56ССИИ ЗГ1- иго». ’Л!гс.с.’.5. М-зс вэ.1-а -С.П'гна зс’!Э^ним лол легате ля1-. ;игеГч-ерэг и ее;*- -лечйнёря'- -*Ач1т: -,гноенье -спртсы Хзтсгра+лш л г-^ланиа рястрсесй гоа^г^- - <-ей Зл,1:анб.г . - .. .. - га Ми«йй»е иопиост ью.. ш г:'-"а. ;::е I————.... Млп<1нм ипих Полной руководство пплыюпашлй. второе издани Л,,.....-..^~-^-.— ......—........................................ Жадаев А. Иаглидньш са*‘оу'«мте»ъ 1С;Пусднр««гм< ’ Торговля и Сила л ". ыоа-ь. «51Й.-Ч4 Рис. 47.2. Отображение содержимого УУеЬ-страницы г- - г. - ’.. ш -И- '! #1пс1иде <0ЪОи1> #1пс1иде <2ШеЬКдХ> хпе та1п(1пЬ агдс, сЬаг** агду) { 0Арр11са1:1оп арр (агдс, агду) ; ОЮеЪУгею юеЬУхезд; тееЬУхею. 1оа<3(СПг1 ("Ы:Ёр: //здлллу.Ыгу.ги")); юеЬУхею. зЪоад () ; геЪигп арр.ехес(); } Класс оиеЬУхем — это центральный класс в \УеЬКй, который управляет ото- бражением и всем взаимодействием с пользователем. Пользователи могут
осуществлять навигацию самостоятельно, щелкая по ссылкам. Для этого нам совсем не понадобится усложнять текст программы— невидимый управ- ляющий элемент возьмет все на себя. В листинге 47.1 мы создаем виджет ХУеЬ-представления («еЬУ1е«) этого класса и вызываем его метод 1оаао, пе- редавая строку с начальной ссылкой. Обратите внимание, что строка должна быть преобразована в оиг1. Вызов метода зЬо^О отображает наш виджет на экране. Примечание Для того чтобы программу можно было откомпилировать, не забудьте добавить в проектный (.рго) файл строку от += шеЬкхс. Хоть старт был и быстрым, но ХУеЬ-браузером это приложение назвать нель- зя. Нашей программе еще не хватает некоторых вещей, без которых пользо- ватель "в паутине" просто не сможет обойтись. Например, ему наверняка захочется иметь функции назад, вперед и обновления в виде отдельных кно- пок. Также обязательно нужно отдельное поле ввода, в котором он смог бы указывать свои собственные адреса. Желательно, чтобы он видел прогресс загрузки содержимого страницы и еще многое другое. Поэтому не будем ос- танавливаться на достигнутом и отправимся дальше. Как говорят в Китае, дорогу осилит идущий. Написание простого УУеЬ-браузера Итак, мы установили, что для того чтобы наша программа могла гордо назы- ваться АУеЬ-браузером, в ней нужно реализовать три следующие возможности: □ ввод ХУеЬ-адресов; □ управление историей; □ отображения прогресса загрузки страницы и ее ресурсов. Теперь давайте остановимся на каждой из этих возможностей поподробнее. Ввод адресов В какой-то момент у пользователя может возникнуть необходимость задать новый адрес, для того чтобы перейти к странице, не связанной с текущей. Эта функция обязательна для ^УеЬ-браузеров, но совсем не обязательна для гибридных приложений. Но если в вашем приложении предусмотрено, что адреса будут вводиться пользователем, необходимо позаботиться о том, чтобы эти адреса были введены в понятном для ХУеЬКк виде, в частности, для мето- да загрузки 1оад(). И об этом нужно позаботиться самому. Например, самой
частой ошибкой или, точнее сказать, упущением является то, что пользова- тель забывает указывать тип сервиса в самом начале адреса. То есть пользо- ватель напишет ’игм'чу.ЬЬу.ги, вместо того чтобы написать ЬИр://ятумг.ЬЬу.ги. Такие ситуации необходимо отслеживать и при необходимости дополнять недостающий тип сервиса к введенному адресу. Управление историей История в ХУеЬКп формируется автоматически в процессе навигации пользо- вателем по ХУеЬ-страницам. Если только что посещенная страница имеет ад- рес, совпадающий с другим элементом истории, то этот элемент истории изымается и заменяется новым, иначе бы подобные списки истории выраста- ли до огромных размеров и были бы не пригодны. За список элементов исто- рии в ХУеЬКк отвечает класс оиеьнхзъогу. Доступ к объекту этого класса мож- но получить прямо из объекта класса оиеьу1е» вызовом метода ЫзъогуО. Сами элементы истории хранятся в объектах класса оиеьнхзъогутьет. При- чем в этих элементах запоминаются также и даты посещения. Поэтому мож- но сгруппировать и показать пользователю все страницы, посещенные им в конкретные дни и часы. Например, можно использовать подменю, в кото- ром будут отображены страницы, посещенные пользователем за последние три дня. Элементы истории можно удалить все сразу вызовом метода с1еаг () объекта класса омеьн1зсогу. Очевидно, что пользователю в 5УеЬ-браузере необходимо предоставить воз- можность перемещаться вперед и назад, согласно списку истории. Например, если он находится на некоторой ХМеЪ-станице и хочет вернуться на страницу, на которой был до этого, вы должны предоставить ему эту возможность. В ХУеЬ-браузерах для этого обычно предусмотрены кнопки со стрелками вправо и влево, которые позволяют пользователям перемещаться вперед и назад. Для реализации этой возможности класс оиеьнхзьогу предоставляет методы ЬаскО и Гог^агао, но ими пользоваться необязательно, так как класс оиеЬУхеш располагает одноименными делегирующими слотами, с кото- рыми очень удобно соединить сигналы ваших кнопок. Загрузка страниц и ресурсов Ресурсы— это любые данные, связанные со страницей, которые должны быть загружены отдельно (дополнительно к самой странице), например рас- тровые изображения, программы сценариев, С88, а также и АУеЬ-страницы, содержащиеся в фреймах. Сама же \УеЬ-страница может содержать несколь- ко ресурсов сразу, каждый из которых получается отсылкой собственного
запроса и обработкой ответного сообщения. Как только эти ресурсы были запрошены, они поступают независимо и в любом порядке. Из всего этого становится понятно, что загрузка страниц — это на самом деле очень слож- ный процесс, который ХУеЬКИ умышленно скрывает от разработчиков. Но ошибки происходят, ведь может получиться так, что запросы будут неуспеш- ны. В подобных ситуациях приложение должно быть в состоянии проинфор- мировать об этом пользователя. Для этих целей класс р\УеЬУ1е\у предостав- ляет сигнал 1оаар1пхзЪеао, который передает значение булевого типа. Это значение позволяет сделать вывод о том, успешно ли произошла загрузка страницы. Самая, пожалуй, частая причина ошибки — это неправильное ука- зание пользователем адреса. Если вы хотите, чтобы приложение уведомляло пользователя о прогрессе загрузки страницы и ее ресурсов, необходимо соединить ваш слот с сигналом 1оайРгодгезз () класса оиеЬУхеш. Этот сигнал передает целочисленное зна- чение от 0 до 100, показывающее прогресс загрузки. Пишем УУеЬ-браузер, попытка номер два Теперь подытожим все вышеизложенное конкретным примером и напишем наконец скромный, но настоящий ХУеЬ-браузер, который показан на рис. 47.3. Рис. 47.3. Простой М/еЬ-браузер
#1пс1иде <2^Сги1> #1пс1ис1е "ЮеЬВгоюбег.Ъ" 1пС та1п(1п1: агдс, скат** агду) { ОАррИсаЪхоп арр(агдс, агду) ; ЮеЬВгсэтллз ег меЬВгоюзег; юеЬВгоюзег.вкою (); геЪит. арр. ехес (•); В основной программе (листинг 47.2) мы просто создаем виджет нашего ХУеЬ-браузера (\уеЬВго\у§ег). #Ипде^ _МеЪВгомзег—И_ #(ЯеНпе _МеЬВго№ег_Ъ_ #1пс1иЛе <0Юз.<1деЪ> с1азз дЫпеЕЛ!:; с1азз ОМёЬУхезд; с1азз ОРизЪВиЁЪоп; // ===================^=========== с1азз ЮеЬВгоюзег : риЬИс 0Ю1с1деЪ{ 0-ОВОЕСТ р^^VаI:е: ОЫпеЕсИЪ* пирСхЪ; ОИеЬУхеад* т_рлЛ7; ОРизЬВиЪЪоп * пирспкЗВаск; ОРизЪВи^оп* хп^рстйРогигагд; риЬИс: МеЬВгоюзег (0Ю1с1де1:* «дЪ = 0);
ргалга^е з1оЪз: уохд зХоЪОо ( ); УО1С1 з1оЪР1п1з11ес1(Ьоо1) ; }; #епд1Г //_МеЬВго1лтзег_11_ В файле определений мы задаем указатели на некоторые элементы управле- ния нашего браузера: на текстовое поле для ввода адресов (указатель т_рихь), на сам элемент АУеЬ-представления (указатель т_р«у) и на кнопки на- вигации по истории назад и вперед (указатели т_рсяпс1ног«ага и т_рставаск). А также в нем определены два слота &1оебо () и гХойПпхзкеа (). Первый нам нужен, чтобы запускать процесс загрузки страницы, а второй будет отвечать за действия, которые надо произвести после загрузки. .........................-.......... IЛ йстинг47.4.ФайлЛУеЬВгои8ег.срр. Конструктор ол______..................................... и МеЬВгоадзег: :МеЪВгоу?зег (ОМхддеЬ* шдГ/*=0*/) : ОМхбде!: (мтдЬ) { т_рПхЪ = пею ОЬхпеЕсНГ ("ЪЪЪр: / Лалам. деосхЪхез. сат/гоз1егт”) ; т_ртту = пете фЮеЬУхею; т_рспкЗВаск = пею ОРизкВиЪЪоп("<"); щ_рспк1Еогюагс1 = пете СРизЪВиЪЪоп(">"); 1ц_ротдВаск->зе1:ЕпаЬ1е(Я (СаХзе) ; т_рапдРогу7агЛ->зеЪЕпаЬ1е<1 (Га1зе) ; ОРгодгеззВаг* рргЬ = пею ОРгодгеззВаг; ОРизЪВиЪЬоп* рспкЯОо = пею ОРизИВиЪГоп ("&Оою); ОРизИВиЬЬоп* рстсИЗЪор = пем ОРизкВиеЪоп ("&51:ор") ; ОРизЪВиЫюп* рспкЗКеСгезк = пею ОРизЪВиГЬоп ("йНе^гезк") ; соппесГ(рстдСо, ЗТСЫАЬ(с1хскей()), ЗЬОТ(з1оЪСо())); согтесЬ (т^рЬхЬ, ЗЮЫАЬ (геЪигпРгеззес! ()) , ЗЬОТ (з1оеСо ())) ; соппес^ (пирстбВаск, ЗКЗЫАЬ (сЫскесЗ. ()), т_рт/у, ЗЬОТ (Ьаск ())) ; соппесЬ (псрстдРог^агс!/ 510ЫАЬ (с1 хскед ()) , т_р\лл/, ЗЬОТ (догмате! () )) ; соппес^ (рспкЗКе€гезк, ЗТСЫАЬ (с11ске<Я ()) , щ_рзду, ЗЬОТ (ге1оад ())) ; соппесЪ (рспкЗЗГор, ЗХСЫАЬ (сИскесЗ. () ) , тираду, ЗЬОТ (зЬор ())) ; согтесЬ (т^руту, ЗЮЫАЬ (1оадРгодгезз (хпЪ)) , рргЬ, ЗЬОТ {зеЪУаХие (хпГ))) ;
соппесГ (т_ротг, 510ЫАЬ (1оас1?1п1зИе<1 (Ьоо1)), ЗЬОТ (зЬоСРхпхзЪед. (Ьоо!))) ; //Ьауоие зе^ир ОНВохЬауоиС* рЪЬх = пеы ОНВохЬауоиЪ; рЪЬх->а<а<ЗЮхс1де11 (щ^рспкЗВаск) ; рИЬх->аскЖ1с1де{1 (пирепкЗГогтеагс!) ; рИЬх->аскШ1<Зде11 (ропДЗЪор) ; ркЬх->а<ММх<1деЪ (рсшЖе^гезИ) ; рЪЬх->аскЗ!Мх(Зде<: ; рИЬх->адсЗМ1ддеЦрстбОо) ; 0\7ВохЬауоиЪ* р1ауоиГ: = пеш ОУВохЬауоиП ; р1ауоиС~>асЗдЬауоиП (рИЬх) ; р1ауоиЪ->аскЗМс1деС. (щ_рълл); р1ауоиЪ->а<3<ЗИхадеГ (рргЬ) ; зеЁЬауоиП(р1ауоиЁ); з1оЪ0о(); } В конструкторе (листинг 47.4) мы создаем все необходимые для нашего ХУеЬ- браузера элементы управления. Виджет для ввода адресов (указатель т_ръхъ) сразу инициализируется текстом стартовой страницы. Далее создаются вид- жет 5УеЪ-представления (указатель ш_р?л/) и кнопки вперед (указатель пцрстйРогшага) и назад (указатель т_рставаск). При старте нашего ^еЬ- браузера эти кнопки должны быть недоступны, так как пользователь еще не успел посетить другие страницы. Это состояние мы устанавливаем вызовом метода зеЬЕпаЫеа(), а сигналы нажатия кнопок соединяем со слотами \УеЬ- представления ьаск () и ^огздага (). Для отображения прогресса загрузки страницы мы создаем виджет индика- ции прогресса (указатель рргЬ). Его слот зеп/а1ие () соединяется с сигналом ХУеЬ-представления 1оаа₽годгезз (). Другими важными функциями, которые могут оказаться полезны пользова- телям, являются Со, 81ор и КеГгеяЬ. Функция Со производит загрузку стра- ницы. С нашим слотом з1о«зо() мы связываем сигнал нажатия этой кнопки, а также сигнал нажатия клавиши <Еп(ег> в поле адресов. Кнопка 81ор пре- рывает выполняемую в данный момент загрузку содержимого \УеЬ- страницы, и ее сигнал нажатия связывается со слотом ХУеЬ-представления зсор(). Функция КеГгеяЬ обновляет текущую ^еЬ-страницу, инициируя
повторную загрузку ее данных с ХУеЬ-сервера, для чего сигнал нажатия этой кнопки соединяется с одноименным слотом. Далее размещаем виджеты в компоновке и вызываем слот з1охоо (). ................... г Дистинг 47л$<Г>аил ^ГеЬВгруузег.с^ЬСлотвХоъаоС) ЮеЬВгот/зег: : з1оЪ0о () { 111 (!ш_р1:хе->ЁехЪ () . зЬагЬзМаЛИ (п ^Ср: //") && ’ щ_р^хГ1->ЪехЬ () . з1:агЪзШЫ1 (: / / ”) && ! щ_рСх!1->1:ех{1 () . зкагЪзМхСЪ ("дорИег: / /") ) { т_рЪхЪ->зеЁТехЁ("къер://” + т_рех±->еехе()); } т_р^пл->1оад(00г1 (т_рГхЁ->Ъехк ())); } Слот з1оисо () запускает загрузку страницы (листинг 47.5). Как мы уже упо- минали, до того как передать строку в метод 1оаЗ{), необходимо перестра- ховаться. Для этого проверяем фрагмент, с которого начинается строка вве- денного адреса, чтобы узнать, не забыл ли пользователь указать в ней тип сервиса. В случае если строка начинается с Йр://, ЬИр:// или §орЬег://, мы ос- тавляем ее без изменений, в противном случае мы исходим из того, что поль- зователь забыл указать сервис Ьир://, и просто добавляем его в начало строки. Теперь все должно быть в порядке, и строку можно передать в метод 1оаа<). уоН ЮеЬВгомзег: :з1о11Г1п1з11ед(Ьоо1 ЬОк) { (!ЬОк) { т__рот->зеЁН^т1(’,<СЕЫТЕВ>Ап еггог Наз оссигге! " н\л7И11е 1оаИпд Ёке тееЬ раде</СЕМТЕК>и ); } т_рСхе->зеГ:Техе (т_рот->иг1 () . ЪоЗЪгхпд ()) ; т_рспкЗВаск“>зе11ЕпаЫед (т_р^лл->раде () ->Ы5!:огу () ->сап(ЗоВаск ()) ; п1_рапдЕопл7ага->зе1:ЕпаЬ1еа(пир>дл->раде () ->Ыз1югу () ->сап0оРогтеагс1 ());
В листинге 47.6 приведен слот 81оьг1п1зкед(), который вызывается по за- вершению загрузки станицы. Прежде всего нам необходимо убедиться в том, что загрузка прошла без ошибок. Для этого мы используем переданный в слот параметр. В случае возникновения ошибки мы сообщаем об этом пользователю, для чего вызываем метод зеънеш1 () и устанавливаем в А^еЬ- представлении текст. Несмотря на то, что управляющий элемент делает для нас почти все, тем не менее, есть моменты, о которых нужно позаботиться. самим. Как только пользователь нажал на ссылку, адрес текущей страницы уже не соответствует предыдущему. Пользователь его наверняка захочет увидеть, скопировать или немного изменить. А это значит, что нам всякий раз после смены адреса не- обходимо актуализировать содержимое виджета текстового поля, предназна- ченного для ввода адресов (указатель т_ръхь), и помещать в него адрес акту- альной страницы. Адрес актуальной страницы мы получаем в программе вызовом метода иг1 () виджета ХУеЬ-представления (указатель т_р«у). Теперь нам осталось только правильно отобразить статус кнопок назад и вперед. Для того чтобы узнать, возможно ли произвести эти действия, в. классе океЬНхзЪогу определены два метода сапооваскО и сап<зоЕогиагд(). В соответствии с их значениями нажатия на кнопки делаются доступными либо недоступными. Резюме Вы теперь можете в течение считанных минут создать свой ХУеЬ-браузер. Модуль 5УеЬКИ — это мощное средство в сфере программирования сети Ин- тернет, которое значительно упрощает сложный процесс загрузки АУеЬ- страниц, предоставляет набор классов для их отображения и реализует меха- низм перехода по нажатым гиперссылкам. В (^1 центральным для ХУеЬКИ классом является <2ИеЬУ1е«, через который предоставляется основная часть возможностей этого модуля.
Глава 48 Интегрированная среда разработки 01 СгеаЮг "Мы должны сделать так, чтобы работа с компьютером стала столь же естест- венной, как с карандашом или ручкой". Билл Гейтс Интегрированная среда разработки— это набор инструментов, объединен- ных в одном приложении. Ее использование существенно облегчает работу разработчика. До недавнего времени Мокла не предоставляла подобного ин- струмента. Теперь ситуация изменилась в лучшую сторону, и Мок1а сделала такой инструмент доступным разработчикам. Он называется СгеаЮг и входит в пакет <218ОК. Цель создания 0( СгеаЮг — заполнить эту нишу и предоставить платформонезависимую среду разработки, ориентированную на 01-проекты. Теперь на любой поддерживаемой платформе вы можете ис- пользовать одну и ту же интегрированную среду разработки. 01 СгеаЮг объе- диняет в себе девять основных компонентов, показанных на рис. 48.1. Система помощи Библиотека .....01 Компоновщик Проектный обозреватель Компилятор Отладчик Мастер проектов Текстовый редактор СП ОеУдпег
Система помощи— это интегрированное в среду разработки расширение рс Неф, которое предоставляет доступ к документации Сама библиотека 01 — собственно краеугольный камень, ради которого и на котором разраба- тывался Ос СгеаСог. Ос СгеаСог не имеет своего собственного компилятора, компоновщика и от- ладчика, поэтому используются доступные на платформе средства. В ХУ1п- с1о№8 это М1пО\У, который содержится в пакете 018ВК. Мастер проектов — это средство для автоматического создания минималь- ных стартовых проектов для ваших программ. Создаваемый проект состоит из ресурсов, исходного текста и заголовочного файла. Текстовый редактор — это, собственно, место, где создается программный код. Сам редактор подсвечивает синтаксис, автоматически дополняет код и облада- ет рядом других достоинств, делающим программирование быстрее и удобнее. С помощью встроенной в среду программы Ос 1)е$фпег (см. главу 45) можно соз- давать или изменять формы, не покидая интегрированную среду разработки. Первый запуск После запуска ОсСгеаСог вы увидите страницу с текстом приглашения (рис. 48.2), которая позволяет вам открыть проект или восстановить послед- ний использованной проект. Если вы до этого не работали с проектами, при первом запуске этот список будет пуст. Рис. 48.2. Окно поиглашения ОС СгеаСог
На странице приглашения вы найдете кнопку СеШп§ 81аг1ед, при нажа- тии на которую СгеаЮг перейдет в режим помощи, и вы увидите встро- енную систему подсказки 01 Не1р, которая в работе очень похожа на (И А$ы$1апС. Создаем проект "НеПо 01 СгеаТог" С миром мы уже поздоровались в главе 1. Давайте создадим минимальный проект и поздороваемся с (^1 СгеаЮг. Проект— это собрание файлов реализации, заголовочных файлов, ресур- сов и самого файла описания проекта (уже знакомый нам рго-файл). Заметьте, для проектов не было придумано какого-то нового формата фай- ла специально для 01 СгеаЮг, и это очень хорошо тем, что вы можете использовать уже созданные вами рго-файлы и загружать их как проекты в СгеаЮг. От СгеаЮг предоставляет готовые шаблоны проектов, с их помощью можно легко начать создание программы. Шаблоны— это "стержень-скелет", со- держащий ресурсы, исходный текст и заголовочный файл. I 4 К.'. - бепегэ! Те4Н1е - СП СП Оеядгда Гост (^Юе51дпегРсгтС1а53 Кисшее П1е ~ С— С— Ног!ег П1е С-» РНе С— С - Рг^еси я» Ц 0.14 Ссгг5о1е АррОсаЬсп О-НЬгап Сойе Сгеаее тоге. Оер1оу еуегужАеге. Сгеасез а 01-АррьсэГоп опе Рис. 48.3. Окно создания нового проекта
Создать проект в СгеаЮг очень просто. Выберите ЕПе | Мечу (Файл | Но- вый) и в открывшемся диалоговом окне (рис. 48.3) выберите раздел Рго|ес1я (Проекты), в котором можно выбрать одну из следующих альтернатив: □ 914 Сш АррНсаНоп — приложение с графическим пользовательским ин- терфейсом 914; □ 014 Соп$о1е АррНсаНоп — консольное приложение; □ С++ ЫЬгагу — библиотека С++. Давайте создадим приложение с графическим интерфейсом, для этого выде- лим 014 Си! Аррйсайоп и нажмем кнопку ОК. В следующем появившемся окне, показанном на рис. 48.4, задайте имя для своего проекта, например МуАрр, в области поля текстового ввода Мате (Имя), и 01 СгеаЮг присвоит это имя новому проекту. ’л 1.»п5 делегат а СО! рпэ)кг, -е ззго-- Ь. ОАрркайоп аг-д щрибег- ал епгЬ’ Сойе 1е$5. СгеаСе тоге. [Юрк)у Мате: | <йте-_МатёЯ~ рагЬ: (с:\115е'5'Аеп1П15С-а(0'-\30С1Т1е''' Рис. 48.4. Окно ввода имени проекта При желании вы можете использовать путь, указанный в этом окне по умол- чанию. Для его изменения в поле Ра1Ь (Путь) укажите путь к папке, в кото- рой должен быть сохранен проект, или нажмите точку с многоточием и вы- берите папку. Нажмите кнопку Мех! (Далее).
В следующем диалоговом окне (рис. 48.5) нам предстоит выбрать, какие из модулей будут использоваться в нашем проекте. Так как мы уже заранее оп- ределили, что будем иметь дело с проектом приложения, обладающим гра- фическим интерфейсом пользователя, два первых модуля СИСоге и рЮш ему просто необходимы, и их выбор нельзя отменить. Однако можно отметить дополнительные модули, которые мы собираемся использовать в нашем про- екте. Для нашего приложения двух обязательных модулей более чем доста- точно. Нажмите кнопку Иех1. 5е1е<1 гес|инес1 тосЫез 5е?е_: сге ^оси!«5 А' 'згч к- -гсЫе ''есс-^^е-сес г 5 уо'ес: е:е Ь* се*е,..и Р >' - - Г ^г.'.еЬ^сМ^.-е Р" - Г* Г* Мосие Г" 0*6->р: '^’оси'е Г” •^оск.'в Сойе 1ея. СгеаСе тоге. Оер1оу оегушйеге. Рис. 48.5. Окно выбора модулей проекта Далее мастер проекта предложит нам выбрать базовый класс (рис. 48.6) для приложения и задать ему имя, а также имена для заголовочного файла (.Ь), файла реализации (.срр) и файла формы (.ш). Файл формы не обязателен, и если вы не собираетесь работать над формой в ОсВеы&пег, с которым мы уже успели познакомиться в главе 45, то вполне можно обойтись без него, для чего достаточно снять флажок Сенегале Гогш (Генерировать форму). В нашем примере мы оставим эту опцию включенной, так как намереваемся воспользоваться 01 Ве81§пег. На этом сбор информации о создании нового проекта подошел к концу, и в завершающем окне мастер проектов информирует нас о создаваемых файлах и об их местонахождении (рис. 48.7). Проверив эту информацию, просто нажмите кнопку ГпйяЪ (Готово), и р(Сгеа1ог создаст директорию
Му Арр и указанные файлы проекта. Сразу после этого мы увидим эти файлы в окне обозревателя проектов, показанном на рис. 48.8. Рис. 48.6. Окно выбора базового класса Сойе 1еи. СгеэГе тоге. Оер1оу ешгушЛеге.
Рис. 48.8. Обозреватель проектов В окне обозревателя проектов показаны все пять исходных файлов. Если их не достаточно, и нужно расширить проект дополнительными исходными файлами, то для этого выделите сам проект, как показано на рис. 48.9, и вы- берите из контекстного меню пункт А<Ш Нет* ГПе... (Добавить новый файл) или, если нужный файл или файлы уже находятся на диске, пункт АсИ ЕхюИпз Н1е$... (Добавить существующие файлы). Если вы выбрали АММе^ГПе..., то вы увидите уже знакомое вам окно диалога Ыетг (рис. 48.3), при помощи которого мы создали наш проект. Здесь вам нужно выбрать нужный тип файла. После создания файла будет задан вопрос о его добавле- нии в файл проекта. Если вы выбрали АЛЛ Ех18Нп|» ЕПез..., то откроется стандартное диалоговое окно выбора файлов. с..... ; ВоНаРгсуес* С1Н-Б ± Р.еЬиПд Ргс)е« С1еап Ргс|ес1 Рис. 48.9. Добавление в проект новых файлов
Пользовательский интерфейс 01 Сгеа1ог Теперь пришло самое время разобрать компоненты графического интерфейса (}(СгеаСог. СгеаЮг в режиме редактирования обладает следующими основными зона- ми (рис. 48.10): □ в самом верху расположено главное меню; □ в самом низу находятся кнопки активации окон выводов с пояснениями справа от их номеров; □ с левого края имеется полоса смены режимов (редактирование, отладка, помощь и т. д.) и секция компилирования и запуска; □ правее него расположено окно обозревателя проектов; □ самое большое окно— это окно редактора (основная рабочая область, в которой можно редактировать файлы). Далее остановимся подробнее на некоторых из перечисленных зон. Рис. 48.10. Графический интерфейс программы СП СгеаГог
Окна вывода Окна вывода (их всего шесть) при запуске по умолчанию не видны и могут отображаться автоматически в зависимости от ваших действий. Например, как только вы начнете компилировать ваше приложение, автоматически бу- дет открыто окно СотрПе, которое служит для вывода сообщений о ходе компиляции и компоновки программы. В частности, в нем отображаются со- общения о возникающих ошибках и предупреждениях. Если вы хотите увидеть то или иное окно вывода, то можете просто нажать на кнопку с именем нужного вам окна в нижней области окна СгеаЮг. Если окно уже открыто, то нажатие на кнопку закроет его. Окно проектного обозревателя С этим окном мы уже успели познакомиться, оно показано на рис. 48.8. При- мечательно, что это окно может содержать более одного проекта. Секция компилирования и запуска Для демонстрации секции компилирования и запуска вернемся снова к на- шему проекту, созданному для нас мастером проектов. Нам осталось только откомпилировать и запустить его. Обе операции сразу можно осуществить на- жатием всего лишь одной кнопки Кип. Эта кнопка—третья снизу (рис. 48.11). Над кнопкой Кип отображается прогресс компиляции и компоновки, а также при помощи индикаторов сообщается обо всех ошибках и предупреждениях компилятора или компоновщика. Если мы хотели только откомпилировать проект, то могли бы ограничиться самой нижней кнопкой. Нажатие второй снизу кнопки откомпилировало бы проект и запустило бы его в отладчике. Работу в режиме отладки мы рас- смотрим немного позже. Рис. 48.12. Созданное приложение Рис. 48.11. Секция компиляции и запуска
И вот перед нами предстало окно приложения, сгенерированного мастером проектов (рис. 48.12). Само приложение ничего не делает, разве что отобра- жает свое окно. Мы можем изменить это, модифицировав код исходных фай- лов проекта. Если вы помните, нашим намерением было поздороваться с 91 СгеаЮг, поэтому давайте добавим надпись приветствия в это окно. Добавить надпись можно двумя способами. Первый — это модифицировать исходные файлы программного кода (.И, .орр) и добавить в него виджет надписи. Второй способ заключается в использовании встроенного 91 Пе81§пег для добавления виджета надписи в форму (.ш). Это очень просто сделать при помощи дга§ & <1гор, и не требует написания ни единой строчки программно- го кода. Для этого дважды щелкнем на файле нашей формы тс!§е1.ш, и она уже готова для изменений в 91 Пев^пег (рис. 48.13). и: Э с1.3, Чпг, Ь? с.; Ц ,е ’ЮгМ 6' Лепе Ри.11Е\.«сл Тс;! БуИсп ф с В.1«сп Бс \ д ЕлютЬ =12 ’,5Г 7'е’ Й :<1'* О л оЬ|е<1Мд1пе , . 'У; -э «г + деоте<гу 1-И » * жеРсИ:, [Ргейпег! О + т1Л«тигп$1ге V •? ♦ т?>п5|;е Хб’Т??!.* < 1б’"7’215 » । ♦ -1ге1-ггепшМ ') » >) ’ 1ЛННИНВНН -хЕЗЭ .г.ЕЗЕЕЭЗ Рис. 48.13. Встроенный СИ 0ез!дпег Теперь найдем в списке виджетов виджет надписи. Чтобы сделать это быст- ро, можно воспользоваться полем ЕЛ1ег (Фильтр) и внести в него "ЬаЬеГ. По мере внесения букв происходит отбрасывание всех виджетов, имена
которых не удовлетворяют нашему критерию, и список виджетов уменьша- ется. В конечном итоге мы увидим только виджет надписи. Теперь выберем в секции ШзрЫу виджет ЬаЬе! и перетянем его в нашу форму. В результате этой операции вы увидите на форме надпись "ТехгЬаЬеГ. Это не тот текст, который мы хотели отобразить, поэтому его необходимо изменить. Для этого дважды щелкните на тексте надписи либо нажмите на тексте правую кнопку мыши и выберите из появившегося контек- стного меню СЬавде Р1атТех1... (Изменить текст), после чего наша надпись перейдет в режим редактирования. Теперь можно набрать нужный нам текст "НеПо 91 СгеаЮг" (рис. 48.14). Снова нажмем кнопку Пип и после успешно проведенного процесса компи- ляции и компоновки увидим окно с нужной нам надписью (рис. 48.15). О- НогИоша! 51Иег 53 Уепка15Мег 01&р1ау «ладей 1аЬе1 Й Тех! Вгошег Тч Сгар^сз У1ел- пБу, ОШедАах. ——. «НеПо 01 Сгеаюг * Рис. 48.14. Изменение надписи Рис. 48.15. Виджет с измененной надписью Редактирование текста Теперь настало время познакомиться с возможностями и некоторыми осо- бенностями инструмента, в котором разработчик проводит основное время, посвященное реализации программы. Это, конечно же, редактор. Как подсвечен ваш синтаксис? Редактор 91 Сгеаюг обладает прекрасной возможностью выделять разными цветами синтаксические элементы ваших программ, что значительно упро- щает их чтение. Таким образом, выделив цветом отдельные элементы, на- пример, комментарии, ключевые слова, значения и переменные, мы быстро распознаем и визуально выделяем их из текста программ. Это облегчает и обнаружение типичных синтаксических ошибок. Например, комментарии
в программе по умолчанию выделяются зеленым цветом, и если мы вдруг откроем комментарий /*, а потом в каком-то месте ошибемся с его закрыти- ем, например поставим *) вместо */, то это сразу же будет заметно, так как далее будет следовать бескрайняя гряда зеленого текста. Или если вы опеча- таетесь и наберете, скажем, Нг вместо Сог, то это слово не будет окрашено в нужный цвет, и ошибка будет сразу видна. Это позволяет выявлять многие ошибки сразу, а не в процессе компиляции. По умолчанию в (^1 СгеаЮг используются следующие основные цвета: □ фон: белый цвет; □ текст: черный цвет; □ числа: синий цвет; □ строки: зеленый цвет; □ ключевые слова: светло-коричневый цвет; □ операторы: черный цвет; □ директивы препроцессора: темно-синий цвет; □ комментарии: зеленый цвет. Если вам не нравится эта расцветка, то вы можете изменить отдельные или все эти цвета, для этого зайдите в ТооГз | ОрНопз | Тех! ЕсШог (Инструмен- ты | Настройки | Текстовый редактор). Сокрытие и отображение кода Иногда обилие исходного кода программы в открытых окнах редактора мо- жет создавать впечатление хаоса. Разработчики СгеаЮг учли это явление и предложили способ для его решения. Он состоит в следующем: фрагменты программных кодов, которые вас в настоящий момент не интересуют, могут быть свернуты, и он перестанет занимать место в редакторе. Если понадо- бится их посмотреть, то их отображение можно быстро восстановить. Рядом с каждым фрагментом расположен знак плюс или минус (рис. 48.16), позво- ляющий либо отображать, либо сворачивать фрагмент кода. 4 ИНйдеъ: :Я1(1де((0Н1<>деъ *рагег±) * Я : 0»1<1дй(рагел1), и1(пе* 1Н: :ЯНдеЪСи»5) * { 7 и1->5вгири1СгИ1з); * } В |Н<1я«::~т<1вЛО 1 {...} ] Рис. 48.16. Сокрытие и отображение кода
Автоматическое дополнение кода Если вы забыли точное название метода и/или количество и типы его пара- метров, то не беда, при вводе текста программы на нужном месте <21СгеаСог предложит вам выбор из списка альтернатив автоматически. Если же вы хо- тите увидеть подсказку на определенном месте, то нажмите <С1г1>+<8расе>. Примечательно еще и то, что эта возможность распространяется также на сигналы и слоты, более того, дополнение производится не только после на- писанного имени объекта, а также и при их соединении. Эта возможность проиллюстрирована на рис. 48.17. соппес^Арр, } и—и соттаОасаЯедие$цО$е55ЮлМападег&) йеягоуейО Ф йе5:гоуей(ОО^есГ) ' Госи5СИапйей((Л*'к1деГ.ф«й9ег) 1 1а5««пс!ол'С1О5еб() !ауе5а«Яерие5Г(О5е$51ОпМападег&) итх5|дпа1('П{) Рис. 48.17. Автоматическое дополнение кода Примечание Для того чтобы автоматическое дополнение работало так же хорошо и для ис- пользуемых файлов, не входящих в 01, необходимо следить за тем, чтобы их заголовочные файлы были указаны в секции хысыгоератн вашего проектного файла (.рго). Поиск и замена В процессе написания программ мы очень часто сталкиваемся с необходимо- стью поиска некоторых фрагментов в уже существующих файлах. Вместо того чтобы самостоятельно просматривать текст всей программы в поиске нужного места, доверьте это занятие редактору (21 СгеаЮг. Чтобы найти ка- кой-либо фрагмент текста программы, есть целых четыре возможности (рис. 48.18). Их увидеть можно, выбрав меню Е<Ш (Правка) и в нем пункт Рпи1/Кер1асе (Найти/Заменить). Можно сделать следующее: □ Сиггеп! Ооситеп! (Текущий документ) — искать в открытом редактором файле; □ РНея оп Бик (Файлы на диске) — найти в файлах на диске;
.ЕЪТ"-'"^’ГХ"Т'еЗГет-•-------— I | ЕиНс Г’е1иТсс к I ' Лгн!.. е Нг!:? | 4-.’р-1-ае .^гт., . ..' I $хг.с1ди1е .г."* | Жс^дег : :Ч1с1дех ( ^«1 ^дег : (рагег.Г! , Ш2. йдес: :~Н^д»г () Сиггеп! ОоситеШ С!г1*Р Н1ез оп 015к АН Рго]ес15 С1г|-5И|Н-Р Сиггеп! Р^ес! Са« $еп>|Ые Л'ко<е 'Логск Оп1у Е,пд Мех! Н Нпс1 Ргечош 5ЫЙ*РЗ Р.ер1асе & Р»п<3 Мех! С!Н* = Кер1асе & Нпд Ргеиои: Рис. 48.18. Меню ОгкАИ-Р □ АН Рго|ес1з (Все проекты)— осуществить поиск во всех загруженных проектах; □ Сиггеп! Рпуес! (Текущий проект) — провести поиск по текущему проекту. Если вы выберете Сиггеп! Воситеп! (Текущий документ), внизу редактора появится дополнительная панель (см. рис. 48.19). В поле Рт<1 (Найти) можно задавать текст для поиска, стрелки позволяют нам перемещаться к следую- щему найденному фрагменту и к предыдущему. Задав в поле Пер1асе чгйЬ (Заменить на) текст, можно заменять им найденные фрагменты, которые бу- дут вам показаны перед замещением. Для того чтобы не просматривать все вхождения заменяемого кода, предусмотрена кнопка Кер1асе АН (Заменить все). При ее нажатии все найденные вхождения текста, указанного в поле Рим!, будут заменены значением поля Аер1асе м'ИЬ. После выбора РЙея оп В1як вы увидите диалоговое окно, показанное на рис. 48.20. В этом окне в поле 8еагсЬ Гог (Искать) можно задать текст для поиска, Также можно использовать регулярные выражения (см. главу 4), для чего нужно
отметить флажок (1$е Кеди1аг Ехргевяопя (Использовать регулярные выра- жения). Если вы хотите осуществить поиск не в текущей директории, то мо- жете в, поле В1гес1огу (Папка) задать свой путь, а найти нужный путь вам поможет кнопка Вгомдо (Выбор папки). Если необходимо искать не только в файлах с расширениями срр и И, нужно в поле ГПе раПегп (Маска файла) задать необходимые вам расширения. СлрсХ!« 1г.г*гг.а Рис. 48.19. Дополнительная панель для поиска в текущем файле С • [ 5еагсЬ ] С1озе Вго'лже ’ Бсоре: Нее оп 0<вк БеагсЫЬг: ' Саве хепэйуе ’ЛТю!е -л’огНз оп!у . Ше ЯедЫаг Ехргеоопз Этесгогу: м Р1ера«егп: | *.фрЛЬ Рис. 48.20. Окно поиска в файлах на диске Окно поиска при выборе вариантов АН Рго|ес<в (Все проекты) и Сиггеп! Рго)ес1 (Текущий проект) практически одинаковы (рис. 48.21). В них дос- тупны все уже разобранные функции предыдущего окна, за исключением поля О1гес1огу, так как оно не имеет в этом случае смысла, ведь поиск произ- водится в проектах, местонахождение которых известно и изменению не подлежит. Обратите внимание: маска в поле ЕПе раНегп настроена по умол- чанию на все файлы (символ "*"). В связи с поиском очень интересна функция ЬосаЮг (Локатор). Эта функция очень полезна для поиска определенного класса, метода, строки кода и для просмотра документации Ос Она расположена возле кнопок окон вывода, а чтобы ее активировать, можно просто нажать <С(т1>+<К>. На рис. 48.22
показан набор префиксов для локатора, которые позволяют переходить к следующим элементам проекта: □ "1" — к строке текущего документа; □ — к классу и методу; □ "о" — к открытому файлу; □ — к статье помощи; О "Г — к файлу, расположенному на диске системы; □ "а" — к файлу, расположенному в одном из загруженных проектов; □ "р" — к файлу текущего проекта. 5еагсН(сг., --------1-------г 5соре: ! АВ Рто^есЬ С ЗеагсЬ (от: „ . \ СЬзе } Сазе зепзШме Ш1е «©гей оп1у Озе йеди!аг Ехргезаопз Не раПегп: = м V ! Рис. 48.21. Окно поиска в проектах о •/ Р ! гггг.Ь тшп.срр [ тей«ар1ауег.срр I тесйар1ауег.Ь « тед!ар1ауег.рго I тей|ар1ауег.ргс ' 5еи1пдз,и» □ пе !П сиггепс доситеп: гг; .г, Нгц' :г.;г . ;п у, р,.Л1 ГЛ- льу ГМ-:; м.-.р&г/кмгг; , ."лггп Ом ке^ик^р^АррКсаНоп Оифис ш
Комбинации для ускорения работы Хотя некоторые программисты и предпочитают взаимодействовать с редакто- ром посредством усердной работой с мышью, знание всего нескольких комби- наций может значительно ускорить работу. Приведу несколько примеров. Вертикальное копирование Чтобы выделить нужные нам строчки текста, мы обычно придерживаем кнопку <8ЫЙ> и перемещением мыши выделяем их. Если вместо <8ЫЙ> на- жать <А1О и повторить операцию, то мы сможем выделять блоки программы вертикально (рис. 48.23). р "Ведхп"; ’ I - 0; I <10; Р'-ь.чО << "г:" << 1; Рис. 48.23. <А1Ь+Перемещение мыши Автоматическое форматирование текста Если вы встретились в программе с ^отформатированным участком кода (подобный пример показан в левой части рис. 48.24), выделите нужный вам текст и нажмите <Сгг1>+<1>, после чего он примет вид, показанный в правой части рисунка. чОеЬидО « Ведт' ; г I - 0; 4 * 10; { й0еЬи8() « ; *•(<-. I - 0; I < 10; { феЬид() « ' ;.. « г, } Рис. 48.24. Автоформатирование <С1г1>+<1> Комментирование блоков Чтобы не утруждать себя комментированием каждой строчки в отдельности, эту операцию можно произвести сразу для всего выделенного фрагмента. Для этого просто выделите нужный вам фрагмент программного кода и на- жмите комбинацию <С(г1>+</>, и выделенный блок будет закомментирован, как показано на оис. 48.25.
Рис. 48.25. Комментирование блоков <С1г1>+</> Просмотр кода метода класса и его определения Очень часто, найдя в заголовочном файле (.Ь) какой-то метод, нам интересно посмотреть его реализацию. Для этого нам понадобится открыть файл реали- зации и найти этот метод. Но можно поступить и проще: если курсор нахо- дится на определении метода, то стоит нам нажать <8ЫЙ>+<Р2>, как редак- тор откроет исходный файл с его реализацией (рис. 48.26). То же самое можно проделать в ситуации, когда мы находимся на месте реализации мето- да и хотим увидеть его определение. Рис. 48.26. Код метода и место его определения <ЗЬ|К>+<Е2> А где определен атрибут? Аналогично только что описанной ситуации, часто возникает потребность найти место определения атрибута класса. Этот атрибут может располагаться и в заголовочном файле другого класса, от которого был унаследован теку- щий, что усложняет поиск вручную. При помощи СгеаЮг наши действия сводятся к минимуму, достаточно установить курсор на имя интересующего нас атрибута и нажать клавишу <Р2>, и нужный заголовочный файл будет открыт в редакторе. Помощь, которая всегда рядом Эта функция позволяет в процессе написания программ иметь прямо из ре- дактора оперативный доступ к помощи и держать ее постоянно в поле зре- ния. Это очень удобно. Для того чтобы получить справочную информацию
о классе объекта или о его методе, нужно установить курсор на его имя и на- жать клавишу <Р1>, и тут же откроется окно с необходимой справочной ин- формацией, как это показано на рис. 48.27. тщп.срр скат •© * , Ж ; Со«Мшй «• * Лпс1иА? ’тгЖт.Г : 1?Г тсзЬС '. сгдс, «агдуЦ) { ШрЬсспоп 0(агдс. агду); Л.5К0К); -с-г’-к б.ехесО; } * Е 0А| ГЬеОА тпа1п МпсЫо Г и псион 5 эрПсаНоп С1а$8 КеГегепсе Зи! тос!и1е] ррПсаиоп с!ам тападех (Ье СМ1 аррИсаиопЧ сотго! По» апй Шпд5. 'Л- <0Арр1са1юп> Рис. 48.27. Помощь <Р1> Использование стороннего редактора Если вдруг обнаружится, что вам в редакторе не хватает какой-либо функ- ции, то вы можете запустить из редактора 01 СгеаЮг другой редактор, уста- новленный в вашей системе. Для этого нужно просто нажать <АИ>+<у>, а затем <А1о+<1>. Интерактивный отладчик и программный экзорцизм В главе 3 мы уже ознакомились с некоторыми приемами отладки программы при помощи отладчика СЭВ. 01 СгеаЮг предоставляет для отладчика СЮВ графический интерфейс. Огромное преимущество такого подхода — это воз- можность иметь доступ к большому количеству информации сразу. Таким образом вы можете наблюдать и держать под контролем в процессе отладки много информации, например, программист может в любое время обратиться к отладчику с требованием отобразить значения определенных переменных. Поэтому такой отладчик и называется интерактивным. Запустить свое приложение в отладчике СгеаЮг очень просто: достаточно нажать <Р5> или кнопку 81аг1 ОеЬи^ш^ (Начать отладку), и СгеаЮг автоматически перейдет в специально созданный для отладки режим (оис. 48.281
а гг.ал.Сйр Й «ЬЬго^.г.срр а «Ыхо-мгп игл1$гмк1 сйаг г«; ЦВаъ «ето» 1 пэд.п О? иеопг . тат. 1 (пе«Ма11жь ’^Р4" <гшк), **®’' |жиУа>Хиг)» («1м»ШЛ1и») .... йсЬнг!;' 0гйЗкро1л^ ЗДгеай < "З!" 1".4 л сор ’ м 3 ШеЬВгОйМг ^еЬВ^ол5»ЦС2№1Йд«*) (лгеЫ 4 *«ЬВго»5ег доТоО змеЫ - Л . к' *'!-! •‘.^ >% Г.... ’“пч« ' А-'хи-л-.гч Ч'^-г-л.. . Г <’ Р г • '.• '- . г Г |Ллгл пд 1!паЫе 1и геяй аутЬо^ Г'-олп “СИСоге* (пси уе: таррей пле'Тгшу! / М ’ V* I Рис. 48.28. Интерактивный отладчик А теперь давайте поговорим немного об ошибках, ведь если бы не они, то от- ладчик нам бы был и не нужен. В идеальном мире, конечно же, каждая про- грамма была бы совершенна при первом ее написании. Однако в действитель- ности даже лучшие программисты делают ошибки или забывают обеспечить все ситуации. Чтобы минимизировать ошибки, чрезвычайно важно освоить ос- новы хорошего программирования и постоянно держать их в уме. Очень хоро- шим источником для освоения хорошего стиля разработки является, на мой взгляд, книга Алена Голуба "Правила программирования на Си и Си++". Главная цель отладчика — это обнаружение мест в программе, где находятся ошибки. Ошибки можно разделить на четыре типа: О синтаксические ошибки; □ ошибки компоновки; □ ошибки времени исполнения; □ логические ошибки. Примечание В процессе компиляции помимо сообщений об ошибках вы можете сталкивать- ся также и с предупреждениями. Они появляются тогда, когда компилятор в со- 25 Зак. 3983
стоянии правильно откомпилировать ваш код, но считает, что его выполнение может привести к возникновению проблем. Например, вы присваиваете пере- менной типа 1пе значение типа С1оа1: или создаете переменную, но ни разу ее не используете. В этих и подобных случаях, компилятор предупредит вас и по- интересуется, все ли здесь правильно. Помните, что в большинстве случаев предупреждения компилятора помогают избежать многих неприятностей, по- этому ни в коем случае не игнорируйте их, а относитесь к ним с должным вни- манием. Синтаксические ошибки Это самые частые ошибки, которые возникают вследствие нарушения син- таксиса или грамматических правил С++. Это может быть передача в функ- цию неверного количества аргументов или аргументов не того типа, непра- вильно набранная команда, присвоение недопустимого имя переменной или функции и т. д. В подобных случаях компилятор не может вас понять, и про- грамма не будет откомпилирована. Поэтому, как только это случится, компи- лятор обратится к вам за помощью и отобразит список синтаксических ошибок в окне вывода (рис. 48.29). Все ошибки будут сопровождаться сообщениями о том, что вы что-то сделали неправильно. Дважды щелкните на этом сообще- нии, чтобы перейти к той строке программы, где эта ошибка произошла. Хотя почти всех программистов синтаксические ошибки раздражают, они на самом деле является самыми безобидными. Просто внимательно изучите найденное место, и, вполне возможно, вы сразу поймете в чем дело, если же сходу понять не удалось, постарайтесь при помощи документации или другого источника распознать причину и откорректировать проблемные места. Ведь для того чтобы программа была успешно откомпилирована, синтаксические ошибки должны быть обязательно исправлены все. ---------ЦАррИсс^оп а(.огд<;, идадее и.5Ьо«С); | '-ес.'-п о.ехесС^; }_________________________ ВиПй Нхиеь * • 7епгп’ пог йес1агес« ш сЫ, $соре ехрестей 'Ье^оге ’а' I________________________________ I Рис. 48.29. Синтаксическая ошибка Некоторые из синтаксических ошибок можно также заметить при помощи расцветки синтаксиса. В примере, показанном на рис. 48.29, мы могли сразу
заметить, что слово геьигп написано неправильно, и тут же на месте испра- вить ошибку до запуска компилятора. Много синтаксических ошибок случа- ется также из-за недостающих или лишних фигурных и круглых скобок, и тут расцветка синтаксиса снова поможет вам. Вот некоторые из рекомендаций, которые могут сэкономить вам время на поиск причин возникновения сообщения о синтаксической ошибке: 1. Проверьте, сохранили ли вы все измененные вами файлы перед компиля- цией. 2. Если вы не можете найти ошибку в указанной строке, проверьте преды- дущие, бывает, что ошибка именно там. 3. Сверьте каждую букву и убедитесь, что вы правильно набрали имена пе- ременных и- функций. Очень многие ошибки происходят именно из-за опечаток. 4. Если ничего не помогает и вы никак не в силах понять причину возникно- вения ошибки, то попросите кого-нибудь о помощи. Свежий взгляд на ве- щи очень часто помогает найти то, что "замыленный" взгляд мог упустить. Ошибки компоновки Этот вид ошибок возникает при невозможности создания исполняемого фай- ла. Это могут быть неправильное использование или недостающие библиоте- ки, объектные файлы и тому подобное. Рассмотрим самый простой способ, которым можно вызвать этот тип ошибки. Просто попробуйте откомпилиро- вать и запустить программу, а затем, не закрывая ее, перекомпилировать ее заново. Вы получите сообщение об ошибке, так как файл запущенной про- граммы не может подвергаться изменениям до выхода из программы (впро- чем, на Ыпих это не распространяется). —(---------------------------------------- «гжЛийе * .лл I | : -л «лпС-'л агдс. гл;- *аг^О) аСогдс, агду); г-лл1" с.ехесС); } Рис. 48.30. Ошибка компоновки
На рис. 48.30 приведен еще один пример ошибки компоновщика: мы объяви- ли функцию, но забыли реализовать ее. Заметьте, что сообщение об ошибке пришло не от компилятора, а от компоновщика, так как создание компилято- ром объектного кода для показанного файла пройдет успешно, но на этапе компоновки будет не найден объектный код для функции сезс (). Так же как и в случае с синтаксическими ошибками, для успешного создания исполняемого файла программы необходимо устранить все ошибки компо- новки. Ошибки времени исполнения После того как будут устранены все (если они были) синтаксические ошибки и ошибки компоновки, компилятор создаст исполняемый файл. Но ошибки могут случаться и во время исполнения программы, причем они могут при- водить к исключительным ситуациям и к аварийному завершению програм- мы, так называемому крэшу. Типичными причинами крэша может быть, на- пример, деление на ноль, извлечение корня из отрицательного числа, выход за границы зарезервированной памяти, ошибки устройств и т. д. Подобные ошибки случаются не часто, но надежная, устойчивая система должна быть к ним готова. Логические ошибки Из всех четырех перечисленных нами категорий ошибок логические ошибки найти наиболее трудно, так как их истоки кроются в ошибочном рассужде- нии разработчика на пути поиска решений поставленной задачи. Здесь сце- нарий такой: приложение вроде бы выполняется без ошибок, однако выдает неверные результаты. Еще хуже, если неверные результаты получаются не всегда, а только в редких ситуациях. Такие феномены обычно обнаружива- ются только при анализе спецификации и в результате работы с самой про- граммой, на основании чего можно сделать вывод, правильно ли она работа- ет. К сожалению, компилятор не в состоянии оценить смысл написанного и проверяет только правильность использования синтаксиса, поэтому только тщательное тестирование в самых разнообразных условиях и с различными исходными данными может дать гарантию того, что программа не содержит логических ошибок. Модульное тестирование, описанное в главе 46, способ- но свести появление логических ошибок к минимуму. Один из самых простых способов локализации и нахождения в программе логических ошибок— это трассировка, то есть пошаговое прослеживание всех операторов программы. Отладчик может при этом отображать значения указанной переменной или выражений в любой точке программы. Другой,
гоже очень простой способ— это использование вывода промежуточных результатов при помощи функции дИеЬидО. Ее вставка в стратегических точках программы помогает обнаружить необычное ее поведение, что часто приводит к раскрытию логических ошибок. Этот способ можно использовать и отдельно от отладчика. В силу своей универсальности он выручил много программистов и спас огромное количество программ. Этот способ мы уже рассмотрели в главе 3, поэтому остановимся лишь на трассировке. Трассировка Встаньте на первую строку функции тахпО и нажмите <Р9>. Вы увидите появившийся кружок красного цвета (рис. 48.31). Это контрольная точка, ее мы разберем позже. А теперь нажмите <Р5>, и по завершении цикла компи- ляции и компоновки вы окажетесь в интегрированном отладчике (^1 Сгеаюг, и программа начнет выполнение с первого оператора в функции та±п(). Если в программе были установлены контрольные точки, программа будет пре- рывать свое выполнение в них. Но если контрольных точек нет, то выпол- нение программы будет продолжаться до тех пор, пока она не закончится нормально или аварийно. В нашем случае установлена всего одна кон- трольная точка. На рис. 48.31 показана часть окна редактора, на которой видна трассировоч- ная стрелка отладчика. Когда вы видите эту стрелку, то можете быть увере- ны, что отладчик запущен. На рис. 48.31 эта стрелка находится напротив оп- ределения объекта класса ОАррИсаьхоп. Об этой стрелке важно помнить, что она показывает не на строкуч которую вы только что исполнили, а на ту, которая будет исполняться, когда вы сделаете следующий шаг трасси- ровки. Кнопки для этих команд расположены на панели, которая показана на рис. 48.32. С их помощью можно выполнить одну строку программы. После выполнения одной строки программа вновь приостанавливается, и можно посмотреть значения переменных или выполнить другие команды отладчика. М + в 1лх та1п(1пх агдс, сЬаг *агду[]) ; { ОАррНсаИоп а(агдс, агду); ' МсйгЖпскж и; я ш.зьожО; 5 геХигл а.ехесО; } Рис. 48.32. Панель команд трассировки Рис. 48.31. Трассировка
Команд трассировки всего три: □ 84ер Оуег (Перешагнуть); □ 81ер 1п1о (Войти); □ 81ер Ои1 (Выйти). Помимо кнопок трассировки первыми на этой панели (рис. 48.32) располо- жены кнопки для продолжения исполнения программы (СопНпие) и останова отладчика (81ор ОеЬи^ег). Команду Сопйпие используют для продолжения выполнения программы, после того как она была приостановлена с помощью 8<ор ОеЬи^еег или при достижении контрольной точки. С помощью коман- ды 81ор ОеЬи^ег можно приостановить выполнение программы. Выполне- ние будет приостановлено в том месте кода, которое будет достигнуто к мо- менту, когда программистом при отладке была выбрана для исполнения команда 81ор ОеЬи^^ег. Команда 81ер Отег После подачи этой команды исполняется текущая строка программы и про- исходит остановка на следующей строке. Если текущая строка являлась вы- зовом функции, она будет вся исполнена, если, конечно, в этой функции не была расположена контрольная точка. Давайте исполним эту команду и уви- дим, как текущая строка будет исполнена и трассировочная стрелка перейдет к следующей строке (рис. 48.33). У 4 в тспп(1.п! агдс, сЬаг *агдуЦ) 5 { 6 ОАррНсаЫоп а(агдс, аг^); Ма1ПйН.гк1ом 8 к.51юм(^; ’ 9 геЪигп а.ехесО* 19 } Рис. 48.33. Исполнение 51ер Оуег Команда 81ер 1п1о Эта команда также исполняет текущую строку программы, но если она явля- ется вызовом, то отладчик проследует и остановится на первой строке внутри этой функции или метода. То есть этой командой нам предоставляется пре- красная возможность входить в вызываемую функцию или метод и затем вы- полнять ее построчно. При работе со стандартными функциями часто отдают предпочтение команде 81ер Оуег, так как их содержимое редко представляет
интерес. Теперь давайте выполним команду 81ер 1п(о. После этого мы ока- жемся в конструкторе нашего класса махпихпйо» (рис. 48.34). I * Мо1пИ1гнкЖ1: :Ма1пй1гк1о*<0МК.йде1 *рагепе) в> 5 В : 0Ма1пМпЗоиСрагеп1), и1(пеи Ш: :Иа1ПШп4смС1ск5) с { и1->5е1ири1С«:1п5); « ) Рис. 48.34. Исполнение 81ер 1п1о Команда 81ер Ои1 Эта команда заставляет программу завершить функцию или метод, в которой находится отладчик, и остановиться на строке, следующей после вызова функции. В нашем примере, как только мы воспользуемся этой командой, отладчик окажется на вызове метода з1ю«(), который следует сразу за вызо- вом конструктора (рис. 48.35). 4 □ хп! лю1пС1п1 агдс, сЪаг *агду[]) { 6 ОАррИсаНоп а(агдс, агду); ? МатгйНггккм ш; з (*.5Ьо»0; 9 ге±игп а.ехесО; } Рис. 48.35. Исполнение $1ер ОШ Контрольные точки Теперь поговорим немного о контрольных точках, которыми мы уже успели воспользоваться в нашем примере. Подчас использовать команды 81ер1п и 84ер Оусг с целью подойти к интересующему нас месту приходится очень долго, прогоняя отлаженные ранее участки программы. Контрольные точки представляют собой выделенные предложения, где необходимо приостано- вить выполнение программы, для того чтобы разобраться, что происходит. Можно просто установить контрольную точку в этом месте и запустить от- ладчик, и мы уже там. Так как они являются фундаментом для отладки, (^С СгеаЮг делает их уста- новку и удаление очень простой. Для того чтобы установить точки контроля,
нужно поставить курсор редактора на нужную строку кода и выбрать в меню ПеЬи§ | То8§1е Вгеакрош! (Отладка | Переключить контрольную точку). В начале строки появится метка в виде красного кружка, которая указывает на то, что здесь установлена контрольная точка. Для удаления контрольной точки необходимо щелкнуть мышью на строке, помеченной красным круж- ком, и выбрать в меню ту же команду ВеЬи§ | То§§1е ВгеакропН. Количество контрольных точек не ограничено, и вы можете установить их столько, сколько вам необходимо. Чтобы не запутаться в том, сколько кон- трольных точек в программе и где они находятся, существует специальное окно, отображающее их все (рис. 48.36). Двойной щелчок на интересующей вас точке сразу же откроет в редакторе нужный файл и поместит курсор на месте ее установки. УййсЬег» .[ Вгеакро!пк КитЬег ГипсЬоп Г<1е Ипе СопсПЪ 1 тат тат.срр 7 м 3 МеЬВгоу^ег №ЬВго^5ег(ОМс(деГ) «еЬЬготег.срр 36 4 \УеЬВгол5вг:.доТоО ^еЫэго^бег.срр 42 Рис. 48.36. Окно контрольных точек Окно переменных (1_оса1 апс! УУа1сЬеб) При выполнении кода в отладчике основное внимание должно быть направ- лено прежде всего на проверку значений критических переменных. Именно для этого существует отдельное окно, где отображается вся информация, ко- торую вам, возможно, потребуется знать о переменной. Это окно содержит три колонки переменного размера. Отладчик автоматически отображает в этих колонках имя, значение и тип переменных. Если в строчке показан массив, объект или структурная переменная, то рядом с именем отображается треугольник. Щелкнув на нем, можно свернуть или развернуть представление переменной. При последовательном разворачива- нии переменной и ее компонентов отображается дерево из объектов, которые она содержит. Повторным щелчком можно свернуть переменную. Значения структурированных переменных, таких как массивы, структуры и классы, не отображаются в окне отладчика, и для того чтобы увидеть все значения, входящие в структурированную переменную, необходимо щелк- нуть мышью по небольшому серому треугольнику, указывающему на имя соответствующей переменной.
Так как Ог СгеаЮг разработан для упрощения процесса отладки (^-приложений, он предоставляет дополнительные возможности, недоступные в других от- ладчиках, рассчитанные на специфику (^-классов. Таким образом, перемен- ные, за которыми вы наблюдаете, при отладке предоставляются в удобной для чтения разработчиком форме. Например, если вы наблюдаете за пере- менной типа огне, то первым вы увидите имя файла; то же касается объек- тов класса оЫзс, которые отображаются как настоящие списки со значениями. Иаше ▼ О’Л'.дде! X ( т_ру%^ Уа1ие Туре 0хЫТ!Т99с ЖеЬВготег • М/еЬВготег 0№с1дес 0х533д10 аипеЕдН* 0x537840 (МеЬУ.е*’ Рис. 48.37. Окно переменных Окно цепочки вызовов (Са11 81аск) В отдельном окне выводится список активных функций, составленных как цепочка вызовов, которая отображает порядок обращения к функциям. На- пример, если при выполнении функции ша1п() был вызван конструктор махпнхпдом, а в нем был вызван метод аегиртн о и т. д., то цепочка вызовов будет выглядеть, как показано на рис. 48.38. 1еуе1 ' Гипсгюп Г<1е иле 0 О8а51сАсот1с1лС.:с1егеГ ра(отгс_г386.п 128 1 05Сгтд:МЖгтд цяппд.И 825 2 исМа^чпдо-лС1а55. 37 3 та«шпс1(жхрр 9 4 тат та1п,срр 7 : Рис. 48.38. Окно цепочки вызовов
Резюме 01 СгеаЮг — это интегрированная среда разработки, специально рассчитан- ная на специфику 01. Он предоставляет разработчику весь необходимый ин- струментарий для создания программ. Его использование облегчает работу и позволяет редактировать и компилировать программы без необходимости использовать другие приложения. Типичные действия, которые можно про- извести в Ос СгеаЮг: □ создать базовое исполняемое приложение при помощи мастера проектов без написания кода; □ редактировать исходный код ваших программ; □ добавлять и создавать новые файлы; □ создавать графическую оболочку для вашего приложения интерактивно, при помощи встроенного 01 Ве$1§пег; □ компилировать и компоновать программы; □ . отлаживать готовые программы. Для отладки программ Ос Сгеаюг предоставляет интерактивный отладчик. Существуют ошибки синтаксиса, компоновки, времени исполнения и логиче- ские ошибки. Отладчик используется для выявления места и причин возник- новения двух последних типов ошибок. Для этого отладчик предоставляет возможность установки контрольных точек, команды трассировки 8Сер Оуег, 8Сер 1пЮ, 8Сер ОиС и окна наблюдения за переменными и цепочками вызовов.
Глава 49 Рекомендации по миграции программ из 013 в 014 "Если вы в своей квартире, лягте на пол — три, четыре.. Владимир Высоцкий Переход с <213 на <214 не такой уж безболезненный, как может показаться на первый взгляд. На самом деле, р14 подверглась массе изменений, внедрению новшеств и удалению всего того, что устарело или начинало устаревать. Так что 014 — это не просто следующая версия библиотеки, а результат полного пересмотра и переосмысления, со стороны разработчиков, того, как можно сделать программирование с использованием 01 еще более интуитивным. К тому же внедрены многие новые подходы и технологии. Бблыпая часть библиотеки была написана с нуля,.так что ее можно в полной мере назвать новой библиотекой. Такие колоссальные изменения еще не происходили за всю историю существования 01. Но их результат — практически полная не- совместимость с предшествующими версиями. Как же тогда быть с кодом старых 013-программ? Чтобы помочь разобраться с этим вопросом, я и решил включить в эту книгу главу. Более того, в этой главе делается акцент на отличия новой библиотеки от старой, благодаря че- му она может оказаться полезной также и в том случае, если вы и не собирае- тесь переводить свои программы с (213 на <214, но уже имеете опыт разработ- ки в <212 или (213 и хотите начать разрабатывать программы на (214. В этом случае, вы можете воспользоваться ею в качестве быстрого введения в 014. Основные отличия 014 от 013 Прежде чем приступить к переводу ваших программ на 014, необходимо разо- браться в основных отличиях. Это поможет более осознанно принимать реше- ния в процессе перевода. Если вы уже ознакомились с предыдущими главами
этой книги, то наверняка некоторые из этих отличий вы уже заметили, но, тем не менее, я думаю нелишним будет еще раз обратить на них свое внимание. Итак, теперь (^14 — это не одна библиотека, а целая коллекция относительно небольших модулей. Основополагающий модуль — 01Соге. Этот модуль со- держит классы, не связанные с программированием графического интерфей- са. В него входят, например, ООЬзесЬ, ОЗСгхпд, 0Р11е, 0Т1тег и т. д. Модуль ОЮш — это краеугольный камень для графики и графического ин- терфейса пользователя. Он содержит такие классы, как оШвдее, ОРахпСег и т. д. Кроме этих модулей, 0( включает в себя также рОЯеМогк, (2(8д1, 01Хт1, ОЮрепОЬ, ОСГезС и др. Класс приложения ддррИсаЫоп в 0(4 разбит на два класса: осогеАррПсаЫоп, находящийся в модуле 0(Соге, и ОАррИсасхоп, находящийся в модуле ОгОш. Это сделано для того, чтобы можно было писать приложения без графиче- ского интерфейса, к которым в процессе компоновки не присоединялся бы ненужный в этом случае балласт в виде компонент графического интерфейса. Более подробную информацию о модулях и классах осогеАррИсаЫоп и ддррЫсаъАоп можно получить в главе 1. В СМ был класс СИ, который был базовым для подавляющего большинства клас- сов библиотеки. В 0(4 это больше не класс, а пространство имен. По этой причи- не использование каждого определения этого пространства должно сопровож- даться префиксом ос::, а если это нежелательно, в начале файла нужно указать изхпд пашезрасе 0^; Метод 0Арр11сас1оп:: зесма1пн1адес () в СМ-программах больше не исполь- зуется. Можете просто удалить его из исходного кода. Модель общего использования данных теперь предусматривает только неяв- ное разделение данных. Явное разделение, по причине возникающей путани- цы, при которой изменение данных одного объекта могло отразиться на дан- ных других объектов, было упразднено в СМ- Теперь классы овусеАггау, <ЭВ1САггау и охтаде и т. п., как и остальные, используют модель неявного разделения данных. Классы графического интерфейса Из (214 были изъяты все виджеты компоновок онвох и отвох, осг1а. Вместо них должны использоваться только классы менеджеров компоновки: ОНВохЬауоиЁ, ОУВохЬауоиЪ И ОСгхбЬауоиЬ.
Классы более не имеют громоздких конструкторов с многочисленными па- раметрами. Например в 0(3 создание виджета ползунка выглядело бы сле- дующим образом: ОЗИйег* рз1<1 = пею 05Ис1ег(1, 12, 1, 3, ОЁ::Ног12ОПЁа1г СЫв, ”Му5Ис5ег") ; В 0(4 это выглядит так: ОЗЦйег* рз1д = пезд ОЗИдег(ОС: :Ног12ОПСа1) ; рз1д->зеСКапде(1, 12); рэ1с(->8еСРадеЗСер(1) ; рз1с1->8еСУа1ие(50) ; Как вы видите в приведенном примере, в конструктор класса озИдег больше не передается указатель на виджет предка. О присвоении виджета предка те- перь заботятся менеджеры компоновки. Также в конструктор не передается имя объекта. При желании его можно установить с помощью метода ООЬд есС::зеСОЬ5есСИаше(). В 0(3 класс оуаадеь обладал методами, которые позволяли избежать прямого использования объекта палитры: ОМхйдеЬ::зеСРа1еССеРогедгоипаСо1ог(), ОКгадеС:: зеСРа1еССеВаскдгоипаСо1ог () И 0Н1адеС:: зеСРаХеССеВаскдгоипаРххгпар (). Вызов этих методов приводил к неявному вызову метода ОИхадеС:: гесРа1ессе (). Эта техника была признана бесполезной, и вышепе- речисленные методы были изъяты из 0(4. То есть в 0(4 теперь необходимо напрямую обращаться к палитре. Вместо того чтобы написать р«дС->зеСРа1еССеВаскдгоипасо1ог(со1ог); нужно будет сделать следующее: 0Ра1еССе ра1; ра1.зеССо1ог(рэддС->Ьаскдгоипако1е(), со1ог); ршдС->зеСРа1еССе(ра1); Также с аналогичным успехом можно воспользоваться С88-стилем и устано- вить цвет фона виджета при помощи всего одной строки: раддЁ.->8ек31:у1еЗ)1ееЬ ("Ьаскдгоип<1-со1ог: Ыие") ; В 0(3 для подавления эффекта мерцания нужно было самому реализовы- вать механизм двойной буферизации, для чего нужно было отключать ме- ханизм автоматического рисования фона. Это делалось вызовом метода ОНхддеЪ:: зеъваскдгоипбмоде (). Этот метод был убран ввиду того, что меха- низм двойной буферизации встроен в 0(4 и используется по умолчанию. В 0(4 все методы класса ОРагпспеухсеМеСгхсз были перемещены в класс ОРахпъсетхсе, а сам класс удален. Некоторые из классов получили новые названия (табл. 49.1).
Таблица 49.1. Переименованные классы 601 013-класс 014-класс фВиССоп ОАЬзСгасЪВиЬЪоп 01соп8е<: фТсоп ОЮМа^гхх ОМаЪгхх ОСиагдедРЪг ОРохпЬег 08сго11Ухете ОЗсгоИАгеа рМхддеЁЗЬаск ОЗЪаскедЮхддеЪ Фундаментально изменилось использование старых классов оЫзьухеад, 01сопУ1е«, оыгсвох и отаЫе. В СИ4 произошло отделение представления от данных. Классы, реализующее старый подход, оканчиваются теперь на нхбдее, а классы с окончанием Ухе» являются классами представлений тех- нологии интервью, которую рекомендуется использовать для новых разрабо- ток (см. главу 12). Класс оЫзьитадее заменил два старых класса: дЫгьвох И ОХсопУхем. Контейнерные классы Контейнерные классы для указателей были убраны из 914. Вам нужно будет заменить их обычными контейнерными классами. Например, вместо ОРегЫзс<ои1адеь> нужно будет использовать 2Ызс<ои1адеь*>. При этом необходимо помнить, что обычные контейнеры не удаляют содержащиеся в них объекты, поэтому вам самим придется позаботиться об этом. Напри- мер, строчку Иве. гетслге (хпдех) следует заменить на де1еее 1хзС. ЪакеАП (хпдех). Класс 0Уа1иеЫзс заменили на два класса: оызс и оЫпкеаЫгс. Теперь немного о классе озегхпд. Не используйте статическую константу озьгхпд: :пи11, так как в 9(4 она существует только для совместимости, а вместо нее нужно использовать ОЗСгхпдО ИЛИ ОЗЪгхпд: :с1еаг(). Методы ОЗЪгхпд: : ^хпд () И рЗЁгхпд: : ^^пдКеV() нужно Заменять на ©Зегхпд: : хпдехОС () И ОЗЬгхпд: : 1азЫпдехО{ (). Класс осзьгхпд из 9*4 был убран, а его функциональность получила вопло- щение в классе овусеАггау. Поэтому вместо использования класса осзегхпд теперь нужно использовать овуеедггау.
Классы программирования сети Классы 0Меъ«огкРгососо1 и оигЮрегаъог были убраны из 014, но остались Орср и оныхр, поэтому используйте эти классы. Более того, интерфейс клас- сов окьр и он с ер остался почти без изменений. Некоторые классы получили новые имена, более точно отражающие их назначение (табл. 49.2). Таблица 49.2. Переименованные классы сети 013-класс 014-класс фОпз ОНовЫп^о 05е!гуег3оске1: дтсрзегтег ОЗоскеЪ ОТсрЗоскеЪ Классы для программирования баз данных Из 014 были удалены следующие классы: 03д1Сигзог, озд1кесогд1п€о, ОПаьаЗоигсе и ооаеаЗтпк. Также удалены классы осведомленных элемен- тов ОЭаСаТаЫе, ООаСаУхем, 03д1Ропп И ОВаЬаВгомзег. Теперь вместо НИХ нужно использовать классы, базирующиеся на концепции интервью (см. гла- вы 12 и 42). 01 Ое$!дпег Из 0< Ве$1§пег был убран менеджер проектов и окно редактирования кода С++. Теперь в нем отсутствует возможность создания собственных сигна- лов и слотов. Растровые изображения записываются в файл ресурсов, а не в ш-файл. Более подробную информацию можно найти в главе 45. Начало перевода на 014 Первое, чем нужно запастись для начала перевода, — это терпением. По- ставьте легкую успокаивающую музыку и выпейте бокал вина. Для людей с более импульсивным характером рекомендую заменить успокаивающую музыку на Капшшсет, Ольгу Романову (Оагк Рппсезз), Ер!са или Еуапевсепсе, а вино — на кофе и сигареты. Единого рецепта здесь нет, словом, делайте все, что может помочь вам сохранить эмоциональное равновесие. Готовьтесь к тому, что вам часто придется обращаться к оригинальной доку- ментации поэтому если вы еще не успели подружиться с программой 01А$ы81ап1, то сейчас самое время это сделать. Особое внимание обратите
в ней на раздел Рогйп§ Ггот 0(3 (о 0(4, в котором вы найдете ответы на большинство интересующих вас вопросов, связанных с переводом ваших программ на 0(4. Модуль совместимости СК38иррог1 Модуль совместимости представляет собой набор классов для облегчения процесса перевода программ с 0(3 на 0(4. Все классы этого модуля реализо- ваны посредством 0(4. Благодаря модулю совместимости можно оставлять многое в старом коде без изменений. Но для этого названия некоторых старых классов должны быть изменены таким образом, чтобы после первой буквы стояла тройка, например: класс овисеоп должен быть переименован в озвисеоп. Также необходимо включить заголовочный метафайл с опреде- лениями классов поддержки: #1пс1ис1е <0Ь35иррог1:> Необходимо включить в рго-файл и сам модуль для его компоновки с основ- ной программой. Для этого в секции ОТ нужно указать опцию деЗзиррогс: ОТ += дСЗзиррогС Переименовывать классы вручную — утомительно, поэтому 0(4 предостав- ляет утилиту ц(3(о4, название которой может быть ошибочно истолковано. Не обольщайтесь, эта утилита не является автоматическим средством для пе- ревода на 0(4, а просто автоматически переименовывает классы. В эту утилиту можно передавать исходные файлы .срр, .И, а также и рго- файлы. При передаче рго-файла произойдет перевод не только этого файла, но и файлов, принадлежащих этому проекту, а также будет автоматически добавлен модуль совместимости д(38иррог(. Прежде чем вы запустите утилиту, позаботьтесь о резервной копии всех фай- лов проекта. Для достижения лучшего результата утилита ц(3(о4 использует синтаксиче- ский анализатор. А это значит, что все включаемые заголовочные файлы должны быть доступны. Если же это не представляется возможным, то нуж- но запустить эту утилиту с параметром -аАзаЫесррРагзхпд. Правила перевода, используемые утилитой ц(3(о4, записаны в файле дЗрогйп§.хт1. Его можно дополнить своими собственными правилами, либо отключить некоторые существующие. При запуске утилиты свой файл пра- вил можно указать в командной строке в опции -гиХезЕИе. Сам файл может выглядеть следующим образом: <Ки1ез> <1пс1иде>/дЁ/Ьоо1з/рогЪ1пд/5гс/дЗроге1пд.хт1</1пс1и<Яе>
<1Ъет Туре="Кепате<аНеадег" В1заЫе="Тгие"> <0Г4>дЗдаЬаЬго»зег,Ь</0Ъ4> <ОЬЗ>д<За«:аЪгоздзег ,Ь </ОЬЗ> </1Ьет> <1Ъет Туре="КепатеС1азз"> <0Ъ4>23Сапуаз</0е4> <0ЪЗ>0Сапуаз</0ЬЗ> </1Сел1> </Ки1ез> В качестве практического примера давайте переведем 013-программу из пер- вого издания этой книги. Исходный текст программы вы можете найти на СТ) в каталоге ХОсЗРПеРшёег. Итак, запустим утилиту д13(о4, передав ей в качест- ве аргумента имя рго-файла: дЬЗЬоД РИеГ1п<Зег .рго В ответ вы получите следующее приглашение на консоли: Изхпд ги1ез СНе: С:\0Ъ\4.5.0\Ьоо1з\рогС1пд\згс\дЗрогС1пд.хт1 Рагзхпд.. СотгегС ЕНе С:\0ЬЗР11еР1пс1ег\Р11еР1пдег.срр? (У)ез, (Л)о, (А)11 А Выберите вариант перевода сразу всех файлов проекта, нажав <А>, а затем клавишу ввода. После работы все результаты будут запротоколированы в файле рогНп§1о§.Ш, который можно просмотреть перед тем, как запускать утилиту цтаке. В нашем случае из этого файла мы увидим, что ^^3^о4 произ- вел двенадцать изменений в трех файлах РПеРтйег.срр, РйеРшдег.Ь и рго- файле. Исследуя протокол, мы можем получить точную информацию обо всех сделанных этой утилитой изменениях. В нашем примере процесс компоновки прошел гладко. Но могло случиться и так, что после запуска процесса компоновки вы получите ошибки при ком- пиляции, связанные с тем, что не все классы библиотеки совместимости под- держивают методы старых классов. В подобных случаях необходимо найти подходящую замену в классах 014. Но ведь замену не всегда удается найти! В этом случае у вас нет другого выхода, кроме как самому реализовать класс для замены. Для перевода форм, созданных в 011>е$1§пег, из 0<3 в 014 используется ути- лита шсЗ. Она переводит все файлы форм, перечисленные в рго-файле в сек- ции ЕНОМ53. Эта секция вносится в рго-файл автоматически при обработке его утилитой я(31о4. Как уже было упомянуто ранее, в 01 Ое81§пег теперь нез возможности создавать свои собственные сигналы и слоты, поэтому в про- цессе перевода могут возникнуть предупреждения об отсутствии поддержки слотов.
Завершение перевода на 014 Завершающий этап перевода на 0(4 заключается в расторжении всех зависи- мостей С модулем ОЬЗЗирроге. Возникает правомерный вопрос: "А зачем это делать, ведь программа теперь запускается и все функционирует нормально?" Причина проста — Ос не мо- жет вечно поставляться с модулем поддержки для 0(3, и с появлением 0(5 этого модуля уже не будет. К этому нужно быть готовым, так как рано или поздно этот шаг придется сделать. И именно поэтому лучше сделать его сразу. Кроме того, используя модуль совместимости, всегда есть риск использовать что-нибудь из него, а это крайне нежелательно. Поэтому все связи с модулем необходимо "рвать" как можно быстрее. Чтобы быть до конца уверенным в том, что ничего из модуля О38иррог( на- шим проектом больше не используется, его необходимо убрать в рго-файле из секции ОТ. При проведении завершающего процесса перевода, для начала нужно оты- скать, в исходном коде проекта, все классы, начинающиеся на 03, и заменить их на классы 0(4. Например, все 03Ви((оп потребуется заменить на ОАЬ$(гас(Ви((оп. А затем заменить в исходном коде все встроенные методы совместимости на методы 0(4. Что просто произнести на словах, не так-то просто складывается на деле. Ос- новная причина в том, что многие классы не имеют подходящей замены, и это может потребовать небольших изменений в архитектуре самой про- граммы. Но каждый проект в отдельности нуждается в индивидуальном под- ходе. Потому мне остается только пожелать вам успехов в переводе ваших проектов на 0(4. Резюме Четвертая версия библиотеки 0( принесла с собой массу изменений. Процесс перевода 013-программ может оказаться совсем не легкой задачей. Для ее облегчения 0(4 предоставляет специальный модуль совместимости О(38иррог( и утилиту д(3(о4, которые помогут вам при переводе вашего 013- приложения на 0(4. После автоматического перевода на новую версию библиотеки, программа использует модуль совместимости. Завершающая стадия перевода программ на 0(4 заключается в устранении всех зависимостей от него.
| II и 1%° -тР Часть VII Язык сценариев 01 8спр1
Глава 50 Основы поддержки сценариев "Хороший сценарий состоит в основном из диалога". Ричард Уолтер Как правило, при создании сложных программ не всегда можно предоставить для пользователей все необходимые им функции, и тем или другим будет че- го-то не хватать. Выход из этой ситуации — реализация основных возможно- стей приложения на С++ и предоставление интерфейса для языка сценариев, позволяющего пользователям самим реализовывать необходимые им функ- ции. Такой подход позволяет удовлетворить запросы одних пользователей, которые не нужны другим. Кроме того, приложения со встроенным языком сценариев упрощают предоставление поддержки для пользователей ваших программ. Таким образом, можно реализовывать код устранения ошибок пу- тем их обхода (жогкагоипд), а также добавить некоторые дополнительные возможности, необходимые для какого-либо конкретного пользователя, пря- мо на месте и без вмешательства в исходный код основного приложения. Это свойство делает программы с поддержкой языка сценариев привлекательны- ми и для фирм, которые могут перепродавать программный продукт вместе с собственными решениями, реализовывая их в виде сценариев. Полезное свойство использования языка сценариев заключается и в том, что для приложений, которые производят длительные операции, можно создать сценарий, способный автоматизировать их исполнение. Еще одна возможность, открывающаяся с использованием в приложениях языка сценария — это реализация гибридного приложения, часть компонент которого реализована на С++, а другая часть — на языке сценария. При этом важно помнить о недостатке интерпретируемого языка, который состоит в том, что для выполнения кода затрачивается сравнительно много време- ни. Поэтому реализовывать компоненты, которые необходимо исполнять особенно эффективно, лучше на С++. Но есть и преимущество: с помощью
интерпретируемого языка можно легко и быстро усовершенствовать исход- ный код программы. Поддержка языка сценариев в приложении — отнюдь не новая идея, она уже прошла проверку временем в таких известных программных продуктах, как М1СГО8ОЙ СНйсе, Соге! Вга\у, МасготесНа НазЬ, Етаск,АЫасйу и т. д. <2с8епр( — это новый модуль, который доступен в <2с начиная с версии 4.3. Он представляет собой среду, обеспечивающую встроенную поддержку сце- нариев в приложениях, написанных на С++ с использованием 01. Он включа- ет в себя классы С++, которые, собственно, и позволяют осуществлять под- держку сценариев в 01-программах, и интерпретатор языка сценариев Ос 8спр(, базирующегося на стандарте ЕСМА-262 (также известном как 1ауа8спрС 2.0,18спрС.ИЕТ или ЯазЬ АсСюп8спрС). Язык сценариев Ос8спр( объектно-ориентирован и использует метаобъект- ную модель, подобную 01. Он также обладает возможностями современных языков, такими как использование и создание классов и управление исклю- чениями. Синтаксис этого языка подобен С++ и 1ауа, но менее сложен. Одно из его достоинств заключается в том, что это сравнительно небольшой язык. И если вы поставляете свой продукт вашим клиентам вместе с документаци- ей, то это облегчит вам задачу при ее написании. Благодаря тому, что поддержка языка сценариев встроена в 01, с ее помощью можно управлять (^-программами без их перекомпиляции, вносить в них из- менения, реализовывать тестовые сценарии, производить настройки прило- жения для специфических запросов пользователей. Принцип взаимодействия с языком сценариев Для реализации поддержки языка сценариев задействованы такие механизмы, как сигналы и слоты, объектные иерархии и свойства объектов (ргорегйек). с которыми хорошо знакомы все программисты, использующие ОС. Никакого дополнительного кода не требуется, кроме того кода, который будет соз- дан препроцессором МОС (МеСа ОСуесС СотрПег). Поэтому, для того чтобы унаследованный от ооь^есь класс сделать доступным для использования в Ос8спрС, необходимо наличие метаинформации, а значит, в определении класса должны использоваться макросы о_оыест и о_ркоректу для его свойств, с которым мы уже встречались в главе 2. В результате, каждый класс и подкласс ооьзесп, а также все их свойства, сигналы и слоты, будут доступ- ны из языка сценариев. Несомненный плюс этого подхода заключается в том, что все унаследованные от ооьэесь классы могут быть доступны для языка
сценария без дополнительных усилий. Поэтому сделать доступными объекты уже существующих в 01 классов очень просто. А как сделать собственные классы, унаследованные от ооЬэесь, доступными для 018спр1, мы покажем на примере (листинг 50.1). с1азз МуСХазз : риЬИс ООЬдесХ ( 0_овггЕст 0_РКОРЕКТУ (ЬооХ геадОпХу МК1ТЕ зеЪКеадОпХу КЕАЛ ХзКеасЮпХу) рг ^а Ъе: ЬооХ пЦЖеасХОпХу; риЬИс: МуСХазз (ООЬдесЁ* роЬэ = 0) : ООЪэесЬ(роЪз) , т_ЬКеа<ХОп1у (1аХзе) • { } риЬИс з1оЪз: тохс! зееКеасЮпХу {ЬооХ ЬКеасЮпХу) ( хц_ЬКеасХОпХу = ЬНеасЮпХу; епиЛ геасХОпХуЗЁаЁеСЬапдед () ; } ЬооХ 1зКеад0пХу() сопзё. { геЁигп пЦэКеасЮпХу; } зхдпаХз: \го1(Х геабОпХуЗЪаЪеСИапдед (); }; Класс, унаследованный от ооьзесь, управляет изменением состояния логиче- ского атрибута (т_ьвеасЮп1у) (листинг 50.1). При создании объекта, атрибут инициализируется значением 1а1ае. В классе определены слоты для установки (зеЬКеад0п1у () ) И получения (1зКеад0п1у ()) значения атрибута Щ_ькеааоп1у.
У вас наверняка возникнет вопрос: зачем мы определили эти методы как сло- ты? Все очень просто — если мы хотим сделать какие-либо методы класса видимыми для языка сценариев, то их необходимо определить как слоты. Но, в данном случае, это не обязательно, так как для изменения и получения зна- чений предусмотрено свойство геабоп1у, которое ассоциировано с этими ме- тодами. И МЫ МОГЛИ бы определить веСКеайОпТу {) И 1зКеай0п1у () как обыч- ные методы, тем самым скрыв их от использования в языке сценариев. Манипулировать логическими значениями объекта класса из языка сценариев при помощи слотов можно следующим образом: гпуОЬд есЬ. зе!:Кеад0п1у (Ъгие); ргтпе ("шуОЬэесЪ хз геа<3 оп1у:" + туОЬэес1:.х5Кеас1Оп1у () ) ; Такая форма привычна для разработчиков на С++, но не для разработчиков на языке сценариев. Для них будет привычна форма изменения значений при помощи свойств, которые наш класс также предоставляет: пгуОЬэес1:.геасЮп1у - 11гие; ргхгП: ("туОЪз ес± хз геад оп1у:" + шуОЬзесЬ .геад0п1у); На практике обычно нужно создать сразу несколько подклассов ооьэесъ, ко- торые будут использоваться для изменения состояния приложения. Поэтому модуль 01§спр1 позволяет разработчикам делать выбранные ими объекты приложения, которые унаследованы от класса ооЬдесс, доступными для язы- ка сценария. Это позволяет добиться динамического предоставления функ- циональности модулю <2<§спр(, и поэтому нет необходимости перекомпили- ровать код основной программы. Но с этим подходом связан и недостаток. Ввиду того что мы получаем из языка сценариев доступ только к конкретным объектам приложения, невозможно получить из сценария доступ сразу ко всей функциональности библиотеки 01 или к функциональным особенностям всего приложения. ЬцО1 Г1 <3г! г 1 01 Приложение [Объект 1 ([Ббъе^З^ [Объект 2 Рис. 50.1. Взаимодействие объектов приложения со сценариями На рис. 50.1 показаны взаимосвязи между механизмом поддержки сценариев, приложением и сценариями приложения, в котором мы делаем доступными
для сценариев два объекта "ОбъектЗ" и "Объект4”. Сценарии получают дос- туп к этим объектам, как будто эти объекты встроены в сам язык (X 8спр(. Объекты могут быть либо объектами приложения, либо объектами, специ- ально созданными с целью предоставления возможности сценариям для про- стого управления приложением. С унаследованными от ооьзесс классами теперь, я думаю, все понятно, но как быть с классами, которые не унаследованы от ооьзесе? Ведь может по- лучиться так, что вы используете стороннюю библиотеку, не имеющую ни- какого отношения к 01, и хотите сделать некоторые ее классы доступными для языка сценариев. В подобных ситуациях вам придется реализовывать, для каждого необходимого вам класса, класс обертки (у/гаррег), унаследо- ванный от ооьэесь. Это класс будет агрегировать объект нужного вам класса и предоставлять делегирующие методы. Предположим, что нам нужно сде- лать в языке сценария доступным С++ класс, не использующий (лис- тинг 50.2). с!азз Ыоп0СС1азз { рггуаЪе: Ьоо! Го—ЪКеас10п1у ; риЬИс: ЫопОЁСХазз {) : ш_ЬКеас10п1у (1а1зе) { } \7‘о1с1 зе^Кеа<ЯОп1у (Ьоо! ЬКеадОп1у) { ш_ЪКеас1Оп1у = ЬКеадОп1у; ) Ьоо! 1зКеадОп1у() сопзЬ { геШш т_ЬНеадОп1у; } }; Представленный в листинге 50.2 класс цопоьстазз не наследует ни одного класса. Для того чтобы можно было использовать этот класс в языке сиена-
риев, нам необходимо реализовать класс обертки, как показано в листин- ге 50.3. .г .«.да.» иЧй..и^.«ГТГ.ГГ п,..д..7.ч. с1азз МуМгаррег : риЬИс ООЪдесС { 0_ОВСГЕСТ 0_РКОРЕНТУ(Ьоо! геа60п!у ИП1ТЕ зеСКеа60п1у НЕАС хзНеайОпТу) ргалгаЪе: Ыоп0СС1азз пцаопОСОЬэесЪ; риЬИс: МуМгаррег(ООЬэесЪ* роЬ} =0) : ООЬдесЬ(роЬЗ) { } риЬИс з1оЪз: Vо^с^ зеЪКеа<1Оп1у (Ъоо1 ЬНеас1Оп1у) { т_попрЪОЬэ есС. зеСКеас1Оп1у (ЬНеа<Юп1у) ; ета.С геа<Юп1у5СаСеС11апдед() ; } Ъоо1 1зКеа60п1у() сопзЬ { геСигп т_попфСОЬдесС.1зКеад0п1у(); } З1дпа1з: тохд геайОпХуЗСаСеСЪапдед(); }; Класс обертки мунгаррег, приведенный в листинге 50.3, агрегирует объект класса ыопоес1азз и предоставляет делегирующие слоты зескеааоп1у () и 1зйеадоп1у() для манипулирования его значениями. Также он определяет свойство геайоп1у, ассоциированное с этими методами. Использование клас- са муИгаррег из языка сценариев выглядит аналогично использованию клас- са. показанного в листинге 50.1.
Привет, сценарий От предоставления в программе поддержки языка сценариев любого (21- разработчика отделяют всего лишь несколько шагов. Чтобы продемонстри- ровать это, возьмем простой пример из главы 1 (рис. 50.2), отображающей виджет надписи с текстом "НеПо, ХУогИ !" (Листинг 50.4). Рис. 50.2. Виджет надписи #1пс1ийе <0ССи1> 1П1: та1п(1пЪ агдс, сЬаг** агду) { ОАррИсаЫоп арр(агдс, агду) ; 0ЬаЬе1 1Ы(мНе11о, Юог1й 1Ы. зком () ; геЪигп арр. ехес () ; } Первый шаг для предоставления поддержки сценариев заключается в добав- лении следующей строки в проектный файл: рт += зсггре Это нужно для того, чтобы приложение собиралось с модулем О(8спр{. Второй шаг — это включение в программу заголовочного метафайла ()18спр1: #1пс1ис1е <0ЬЗсг1рС> Теперь давайте перепишем пример, приведенный в листинге 50.4, организо- вав в нем поддержку языка сценариев. Возложим на язык сценариев ответст- венность за инициализацию виджета надписи текстом и его отображение. Для этого изменим программу так, как это показано в листинге 50.5. ГМ 05г #1пс1иде <ОЬ(Зи1> #1пс1ис1е <0Ь8сг1рЪ> 1пе та1п(1пь агдс, скаг** агду)
{ □АррИсаЪхоп арр(агдс, агду) ; рЬаЬе1 1Ы ; ОЗсгхрЪЕпдхпе зсгхрЬЕпдхпе; 05сг1рЪУа1ие зсгхрЬЬЫ = 8СгхреЕпд1пе.пеадООЪэес1:(&1Ы) ; зсгхр^Епдхпе. д1оЬа10Ь} есС () . зеЪРгорегЬу (" 1Ы ” , зсггрЪЬЫ); зсгхрЬЕпдхпе. ета1иаЪе (" 1Ы. ЪехЪ = ' Не11о, Мог1й I 'и); зсгхр^Епдхпе. еуа1иаЬе (и 1Ы. зкош () ”) ; ге^ига арр.ехес(); } В листинге 50.5 мы просто создаем виджет надписи. Далее создается объект класса озсгхръЕпдхпе, который является средой для исполнения команд язы- ка сценариев. Из объекта этого класса мы вызываем метод пеэдооЬэесс() со ссылкой на виджет надписи 1Ы, который возвращает объект класса 05сг1рсуа1ие. Этот объект является контейнером для хранения типа данных языка сценария. Поскольку добавлять свойства можно динамически, мы ус- танавливаем в глобальном объекте свойство нашего виджета надписи, с по- мощью метода ОЗсг 1рЬУа1ие: : зеСРгорегЬу (). После этого можно исполнять код сценариев на языке Р(8спр1, передавая его в метод еуа1иаее (), что мы и делаем. При первом вызове этого метода мы модифицируем виджет надписи (1Ы), присваивая его свойству ьехь стро- ку текста "Не11о, \Уог1с1!". При втором — вызываем его метод зПои(). Резюме Сколько бы не было реализовано функций в вашей программе, их все равно может оказаться недостаточно. Реализация приложений с поддержкой языка сценариев делает возможным динамическое расширение вашего приложения и его изменение под конкретные требования без необходимости перекомпи- ляции. Это позволяет разработчикам сосредоточиться на написании базовых функций и возможностей приложения. (X предоставляет возможность для организации поддержки языка сценариев в ваших программах при помощи модуля ОсЗспрС. Этот модуль содержит ин- терпретатор языка сценариев и классы С++ для его поддержки. Язык сцена- риев 918спр4 базируется на ЕСМА 8спр{ — популярном стандарте, полу- чившем распространение благодаря Интернету. Благодаря метаобъектной модели (X любой подкласс ооь^есе можно сделать доступным для языка сценариев.
Глава 51 Синтаксис языка сценариев Девушка-программист едет в трамвае, читает книгу. Старушка смотрит на де- вушку, смотрит на книгу, крестится и в ужасе выбегает на следующей оста- новке. Девушка читала книгу "Язык Ада”. Прежде чем приступить к написанию сценариев, нужно освоить их синтаксис. Если вы уже знакомы с программированием на таких языках, как 1ауа$спр1 или АсбопЗспрС, то можете пропустить эту и следующую главы и перейти сразу к главе 53, посвященной практическим примерам. Основное преимущество синтаксиса языка сценариев заключается в том, что он очень похож на С++ и 1ауа. Но, в отличие от этих языков, программа язы- ка сценария начинает свое исполнение не с конкретной функции, как, напри- мер, с функции ша1п() в С и С++, а е кода, находящегося вне функции, начи- ная сверху. Зарезервированные ключевые слова В любом языке программирования присутствует своя специфика; она вклю- чает в себя и список ключевых и зарезервированных слов, составляющих яд- ро для программирования на этом языке. Ключевые слова всегда доступны программисту, но для их использования нужно придерживаться правильного синтаксиса. В табл. 51.1 указан список ключевых слов 018спр1. Таблица 51.1. Ключевые слова О18спр1 Ьгеак саге саЪсЪ СОП311 соп^хпие с1е^аи1е до е1зе ^1па11у ^ог €ипсЬ1оп
Таблица 81.1 (окончание) 1П Ыею ргоЪоЬуре геЪигп 8 VI Ъс11 СЫз Ьурео! еИгом Ъгу Vа^ ЛлгИНе VI Список зарезервированных слов, предназначенных для будущего использо- вания, приведен в табл. 51.2. Таблица 51.2. Зарезервированные слова О! 8спр1 Ьоо1еап ЪуЪе скаг Ыесоюз доиЫе епшп ехрогЁ ^1оаЪ доЪо 1тр1етеп1:з хтрогЪ хпСег^асе 1опд паЁдлге зИоге Ключевые и зарезервированные слова, приведенные в табл. 51.1 и 51.2, нель- зя использовать в качестве идентификаторов. Комментарии Комментарии служат для пояснения кода сценариев. Не забывайте о том, что ваш код будут читать и изменять другие разработчики, да и вы сами по про- шествии времени позабудете, что*к чему, поэтому не скупитесь на них, и ва- ши усилия будут оправданы в будущем. Тем более, что комментарии никоим образом не влияют на эффективность исполнения вашего сценария и при ин- терпретации они будут просто проигнорированы. СИ 8спр1 предоставляет комментарии для одной строки (или даже ее части со знака комментария и до конца строки), а также и для целой серии строк: // одна строка /* много строк */ Переменные Переменная — это одна или несколько ячеек памяти компьютера, в которых хранятся определенные данные, а ее имя является своеобразной ссылкой на эти данные, при помощи которой данные можно считывать и изменять.
$спрс — слабо типизированный язык, и он не требует объявления всех переменных и определения их типов перед началом использования. Этим он отличается, например, от С++ и 1ауа. В этом и состоит его гибкость, так как вы не обязаны объявлять переменные конкретного типа. Переменные определяются при помощи ключевого слова уаг. Определение переменной может производиться в любом месте программы. Имя перемен- ной должно отвечать следующим требованиям: □ имя должно начинаться со строчной или заглавной буквы или знака под- черкивания. После этого могут следовать остальные символы имени: циф- ры, буквы и знаки подчеркивания; □ имя не должно содержать никаких специальных символов, например: !, ?, |, < и т. д.; □ имя не должно совпадать с ключевыми словами языка (см. табл. 51.1 и 51.2); □ одно и то же имя нельзя определять в пределах одной области видимости более одного раза. При определении переменные можно инициализировать значениями. Можно определять сразу несколько переменных с помощью одного ключевого слова Vа^. Vа^ х; //Переменная без инициализации Vа^ у = 100; //Переменная с инициализацией Vа^ 1, з = 100; //Определение нескольких переменных //с инициализацией одной из них Тип переменной задается при ее инициализации значением. Если перемен- ную просто объявить и не инициализировать значением, то ее значение будет ипйеЛпед, а ТИП ПпдеЛпед. НарИМер: уаг х; //имеет тип ПпдеЛпед и значение ипдеЛпед Хотя определение переменных при помощи ключевого слова уаг и является практикой хорошего тона программирования, но оно не является обязатель- ным, и можно прекрасно обойтись без него. Например: зСг = "Не11о"; Имена переменных чувствительны к регистру, а это значит, например, что х и х являются двумя совершенно разными переменными. Примечание Вы наверняка заметили, что в конце каждого оператора стоит точка с запятой (;). На самом деле в языке сценария это совсем не обязательно, но все-таки рекомендуется их ставить, это поможет вам избежать неприятных сюрпризов.
Предопределенные типы данных В (2< 8спр( при объявлении задавать тип данных не нужно, так как он опре- деляется автоматически при присвоении переменной значений и сохраняется до тех пор, пока не будет произведено следующее присвоение. В Ос 8спр( используются три основных типа данных: числовой (целый и вещественный), строковый и логический. Целый тип Целый тип представлен 32-битовыми значениями. Их диапазон лежит в пределах от -2 147 483 648 до 2 147 483 647. Значения могут задаваться в десятичной, восьмеричной и шестнадцатеричной системах счисления. В десятичном формате задаваемое число может содержать любую последова- тельность цифр, но не может начинаться с нуля, так как начинающаяся с нуля последовательность цифр считается числом, записанным в восьмеричной системе. В восьмеричном формате можно включать цифры в диапазоне от 0 до 7. Для представления числа в шестнадцатеричном формате, необходимо поста- вить в самом начале Ох или ОХ. В шестнадцатеричном формате можно вклю- чать цифры в диапазоне от 0 до 9 и буквы от А до Р. Например: Vа^ хОес = 123; // Десятичная Vа^ хОсЪ = 023; // Восьмеричная Vа^ хНех = ОхГК; // Шестнадцатеричная Вещественный тип Вещественный тип также представлен 32 битами. Диапазон значений лежит в пределах от -1.17549435Е-38 до 3.40282347Е+38. Значения можно отобра- жать в стандартной и экспоненциальной формах. В экспоненциальном пред- ставлении используется е или Е для определения порядка числа, которое за- дает показатель степени. Порядок может быть как положительным, так и отрицательным. Например: Vа^ ^Ехр = 23.4524Е-12; // Экспоненциальная форма записи Vа^ = -13.3451; // Стандартная форма записи Строковый тип Под этим типом понимается ряд символов в формате ЦМСООЕ. Символы стро- ки должны заключаться в одинарные (') или в двойные (") кавычки. Например: Vа^ зСг1 = "Не11о"; //Можно так Vа^ зЪг2 = ’Не11о’; //или так Vа^ зЬг2 = "80'8"; //и так
Для форматирования текста особый интерес представляют специальные сим- волы, указанные в табл. 51.3. Например, используя только одинарные кавыч- ки, мы могли бы при помощи специального символа \1 переписать послед- нюю инструкцию следующим образом: Vа^ зЪг2 = '80\'з'; Таблица 51.3. Специальные символы в строках Символ Предназначение \Ь Возврат на один символ с его удалением \е Горизонтальная табуляция \п Новая строка XV Вертикальная табуляция \г Возврат каретки V Двойная кавычка \* Одинарная кавычка \\ Обратная косая черта Логический тип Переменные логического (или булевого) типа принимают только два значе- ния: ъгие (истина) и Еа1зе (ложь).Как правило, используется этот тип для определения выполнения заданного условия. Vа^ Ь = Ыие; Ь = (3 == 5); //Ь-^а1ве Преобразование типов Тип любой переменной можно узнать в процессе исполнения сценария при помощи оператора ьуреоС(). Например, для четырех переменных п, Е, ъ, зьг разного типа и одной переменной ипкпошп, которой не присвоено какое-либо значение, это делается так: Vа^ п - 7; Vа^ Г = 5.2018; Vа^ Ь = егие Vа^ = иНе11о” ; Vа^ ипкпсжп; рггпЁ(ГуреоС(п)); ргхпе(Ёурео^(I)); 26 Зак. 3983
ргхпП(Ёурео^(Ь)); рга-пЪ (Ъурео! (зЪг) ) ; ргхпЪ (СуреоГ (ипкпотеп) ) ; На экране будет отображено следующее: пшпЬег пшпЬег Ьоо1еап зСгхпд ипЗеИпед Примечание Для того чтобы проверить работу этого и следующих примеров, вы можете вос- пользоваться небольшим интерпретатором 01 8слр1, который находится в ка- талоге СП-примеров /ехатр1е$/зспр1/дзсг1р1 После того как переменной было присвоено'значение, в соответствии со зна- чением переменной ей присваивается тип. Мы видим, что в приведенном примере, для переменной ипкпомп, которую мы не инициализировали значе- нием, оператор суреоЕ () вернул значение ипйеНпеа. Это значит, что для нее тип еще не задан. Тип переменной можно изменить в любой момент времени, присвоив переменной значение другого типа. Например: •уаг V = "Не11о"; // переменная строкового типа V = 32.3; // стала переменной вещественного типа Кроме того, часто тип переменной можно изменить, практически не меняя представленное ей значение. Различают неявные и явные преобразования ти- па. В первом случае, если переменная является результатом действий над пе- ременными разных типов, то ее тип будет задан той переменной, тип которой позволяет представить полученный результат без искажений. Например, ре- зультатом умножения значений целого и вещественного типа будет вещест- венное значение: уаг п = 56; //Целое значение Vа^ = 0.27; //Вещественное значение чаг гез = п * С; //Результат является вещественным: 15.12 Можно самому влиять на процесс конвертирования при помощи явного преобразо- вания типа. Это достигается при помощи функций рагзеПоас () и рагветпс (), предназначенных для преобразований строковых значений в числа. Вто- рой функцией можно преобразовывать и вещественные значения к целым, например: чаг ± = 3.5; уаг п = рагзеТпЦС); ргхпЬ(п);
При этом будет выполнено неявное преобразование переменной I к строке "3.5", после чего из строки будет прочитано целое значение. Поскольку точ- ка не может входить в целое число, она и все, что за ней следует, будет про- игнорировано. В результате произойдет "урезание” значения, и на экране бу- дет отображено число 3. С численными значениями все понятно, но что происходит, если сложить число со строкой? Vа^ п = 7, 1: = 5.2018, Ь = Ёгие; Vа^ гез! = п + " 13 а питЬег"; Vа^ гез2 = ^ + " 13 а ^1оаЪ питЬег"; Vа^ гезЗ = Ь + " 1з а ЬооХеап Vа1ие"; рг1пЬ(гез1); рг1пЁ(гез2); рг1пЪ(гезЗ); Во всех трех случаях результат будет преобразован к строковому типу и на экране будет следующее: 7 13 а питЬег 5.2018 13 а Г1оаЬ питЬег Пгие 13 а Ьоо1еап Vа1ие Разумеется, оператор + позволяет складывать и две строки, соединяя их в одну. Константы Отличие переменных от констант состоит в том, что значения последних нельзя изменить. Объявляются константы при помощи ключевого слова сопзъ. Значения констант могут быть разных типов. Например: сопз!: пит = 2; сопзЪ зЁгЫате = "Это неизменяемый текст"; Некоторые константы являются частью самого языка: ш€1п1еу (представ- ляющая "бесконечность”), ыаы (Мо1 а ЫишЬег, ”не число”) и ипаеПпеа, зна- чение неинициализированных переменных. Операции Самой важной синтаксической частью языка являются операции, с помощью которых можно сравнивать, изменять и получать доступ к данным.
Операторы присваивания С этим оператором нам уже пришлось столкнуться в самом начале главы. С его помощью мы инициализировали переменные, присваивая им опреде- ленные значения. Присвоения переменным значений и есть основная функ- ция этого оператора. Например: выражение х = 13 присваивает перемен- ной х значение 13. Арифметические операции Для вычисления численных значений используются арифметические опера- ции. Эти операции делятся на две группы: бинарные (двуместные) и унарные (одноместные). Первая группа состоит из следующих операций: □ сложения, представленной знаком плюс (+). Пример: х + у; □ вычитания, представленной знаком минус (-). Пример: х - у; □ умножения, представленной знаком звездочка (*). Пример: х * у; □ деления, представленной знаком косой обратной черты (/). Пример: х / у; □ операция остатка от деления, представленная знаком процент (%). При- мер: х % у. Вышеперечисленные операции можно, в целях сокращения записи, комбини- ровать с оператором присвоения. В табл. 51.4 указаны примеры этих комби- наций. Таблица 51.4. Примеры комбинирования Операция Описание X += у Сложение с присвоением. Сокращенная запись для х = х + у х -= у Вычитание с присвоением. Сокращенная запись для х = х - у X *= у Умножение с присвоением. Сокращенная запись для х = х * у х /= у Деление с присвоением. Сокращенная запись для х - х / у X %= у Остаток деления с присвоением. Сокращенная запись для х = х % у Вторую группу унарных арифметических операций составляют два операто- ра: инкремента (++) и декремента (—). Первый оператор увеличивает переменную на единицу, а второй уменьшает ее на единицу: То есть ++1 — это то же самое, что и 1 = 1 + 1, а —1 — то же. что и 1 = 1 - 1,
Эти операторы допускается использовать в префиксной и постфиксной фор- ме. Это изменяет порядок возврата значения, что важно при его дальнейшем использовании. Например, рассмотрим сначала префиксную форму записи: уаг 1 = 0; уаг а = ++1; В этом случае результат переменной 1 будет сначала увеличен на единицу, а затем присвоен переменной а. В результате переменной а будет присвоено значение 1. Теперь давайте рассмотрим случай с постфиксной формой записи. уаг 1=0; уаг а = 1++; Здесь сначала значение переменной 1 присваивается переменной а, и только после этого значение переменной 1 увеличивается на единицу. Поэтому пе- ременной а будет присвоено значение 0. Поразрядные операции Поразрядные операции используются для действий над целыми десятичными значениями как с последовательностью двоичных разрядов. По своей сути, эти операции сходны с обычными логическими операциями, только приме- няются к битам. Обычно эти операции используются для оптимизации кода программ. Они были унаследованы от языка С. Но при программировании на языке сценариев задачи оптимизации не стоят так остро, поэтому эти опера- торы используются крайне редко. К ним относятся: □ операция поразрядного логического И (&); □ операция поразрядного логического ИЛИ (|); □ операция поразрядного исключающего ИЛИ □ операция сдвига битов влево «; □ операция сдвига битов вправо »; □ операция сдвига битов вправо с заполнением нулями »>. уаг VI = 5; //0101 уаг V2 = 15; //1111 уаг VI = 0; //0000 рг1ПС(у1 & у2); //=>5 (0101) рг1пЪ(у1 & V3);. //=>0 (0000) рг1пС^1 л у2); //=>10 (1010) рг1пС^1 « 1); //=>10 (1010) рг1пе^1 >» 1); //=>2 (0010) Эти операции можно комбинировать с оператором присваивания.
Операции сравнения Как понятно из названия, эти операции используются для сравнения значе- ний выражений. Результат операций сравнения может иметь только два зна- чения: егие (истинное) и 1а1зе (ложное). В табл. 51.5 приведен список всех операций сравнения. Таблица 51.5. Операции сравнения Операция Описание а == Ъ Проверка равенства. Возвращает ыие, если операнды между собой равны. В противном случае — Еа1зе а ’= Ъ Проверка неравенства. Возвращает егие, если операнды не равны между собой. В противном случае — €а1зе а === Ь Проверка на совпадение (идентичность). Иногда называется операци- ей жесткого равенства. При проведении этой операции не производит- ся преобразования типов операндов. Если значение а равно значе- нию ь и их типы совпадают, то возвращается сгие. В противном случае — Еа1зе а !== Ь Проверка на несовпадение. Иногда называется операцией жесткого неравенства. При проведении этой операции не производится преоб- разования типов операндов. Если значение а не равно значению Ь, то возвращается егие. В противном случае — Еа1зе а < Ь Меньше. Возвращает езде, если а меньше Ь. В противном случае — Еа1зе а <= Ъ Меньше либо равно, возвращает ъгие, если а меньше Ь или равно Ь. В противном случае — Еа1зе а > Ь Больше. Возвращает ъгие, если а больше Ь. В противном случае — Еа1зе а >= Ь Больше либо равно, возвращает егие, если а больше Ь или равно Ь. В противном случае — Еа1зе Давайте рассмотрим несколько примеров: Ъгие==1 // => Ъгие (Ъгие преобразуется в 1) Ъгие===1 // => Га1зе (1:гие не преобразуется в 1) 5 === "5" // => Га1зе (строка ”5” не преобразуется в число) 5 == "5" // -> Ъгие ("5" преобразуется в число) 5 > 4 // Х> Ьгие (5 больше 4) пи 11 == ипде^хпес! // => Ъгие (пи11 преобразуется к ипс1еГ1пес1) пи 11 === ипсЯе^хпед // => €а1зе (пи11 не преобразуется к ипйеСхпед)
Приоритет выполнения операций Каждая операция имеет свой приоритет выполнения, а это значит, что если в одном выражении задано несколько операторов, то сначала будут исполне- ны действия операторов, обладающих большим приоритетом. Поэтому важно знать, каким уровнем приоритета обладает та или иная операция. Иначе можно запутаться с получаемым значением. Вот конкретный пример, попро- буйте угадать значение переменной п: уаг 1 = 8; уаг п = 11 + ++1 * 9 % 5; -- -- с ! - - : ‘ Направление убывания приоритета • • » I I I I ( ( < » с. ' Направление возрастания приоритета Рис. 51.1. Диаграмма старшинства приоритетов операций Воспользовавшись шкалой, показанной на рис. 51.1, мы можем вычислить результат. Итак, самая старшая в выражении — это операция инкремента- ции (++). Значит, сначала переменная будет увеличена на единицу и будет равна 9. Наш промежуточный результат выглядит следующим образом: уаг п = 11 + 9 * 9 % 5; Операции (♦) и (%) имеют одинаковый приоритет, поэтому действия будут исполнены слева направо. То есть сначала 9*9. Промежуточный результат: уаг п = 11 + 81 % 5; А теперь будет вычислен остаток от деления 81 на 5 (%). Наш промежуточ- ный результат: уаг п = 11 + 1; Значит, в итоге п равно 12. Управляющие структуры Посредством управляющих структур можно управлять ходом действия про- гпаммы.
Условные операторы Условные операторы являются наиважнейшей структурой языка. Именно благодаря тому, что с их помощью в зависимости от условий можно напра- вить исполнение программы по различным путям, можно реализовать любую логику ваших программ. В качестве условий выступают любые логические выражения. Оператор И... е/«е Этот оператор относится к числу наиболее часто используемых. Он направ- ляет исполнение программы по определенному пути, в зависимости от задан- ного в круглых скобках условного выражения. Его общий синтаксис выгля- дит следующим образом: (условное выражение) действие [е!зе действие] Как вы заметили, применение е1зе не является обязательным, но позволяет выполнить какие-то действия в том случае, если условное выражение возвра- тит 1а1ге. Ключевое слово е1зе не является самостоятельной частью языка и может использоваться только си. Например: « Vа^ а = 3; Vа^ Ь = 4; И (а > Ь) ргхпъ("а больше Ь”); е1зе рг1ПС("а меньше либо равно Ь”); Если действий много, то они должны быть заключены в фигурные скобки {}. Условные операторы и можно вкладывать друг в друга и комбинировать е1зе с другим оператором и. Например: И (а > Ь) { // произвести действия } е1зе 11: (а == Ь) { // произвести действия } е1зе { // произвести действия
Оператор 51У/?с/> В качестве альтернативы оператору И... е1зе можно воспользоваться опе- ратором зиЗхсЪ. Особенно эффективен этот оператор для проверки целого ряда значений. Общий синтаксис оператора зм1есЪ выглядит следующим об- разом: зюзЛсЪ. (выражение) { сазе пункт1: действия [Ьгеак] сазе пункт2: действия [Ьгеак] [Де^аиК:: действия] } В отличие от С/С++, значения пунктов этого оператора не ограничены только целым типом. Его исполнение начинается с вычисления значения выражения в круглых скобках. Затем это значение сравнивается с пунктами, стоящими после ключевого слова сазе. В случае совпадения выполняются действия этого пункта. Если ни одного совпадения с пунктами сазе не найдено, то, в случае наличия секции аеЕаи1ь, выполняются ее действия. Если действия пункта завершаются ключевым словом Ьгеак, то оператор зздИхсь после ис- полнения действий пункта будет покинут. Если Ьгеак не стоит, то выполнят- ся действия следующего пункта и так далее, пока не встретится слово Ьгеак, последний пункт сазе или секция аеСаи1ь. В следующем примере перемен- ной Vа1ие присваивается значение, зависящее от значения переменной пшп. зипХсИ (пит) { сазе 1: сазе "опе": Vа1ие = "1"; Ьгеак; сазе 1п1Ъ1а1Уа1ие: уа1ие = "0я; Ьгеак; йе^аиЗЛ: Vа1ие = "ипкпоюп";
Оператор условного выражения 1 Тернарный (трехместный) оператор условного выражения ? может быть использован в тех случаях, когда не нужно изменять ход действия програм- мы, в качестве краткой формы записи оператора к. Он возвращает, в зави- симости от условия, одно из двух указанных альтернативных значений. В ка- честве простой демонстрации реализуем тот же самый пример, который был создан нами для оператора И: таг а = 3; таг Ь = 4; таг зЬг = (а > Ь) ?’а больше Ь* : "а меньше либо равно Ь"; рг1пЬ(зЫ) ; Условные операторы можно вкладывать друг в друга. Это может быть полез- но, например, для того, чтобы предотвратить выход какого-либо значения за заданный диапазон. Например, зададим диапазон допустимых значений от О до 100: п = п > 100 ? 100 : п < 0 ? 0 : п; Циклы Циклы — это конструкции для выполнения части кода несколько раз. Операторы Ьгеак и сопНпие Прерывание цикла происходит при наступлении какого-либо условия, но иногда может потребоваться досрочно покинуть цикл. Это достигается при помощи ключевого слова ьгеак, помещенного внутри цикла. Ключевое слово сопЫпие поступает похожим образом, только не производит выход из цикла, а пропускает оставшиеся операторы текущей итерации и пе- реходит' к следующей итерации. Цикл /ог Это самый популярный цикл при программировании на С/С++, 1ауа и 01§спр(. Его популярность обоснована гибкостью, позволяющей выпол- нять дополнительные действия. Цикл Сог состоит из трех основных частей: инициализации, условного выражения и секции для изменения переменных. Его общий синтаксис выглядит следующим образом: 1ог {инициализация; условное выражение; изменения переменных) { действия
При первом вызове цикла производится инициализация. До тех пор пока ус- ловное выражение будет иметь значение ыие, цикл будет повторять дейст- вия в фигурных скобках, а затем проводить изменения переменных и снова проверять условное выражение. Рассмотрим этот цикл на простом примере подсчета суммы: уаг зит = 0; Сог (уаг 1=1; 1 <= 10; ++1) { зит = зит + 1; } При старте цикла производится определение переменной х и ее инициализа- ция значением 1. Затем значение переменной 1 сравнивается со значением 10. Если оно будет больше 10, то работа цикла будет завершена. Иначе значение переменной 1 будет, при каждой итерации цикла, увеличиваться при помощи оператора инкремента и прибавляться к переменной гит. Цикл шЫ1е Оператор иЫ1е действует подобно циклу гог, но не включает в себя функ- ций, отвечающих за инициализацию и изменение переменных. В остальном эти циклы идентичны. На практике не имеет значения, используете вы €ох или мЪИе. Эти циклы всегда можно заменить один на другой. В общем виде цикл «М1е выглядит следующим образом: «Ы1е (условное выражение) { Действия } Давайте заменим цикл Еог в нашем примере подсчета суммы на шЪПе: уаг зит = 0; уаг 1=1; ад1111е (1 <= 10) { зит = зит + 1; } Цикл с/о...1мЫ1е Цикл ао...шЫ1е представляет собой разновидность цикла шЪИе. Разница с циклом «Ы1е заключается в том, что перед проверкой условия тело цикла выполняется один раз. В общем виде этот цикл выглядит следующим образом: ао { Действия } уй111е (условное выражение);
Наш пример подсчета суммы с использованием этого цикла будет выглядеть так: таг зит = 0; таг 1 = 1; до { зит = зит + 1; ++1; } юИНе (1 <= 10); Оператор мяТй Оператор задуман для избежания многократного повторения ссылок на объект при доступе к его свойствам и методам. Например, вместо записи ргхпЪ (Маек. Р1) ; рг1пЪ (МаЪЪ. аЬз (-2)) ; ргхпЬ(МаеЪ.шах(4, 10, 7, б)); можно прибегнуть к эквивалентной записи с использованием оператора ™1 си: Ю1СИ (МаСЪ) { рг!пС(Р1) ; рг!пС(аЬз(-2)); рг1пС(тах(4, 10, 7, б)); } Исключительные ситуации Исключительные ситуации могут возникнуть в тех случаях, когда какие-либо действия алгоритма программы не могут быть выполнены корректно. На- пример, произошло обращение по несуществующему индексу массива, файл, открываемый для чтения, не существует и т. д. Для того чтобы ваша про- грамма в подобных случаях не потеряла "равновесие" и продолжала работать дальше, с учетом этих ситуаций, необходимы конструкции, позволяющие перехватывать, анализировать и обрабатывать подобные ситуации. Оператор 1гу...са1с11 Этот оператор служит для отделения действий, в которых могут быть* неза- планированные ошибки, с последующей возможностью перехвата сгенери- рованных исключений, которые производятся в секции саесИ. В общем виде оператор егу.. .саьсъ выглядит следующим образом: Егу { сомнительные действия
} саСск (ошибка) { действия обработки ошибки ) Ппа1 { завершающие действия ) Итак, в теле егу находятся некоторые "сомнительные" действия, исполнение которых может привести к ошибке. В случае ее возникновения будет произ- веден переход в секцию саъск, для ее дальнейшей обработки. В секции Ипа1 находятся действия, которые должны быть выполнены независимо от того, возникло исключение или нет. Давайте рассмотрим пример перехвата и об- работку исключения на примере открытия файла. Предположим, что мы рас- полагаем специальным классом Ше. В нашем случае мы просто отображаем код ошибки и закрываем файл. уаг Н1е = нею РИе("СНе.даЪ*') ; Пгу { С11е.ореп(РИе.Кеас1Оп1у) ; } саЁсИ(е) { рг1пЁ("Соде еггог:" + е); } Нпа11у { Н1е.с1озе(); } Оператор 1Ьгоуу При помощи оператора <Лго« можно генерировать свои собственные исклю- чения для их последующего перехвата в операторе ыу... саеск. Исключение может быть числом, строкой или даже объектом собственного класса. Вы- брос, исключения в общем виде выглядит следующим образом: ЪИгоуг еггог; Функции Функции — это важный инструмент для разделения задач на подзадачи. В подавляющем большинстве функция представляет собой блок действий над полученными исходными данными с последующим возвращением ре- зультата. Объявление функции производится при помощи ключевого слова
Гипсехоп. Функции можно определять в любом месте программы. В общем виде, синтаксис объявления функций выглядит следующим образом: ЬтсЫоп имя ([аргумент!] [..., аргументы]) { [действия] } В качестве примера реализуем функцию, перемножающую два числа и воз- вращающую полученный результат: (ипсЪ1оп ти1Ъ1р1у(уаг1, уаг2) ( геЬигп уаг1 * уаг2; } Вызов функции производится посредством указания ее имени. При вызове функции, если она допускает, можно передать ей аргументы. Произведем вызов нашей функции перемножения двух чисел и сохраним возвращенный результат в переменной: уаг {. = ти1С1р1у(2.3, 13.7); Ключевое слово СипсЫоп позволяет создавать функции "на лету", как только в этом появится необходимость: уаг 1пуМи1С1р1уРипсЬ1оп = ЬтсЫоп ти1Ыр1у(уаг1, уаг2){ геСит уаг! * уаг2;}; уаг € = щуМиХЬхрХуРипсСхоп(2.3, 13.7); Также можно при помощи специального объекта РипсЫоп создавать и изме- нять функции в процессе исполнения самой программы. Такой подход дает очень большое преимущество ввиду предоставляемой гибкости. Благодаря его использованию можно предоставить пользователю возможность задавать действия функции самому, набрав ее в текстовом поле вашей программы. Следующий пример демонстрирует создание подобной функции: уаг туМи1е1р1уРипсЬ1оп = пем ГипсЫоп("уаг1", "уаг2", "геЬигп уаг1 * уаг2;"); уаг С = гоуМи1Ыр1уРипсе1оп(2.3, 13.7); Количество и значения аргументов, переданных функции, можно получить в самой функции, при помощи встроенной в язык переменной агдшпепьз. Эта переменная называется объектом активизации функции и инициализируется всякий раз при вызове функции. Таким образом можно определить функцию, скажем, для перемножения любого количества переданных в нее значений: СипсСхоп ши1С1р1у()
Vа^ гези1Ь =1; Гог ^аг 1=0; 1 < агдитеп^з.1епдПЪ; ++1) { гези1Ь *= агдитепЪз[1]; } геЬига гези111; } Теперь нашу функцию можно вызывать с любым количеством аргументов: Vа^ Г = пти1Ь1р1у(34.5, 14.2, 8.7, 3.4, 7.1); Функции могут быть вызваны рекурсивно. Классический пример тому — функция вычисления факториала: Гипс^хоп ГасЬог1а1(п) { 1Г ((п == 0) || (п == 1)) { геЪит 1; ) е1зе { гези1Ь = (и * ГасСог1а1(п - 1)); } геСигп гези1С; } рг1пС("ГасСог1а110=" + ГасСог1а1(10)); Есть еще небольшой нюанс, связанный с использованием ключевого слова уаг, который необходимо учитывать. Все дело в том, что если мы внутри объявим переменную посредством Vа^, то она будет является локальной переменной, то есть по завершению работы функции будет разрушена. Но если мы просто используем переменную, не определяя ее, то она станет глобальной и после завершения работы функции продолжит существовать. Например: ГипсСхоп Гоо() { т = 2; } Гоо () ; рг1пС(т); //=>2 Если бы в этом примере переменная т была бы определена с Vа^, то это при- вело бы к исключительной ситуации, так как в данном случае мы бы пыта- лись получить доступ к несуществующей переменной.
Встроенные функции 91 ЗспрГ предоставляет ряд функций, определенных в глобальном объекте и являющихся неотъемлемой частью самого языка. К таким функциям отно- сятся: □ еуа! () — исполняет содержимое строки, понимаемое как код Зспрц □ хзыаыо —возвращает ъгие, если переданное выражение не является чи- словым значением; □ рагзеПоаь () — преобразует переданную строку к вещественному типу. В случае неудачи функция возвращает значение ыагг, □ рагзеХпсо —преобразует переданную строку к целому типу. В случае неудачи функция возвращает значение ыац. Объектная ориентация 9(8спр1 является объектно-ориентированным языком. Класс в 9(8спр1— это своего рода шаблон для создания объектов. Классы определяются при помощи функций, которые являются конструкторами. Для создания объекта имени такой функции должен предшествовать оператор лею. Конечно же, можно и просто вызвать функцию конструктора, но это не приведет к созда- нию объекта и поэтому в подобном вызове смысла не будет. ЬдлсЪхоп Ро1лЪ(х, у) { ЬЫз.ш.х = х; ЪИ1з.ш_у = у; еЫз.зеЪХ = ^илсЫоп(х) { 1к1з.т_х = х; } ЁЫз.зеПУ = ^илсЪ1оп(у) { СЫз.щ_у = у; } Ы115.Х = ^ипсЪхопО
{ геСигп ьЫз.пих; } СЫз.у = ЕипсЬ1оп() { геЬит ЬЪ.13.щ_у; } Функция Ро1пео в листинге51.1—это функция конструктора, в которую передаются два аргумента: координаты х и у точки. В конструкторе мы опре- деляем два атрибута, ш_х и щ_у, предназначенные для хранения координат. Далее, при помощи ключевого слова сыз мы также задаем имена методов, предназначенных для изменения (зеехо и зеьто) и получения (х() и у()) значений координат. Ключевое слово еЫз относится к созданному объекту и является ссылкой на него. Создание объектов при помощи функции ро1пь () может выглядеть следующим образом: тег ре = пею Ро1пе(20, 30); рг1пе("Х=” 4- ре.х() + ";У=" + рП.уО); На экране будет отображено: х=20 ;У=зо. Наследование Для того чтобы унаследовать существующий класс, нам понадобится создать функцию конструктора нового класса и запустить из него при помощи сан () конструктор наследуемого класса. Покажем это на примере создания класса трехмерной точки тьгеепрохпе. Другими словами, расширим класс Ро1пь еще одной координатой г (листинг 51.2). €ипсС1оп ТЬгееОРо1пС(х, у, г) { РохпЬ.саИ(1Ыз, х, у) ; Ыпз.щ_г = г; ЪЫз.зеЬг = ЬдпсЫоп(г) { СЫз.л\_2 = г;
} й11з.2 = ГипсЪ1оп() { геСигп Й118.т_г; } ) Итак, наш класс принимает три агрумента: х, у, г. Два первых вместе с указа- телем (Из передаются в са11() класса Ро1пе. Далее мы определяем атрибут т_2 и инициализируем его в соответствии с переданным в конструктор зна- чением, а также реализуем два метода для установки и получения его значе- ния, зеег() и 2 (}. Создание объекта нашего нового класса выглядит сле- дующим образом: уаг рЬ = пеш И1гееОРо±пС(20, 30, 40); рг1пЬ("Х=‘ + рЬ.х() + *;У=“ + рЬ.у() + *;2=" + рС.г()); На экране будет отображено: х=2 о; у=зо 2=40. После того как объект создан, при необходимости можно добавлять к нему новые методы и переопределять уже существующие. Покажем это на сле- дующем примере — добавим к только что созданному нами объекту ре метод сезь () и переопределим метод х (), унаследованный от класса Ро1пс. рЪ.ЪезЬ = €ш1сЪ1оп() { геЬигп "ТезЪ"; } рЪ.х - ^ипсехопО { геЪигп -1; } рг:шС("Х=" + рЁ.х() + + рЪ.ЪезЪО); На экране вы увидите: х=-1; тезе. Резюме Язык 018спр< очень похож на С++ и 1ауа. Он является полноценным языком программирования и предоставляет все конструкции, необходимые для напи- сания программ: от объявления переменных до объектной ориентации.
Глава 52 Встроенные объекты 01 8спр1 ’ В любой науке» в любом искусстве лучший учитель — опыт”. Мигель Сервантес Язык Ос 8спр1 не содержит обычных классов, как к этому привыкли разработ- чики на С++ или 1ауа, он предоставляет конструкторы, которые создают объ- екты при исполнении кода. Однако, в соответствии со стандартом ЕСМА-262, язык ОсЗспрС предоставляет серию встроенных объектов, в число которых ВХОДЯТ глобальный объект, объекты ОЬ^ес!:, ГипсСхоп, Аггау, ЗСгтпд, ВооЛеап, ПшпЬег, МаЬЪ, ИаЪе, КедЕхр, а также объекты ошибок ЕVа1Е^^о^, КапдеЕггог, ке^егепсеЕггог, зуп^ахЕггог, туреЕггог и пктеггог. Рассмотрим некоторые из них. Объект (я1оЬа1 Свойства и методы глобального пространства имен, о которых говорилось в главе 51, реализованы как свойства и методы базового объекта языка сце- нариев, объекта 01оЬа1. Он является объектом верхнего уровня и не имеет родительского объекта. В нем определены свойства конструкторов для всех используемых в сценарии объектов, например: оьзесе, иитЬег, зеггпд, оаье И Т. Д., а также методы еуа1 (), 1зЕ1п1Ье (), 1зЫаЫ (), рагзеПоаС (), рагзеТпЬ () и др. Этот объект можно расширять новыми свойствами, что позволяет до- бавлять в глобальное пространство новые объекты прямо из (^-программ. Объект ЫитЬег Объект ышпЬег служит для хранения целых чисел. С помощью этих объектов можно определять системные пределы: мах_уаше и мгк_уаше. Переменной
числового типа могут быть присвоены нечисловые значения, в этом случае метод ггыаыо возвращает значение егие. Результаты арифметического вы- ражения могут выйти за пределы, ограниченные максимальным и минималь- ным значениями, в этом случае значение будет равно хпНпхгу, а для его проверки можно использовать метод । хзрхпхсе (), который вернет, в этом случае, значение Са1зе. Объект Воо1еап Хотя (^1 5спр1 предоставляет этот объект, но его создание не рекомендуется. Вместо него лучше использовать константы сгие и Га1зе. Каждое выражение может быть преобразовано к логическому типу. Значе- ния о, пи11,1а1зе, май, ипае€1пес1 и пустая строка преобразуются к значению Са1зе, а во всех остальных случаях, выражение преобразуется к значе- нию Сгие. Объект 31ппд До сих пор мы умели только создавать строки и объединять их. При помощи объекта зег1пд со строками можно проводить целую серию разнообразных операций. Преобразование строки к нижнему и верхнему регистрам Преобразовать символы строки к верхнему или нижнему регистру возможно при помощи методов 1ошег () и иррег (), которые возвращают новые строки: уаг зёг = лею ЗЬг1пд( "Те31: З^КхЫд"); ргхпЪ ("ЬоюегСазе=" + зъг. 1оюег ()); //=> ЬсжегСазе= ЪезЪ зЪгхпд рг1п1:( "ПррегСазе=" + зЪг.иррег()); //=> ПррегСазе= ТЕЗТ ЗТК1ЫО Замена Воспользовавшись методом гер1асе(), можно заменить одну часть строк» другой: уаг з^г = пем ЗЬг1пд( "ОЪЗсгхр!:"); уаг з^г2 = зЪг.гер1асе("ЗстхрЬ", "4"); //зёг2="2ё4"
Получение символов Для получения символа на определенной позиции в строке, можно вызвать метод сЬагАе (). Номер позиции начинается с нуля: уаг зСг = пе» ЗСгХпдСОЬЗсгхрС"); уаг с = зЬг.сНагАЪ(2); //с='3' Получение подстроки Для получения подстроки можно воспользоваться методом зиЬзыХпдО, в который необходимо передать начальную и конечную позиции. Например: уаг зСг = пей 5Ьг1пд(*0еЗсг1рЬ"); уаг зсг2 = зЬг.зиЬвЬг1пд(2, зЬг.1епдЬЬ); 7/з1:г2="Зсг1рЪ’ Объект НедЕхр Назначение объекта регулярного выражения заключается в проверке строк на соответствие заданному шаблону. Описание принципа работы регулярных выражений можно найти в главе 4. Поэтому мы остановимся только на особен- ностях, присущих Ос 8спрС. Итак, создавать регулярные выражения в языке сценариев можно двумя способами: инициализацией и созданием объекта. Первый способ заключается в присвоении переменной регулярного выраже- ния, которое должно задаваться в стиле С#, то есть быть заключено в косые / и /. Например: Vа^ шуНедехр = / ([А-2а-г] +) = (\д+) /; Второй способ — это создание объекта регулярного выражения, что произ- водится при помощи оператора пей. Например: Vа^ туКедехр = пею КедЕхр(” ([А-2а-з] +) = (\с1+) "); Проверка строки Для того чтобы проверить строку на полное соответствие регулярному выра- жению, МОЖНО воспользоваться методом ехасЬМаЬск (): гпуКедехр.ехасЪМаЬсЫ "туУаг=1 //ЕхаварХе"); //=> ЕаХзе туКедехр.ехасЬМаСск("шуУаг=Х"); //=> Ьгие Поиск совпадений Для получения совпадений позиции строки с шаблоном регулярного выраже- ния можно воспользоваться методами зеагсЩ) или зеагсъкеу() (для поиска, начиная с конца строки): туКедехр.зеагсМ"уаг гауУаг=Х //ЕхатрХе"); //=> 4
Объект Аггау Объект массива Аггау является контейнером, содержащим элементы дан- ных. В строго типизированных языках, таких как, например, С++ и 1ауа, элементы массива должны иметь одинаковый тип, но в 01 8спр( это не обязательно. Таким образом, в одном и том же массиве можно размещать элементы различных типов. Создать массив можно посредством инициали- зации: Vа^ агг = [ "БХлапезсепсе" , "Ерхса", "ХапсЗгха" ]; Или вызовом оператора пе»: Vа^ агг = пею Аггау ("ЕХгапезсепсе", "Ерхса", "Хапдгха"); Дополнение массива элементами После создания, массив может быть динамически дополнен элементами, при ссылке на индекс несуществующей позиции: Vа^ агг = ["ЕХгапезсепсе", "Ерхса", "Хапдгха"]; агг[3] = "ТЪегхоп"; / /агг= [ ” Еуапезсепсе", "ЫхдЪ^хзЪ” , ” Ерхса", " ТЪегхопи ]; Элементы можно добавлять в массив и при помощи методов ризьо, добав- ляющего свои параметры'в конец массива, ипзЫй(|, добавляющего их в начало, а также врИсеО, вставляющего их в середину. В последнем пер- вым параметром нужно указать индекс начала вставки, вторым — количество символов, которые надо заменить при вставке, а далее перечислить вставляе- мые элементы, например: Vа^ агг = пеш Аггау(мЕХгапезсепсе“, "Ерхса”, "Хапдгха"); агг. зр1 хсе (1, 0, "ТЪегхоп", "ЫхдЪ^хзЪ") ; //агг=["Еуапезсепсе", "ТЪегхоп", "ЫхдЪЬшхзЪ", "Ерхса", "Хапдгха"]; Адресация элементов Доступ к элементам массива производится посредством их имен. Имена мо- гут быть не только целыми числами, но и строками: Vа^ агг = пею Аггау(2); агг ["Гхгз11" ] = "гГоЪп”; агг [" зесопс!" ] = " Раи1" ; Vа^ ^хгзЪВеаЫе = агг [ *^хгзС" ]; / / ^хгзЪВеа1:1е=СГоЪп агг.зесопс! += " МсСагСпеу"; Vа^ зесопс1Веае1е = агг.зесопд; // зесопдВеае1е=Раи1 МсСагЬхеу
Изменение порядка элементов массива При помощи метода геуегзе () можно менять порядок расположения элемен- тов массива на противоположный: уаг агг = ["Еуапевсепсе", нЕр1са", "Хап<3г1а"]; уаг агг2 = агг.геуегзе(); //агг2=["Хапс1г1а", "Ер1са", "Еуапезсепсе"] Можно также обойтись и без первого объявления: уаг агг2 = ["Еуапезсепсе", "Ер1са", "Хапдгха"].геуегзе(); Преобразование массива в строку Вызов метода }о1п() преобразует массив в строку, а в его необязательном параметре можно указать разделитель: уаг агг = [ "Еуапезсепсе", "Ерхса", "Хапдгха"]; уаг зёг = агг.дохп("*♦"); //зЁг=”Хапс1гха** Ерхса**Еуапезсепсе” Объединение массивов Массивы можно объединять друг с другом при помощи метода сопсас (): уаг зёг = ["Еуапезсепсе", "Ерхса", "Хапдгха”]; уаг зёг2 = зЁг.сопсаЁ ([ "ТИегхоп", "ЫхдЪЁтехзЪ"]) ; //=> зёг2= [ "Еуапезсепсе”, "Ерхса", "Хапдгха", "ТИегхоп", "ЫхдИШхзТГ’ ] Упорядочивание элементов При помощи метода зогс () можно упорядочивать элементы массива: уаг зёг = ["Хапдгха", "Еуапезсепсе", "Ерхса"]; уаг зёг2 = зёг.зогё(); //=> зёг2= [ "Ерхса", "Еуапезсепсе”, "Хапдгха" ] Объект Оа1е Объект паре предназначен для хранения даты и времени, с точностью до миллисекунд, и проведения над ними операций. Для создания объекта даты, в его конструктор можно передать значение года, месяца (январю соответст- вует 0, февралю — 1 и т. д.), дня, часа, минуты, секунды и миллисекунды. Все эти параметры не обязательны, и если при создании объекта не переда- вать никаких аргументов в конструктор, то будет сконструирован объект с ак- туальной датой и временем. Создадим объекты дат, с параметрами и без них: уаг тотепр = пе» ПаРе(2009, 2, 6, 23, 55, 30); II 6 марта 2009,' 23:55:30 уаг пош = пе« ИаСеО; // текущая дата и время
Для получения значения даты существует метод деы>аъе(). Также можно воспользоваться методами: деЪУеагО, деШопСМ), деМОауО, де^Ноигз (), де^МхпиЪез(), деЪЗесопДз () И деЪМИИзесопдз () ДЛЯ получения года, номе- ра месяца (январю соответствует 0), дня недели (воскресенье — 0, понедель- ник — 1 и т. д.), часов, минут, секунд и миллисекунд, соответственно. Для установки этих параметров можно воспользоваться аналогичными методами, но с префиксом зес. Следующий пример выводит на экран текстовое пред- ставление текущего дня недели: Vа^ 1пс1ехТоПау = [ "Зипдау", "Мопдау", "Тиезбау", “Мебпездау" , •’ТИигздау”, "Ггхбау", "ЗаЪигдау" ]; Vа^ пою = пею СаСе () ; рг1пе (1пс1ехТо1)ау [пою. деЫЭау () ]) ; Объект МаИ1 Объект Мае И содержит в себе атрибуты и методы, используемые для прове- дения математических операций. Доступ к этому объекту можно получить без использования конструктора. Все его атрибуты и методы определены как статические (табл. 52.1). Таблица 52.1. Атрибуты объекта масй Константа Значение Е Число Е. Значение константы Эйлера (2.718281828459045) ЬЫ2 Натуральный логарифм числа 2 (0.6931471805599453) ьию Натуральный логарифм числа 10 (2.302585092994046) ЬОС2Е Логарифм числа Е по основанию 2 (1.4426950408889633) ЬОСЮЕ Логарифм числа Е по основанию 10 (0.4342944819032518) Р1 Число тт (3.141592653589793) 50КТ1_2 Квадратный корень числа 1/5 (0.7071067811865476) 30КТ2 Квадратный корень числа 2 (1.4142135623730951) Рассмотрим некоторые методы, предоставляемые объектом маек.
Модуль числа Метод аЬз () возвращает абсолютное значение переданного в него числа. Пе- редаваемые числа могут быть целого и действительного типа. Например: уаг х1 = МаЫ1.аЬз(-123.5); //х1=123.5 уаг х2 = МаОЬ.аЬз(123); //х2=123 Округление Для округления в объекте маек есть три метода сеН(), Г1оог() и гоипао. Метод сеНО возвращает целое число, большее или равное переданному значению. Если в этот метод передать целое число, то он вернет его без из- менений. Например: уаг х1 = Маек.сеИ (5.2) ; //х1=б уаг х2 = Маек.сеИ (-5.5) ; //х2=-5 уаг хЗ = Маек.сеИ(б) ; //хЗ=б Метод 11оог() также возвращает целое число, но меньшее или равное пере- данному значению. уаг х1 = Маек.Иоог(-5.4); //х1=-б уаг х2 = Маек.€1оог(5.9) ; //х2=5 уаг хЗ = Маек.Ноог (б) ; //хЗ=б Метод гоипао округляет число до ближайшего целого значения: уаг х1 = Маек.гоипа(5.4); //х1=5 уаг х2 = Маек.гоипа(5.5) ; //х2=б уаг хЗ = Маек. гоипа (б) ; //хЗ=б Определение максимума и минимума Для вычисления максимума существует метод шах(), который принимав! несколько аргументов и возвращает значение большего из них. Аргументы могут быть как целого, так и вещественного типа. Например: уаг х1 = Маек.шах(5.4, 5.5); //х1=5.5 уаг х2 = Маек.тах(10, 2, 5, 3); //х2=10 Вычисление минимума производится с помощью метода ш1п (), который воз- вращает наименьшее из переданных значений. Например: уаг х1 = Маек.пйп(5.4, 5.5); //х1=5.4 уаг х2 = Маек.тхп(10, 2, 5, 3); //х2=2
Возведение в степень Метод роад о принимает два аргумента, число и показатель степени (может быть как целым так и дробным), и возвращает результат возведения числа в заданную степень. Например: уаг х = МаЫ1.роад(2, 3); //х=8 Вычисление квадратного корня Метод здге() является частным случаем операции возведения в степень с показателем Мг. Конечно, можно было бы использовать для вычисления квадратного корня и метод роад (), но использование метода здгь () будет бо- лее эффективным. уаг х = Ма«1.здгЪ(2) ; //х=1.414213562373095 Если передать в этот метод отрицательное число, то он вернет значение ыаы. Генератор случайных чисел Метод гапс1от() — это один из самых часто используемых методов, который возвращает случайное действительное число в диапазоне от 0 до 1. Если вам нужно число из произвольного диапазона, то можно реализовать на его базе следующую функцию: СипсЪхоп гапдотхге (гапде) { геЪигп (МаЫд. гапдопЛ) * гапде) } ргхпе(гапдат±2е(1000)); Тригонометрические методы Эти методы предназначены для использования геометрических вычислений. Самыми известными тригонометрическими функциями являются косинус (метод соз ()), синус (метод з1п ()) и тангенс (метод сап ()). Все эти методы принимают значения углов в радианах. А это значит, что если вы хотите ис- пользовать значения в градусах, то вам нужно будет перевести их радианы. Для этого нужно умножить значение в градусах на число л и разделить на 180. Например, перевод 270 градусов (-90 градусов) в радианы и получение значения синуса будет выглядеть следующим образом: Vа^ гад = -90 ♦ МаЫд.Р! / 180; ргхпЪ (МаЪ1ьз1п(гас1)) ; //=> -1
Вы, наверное, уже заметили, что среди перечисленных методов не хватает метода для вычисления котангенса. Объект маеъ не содержит его, но его можно легко реализовать самому, разделив значение косинуса на значение синуса: 4ипсЪ1оп сСд(апд1е) { геЬихп МаСЬ.соз(апд1е) / МаЫ1.з1п(апд1е) ; } Объект мани предоставляет методы и для обратных тригонометрических функций: арккосинус (асозО), арксинус (аз!по) и арктангенс (аъапо). Да- вайте теперь найдем угол, при котором значение синуса равно -1: рг1пе(МаеИ.аз1п(-1) / МаЬИ.Р! * 180); //=> -90 Вычисление натурального логарифма Метод 1од () производит вычисление натурального логарифма: уаг х = МаЫ1.1од(МаЫ1.Е); // х=1 В 0( 8спр( не предусмотрен метод, вычисляющий десятичные логарифмы, но его несложно реализовать самому: ЬтсЪхоп 1од10(агд) { гееигп МаЙ1.1од (агд) / МаЪЬ.ШЮ; } уаг х = 1од10(10); // х=1 Объект ЕипсНоп Этот объект уже был рассмотрен в главе 51. С его помощью можно исполь- зовать строку в качестве функции во время исполнения сценария. Для созда- ния новой функции необходимо передать параметры и текст в конструктор. Давайте создадим свою собственную функцию для объединения строк, при- нимающую, в качестве аргументов, четыре строки, причем последняя строка будет использоваться в качестве разделителя: уаг щуЛпп = пем ЕипсЫоп("а", "Ь“, "с", "вер", "геЬигп а + зер + Ь + зер + с1'); ргхпЁ (гпуЗЫп (• ТЬег 1оп", " Ер1са " , ”Ы1д11<1и1з11" , **')); //=> ТЬег1оп**Ер1са**М1дЫл;1з11
При создании объектов функций важно учитывать то обстоятельство, что трансляция объекта РипсЫоп выполняется при каждом его использовании, вследствие чего исполнение кода будет гораздо медленнее, чем при исполне- нии обычных функций языка сценариев. Резюме В данной главе кратко рассмотрены объекты, встроенные в Р^ЗспрЕ Эти объекты предоставляют целый набор функциональных возможностей для проведения манипуляций со строками, массивами, выполнения математиче- ских операций и др.
Глава 53 Классы поддержки 018спр1 и практические примеры Разговор двух программистов: — Что пишешь? — Сейчас запустим — узнаем! Для того чтобы сделать возможным использование языка сценария в про- граммах, нужны классы, созданные на С++. Модуль Ог8спрС предоставляет три основных класса для организации поддержки языка сценария в Со- программах (о двух из них мы уже вскользь упомянули в главе 50): □ 05сг1рСУа1ие — является контейнером для типов данных <0 8спр1; □ озсгхръсопсехс — представляет вызовы функций (О ЗспрЦ □ ОЗсгхрьЕпдхпе — представляет среду для исполнения программ, написан- ных на языке сценариев (О 8спр(. Класс 08спр1Уа1ие Этот класс поддерживает типы, определенные стандартом ЕСМА-262: ипаеПпеа, ши, вооХеап, ыишЬег, зьгхпд и окдесс. Дополнительно к этому, в <0 8спр1 определены два типа: Уагхапь и ооь^ все. Класс озсг1рс7а1ие предоставляет возможность для определения типов зна- чений. Для этого он предоставляет серию методов 1зт (), например: для ло- гического типа 1зВоо1еап(), ДЛЯ строки ГзЗкгхпдО, ДЛЯ массива 1зАггау() и т. д. Также имеются методы еот () для преобразования значений к конкрет- ному типу: ЬоЗСгхпд (), коВоо1еап (), ИоШтЬег () И Т. Д. Класс ОЗспр1Соп1ех1 Объекты этого класса содержат аргументы, передаваемые в функцию сцена- рия. Обычно эта информация нужна при написании функций на С++, предна-
значенных для вызова прямо из языка сценария. Например, после вызова из сценария функции СсС(3, "ТехЬ") произойдет создание объекта озсгхрьсопьехе, который будет содержать пер- вым элементом число 3, а вторым — строку "ТехГ. Для получения значений аргументов предусмотрен метод агдишепь (), в котором нужно указать номер аргумента. Их количество можно получить вызовом метода агдишепьсоипь (). Класс О5спр1Епдте Класс озсгтрЕЕпдхпе является главным классом модуля ОДЗспрЬ Любое <21- приложение, использующее язык сценариев, должно создать хотя бы один объект этого класса. Он предоставляет методы, связанные с исполне- нием кода сценария. Исполнение сценария производится вызовом метода 03сг1рС.Епд1пе: :еуа1иаЬе(), который возвращает объект класса 05сг1рьуа1ие. Вот пример исполнения простейшего сценария: ОЗсгхрСУаХие Vа1ие = епдХпе.етаХиаСе("5 * 5"); хпС пшпЬег = уа1ие.Ъо1пЬ32(); //пишЬег=25 Возникновение ошибки исполнения кода можно проверить вызовом метода хэеггог () объекта класса озсгхреуаХие. Например: ОЗсггрСУаХие VаXие = епд!пе.етаХиаЬе("зсгХрЬ еггог ЪезЬ"); 1С (уаХие.ХзЕггог()) { // Произошла ошибка при исполнении сценария дОеЬид() « VаXие.ЬоЗЬгХпд(); } Для создания объектов класс озсгХрьЕпдХпе предоставляет метод пе«т(), например, для даты — пеюоаьео, для объекта регулярного выражения — пеиКедЕхр () и т. д. Для того чтобы сделать объекты доступными в сцена- рии, нужно вызвать метод певдООЬэессО - В этот метод передается указатель на объект ооЬэесе или на объект унаследованного от него класса. Сценарию будут доступны свойства, потомки, сигналы и слоты переданного в этот ме- тод объекта. Следующий шаг — установка свойств. Установка свойств обычно произво- дится в глобальном объекте. Это делается для того, чтобы сделать собствен- ные расширения доступными для сценария. Вот пример установки свойства переменной целого значения: ОЗсгХрСУаХие туХпС. = СЗсг1рСУаХие(&зсг1рЬЕпдХпе, 2007); епдхпе. д1оЪаЮЬд есЬ () . зеЪРгорегеу (”ту1пЪ", щуТпЁ);
Можно предоставлять сценарию и функции, написанные на С++, но они должны иметь следующую сигнатуру: 95сг1рЪУа1ие туРипсЫоп (С8сг1р^Соп!:ехЬ*, ОЗсгхрЪЕпдхпе*) В качестве примера давайте реализуем очень полезную функцию, которая даст нам возможность получать из сценария доступ к любому виджету наше- го приложения по его имени. 05сг1рЬ7а1ие деем1<ХдеСВуНате(05сг1р^Соп1:ех^* рсопЪехЪ, ОЗсгхрЬЕпдхпе* репдхпе ) { 05Сг1пд зЁгЫате ' = рсопЪехЪ->агдитепЪ (0) . ЪоЗЪгхпд () ; ООЬдесЁ* роЬэ = 0; СогеасЪ (О^хйдеЪ* рмдЪ, ОАррНсаЫоп:: ЪорЬете1Ю1адеЪ8 ()) { (зЪгЫате == ридЪ->оЬзесЁЫате()) { роЬз = раддС; Ьгеак; } е1зе К (роЬэ = р«де->Х1псЗСИ116<ООЬэесе*> (з^гЫате)) { Ьгеак; } } ге1ит репд!пе->пе«ООЬзесЁ(роЬ^) ; } Итак, наша функция деъихвдеевуНате (), приведенная в листинге 53.1, в сце- нарии принимает один аргумент—имя искомого виджета. Поэтому вызов метода ОЗсгхрЬСопЬехЬ: :агдитепъ() ПРОИЗВОДИТСЯ ТОЛЬКО ОДИН раз, С ИН- дексом о. Возвращаемое значение преобразуется с помощью метода огсгхреУаХие: :Ьо5ег1пд() в строковое значение и присваивается перемен- ной зымате. В цикле Гогеасъ мы проходим по всем виджетам верхнего уровня. В том случае, если виджет верхнего уровня не соответствует иско- мому имени, поиск производится среди его потомков, при помощи шаблон- ного метода Ппасы1а<т> (). Для того чтобы сделать определенную нами функцию доступной для сцена- рия, ее необходимо установить в качестве свойства глобального объекта сле- дующим образом: 05сг1рЪУа1ие СсС = епдхпе. зсгхр^аХие (деШхддеевуЫате) ; епдхпе. дХоЬаЮЬдесЪ () . зеЪРгорегеу ( "деШхбдееВуЫате", ЕсЪ) ;
Теперь сценарий может вызывать функцию деъиМдеьвуЫате () как свою собственную. Например, использование в сценарии виджета нашего прило- жения с именем туихадеЫ будет выглядеть следующим образом: Vа^ тедС = деШхддеЪВуЫате (’туЮхсЯдеЪ! ') ; Практические примеры После того как мы ознакомились с основными классами поддержки языка сценария, самое время приступить к рассмотрению их применения в кон- кретных ситуациях. Итак, давайте перейдем от теории к практике. "Черепашья" графика Многие из вас, наверное, помнят из школьных уроков информатики "чере- пашью" графику, которая используется в языке программирования Ьо§о. Принцип рисования прост. "Черепаха" помещается в середину экрана, и пе- ремещать ее можно при помощи специально предусмотренных команд. Дви- гаясь по экрану, "черепаха" оставляет за собой след, складывающийся в раз- личные рисунки. Рис. 53.1. "Черепашья* графика
В примере, показанном на рис. 53. Г, мы предоставляем возможность для на- писания собственных сценариев с использованием "черепашьей" графики. Пользователь может вводить в левой части окна свои собственные сценарии и исполнять их нажатием кнопки Еуа1иа1е (Вычислить). В сценарии можно использовать следующие команды: □ €огу7ага(п) —перемещение "черепахи" вперед, с рисованием следа, на п пикселов; □ Ьаск(п) —перемещение "черепахи" назад, с рисованием следа, на п пик- селов; О 1еГ1: (пАпд1е) — поворот "черепахи" влево, на угол пАпд1е; □ гхдъе (пАпд1е) — поворот "черепахи" вправо, на угол пАпд1е; □ гезеъ () — очищение экрана и установка "черепахи" в середину. #1пс1ис1е <0Арр1хсаЫоп> #1пс1ис1е "ТигЫе^огкАгеа.И” хпЪ тахп(хпЪ агдс, скат** агду) { ОАррИсаЪхоп арр(агдс, агду) ; ТигЫёМогкАгеа СигЫеМогкАгеа; Ъиг ЫёЮогкАгеа. зкою () ; ге^игп арр.ехес(); } В основной функции, приведенной в листинге 53.2, мы создаем виджет рабо- чей области для "черепашьей" графики и показываем его на экране. #1^п<1е^ _Тиге1е_Ъ._ #йе^хпе _ТигЫе_Ъ_ #хпс1ис1е <О(:Сих> #1пс1ис1е <таЬк.11> с1азз ТигС1е : риЫхс ОЮхсЗдеЪ {
0_ОВ7ЕСТ ргдлгаЬе: ОРххшар ш_р1х; СРахпЪег ш._ра1пГег; риЬИс: ТигЫе(ОИхддее* раддЪ = 0) : 0№иЯдеЬ (рзддС) , щ_р1х(400, 400) , пираалЁег (&ш_р!х) { 5еЬГ1хед512е (пирхх. зхге ()); 1шХ () ; } ргоЪесЪеск ТО1С1 1ПДЛ() { щ_р1х. ЕИ1 (ОЪ: :уе11оот); щ_ра!пСег. 1:гапз1аСе (гее!:().сепЪег ()); щ^рахпЪег.гоЪаЪе(-90); } V^^^л1а1 Vо^с^ раЗ-п^Еуепг (дРазлЬЕХгепе*) { ОРахпСег рахгЛег (ЪЫз); рахпСег. дга^Ргхшар (гесЪ (), щ_р!х); } риЬИс 51оСз: €огугагд(1п1: п) { пцрахпЪег. дгаюЫпе (0, 0, п, 0); Щ-РахпЪег.Сгапз1аЪе(п, 0); герахпЪ(); } тоаЛ Ьаск(1пЪ п)
1ц_ра1п):ег.дга5«Ыпе(0, 0, -п. О); псрахгЛег.Ьгапз1аие(-п, 0) ; герагпг(); } уохд 1еЕЪ(1п(1 пХпд1е) { щ_ра1пьег.гоСаЕе(-пАпд1е); уохд г1дИг(1пС пАпд1е) { щ_ра1пСег.гоЬ^Ье(пАпд1е); уохд гезеЦ) { т_ра!пЬег. гезеШаЬгтх () ; 1п1е(); гера!пЬ(); } }; #еп<31 I / /_ТигЫе_^1_ В листинге 53.3 приведен класс тигЫе, реализующий команды рисования "черепашьей" графики. Этот класс определяет два атрибута т_р1х и пцрахпЁег. Первый атрибут — это растровое изображение, которое является полотном для нашей "черепахи". Второй атрибут — это объект, с помощью которого наша "черепаха" будет рисовать. При инициализации мы задаем в конструкторе размер растрового изображения для нашего полотна: 400x400. Для того чтобы все операции объекта рисования (т_ра1пеег) ассоциирова- лись с растровым изображением (щ_р1х), при инициализации объекта рисо- вания мы передаем ссылку на растровое изображение. Также мы запрещаем изменять размер растрового изображения в нашем виджете при помощи метода оихадес: :зепр1хеаз12е(). В принципе, ничего страшного не слу- чилось бы, если бы мы позволили изменять размеры, так как в методе 0н1адеЪ: -.рахпЪЕуепсо эта возможность предусмотрена методом ОРахпьег:: агамРххтар (), который растягивает наше полотно на всю область виджета. Однако фиксированный размер позволяет показывать результат ри- сования в масштабе 1:1, то есть без искажений, связанных с изменением раз-
мера. После установки размера мы вызываем метод инициализации В методе 1пИ: (), для того чтобы можно было визуально отличить область рисо- вания, мы закрашиваем ее желтым цветом при помощи метода о₽1хтар:: Ш1 (). При помощи метода ОРахпеег:: сгапз1аье () мы производим трансформацию нашего объекта рисования и смещаем его начало координат в центр полотна. Вызов метода ОРххатр:: гоиасе () производит поворот на 90 градусов. Теперь наша "черепаха" приведена в исходное положение и готова для рисования, дело осталось только за реализацией команд. Для того чтобы команды были доступны из сценария, мы объявляем их как слоты. Реализация команды ^огмагсЦ) заключается в вызове метода рисова- ния линии, из нулевой точки, длиной п, с последующим перемещением объек- та рисования на эту длину. Для того чтобы результат был виден сразу после исполнения команды, мы вызываем метод перерисовки оиладес: :гера1пь(). Команда Ьаск () реализована аналогично, с той разницей, что у п меняется знак. В реализации команд () и гхдис () производится поворот нашего объекта рисования на заданный угол пАХрИа, в градусах. Самая последняя команда — это команда гезеьо. Цель этой команды — вернуть нашу "черепашью" графику в исходное состояние. Для этого сначала вызывается метод ОРахпСег: :гезесмаьг1х(), возвращающий все трансфор- мации объекта рисования в первоначальное состояние. Затем вызывается ме- тод 1п1е (), который очищает область рисования, устанавливает "черепаху” в центр поля и разворачивает ее в нужном направлении. В завершение вызы- вается метод онхадес:: герагпе () для обновления области рисования. #1Егк1еС _ТигС1еИогкАгеа_й_ #<ЗеЛпе _ТигЪ1еИогкАгеа_И_ #1пс1иде «ЭИМдеО #1пс1и<3е <05сг1рЪЕпдд.пе> с1азз ОТехЬЕсИЪ; с!азз ТнгЫе; // ====================================================================== с1азз ТигЫёНогкАгеа : риЬИс 0И1<ЗдеЬ { 0_овлзст ргзллаЬе: ОТехЪЕсНС* щ_рСхС;
03сг1р^пд1пе щ^зсгхрЬЕпдхпе; ТигЫе* Пк_рТихХ1е; риЫхс: Тиг1:1еМогкАгеа(0М1адеЁ* ридС = 0) ; ргхуаъе з1о1:э: тохд з1о1:Еуа1иаЪе ( ) ; тохд з1оЪАрр1уСоде(хпЬ); }; #епдх€ //_ТигЪ1ёЮогкАгеа_Ъ_ Для того чтобы разрешить давать команды нашей "черепахе” и исполнять их, нам нужно объединить необходимые для этого компоненты. Именно эту роль и выполняет класс тигъхеиогкдгеа, приведенный в листинге 53.4. В этом классе определены атрибут области написания программ сценария (указатель т_рехе), атрибут для исполнения программ (т_зсгхръЕпдхпе) и область рисо- вания "черепашьей" графики (указатель щ_ртигЫе). ТигЫе^огкАгеа: :ТигС1еЮогкАгеа(0Ихдде1:* рюдС/*=0*/) : 0МхддеЪ(радд11) { щ_рТихХ1е = пею Тиг^1е; щ_рТиг1:1е->зе1:РххедЗх2е(400, 400); псрЪхЪ = пе\л; ОТехСЕдхЪ; ОСотЬоВох* рсЬо = пею ОСстЬоВох; 03егхпдЬхз1: 1зЕ; 1зС « “Наиз Vот ШкоХаиз" « ”Сиг1у" « "Схгс1е" « "ГапГхазу"; рсЬо->адд1Ъетз(1з^); соппесНрсЬо, ЗКЗЫАЦасЬ^аЪесЦхп^)), ЗЬОТ (з!оЬАрр1уСоде (хпЪ))) ; з1оЁАрр1уСоде (0); ОЗсгхрЪУаТие зсгхрЪТигСХе = Го—зсгхрСЕпдхпе. пеюООЪэ есЪ (щ_рТигЫе) ; щ_зсгхрСЕпдхпе.д1оЬаЮЬдесС () . зе^Ргорег^у (” Г;ихХ1е” , зсг 1рСТигЪ1е) ; ОРизЪВиЪеоп* рсшд = пете 0РизЪВи1:Ьоп ("&Еуа1иаСе");
соппесС (рапй, ЗЮМАЦсИскеЗО), ЗЬОТ(з1оЬЕуа1иаСе())); ОбгхдЬауоиЪ* рдгбЬауоиЪ = пей ОСгхдЬауоиЪ; рдг<ЗЬауоиЬ->а<1ЭД1с1деЪ(рсЬо, 0, 0); рдгс1ЬауоиЬ->а<а<ЗИ1<аде1: (щ_рЪхЪ, 1, 0); рдгс1Ьауоие->а<а<ЗИ1<адеЪ(1Ц_рТигЪ1е/ 0, 1, 2, 1) ; рдгдЕауоиЬ->адсМ1с1деЪ(рсзпс1, 2, 0, 1, 2); зеЬЬауоиС(рдгдЬауоиС); В конструкторе (см. листинг 53.5) мы создаем виджет для работы с чере- пашьей графикой псртигЫе и виджет выкидного списка для возможности выбора готовых примеров черепашьей графики. Их четыре: Наш уош МИайаш (Дом Николауса), Сиг1у (Кудряшка), С1гс1е (Окружность) и РапСаау (Фанта- зия). За смену примеров отвечает слот з1оеАрр1уСоае <), поэтому мы соеди- няем его с сигналом асе1уаееа(), который высылает выкидной список при смене элемента. Вызовом слота з1оЪАрр1усоае() мы устанавливаем текущий пример. После этого мы вызываем метод пе«ооьзесъо объекта класса ОЗсгхрЬЕпдхпе со ссылкой на виджет "черепашьей" графики. Этот метод воз- вращает объект типа данных языка сценария, который сохраняется в объекте класса озсг1рьуа1ие. Мы передаем это значение вторым параметром в метод 03сг1ръУа1ие:: зеЬРгорегЪу () глобального объекта, а в первом параметре мы устанавливаем имя, по которому этот объект можно будет использовать. Затем мы создаем кнопку (указатель рста), при нажатии на которую будет исполняться программа, введенная или модифицированная в виджете много- строчного текстового поля. Для этого кнопка соединяется со слотом 81огЕуа1иаее(). В завершение объекты размещаются в виджете рабочей об- ласти с использованием табличной компоновки. \го1сЗ. Тиг(:1е97огкАгеа:: з1о!:ЕХга1иаЬе () { 03сг1ре7а1ие гези1Ъ = т_зсг1реЕпд1пе. ета1иаЬе (щ_рСх11->11оР1а1пТех^ ()) ; 1Г (гезиИх.хзЕггог()) { ОМеззадеВох::сг1ъ1са1(0, "ЕХга1иаЁ1пд еггог",
гези1С.СоЗЪгхпд(), ОМеззадеВох::Уез ); В слоте 51оСЕ\га1иСе(), приведенном в листинге 53.6, методом ОТехсЕа^с:: ъо₽1а1пТехъ () извлекается текст, находящийся в виджете мно- гострочного текстового поля, и передается на исполнение в метод ОЗсгхрсЕпдхпе: :еVа1иа^е(). Возвращаемое этим методом значение анализи- руется на наличие ошибок в операторе 1Г при помощи вызова метода 1зЕггог(). В случае возникновения ошибок при исполнении сценария выво- дится окно сообщения, Vо^с^ ТигС1ёЮогкАгеа:: з1оСАрр1уСобе (1пС п) { СЗСгхпд зСгСоде; зVI Сек (п) { сазе 0: зСгСойе = ^аг к = 100 ;\п" ”СигС1е.гезеС(); \п" "СигС1е.г1д11С(90) ;\п" ”СигС1е.Ъаск(-к) ;\п" иСигС1е.1е^Ъ(90);\п" " СигС1е. догмате! (к); \пи "СигС1е.1е^С(30);\п" •СигС1е.Согюагд(к);\п" "СигС1е.1еСС(120);\пи " СигС1е. Согмагс! (к); \п" "СигС1е.1е€С(30);\п" " СигС1е. Сог^агс! (к); \п" •'СигС1е. 1е^С(135) ;\п” мСигС1е. СогюахсЦМаСк.здгС (2) *к) ;\пи "СигС1е.1е€С{135); \п" ”СигС1е.Согюагд(к);\п" "СигС1е.1е^С(135);\п" "СигС1е. ^огигагд(МаСЪ. здгС (2) *к); \п";
Ьгеак; сазе 1: зСгСоде = "СигС1е.гезеС();\п" "Гог (х = 0; 1 < 2; ++1) {\п* ” Гог(3 = 0; 3 < 100; ++3) {\п" " СигС1е. Согюагд (15); \п" " СигЫе. 1еГС(100 - 3);\п" " }\п" " СигС1е.г1дИС(180);\п" Ьгеак; сазе 2: зЪгСобе = "СигС1е.с1гс1е - ГипсСхоп() \п" Ч\п" ” Гог ^аг 1 = 0; 1 < 360; ++1) {\п“ " 11118. ГогиагсЗ. (1) ; \п" •’ ЪЫз.1еГ1:(1);\п" " } \п" "}\П" " СихЫе. гезеС (); \п " •' СигС1е. схгс1е (); \п" ; Ьгеак; с(еГаи1С: зСгСоде = ”СигС1е.гезеС(); \п" "Гог (1 =0; 1 < 200; ++1) {\п” " СигС1е.Гогюагд(1 * 2);\п" " СигС1е.1еГС(91);\п" ” } " ! } т_рСх(:->зе(1Р1а1пТехЬ (зЬгСоде) ; } Слот з1омАрр1уСоае(), показанный в листинге 53.7, предоставляет четыре готовых примера для черепашьей графики, написанных на языке сценария. Вы можете экспериментировать и изменять их исходный текст в виджете многострочного текстового поля. Проявите немного творчества, и вам навер- няка удастся сделать их еще более оригинальными. Итак, давайте перейдем теперь к рассмотрению этих примеров: □ пример самой первой секции зздИзсЪ (сазе о) — это рисунок дома Нико- лауса. В нем использованы все команды, предоставляемые нашим видже-
том черепашьей графики, поэтому он является прекрасным тестом про- верки работоспособности всех команд нашей черепашки; □ пример во второй секции зм1ЬсН (сазе 1) рисует две кудряшки; □ третий пример (сазе 2) отображает окружность. Обратите внимание, что в этом примере мы расширяем виджет нашей черепахи еще одним мето- дом с1гс1е(). Таким образом можно расширять объекты и виджеты 01 дополнительными методами прямо из 018спрЦ □ код последней секции змхесъ (ае?аи1ъ) я назвал фантазией. Поэкспери- ментируйте и замените значение 91 в методе на 120, и вы увидите египетскую пирамиду. Значение 171 отобразит солнце, 160— звезду, 181 — веер, 45 — лабиринт, 115 — подсолнух, а 31 — спираль. Быть мо- жет, вы найдете еще какие-либо интересные узоры, изменяя этот пара- метр, попробуйте! Сигналы, слоты и функции После того как объект добавлен, автор сценария может вызывать слоты объ- екта, изменять любые его свойства и получать доступ к его потомкам. Но это еще не все! В программе сценариев можно Присоединяться к сигналам 01- объектов. Эти сигналы могут быть присоединены к (^-слотам, а также к функциям и методам классов, определенных в самом сценарии. Для того чтобы соединить сигнал, нужно вызвать метод соппесьо высы- лающего сигнал объекта и передать в него слот, функцию или метод объекта сценария. Возьмем, например, соединение сигнала сИскеао кнопки с функ- цией сценария Ьисъопсг хскеа (): ^ипсЁхоп ЬиЪЪопС11скес1 () { рг1пЪ(”Т11е ЬиЪЁоп " + еЫз.ЪехЕ. + " теаз сИскед"); } сшд.сИскеб.соппесС (Ьи^СопСИскед) ; Заметьте, что объект, выславший сигнал сИскеа, а в нашем случае это кноп- ка ста, доступен в функции сценария посредством указателя сЫз. Благодаря этому мы можем получить всю необходимую информацию об объекте, при- ведшему к вызову функции. Для отсоединения сигнала нужно поступить ана- логичным образом, только вместо метода соппессо нужно вызвать метол агзсоппесЁ (). Например: ста.сИскеа.ахзсоппесС (ЬиЬЪопСИскеа) ; Пример, показанный на рис. 53.2, демонстрирует соединение сигнала из язы- ка сценария тремя способами: со слотом (^-виджета, функцией и методом
объекта класса сценария. Также этот пример демонстрирует возможность доступа к потомкам. 22 51дпа1 Апс! 51сн * ~~ ' 41 1”-^1 * 'ТИеГей13;из!аз1Фр1е1е4 ‘ / Тпе’ей а ято1е1ей ;; Тле в а егток 1ех1 > ' < ТЪе»5118<ш1а51тр1е1ей Рис. 53.2. Пример соединения сигнала разными способами *...............‘“ч*’^^****?’**^ #1пс1и6е <0ЬСи1> #1пс1ис1е <0^5сг1рЁ> 1пС та1п(1п1: агдс, сЪаг** агди) { ОАррИсаЪхоп арр(агдс, а^дV); ОЮхддеС аддЪ; ОЫпеЕсИЁ* рОсС = пею ОЫпеМхе; рЪхЪ->зеЁОЬэесеЫате ("ИпеесИС") ; 0ЬаЬе1* р1Ы1 = пем ОЬаЬе! (" 1"); р1Ы1->зеЁОЬзесЬЫате (" 1аЬе11") ; 0ЬаЬе1* р1Ы2 = лею 0ЬаЬе1("2"); р1Ы2->зеЬ0ЬдесШате( "1аЪе12") ; 0ЬаЬе1* р1ЫЗ = пею ОЬаЬе! ("3"); р1ЫЗ->зеЁОЬзесСКате (" 1аЬе13 •) ; //ЬауоиЁ 8еСир ОХТВохЬауоиЪ* рVЪx^ауои1: = пек ОТВохЬауоиС; р^хЬауоиС->а<±ЗМ1с1деЬ (рЪхС);
р^ЬхЪауоиС->аскЗМ1с1де1: (р1Ы1) ; р^хЬауоиС->а<1сЭМ±с1деС (р1Ы2) ; ртЪхЬауоиС->а(За!М1вде1: (р!ЫЗ) ; аддС. зеСЬауоиЪ (р\тЬхЬауоиС) ; идС.зИоюО ; //5сг1рС рагС ОЗсгхрСЕпдхпе зсг1рСЕпд1пе; ОРИе Ше (" зсг1рС. э з"); (€Ие.ореп(ОРИе: :Неа<Юп1у)) { 28сг1рСУа1ие зсггрШдС = зсгхрЬЕпдгпе.петтООЬЗесС(&™дС) ; зсгхрСЕпдхпе. д1оЪа10Ьэ есЪ () . зеСРгорегСу (" тлдС *, зсгхрСЮдС) ; 05сг1рС7а1ие гези1С = зсг1рСЕпд!пе. еуа1иаЪе (<2ЬаС1п13Сг1пд (^11е. геабАИ ОН; И (гези1Ь. тзЕггог ()) { ОМеззадеВох:: сгхС1са1 (0, "ЕХла1иаС1пд еггог", гези1С.Ъо5Ъг1пд(), ОМеззадеВох: :Уез ); } } е!зе { ОМеззадеВох::сгхЪхса!(0, "РИе ореп еггог", "Сап поЪ ореп Ле зсгхре €11е", ОМеззадеВох::Уез ); } геСигп арр. ехес О ; } В основной программе, приведенной в листинге 53.8, мы создаем виджет верхнего уровня мдъ. Отметьте очень важный момент — для всех создавае- мых дальше виджетов мы вызываем метод зеьоь^есеиашеО, предназначен- ный для установки имени объекта. Это необходимо для того, чтобы мы могли,
посредством виджета предка («дь), получить доступ к ним из сценария. Их четыре: виджет текстового поля (указатель реже) и три виджета надписи (указатели р1Ы1, р1Ы2 и р1Ь1з). После размещения этих виджетов при по- мощи вертикальной компоновки, мы производим показ виджета «де с помо- щью метода 5ко«(). Затем мы загружаем файл сценария $спр1.]$, и так как этот файл не является составляющей ресурса, нам необходимо проверить ус- пешность операции загрузки. Установив виджет «де в глобальном объекте сценария под именем мы исполняем, вызывая метод етаХиаее (), код сценария, приведенный в листинге 53.9. Этот метод возвращает объект клас- са огсг1реУа1ие, который нам нужен для того, чтобы узнать о возникновении ошибок при исполнении и отобразить их в окне сообщения. ^ипсСхоп МуС1азз(1аЬе1) { еЫз.1аЬе1 = 1аЬе1 } МуС1азз.ргоеоеуре.ирдаееТехе = ^ипсЫоп (зЪг) { еЫз. 1аЬе1. еехе = зег; } 5ипсе1оп ирйаСеТехЪ(зег) { «де.1аЬе11.еехе « зег; } «де.«1пдо«зТ1е1е = "51дпа1 Апд 51оез"; «де.Ипввдхе.еехе = "Тезе"; «де. Ипеед1е. ЪехесИапдед. соппесе (ирдаЪеТехЪ); Vа^ хггуОЬ^есе = пе« МуС1азз («де. 1аЬе12); «де. Ипеедхе. ЪехеСИапдед. соппесе (туОЬд есе, "ирдаееТехе") ; «де. Ипеедхе. еехеСИапдед. соппесе («де. 1аЬе13 . зеетехе) ; В сценарии, приведенном в листинге 53.9, мы определяем класс мус1азз, со- держащий метод ирдаеетехео и атрибут виджета надписи 1аЪе1, который
инициализируется переданным в конструкторе в качестве аргумента видже- том. При помощи метода ирсЫеТехЦ) мы получаем доступ к виджету надписи посредством атрибута 1аЬе1. В объекте надписи мы устанавливаем в качест- ве текста (свойство еехе) строку, переданную в этот метод. Реализация функции ирааеетехе () по своей сути похожа на метод, описан- ный ранее, с той лишь разницей, что мы производим изменение текста у1аЬе11. После этого мы устанавливаем в виджете верхнего уровня шде заголовок окна ”81§па1 Апд 81о1з" при помощи свойства «1пдомзт1е1е. Свойство еехе нашего текстового поля мы инициализируем строкой "ТезГ. Затем мы соединяем его сигнал ьехссьапдеа с функцией ирааеетехе (). Теперь эта функция будет вы- зываться при любых изменениях текста этого виджета. Далее мы создаем объект туоьэесе нашего класса мус1азз, передаем в его конструктор виджет 1аЬе12 и соединяем его метод ирйаеетехео с сигна- лом ЬехссНапдед. Заметьте, что название слота передается в качестве строки "ирда^еТехГ. При вызове метода ирааеетехе () ключевое слово еыз будет указывать на первый аргумент, переданный в метод соппесе (). Это необхо- димо для того, чтобы мы могли получать доступ к атрибутам нашего объекта туоЬэесг. Можно было бы также использовать эквивалентный вызов без пе- редачи строки во втором аргументе: юдЪ. ИпеедхЪ. ^ехЬСЪапдед. соппесЪ (туОЬд есЪ, туОЬ} есЪ. ирдаЬеТехП) ; Самое последнее соединение производится между двумя виджетами (^1 на- прямую, для чего мы передаем в метод соппесе () слот виджета надписи (1аЬе13) зеетехе(). Теперь, после изменения текста в виджете текстового поля, новый текст бу- дет отображен сразу в трех виджетах надписей (см. рис. 53.2). Примечание Воспользовавшись функцией листинга 53.1, мы могли бы обращаться к любому, из наших виджетов без использования объекта-предка. Исключение составил бы только сам предок (объект «где), так как для него в программе не установле- но имя (см. листинг 53.8). Отладчик О« 8сг1р( Для того чтобы использовать отладчик, необходимо задействовать модуль рЙспр^Тоок. Его необходимо подключить к проектному файлу (.рго) в сек- ции (^1 вместе с модулем (ДОспрС. ОТ += зсг1рЬ зсг1рП1:оо1з
Сам отладчик находится в заголовочном файле 08спрФп§1пеВеЬи§§ег. Про- демонстрируем использование отладчика на простом примере, показанном на рис. 53,3. * 015«<рЮеЬиддег ОеЬид 5««сН Шеш > М <? О'! -Е а ® 1оадей ЗспрЬ ® X «аполутоиыспр!. ,«> I (Г , ( - 1; < < 5; <-!+} ( 51лА 1 ЬеиН Ыэпк ;0 «апслуташ* Ьосаьол < апспутпсу? $€Г1рС, 1СЬ 5 х ОХ 1оса1<ол 1.^5 Ш1ме 14М*« <ипсъопгц [паЪ.е]: 1мпс1еоп 0 (! р|г«Г1с« 1иллоп (.) ([лМ».е] ра«е1п< 1ипсШл>:«ЦпММ) рнп( (игкшгцн (пЛ.е] 1 ипйейпей лпйе?1Ле;1 Сегале Туре ”.Мр" Гог №р. 2 3 3 Гог (уаг 1= 1; I < 5; I**) { 5ит += _______________ Е'тйПос | ОекмоСММ ' СоиЫе Рис. 53.3. Отладчик 018спр1 #1пс1иде <0ССи1> #1пс1иде <0Ъ5сгхрЪ> #1пс1ис1е <03сг1рСЕпд1пеОеЬиддег> хпС хпа1п(1п1: агдс, сЪаг** агду) { ОАррИсаЪхоп арр(агдс, агду) ; 03сг1р1:Епд1пе зсг1рЪЕпд!пе;
08сг1рЪЕпд1пеОеЪиддег зсгхр^ПеЬиддег; зсгхреВеЬиддег. аЬЪасМо (ЬзсггрСЕндхпе) ; ОАс^хоп* расЬ = 'зсгхрЬПеЬиддег .асЫоп (05сг1рЪЕпд1пеВеЬиддег:: 1пЪеггирЪАс±1оп); расе->егтддег(); ОЗЬгхпд з^гСоде = "ттаг зит = 0;\п" "^ог ^аг 1 = 1; 1 < 5; 1++) {\п" зит += 1;\п" ОЗсгхрЪУаХие гези1С = зсг1рЪЕпд1пе.ета1иаЁе(зЬгСос1е); геЬит арр. ехес () ; } В основной программе (листинг 53.10) после создания объекта языкового движка озсгхрЬЕпдхпе создается отладчик 08сг1рЪЕпд1певеЬиддег. Теперь нам необходимо подсоединить его к языковому движку, для чего мы вызыва- ем метод аееасИто () и передаем в него адрес объекта движка (переменная зсг1рЪЕпд1пе). При возникновении исключительных ситуаций окно отладчика вызывает- ся автоматически. А для того чтобы "по требованию" прервать выпол- нение программы сценария и вызвать отладчик в любой нужный нам момент, нужно воспользоваться (28сг1р(:Епд1пеПеЬиддег: : ТпЬеггирЬАсЪхоп и получить указатель на объект действия, который объект отладчика нам вернет при вызове метода асЫоп (). Мы хотим запустить отладчик сразу же при запуске программы, поэтому мы в следующей строке вызываем из объекта действия метод ы1ддег(). Наша программа на языке сценария представляет собой цикл, в котором производится суммирование в пере- менной гит. При помощи операций трассировки 81ер 1п1о, 81ер Отег и 81ер Ои1, которые находятся на панели инструментов, вы можете просле- дить, как изменяются значения переменных вит и г в окне Ьоса! (в пра- вой части).
Резюме В завершающей главе книги мы ознакомились с ОС-классами, при помощи которых можно обеспечивать поддержку сценариев в приложениях. Их ис- пользование позволяет сделать любые свойства, сигналы и слоты и потомки объекта доступными для Ос5спрС. Центральным классом поддержки являет- ся озеггрсЕпдхпе, который дает возможность исполнять код написанных на 018спрС сценариев. Язык сценариев поддерживает механизм сигналов и слотов. Сигналы, кото- рые посылает объект, могут быть связаны как с функциями и методами клас- сов Ос 8спрС, так и с обычными слотами Ос-объектов.
Эпилог "Много говорить не буду, а то опять чего-нибудь скажу". В. С. Черномырдин Вот вы и достигли, дорогой читатель, последней страницы книги. И теперь вы по праву являетесь моим главным критиком и можете откровенно выска- зать: что, по вашему мнению, было написано неправильно, что можно было бы сделать лучше и, конечно же, что осталось без внимания. Мне, как автору, очень интересны все ваши замечания, которые вы можете отправлять по ад- ресу Мах^сЫее@пеотгау.сот. Подготавливая к изданию данную книгу, я, прежде всего, стремился расска- зать об изменениях, которым подверглась библиотека р1. Хочется надеяться, что все мои знания и старания, вложенные в подготовку этой книги, не про- падут даром, а послужат вам хорошим подспорьем в дальнейшей работе. Ус- пехов в ней я вам от всей души желаю!
10 ПРИЛОЖЕНИЯ
Приложение А Таблицы семибитной кодировки А8СП Кодировка А8СП (Атерсап 81ап<1агд Соде Гог ТпГогтаНоп ЫегсЬап^е, амери- канский стандартный код для обмена информацией) является наиболее рас- пространенной кодировкой для представления десятичных цифр, символов латинского алфавита, знаков препинания и управляющих символов. Первые 128 символов (для представления которых достаточно семи бит) являются фиксированными и приводятся в табл. А. 1 (управляющие символы) и А.2 (алфавитно-цифровые символы). Также стандартизированы и старшие 128 ко- дов, которые представляют символы национальных алфавитов, но их значе- ния зависят от выбранной кодовой страницы и здесь не приводятся, поскольку в (2( они не используются из-за поддержки стандарта Пшсоде. Таблица А.1. Управляющие символы ОСТ ОЕС НЕХ Символ Описание 00 00 00 шь Пустой 01 01 01 зон Начало заголовка 02 02 02 5ТХ Начало текста 03 03 03 ЕТХ Конец текста 04 04 04 ЕОТ Конец передачи 05 05 05 ЕЫО Запрос о подтверждении Об Об Об АСК Подтверждение 07 07 07 ВЕЬ Звонок 10 08 08 ВЗ Возврат на один символ 11 09 09 ТАВ Горизонтальная табуляция 12 10 0А ЬЕ , Перевод строки
Таблица А.1 (окончание) ОСТ ОЕС НЕХ Символ Описание 13 11 ов УТ Вертикальная табуляция 14 12 ОС рр Новая страница 15 13 ОБ >св Возврат каретки 16 14 ОЕ 50 Переключение на стандартный код 17 15 ОР 51 Переключение на другие таблицы ко- дировок 20 16 10 ББЕ Отмена соединения с данными 21 17 . 11 ОС1 Управление устройством 1 22 18 12 Г>С2 Управление устройством 2 23 19 13 ОСЗ Управление устройством 3 24 20 14 ОС4 Управление устройством 4 25 21 15 ЛАК Не подтверждаю 26 22 16 ЗУЛ Синхронизация передачи 27 23 17 ЕТВ Конец блока текста 30 24 18 САЛ Отмена 31 25 19 ЕМ Конец ленты 32 26 1А 5ПВ Подставить 33 27 1В Е8С Отмена 34 28 1С Р5 Разделитель файлов 35 29 ю 05 Разделитель групп 36 30 1Е Н5 Разделитель записей 37 31 1Р П5 Разделитель модулей Таблица АЛ. Символы ост □ЕС НЕХ Символ ост ОЕС НЕХ Символ 40 32 20 (пробел) 45 37 25 % 41 33 21 46 38 26 & 42 34 22 п 47 39 27 1 43 35 23 # 50 40 28 ( 44 36 24 $ 51 41 29 )
Таблица А.2 (продолжение) ост ОЕС НЕХ Символ ост ОЕС НЕХ Символ 52 42 2А * 92 74 4А а 53 43 2В + 93 75 4В к 54 44 2С ! 94 76 4С ь 55 45 20 — 95 77 40 и 56 46 2Е • 96 78 4Е ы 57 47 2Р / 97 79 4Р О 60 48 30 0 100 80 50 р 61 49 31 1 101 81 51 0 62 50 32 2 102 82 52 в 63 51 33 3 103 83 53 5 64 52 34 4 104 84 54 Т 65 53 35 5 105 85 55 П 66 54 36 6 106 86 56 V 67 55 37 7 107 87 57 М 70 56 38 8 110 88 58 X 71 57 39 9 111 89 59 У 72 58 ЗА : 112 90 5А 2 73 59 ЗВ г 113 91 5В [ 74 60 ЗС < 114 92 5С \ 75 61 30 = 115 93 50 ] 76 62 ЗЕ > 116 94 5Е Л 77 63 зе 117 95 5Р — 80 64 40 @ 120 96 60 81 65 41 А 121 97 61 а 82 66 42 В 122 98 62 Ь 83 67 43 С 123 99 63 с 84 68 44 о 124 100 64 а 85 69 45 Е 125 101 65 е 86 70 46 Е 126 102 66 Г 87 71 47 6 127 103 67 д 90 72 48 И 130 104 68 ъ 91 73 49 I 131 105 69 1
Таблица АЛ (окончание) ОСТ ОЕС ' НЕХ Символ ост ОЕС НЕХ Символ 132 106 6А 3 145 117 75 и 133 107 6В к 146 118 76 V 134 108 6С 1 147 119 77 V 135 109 60 т 150 120 78 X 136 110 6Е п 151 121 79 У 137 111 6Р о 152 122 7А 2 140 112 70 Р 153 123 7В { 141 113 71 д 154 124 7С 1 142 114 72 г 155 125 7П } 143 115 73 3 156 126 7Е — 144 116 74 п 157 127 7Р РЕЬ
Приложение В Таблица простых чисел 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 < 739 743 751 757 761 769 773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997 1009 1013 1019 1021 1031 1033 1039 1049 1051 1061 1063 1069 1087 1091 1093 1097 1103 1109 1117 1123 1129 1151 1153 1163 1171 1181 1187 1193 1201 1213 1217 1223 1229 1231 1237 1249 1259 1277 1279 1283 1289 1291 1297 1301 1303 1307 1319 1321 1327 1361 1367 1373 1381 1399 1409 1423 1427 1429 1433 1439 1447 1451 1453 1459 1471 1481 1483 1487 1489 1493 1499 1511 1523 1531 1543 1549 1553 1559 1567 1571 1579 1583 1597 1601 1607 1609 1613 1619 1621 1627 1637 1657 1663 1667 1669 1693 1697 1699 1709 1721 1723 1733 1741 1747 1753 1759 1777 1783 1787 1789 1801 1811 1823 1831 1847 1861 1867 1871 1873 1877 1879 1889 1901 1907 1913 1931 1933 1949 1951 1973 1979 1987 1993 1997 1999 2003 2011 2017 2027 2029 2039 2053 2063 2069 2081 2083 2087 2089 2099 2111 2113 2129 2131 2137 2141 2143 2153 2161 2179 2203 2207 2213 2221 2237 2239 2243 2251 2267 2269 2273 2281 2287 2293 2297 2309 2311 2333 2339 2341 2347 2351 2357 2371 2377 2381 2383 2389 2393 2399 2411 2417 2423 2437 2441 2447 2459 2467 2473 2477 2503 2521 2531 2539 2543 2549 2551 2557 2579 2591 2593 2609 2617 2621 2633 2647 2657 2659 2663 2671 2677 2683 2687 2689 2693 2699 2707 2711 2713 2719 2729 2731 2741 2749 2753 2767 2777 2789 2791 2797
2801 2803 2819 2833 2837 2843 2851 2857 2861 2879 2887 2897 2903 2909 2917 2927 2939 2953 2957 2963 2969 2971 2999 3001 ЗОН 3019 3023 3037 3041 3049 3061 3067 3079 3083 3089 3109 3119 3121 3137 3163 3167 3169 3181 3187 3191 3203 3209 3217 3221 3229 3251 3253 3257 3259 327) 3299 3301 3307 3313 3319 3323 3329 3331 3343 3347 3359 3361 3371 3373 3389 3391 3407 3413 3433 3449 3457 3461 3463 3467 3469 3491 3499 3511 3517 3527 3529 3533 3539 3541 3547 3557 3559 3571 3581 3583 3593 3607 3613 3617 3623 3631 3637 3643 3659 3671 3673 3677 3691 3697 3701 3709 3719 3727 3733 3739 3761 3767 3769 3779 3793 3797 3803 3821 3823 3833 3847 3851 3853 3863 3877 3881 3889 3907 3911 3917 3919 3923 3929 3931 3943 3947 3967 3989 4001 4003 4007 4013 4019 4021 4027 4049 4051 4057 4073 4079 4091 4093 4099 4111 4127 4129 4133 4139 4153 4157 4159 4177 4201 4211 4217 4219 4229 4231 4241 4243 4253 4259 4261 4271 4273 4283 4289 4297 4327 4337 4339 4349 4357 4363 4373 4391 4397 4409 4421 4423 4441 4447 4451 4457 4463 4481 4483 4493 4507 4517 4523 4547 4549 4561 4567 4583 4591 4597 4603 4621 4637 4639 4643 4649 4651 4657 4663 4673 4679 4691 4703 4721 4723 4729 4733 4751 4759 4783 4787 4789 4793 4799 4801 4813 4817 4831 4861 4871 4877 4889 4903 4909 4919 4931 4933 4937 4943 4951 4957 4967 4969 4973 4987 4993 4999 5003 5009 5011 5021 5023 5039 5051 5059 5077 5081 5087 5099 5101 5107 5113 5119 5147 5153 5167 5171 5179 5189 5197 5209 5227 5231 5233 5237 5261 5273 5279 5281 5297 5303 5309 5323 5333 5347 5351 5381 5387 5393 5399 5407 5413 5417 5419 5431 5437 5441 5443 5449 5471 5477 5479 5483 5501 5503 5507 5519 5521 5527 5531 5557 5563 5569 5573 5581 559! 5623 5639 5641 5647 5651 5653 5657 5659 5669 5683 5689 5693 5701 5711 5717 5737 5741 5743 5749 5779 5783 579) 5801 5807 5813 5821 5827 5839 5843 5849 5851 5857 5861 5867 5869 5879 5881 5897 5903 5923 5927 5939 5953 5981 5987 6007 6011 6029 6037 6043 6047 6053 6067 6073 6079 6089 6091 6101 6113 6121 6131 6133 6143 6151 6163 6173 6197 6199 6203 6211 6217 6221 6229 6247 6257 6263 6269 6271 6277 6287 6299 6301 6311 6317 6323 6329 6337 6343 6353 6359 6361 6367 6373 6379 6389 6397 6421 6427 6449 6451 6469 6473 6481 6491 6521 6529 6547 6551 6553 6563 6569 6571 6577 6581 6599 6607 6619 6637 6653 6659 6661 6673 6679 6689 6691 6701 6703 6709 6719 6733 6737 6761 6763 6779 6781 6791 6793 6803 6823 6827 6829 6833 6841 6857 6863 6869 6871 6883 6899 6907 -6911 6917 6947 6949 6959 6961 6967 6971 6977 6983 6991 6997 7001 7013 7019 7027 7039 7043 7057 7069 7079 7103 7109 7121 7127 7129 7151 .7159 7177 7187 7193 7207 7211 7213 7219 7229 7237 7243 7247 7253 7283 7297 7307 7309 7321 7331 7333 7349 7351 7369 7393 7411 7417 7433 7451 7457 7459 7477 7481 7487 7489 7499 7507 7517 7523 7529 7537
7541 7547 7549 7559 7561 7573 7577 7583 7589 7591 7603 7607 7621 7639 7643 7649 7669 7673 7681 7687 7691 7699 7703 7717 7723 7727 7741 7753 7757 7759 7789 7793 7817 7823 7829 7841 7853 7867 7873 7877 7879 7883 7901 7907 7919 7927 7933 7937 7949 7951 7963 7993 8009 8011 8017 8039 8053 8059 8069 8081 8087 8089 8093 8101 8111 8117 8123 8147 8161 8167 8171 8179 8191 8209 8219 8221 8231 8233 8237 8243 8263 8269 8273 8287 8291 8293 8297 8311 8317 8329 8353 8363 8369 8377 8387 8389 8419 8423 8429 8431 8443 8447 8461 8467 8501 8513 8521 8527 8537 8539 8543 8563 8573 8581 8597 8599 8609 8623 8627 8629 8641 8647 8663 8669 8677 8681 8689 8693 8699 8707 8713 8719 8731 8737 8741 8747 8753 8761 8779 8783 8803 8807 8819 8821 8831 8837 8839 8849 8861 8863 8867 8887 8893 8923 8929 8933 8941 8951 8963 8969 8971 8999 9001 9007 9011 9013 9029 9041 9043 9049 9059 9067 9091 9103 9109 9127 9133 9137 9151 9157 9161 9173 9181 9187 9199 9203 9209 9221 9227 9239 9241 9257 9277 9281 9283 9293 9311 9319 9323 9337 9341 9343 9349 9371 9377 9391 9397 9403 9413 9419 9421 9431 9433 9437 9439 9461 9463 9467 9473 9479 9491 9497 9511 9521 9533 9539 9547 9551 9587 9601 9613 9619 9623 9629 9631 9643 9649 9661 9677 9679 9689 9697 9719 9721 9733 9739 9743 9749 9767 9769 9781 9787 9811 9817 9829 9833 9839 9851 9857 9791 9803 9859 9871 9883 9887 9901
Приложение С Глоссарий Абстрактный класс (аЬвСгасС с1аа$). Класс, объекты которого нельзя созда- вать. Применяется для объединения общих свойств различных классов в од- ном либо для определения полиморфного интерфейса. Альфа-канал (а!рЬа сЬаппе!). Четвертый цветовой компонент, используе- мый для задания уровня прозрачности. Содержит 8 дополнительных битов информации о прозрачности для каждого пиксела. Анимация (апйпайоп). Повторяющиеся изображения, создающие иллюзию движения. Антиалиасинг (апНа11а81п^). Устранение эффекта ступенчатости на грани- цах однородных цветовых областей изображения путем раскраски пикселов, непосредственно прилегающих к границе, в цвета, промежуточные между цветами граничащих областей. Булев тип (Ьоо1еап Суре). Тип, который может принимать только два значе- ния: истина (сгие) или ложь (Га1$е). Буфер обмена (сПрЬоагд). Область в памяти, предоставляющая возможность обмена данными между программами. Венгерская нотация. Соглашение об именовании переменных, функций и классов с включением в их имя информации, описывающей тип и назначение. Выражение (ехрге$$1оп). Комбинация переменных, констант и операторов, соединенных в одной записи. Глобальная система координат (м'ОгШ соопИпаСе). Базовая система коор- динат виртуального трехмерного пространства, не зависящая от положения наблюдателя. Глубина цвета (со1ог <1ерСЬ). Величина, задающая максимальное количество цветовых оттенков, которые можно хранить в изображении. Например, изо- бражение с глубиной цвета 8 бит содержит до 256 оттенков цвета. .
Двойная буферизация (ЛоиЫе-ЬиПеНп^). Механизм, при котором для осу- ществления гладкой анимации изображение формируется в промежуточном буфере (который не отображается), после чего осуществляется обмен с ос- новным буфером. Заголовочный файл (кеа<1ег П1е). Файл, в основном содержащий объявления классов, переменных и функций. Такие файлы зачастую используются более чем в одной единице компиляции. Такие файлы обычно имеют расширения Ьрр, Ь, Н, ЬЬ, Ьхх и иногда называются включаемыми файлами. Интернационализация (1п(егпайопаи2аИоп). Также называется И8п. Пред- ставляет собой процесс создания программы, поддерживающей несколько языков и/или стандартов. Этот процесс может заключаться в изменении фор- матов даты и символа денежной единицы, а может требовать также перевода всей выводимой текстовой информации, включая кнопки и меню. Интерфейс (1п1егГасе). Именованное множество операций, характеризующее поведение отдельного элемента модели. Интерфейс графического устройства (СгарЫс Вепсе 1пСегГасе, 601). Подсистема операционной системы предназначенная для вывода графики. Итератор (1п(ега(ог). Объект, который обеспечивает обход последователь- ности элементов, принадлежащих объекту контейнера. Класс (с1а$а). Класс определяет множество характеристик создаваемых объ- ектов. Классы объявляются с использованием ключевого слова с1авг или вСгисе. Компонент (сошропепС). Соответствует физической реализации набора ин- терфейсов и обеспечивает их реализацию. Представляет собой независимую и заменяемую часть программы, выполняющую определенные действия. Контейнер (сонЫнег). Класс, используемый для управления группой объ- ектов. Контекст (соп1ех1). Поверхность для вывода графики. Масштабируемый шрифт (ТгиеТуре Гоп1). Шрифт, то есть набор согласо- ванных по стилю и другим параметрам форм символов, представленный в векторном формате, благодаря чему можно гибко изменять размеры и форму текста. Метафайл (ше1аП1е). Описание изображения в файле, который хранит по- следовательность операторов графического вывода. Метод (теШод). Реализация конкретного алгоритма или процедуры, ассо- циированной с соответствующей операцией.
Многоугольник (ро1у§оп). Замкнутая двумерная геометрическая форма с тремя или более сторонами. Модальное диалоговое окно (МоЛа! <На1о&). Диалоговое окно, не позво- ляющее работать с элементами интерфейса вызвавшего его приложения до завершения работы с окном. Модуль расширения (р1и§-т). Дополнительный программный модуль, спо- собный функционировать как составная часть программы. Модули расшире- ний широко применяются для предоставления новых возможностей без реа- лизации новых версий программ. Немодальное диалоговое окно (№п тоЛа! сКа1о§). Диалоговое окно, до- пускающее одновременную работу с элементами интерфейса приложения, вызвавшего его. Объект (оЬ]ес1). Экземпляр класса. Объект-потомок (сЫ1<1 оЬ]ес1). Объект иерархической цепочки, связанный с другим объектом, расположенным ближе к вершине дерева иерархии (объ- ектом-предком). Объект-предок (рагеп1 оЬ]ес1). Объект иерархической цепочки, распола- гающийся ближе к вершине дерева иерархии, чем связанный с ним объект, называемый объектом-потомком (сЫ1д оЬ]ес1). Объектная иерархия (оЬ]ес1 ЫегагсЬу). Цепочка или дерево, отображаю- щее порядок наследования одних объектов от других. Отсечение (сПррш§). Исключение части геометрического примитива, нахо- дящейся вне заданного пространства. Палитра (ра!еИе). Набор цветов, доступных для использования в составе изображения. Перечисление (епшпегаНоп). Список именованных значений, применяемых в качестве отдельного типа. Пиксел (р1хе1). Наименьшая область, которая может быть отображена на эк- ране. Положение каждого пиксела определяется уникальным набором двух чисел, именуемых координатами. Примитив (ргшпйуе). Простая геометрическая фигура, например, точка, линия или многоугольник. Прототип (рго1о1уре). Версия программы, не обязательно подчиняющаяся управлению и отвечающая полной спецификации. Процесс (ргосеаа). Самостоятельная единица исполнения в операционной системе.
Растровое изображение (ипадо, Ыйпар). Изображение, описанное двумер- ным массивом пикселов разного цвета. Система координат (соопНна<е куя^ет). Пары или тройки чисел, исполь- зуемые для задания положения точки на плоскости или в пространстве. Сигнал (8цгпа1). Метод, предназначенный для осуществления высылки со- общения. Слот ($1о1). Метод, реализующий ответное действие на полученный сигнал. Событие (еуеп1). Условие, обычно получаемое в результате выполнения не- которой операции пользователем. Строка ($СНп§). Последовательность символов текста. Текстура (СехШге). Одно- или двумерное изображение, применяемое для из- менения внешнего вида фрагментов, произведенных растеризацией. Формат файла (Ше Гоппа1). Способ организации данных в файле. Напри- мер, к числу распространенных форматов файлов растровых изображений относятся ВМР, ОП7,1Р6. Шаблон (1етр1а1е). Конструкция, представляющая семейство классов или функций. Шрифт (Гоп<). Группа представлений графических символов, обычно ис- пользующихся для отображения строк текста. Вга§ & йгор. Перетаскивание элементов интерфейса мышью. СМ11 (СМЛ’з Ыо111МХ). Рекурсивный акроним, который означает — ОМЗ не 1ЛЧ1Х. Так называется организация, которая борется за открытый исход- ный код программ. Одним из ее достижений является общедоступная лицен- зия на программное обеспечение (ОРЬ). СРЬ (СРШ РиЬПс Ысепве). Общедоступная лицензия ОЬШ. НТМЬ (Нурег1ех1 Магкир Ьап^иа^е— язык разметки гипертекстовых документов). Гипертекстовый язык разметки документа. Формат файлов для документов, в которых присутствует текст, графика и другие элементы. Ши- роко используется в Интернете. Н8У (Ние, 8а1игаНоп, Уа1ие — Цветовой тон, Насыщенность, Интен- сивность). Цветовая модель, предоставляющая возможность задавать цвет по его тону (чистому цвету), насыщенности (глубине) и интенсивности (яр- кости). П8п. Сокращение от английского слова 1п1ета11ОпаНгайоп (см. Интернацио- нализация). Число 18 в этой аббревиатуре указывает, что между первой бук- вой (1) и последней (п) находится еще 18 букв.
Ъ2УУ (Ьетре1-21у-\Уе1сЬ). Метод сжатия информации, который использует- ся, например, в файлах формата СП7. МВ1 (МиШр1е ОосшпепС 1п1егГасе). Графический интерфейс для одновре- менной работы с несколькими документами. М1МЕ (МиШригроае 1п1егпе1 МаП Ех&пяопа). Метод, с помощью которого данные могут быть закодированы в форму, легко передаваемую через Интер- нет или другое соединение, и затем преобразованы получателем к исходному виду. Был разработан для поддержки подключений к электронной почте, но применяется так же и в других целях, например, для поддержки Лгав & <1гор. ОрепОЬ (Ореп СгарЫс ЫЬгагу). Библиотека графических функций, разра- ботанная компанией 8Шсоп ОгарЫсх. КС В (Кед, Сгееп, В1ие). Цветовая модель, согласно которой цвет задается тремя компонентами: красной, зеленой и синей. КСВА (Кеб, Сгееп, В1пе, А1рЬа). Цветовая модель, включающая помимо красного, зеленого и синего цветов также и альфа-компонент (прозрачность). ХРМ (ХРЬкМар). Формат графических файлов в виде исходного кода на языке С. Файл в этом формате можно напрямую включить в программу или использовать как независимый графический файл.
Приложение О Описание ОУО-диска На прилагаемом к данной книге ОУО помещена библиотека (}( и исходные тексты описанных в книге примеров. Если вы пользователь АУтйошз, то вам удобнее всего будет воспользоваться программой установки книги (рис. 0.1), которая запускается автоматически при вставке ОУО. Эта программа напи- сана на СМ, и ее исходный код приложен на ОУО. Рис. 0.1. Установочная программа книги Данная программа содержит следующие кнопки: □ Установить примеры — запускает самораспаковывающийся архив при- меров. Вам нужно указать только путь для их распаковки;
□ Установить 018ПК — запускает установщик пакета, который содержит: • С++ М1пС\У (МЫтаНкГ ОВД) Гог АУтдохуя, минимальный <Ж11 для \Утс1о\У8). МтСАУ представляет собой коллекцию свободно распро- страняемых библиотек с компилятором С++, позволяющих создавать приложения АУтскжв; • библиотеку 0(4.5, предназначенную для использования с М1пСХУ; • отладчик ОТШ ПеЬи§§ег (ОПВ). Работа с этим отладчиком описана в главе 3', • среду разработки 01 СгеаЮг П)Е (1п(е§га(ес1 Пеуе1оретеп( Епуиоптепс, интегрированная среда разработки). □ Просмотр ПУП — открывает проводник 5Утс1о'У8 в коренной директории компакт-диска; □ Сетевая поддержка— производит запуск интернет-браузера с адресом ХУеЬ-сайта, где будут предоставляться дополнения и исправления к книге; □ Закрыть — закрывает окно основной установочной программы. Если на вашем компьютере не установлено ничего из вышеуказанного, то установку лучше всего производить в той очередности, в которой располо- жены кнопки, сверху вниз. Исходные коды примеров книги сгруппированы в отдельные каталоги глав. Каждый из примеров хранится в отдельном каталоге. Для их компиляции следует создать Маке-файл при помощи программы ОМАКЕ и запустить утилиту МАКЕ. В табл. П.1 приведено описание ПУЛ. Таблица 0.1. Содержимое компакт-диска Папка Описание /Аи1ошп/8гс Исходный код программы эксперта установки ком- пакт-диска /1пз1а11/Мас Программа установки 018ОК для Мас 08 X Лп$1а11ЛАЛпс1ош8 Программа установки О1 80К для \Л/1пс1о^8, а также компилятора С++ и программного отладчика /1пз1а11/Х11 Программа установки О1 80К для Отх/Нпах 32/64 /Тоо1э/Ооху6еп Утилита создания документации из исходного тек- ста программ /Тоо1з/'|деп(-2.2.9.ехе Утилита для форматирования исходного текста программ
Таблица Р.1 (продолжение) Папка Описание /Тоо1з/К01й38е1ир_0.9.90.ехе Утилита для сравнения различий исходных тек- стов программ /Ехатр1ез/сЬар1егО1 Пример первой главы: • НеПо — программа, отображающая надпись '•НеПо, УУог1сГ /Ехатр1ез/с11ар1ег02 Пример второй главы: • СоигПег — приложение, демонстрирующее механизм сигналов и слотов /Ехатр1ез/сИар1егО5 Примеры пятой главы: • Васкдгоипс! — демонстрация установки фона виджета; • МоизеСигзог — пример изменения указателя мыши; • ЗсгоПАгеа — иллюстрация виджета видовой прокрутки /Ехатр1ез/сИар1егО6 Примеры шестой главы: • Ас1с18!ге1сЬ — приложение, где вместо одной из кнопок производится добавление фактора растяжения; • Са1си1а1ог — калькулятор, демонстрирующий табличную компоновку (0<Зг1аьауоип); • НВохЬауосН — приложение, демонстрирующее использование горизонтальной компоновки (ОНВохЬауои Ъ); • 1_ауои1 — иллюстрация совместного использо- вания горизонтальной и вертикальной компо- новок; • 8рППег — демонстрация виджета разделителя (ОЗрИ^еег); • 81ге1сЬ — пример использования фактора рас- тяжения; • УВохЬауои! — приложение, демонстрирующее использование вертикальной компоновки /Ехатр1ез/сЬар1егО7 Примеры седьмой главы: • 1_аЬе1 — пример использования виджета надписи (оьаЬе!); • ЬаЬе1Ви(Му — демонстрация возможности ас- социации виджета надписи с другими видже- тами;
Таблица ОЛ (продолжение) Папка Описание /Ехатр1ев/сйар1вг07 Примеры седьмой главы: • ЬаЬе1Р|хтар — иллюстрация использования растровых изображений в виджете надписи; • ЮО — приложение, демонстрирующее вид- жет электронного индикатора (дьсэыитЬег); • Ргодгезз — демонстрация работы виджета индикатора прогресса (ОРгодгеззВаг) /Ехатр1ез/сКар1егО8 Примеры восьмой главы: • ВиПопбгоир — иллюстрация группировки кно- пок; • ВийопРорир — пример кнопки с всплывающим меню; • Вийопз — демонстрация различных режимов работы кнопки (ОРизИВи^Соп); • СКескВох — иллюстрация кнопок-флажков (ОСкескВох); • АасПоВийоп — демонстрация виджета кнопок переключателей (ркадхови^еоп) /Ехатр1ез/сЪар1егО9 Примеры девятой главы: • О!а1 — пример использования виджета уста- новщика (12О1а1); • 8его11Ваг — демонстрация виджета полосы прокрутки (03сго11Ваг); • $||с!ег — пример использования виджета пол- зунка (озиаег) /Ехатр1ез/сЬар(ег10 Примеры десятой главы: • Оа1еТ|гпеЕс1й — пример работы с виджетами отображения даты и времени (0ПаЁеТ1теЕ(11 е); • ЫпеЕс1й — демонстрация виджета одностроч- ного текстового поля (дЫпеЕдзЛ); • 8р1пВох — иллюстрация использования вид- жета счетчика (озрхпвох); • Тех1Ес1й — приложение, использующее виджет многострочного текстового поля (рТехЬЕЗ!^); • 7а11с1а1ог — приложение, демонстрирующее проверку пользовательского ввода (рУаИбаСог)
Таблица ОЛ (продолжение) Папка Описание /Ехатр1е$/сЬар1ег11 Примеры одиннадцатой главы: • СотЬоВох — демонстрация виджета выпа- дающего списка (осошЬоВох); • 1сопМос1е — иллюстрация режимов показа иконок; • ЫзЙЛ/1с1де1 — пример использования виджета простого списка (оЫзсвох); • ТаЫеУУЮде! — демонстрация возможностей виджета таблицы (рТаЪХемХддес); • ТаЬ\Л/|с1де1 — пример использования виджета закладок (отаЬмХадес); • Тоо1Вох — иллюстрация работы с виджетом инструментов (стооХВох); • ТгееУ\Лс1де1 — демонстрация возможностей виджета иерархического списка (ОТгееМХадеС) /Ехатр1ез/сНар1ег12 Примеры двенадцатой главы: • О1гМоде1 — иллюстрация использования гото- вой модели ОБХгМоаеХ; • Ехр1огёг— приложение, имитирующее про- водник на базе использования модели ОВХгМодеХ; • Н|егагсЫса1Мос1е1 — приложение, использую- щее модель озсагкХагЪТСешМодеХ для соз- дания иерархии; • 1п1из!Мос1е1 — реализация собственной моде- ли данных для списка целых чисел; • РгохуМобе! — пример использования проме- жуточной модели для осуществления отбора данных; • По1ез— демонстрация использования ролей для отображения данных; • 8е1ес1юп8Иаппд — иллюстрация разделения выделений элементов между представлениями; • 8|тр1е0е1еда1е— реализация делегата, про- изводящего выделение элементов при попа- дании на них указателя мыши; • ТаЫеМоде! — реализация табличной модели; • МбдеТАпсМем— демонстрация использования моделей элементно-базированных классов
Таблица 0-1 (продолжение) Папка Описание /Ехатр1е&/сЬар1ег13 Пример тринадцатой главы: • У\Лс1де1Ра1еие — демонстрация изменения па- литры виджета /Ехатр1ез/сИар1ег14 Примеры четырнадцатой главы: • МоивеЕуеп! — пример обработки событий мыши; • Пез12еЕуеп1 — иллюстрация обработки собы- тия изменения размеров /Ехатр1ез/сЬар1ег15 Пример пятнадцатой главы: • ЕуегйПИег — демонстрация механизма фильтрации событий /Ехатр1ез/сЬар1ег16 Примеры шестнадцатой главы: • ЕуепЮКапде — приложение, демонстрирую- щее подмену событий; • Еуеп181ти1а1юп — пример искусственного соз- дания событий /Ехатр1е$/сЬар1ег18 Примеры восемнадцатой главы: • Сотро8ЙюпМос1е8— демонстрация режимов совмещения пикселов; • СопюаЮгасИеп! — отображение конического градиента; • ЫпеагбгасЛеп! — отображение линейного гра- диента; • Рат1егРа1К— пример отображения графиче- ской траектории; • НасПаЮгасМеп!— отображение лучевого гра- диента /Ехатр1ез/сЬар1ег19 Примеры девятнадцатой главы: • 1тадеОгауу — рисование в контексте растрово- го изображения (оттаде) с его последующим отображением; • \ЛЛпс1ош — приложение, демонстрирующее применение прозрачности к виджету верхнего уровня /Ехатр1е$/сЪар1ег20 Примеры двадцатой главы: • ОгашТех! — отображение текстовой строки; • бгасНепГГех! — отображение текстовой строки, заполненной градиентом
Таблица Р.1 (продолжение) Папка Описание /Ехатр1ез/сИар1ег21 Примеры двадцать первой главы: • Сиз1от6гарЫс8\/|еуу — отображение элемен- тов с изменяемым местоположением; • Сиз1от6гарЫсз\Леуу — демонстрация реали- зации собственного класса представления и собственного класса элемента с возможно- стью обработки событий и группировки /Ехатр1ез/сИар!ег22 Пример двадцать второй главы: • Моше — программа, отображающая анимацию (фМсг^хе) /Ехатр1ез/сИар1ег23 Г-1 ' Примеры двадцать третьей главы: • ОбЬОгам — приложение, демонстрирующее эффект сглаживания цветов вершин четырех- угольника; • ОО1_Ругат|‘с1 — вращение пирамиды, демонст- рирующее трехмерную графику ОрепОЬ; • ООЮиас! — пример вывода графических при- митивов ОрепОЬ /Ехатр1ез/сИар1ег24 Пример двадцать четвертой главы: • Рпп1ег — программа, выводящая картинку на печать с использованием класса ОРгхп^ег /Ехатр1ез/сЬар1ег25 Пример двадцать пятой главы: • Сиз1отУУ1с1де1 — приложение, демонстрирую- щее создание и использование собственных виджетов /Ехатр1е$/сЬар1ег26 Примеры двадцать шестой главы: • Арр81у1е — иллюстрация использования раз- личных стилей; • С8881у1е — пример использования каскадных стилей; • Сиз1огп81у1е — приложение, иллюстрирующее создание и использование своих собственных стилей; • 81у1ез — демонстрация интегрированных в 01 стилей /Ехатр1вз/сЪар1ег27 Пример двадцать седьмой главы: • 8оипбР1ауег — демонстрация простых воз- можностей воспроизведения звука (озоипа)
Таблица ОЛ (продолжение) Папка Описание /Ехатр1е&/сйар(ег28 Примеры двадцать восьмой главы: • МесИаР1ауег и 81тр1еР1ауег — пример, показы- вающий возможности использования модуля для мультимедиа РЬопоп /Ехатр1ев/сЬар!ег29 Примеры двадцать девятой главы: • Зеззюп — пример управления сеансом (ОЗеззхоп); • 8еШпдз — приложение, сохраняющее свои настройки (дзеъеХпдз) /Ехатр1ез/сЬар1егЗО Примеры тридцатой главы: • Спад — приложение, реализующее сторону источника для перетаскивания; • Огор — приложение, реализующее прини- мающую сторону для перетаскивания /Ехатр1ез/сИар!ег32 Примеры тридцать второй главы: • СоШехТМепи — иллюстрация применения кон- текстного меню; • Мели — пример встраивания меню в приложение; • ТеагОПМепи — приложение, демонстрирую- щее отрывное меню /Ехатр1ез/сКар1егЗЗ Примеры тридцать третьей главы: • 1приЮ1а1од — приложение, демонстрирующее реализацию собственного окна диалога; • МеззадеВохез— пример использования окон сообщений; • 81апс1аг(Ю1а1од8 — демонстрация стандартных диалоговых окон /Ехатр1ез/сЬар1ег34 Примеры тридцать четвертой главы: • Не1рВгоуузег — приложение, предоставляю- щее систему помощи; • УУЬа1зТЫз — приложение, демонстрирующее предоставление подсказок "Нто это" /Ехатр1еа/с11ар(ег35 Примеры тридцать пятой главы: • 81а1изВаг—приложение со строкой состояния; • Тоо1Ваг — демонстрация использования пане- лей инструментов (ртоо1ваг); • МО1 — пример МОЬприложения (редактора); • 801 — простой редактор для одного документа; • 8р1азК8стееп — приложение, отображающее окно заставки (ОЗрТазИЗсгееп)
Таблица Р.1 (продолжение) Папка Описание /Ехатр1ез/сНар1ег36 Примеры тридцать шестой главы: • 8уз1етТгау — пример использования области оповещений; • 8сгееп8Ко1 — демонстрация использования рабочего стола /Ехатр1ез/сЬар1ег37 Пример тридцать седьмой главы: • ЕПеЕ|пс1ег — приложение для нахождения файлов, демонстрирующее использование класса овЬг /Ехатр1е8/сЬар1ег38 Примеры тридцать восьмой главы: • ВПпкЬаЬе! — приложение, демонстрирующее работу таймера (отЬтег); • С1оск — приложение электронных часов, ил- люстрирующее использование таймера и классов даты и времени (два^етЬте) /Ехатр1ез/сИар1ег39 Примеры тридцать девятой главы: • Ргосезз — приложение командной оболочки, демонстрирующее создание процессов (ОРгосезз); • ТЬгеабЕуеп!— демонстрация отправки собы- тий из потока; • ТЬгеаб8|дпа1— иллюстрация высылки сигна- лов из потока; • ТИгеасГПтег — пример использования сиг- нально-слотовых соединений в потоках /Ехатр1ез/сНар1ег40 Примеры сороковой главы: • СПеп1 и 8еп/ег — приложения, иллюстрирую- щие возможности классов отсрЗе^ег и рТср5оске.ь /Ехатр1ез/сИар1ег41 Примеры сорок первой главы: • ХтЮОМНеаб — приложение, читающее ХМЬ- документ при помощи ООМ; • ХтЮОМУУгйе — приложение, демонстрирую- щее создание ХМЬ-документа при помощи ООМ и его запись; • Хт18АХНеад — пример чтения ХМЬ-документа при помощи 8АХ
Таблица 0.1 (окончание) Палка Описание /Ехатр1ез/сНар1ег42 Примеры сорок второй главы: • 8ОЬ — приложение, осуществляющее чтение и запись в базу данных; • 8ОЬОиегуМос1е1 — демонстрация проведения отбора данных; • 8О1_ТаЫеМос1е1 — использование класса мо- дели 05д1ТаЫеМоде1 /Ехатр1е8/сКар1ег43 Примеры сорок третьей главы: • ОупЫЬ — демонстрация создания и загрузки динамических библиотек; • Р1ид1п — демонстрация создания и загрузки расширений /Ехатр1ез/сЬар1ег44 Пример сорок четвертой главы: • \МпАР1 — использование платформозависи- мых функций \ЛЛпс1слл/8 /Ехатр1ез/сИар1ег45 Пример сорок пятой главы : • МуГогт — приложение, созданное с помощью 01 0ез1дпег /Ехатр1ез/сНар1ег46 Примеры сорок шестой главы: • 0а1а0пуепТез1— проведение теста с переда- чей данных; • 6иПев1 — тест графического интерфейса; • Тез1ЫЬ — программа проведения теста /Ехатр1ев/сЬар1ег47 Примеры сорок седьмой главы: • 81тр1е\/|е7У и \Л/еЬВго\л/зег — демонстрация использования модуля УУеЬКЙ /Ехатр1ез/с11ар(ег49 Пример, используемый в сорок девятой главе: • СМЗЕПеНпйег — иллюстрация процедуры пе- ревода 013-программы на 014 /Ехатр1е8/сЬар1ег53 Примеры пятьдесят третьей главы, демонстри- рующие использование языка сценария в 01- приложениях, а также и их отладки: • ОеЬид — демонстрация возможности приме- нения отладчика для языка сценария 01 8спр1; • 81дпа1Апс181о18 — иллюстрация возможности использования механизма сигналов слотов в языке сценария 018спрГ, • ТигИе — пример черепашьей графики, управ- ляемой языком сценария 018спр1
Предметный указатель О .рго 62 .цгс 67 ПазкЬоагд 727 Паи Сотргездюп 5? Паи§гап) 632 А ПеасИоск 630 АсСюпЗспрС 785 Ап<1го1<1 727 Апй-АНа$т§ 318,384 А8СП 262 Аи&оОшриС 452,454 Пезкюр 35 П1гесС8Ьо\у 455 П1гесСХ 450 ПкчаЫес! 116 П15р1ауКо1е 236 ПОМ 652 В ПоиЫе ЬиНеппз 266 Пга§ 477 Васкепд 450 Васкт$81оге 117 ЬедоО 79 ВМеуе! 351 Вйтар 337 ВМР 337 Пга§ & (кор 184, 273 Пгор 477 Пиа1соге 614 НУЭ 452, 453 Е С ЕГГесС 452 Са118иск 765 СаПЬаск Гипсйоп$ 41 СП 452 с!еаг() 79 СКрЬоагд 474 СМУК 304 Со 11 шоп деСесйоп 362 Со1огго1е$ 253 сопзШе^пО 79 сопзсЕпсЦ) 79 соипЦ) 79 С88 433,727,731 ЕПесСРагатеГег 453 ЕГГес1\У1с1§е1 453 етк 47, 51 етрСуО 80 ЕпаЫеё 116 епао 79 Ехр1огег 727 Г Пас Випоп 163 ГогеасЬ 84. 197
Рогт III: (11гес( арргоасЬ 706 тЬегИапсе арргоасЬ 707 ти1(1р1е тЬепСапсе арргоасЬ 708 Гогу/ап! дес1агаЬоп 457 РТР 634, 646 21Еп4Ь151() 401 ОЕепит 386 бЬЛоаС 385 ё1Ргиз(ит() 399 ЗЮепЬЬиО 401 бит 385 §1ЕоасП(1ет1(у() 390 6 §1Ма(пхМо(1е() 390, 399 §1МежИш) 401 60В 69,756 аиасЬ 72 Ьгеак 72 с!еаг 73 сопйпие 73 <1е1е1е 73 де1асЬ 72 (ИхаЫе 73 епаЫе 73 Ье1р 72 пех( 73 ций 72 гип 72 8(ер 73 (Ьгеак 72 ипСП 73 601 692 61Р 337, 338, 379 6Е_СОЕОК_ВЕРРЕК_В1Т 391 6Е„С0МР1ЬЕ 401 6Ь_ОЕРТН_В11ЕРЕК_ВГГ 391 бЬРЕАТ 391, 398 6Е_Е1КЕ_Е00Р 395 6Е_Ъ1МЕ_8ТК1Р 395 6Е_ЫМЕ8 395 6Е,РО1МТ8 395 6Е_Р0ЕУ60И 395 6Е_(}11АО8 391,395,401 6Е.8МООТН 391 6^_ТКIА^6^Е_РА^ 401 6Е-ТК1АМ6ЕЕ-8ТК1Р 395 6Е_ТК1АМ6ЬЕ8 395 §1Ве$рп() 391 6ЬЬу(е 385 е1СаПЕ15С() 399 §1С1еаг() 391 ё!С1еагСо1ог() 389 §1Со1ог() 391 бЕдоиЫе 386 й1Епс1() 391 51ОпЬо() 390 §1Рот1$1хе() 395 §18Ьас1еМо<1е() 398 §18Ьа4еМо(1е1() 391 6Е$Ьог1 385 6ЕиЬу(е 386 6Ьшт 386 6Еи$Ьой 386 §1Уейех() 391 §1У1е\уРоП() 390, 399 6Г4ОМЕ 567 6оо§1е 727 6гарЬ1сз 1тегсЬап5е Рогтас 338 бгарЫсзИет 370 бгарЫсзЕтеКет 367 68(геатег 450, 455 Н Н8У 300, 303 НТМЬ 725, 727 НТТР 648 1 1С9 568 1П8еП() 79 1РЬопе 727 18Етр(у() 80 |Типе& 725, 727 и 1ауа8спр( 727, 785 .кмт РЬоЮ§гарЬ1с Ехреги бгоир 338 ^Е6 337 К КОЕ 450, 567, 727 КОЕММ 450
Кеу 463 Кеу Уа1ие 463 Копдиегог 727 О ОЬ]есЮе8сприоп 453 ОрепОЬ 384 и Р ЬауоиГ 128 Ьауои! тапа^егз 129 Нпк 68 Ыпих 450,455 Ьоок&Гее! 35,417 1ге1еазе 491,495 1ир<1аСс 491,492 Ь2\У 338 РаЛ 452 РВМ 337 РОЕ 403 РОМ 337 РЬопоп 450,451 Сё 455 сгеаиРагЬО 452,454 Оуа 455 М Уса 455 РМС 337, 338 Мас 08 X 450, 455, 567, 725 Мш1 727 такеШе 62 таке-файл 62 МЫ 550, 566 МеШаСоШгоПег 453 Ме<йаОЬ]ес1 454 епциеие() 455 ГткЬесЮ 454 те1аОагаСЬап§еа() 452 раизе() 452 р!ау() 455 зеГСиггетЗоигсеО 452, 455,459 зГаП() 452 $Гор() 452 МесНаЗоигсе 452 Метогу1еак 132 Метогу таррес! Я1ез 586 МепиЕхГгаз 567 МРС 42 М1СГО8ОЙ 727 М1СГО8ОЙ У1зиа1 8шд1о 69 М1МЕ 476, 727, 728 М1пС\У 450 МЫС 337, 338, 379 МОС 44,46, 65, 66, 620 Моде1у1еу/ таСпх 390 Мо1И 42 МР1ауег 451 Ми11|р1е ОоситепГ ХУтск)\у [пГегТасе 550, 566 МиГех 627 Р^ 337 РоПаЫе ЫеМогк ОгарЫсз 338 РРМ 337 Ргодгезз Ьаг 154 Рпуесйоп таГпх 390 О 0_А88ЕВТ() 73 0_СНЕСК_РТК() 73 0_ОЕСЬАКЕ_1МТЕКРАСЕ() 684 9_ЕХРОКТ_РЬЬЮ1М2 681 0_1МТ16 75 ОЛЧТ32 75 (2ЛЧТ64 75 ОЛЧТ8 75 (2_1МТЕКРАСЕ8() 688 0-ОВ1ЕСТ 44, 65 0_О8_А1Х 692 0_О8_РКЕЕВ8О 692 (2_О$_ПиХ 692 <?_О8_ЫЬШХ 692 0_О8_МАС 692 0_О8_8ОЬАК18 692 0_О8_\У1М32 692 0_О8_\У1Ы64 692 (2_РЯОРЕКТУ 40 Р.О1МТ16 75 О-1Л1ЧТ32 75 0_1ЛМТ64 75 О_ДЛ1ЧТ8 75 (}АВ8() 75 РАЬзСасГБсгоПАгеа 224
РАЬз1гас1ВиПоп 161 сПскед() 162 1соп() 162 1соп81хе() 162 1зСкеске<1() 162, 173 1$Оотуп() 162 1зЕпаЫес1() 162 ргеззес1() 162 ге1еазес1() 162 зесСЬескедО 162, 169 зеЮо\уп() 162 $е1ЕпаЫе<1() 162 зеПсопО 161 зе11соп81хе() 161 зе(Техг() 161 1ех1() 161 Ю§§1ес1() 162 сНскедО 163 РАЬзкасгСгарЫсзЗНареПет 365 рАЬз1гасШет 207 9АЬз(гас1кетОе1е§а1е 228 РАЪзггасШетМоёе! 222 <1а1а() 230, 236 т<1ех() 230 РАЪз1гасШетУ1е^ 206,224 ВоиЫеСКскес! 224 тсх1е1() 249 МиШ8е1ес6оп 206 НоЕдкТп^егз 224 Ыо8е1ессюп 207 8е1есГе(1С11скес1 224 $е1есйопМо<1е1() 226 8е1Ес111Тп$§ег8() 224 5еШетВе1е§а1е() 229 зеСКооПпдехО 234 зес8е1ес1юпМо(1е() 206 зег8е1есСюпМо<1е1() 226 рАЪзкасИлзсМойе! 222 (2АЬ$:гас1РппЮ1а1о§ 6готпРа§е() 407 1оРа§е() 407 0АЬ$1гас1РгохуМ(м1е1 223 (}АЬ8ГгасС8сго11Агеа 124 согпег\У1д§е1() 124 Ьопгоп1а18сго11Ваг() 124 зеМе>уРоП() 365 уег6са18сго11Ваг() 124 У1еироП() 124 (}АЬ8Сгас181к1ег 175 зе1Мах1шит() 176 зеСМЫтитО 176 зеЮпетайопО 175 8е1Ра§е81ер8() 176 8есКап§е() 176 8е181п$1е8сер() 176 зеСТгаскт^О 176 8е1Уа1ие() 176, 178 811(1егСИап§е() 175 зНдегРгеззесК) 176 $11с1егНе1еа8ес1() 176 уа!ие() 176 9АЪз1гас(8оске1 633 9АЬз1гас(8ртВох 198 рАЪз!гас1ТаЫеМо(1е1 222 ОАсбоп 541 а<1с1АсС10П() 561 зе!ЕпаЫес1() 503 1п^бегес1() 561 цА1рЬа() 302 ОАррПсаСюп арр11сайопВиРа1Ь() 587 аррНсайопГПеРаШО 587 Ьеер() 442 сПрЬоагдО 475 с1о8еАП\У1пс1о\У8() 561 сошпйсОаиО 471,473 дезкЮрО 574 1П81а11Р1кег() 287 188е831опКе81гогед() 472 ргосеззЕуепЮ 591 ргосеззЕуепиО 602 гез^огеОуегпдеСигзогО 121 зауе81а1е() 471 зе(Е)оиЫеС11ск1п1егуа10 268 зе1Гоп1() 357 8есМа1п\У1(1§е1() 768 зеЮуегпёеСигзогО 121 5е1Ра1еПе() 256 5е1<ЭшЮпЕа8С\Утс1оуу() 569 зег8Су1е() 419,424 зе181у1е8ЬееС() 433 8СапОга$В181апсе() 480 0Ваз1сТ1тег 608 цВ1пагуР1п<1() 100 (ЗВкАггау 88 рВктар 351 цВ1ие() 302 рВохЬауои! 129 аМЬауоиЮ 135, 137 аск18рас1Пё() 131
адс18(ге(сЬ() 132 В о МотТо Тор 130 1пзепЕауоиС() 131 тзеп8раст§() 131 тзеп8(ге1сН() 131 тзеп^к^еЮ 131 ЬеЛТоК^Ы 130 К^МТоЬей 130 зеСМаг^пО 137 ТорТоВоПот 130 рВгизЬ 253,314 ОВиГГег 483, 581, 586 ЬиЕГегО 586 зеШиГГегО 586 РВиНоп 52, 142 ОВуГеАггау 87, 477,483, 639, 770 ГготВазе64() 88 1оВазе64() 88 0СапуазВес1ап§1е 368 0СОЕ81у1е 419 ОСкескВох 166 ОСЫНЕует 274 ОС1еа1оок8гу1е 419 ОСПрЬоагй 474 йаиСНап§ес1() 474 1та§е() 475 т!теОа(а() 475 р1хтар() 475 зе11та§е() 475 $е1М1теОа1а () 475 зеСР1хтар() 475 зеПехЦ) 475 (ехЦ) 475 0С1озеЕует 274 0Со1ог 253, 301 а!рЬа() 302 а!рЬаГ() 302 Ыие() 302 ЫиеР() 302 со1огО 121 со1ог1 121 йагкег() 307 §е1Нзу() 304 ёе1КёЬ() 302 §гееп() 302 §геепР() 302 «УаВД) 518 11§Ыег() 307 гед() 302 ге<1Р() 302 гёЬ() 303 зеСНзуО 304 зегК^О 302 ОСо!огВ1а1о§ 518 ОСотЬоВох 214 асйуа!ес1() 214 аскШетО 214 аскШетзО 214 сиггепПпйехО 214 еШсТех1Скапёес1() 214 зеСОирИсаСезЕпаЫёёО 214 зесЕШсаЫеО 214,215 5е1Уа1ШаЮг() 200 0Соттоп81у1е 419 0СОМРАВЕ0 717,721,723 дСотргеззО 88, 585 ОСотсаЮгаШет 317 ОСогНепШапШег 659 0Соп1ех1МепиЕуеш 506 цСоруО 100 цСоруВаск\уагд() 100 ОСогеАррПсайоп 34,74,261,281,288 ехес() 30,261,277 ехк() 31 тзшППкегО 287 тз1а1!Тгапз1аи>г() 496 пойГуО 287 роз(Еуеп1() 277, 288, 622 ргосеззЕуепГзО 281 цик() 454 зеп(1Еуеп1() 277, 288, 622 зегАррНсайопНатеО 464 зеЮг^атзаГюпЫатеО 464 цСоипС() 100 0С81пп§ 770 ОСигзог 119 Р0а(а8Сгеат 639 (ДОМе 598 аскЮауз() 600 сипгетОа1е() 600 <1ау() 599 <1ауОГ\Уеек() 599 дауОГУеагО 599 ёаузЬМопЛО 599 ёауз1пУеаг() 599 ёаузТоО 600 й-от81пп§() 600 топ1Ь() 599 8еЮа(е() 598 АУеекМитЬегО 599 уеагО 599
рВаГеЗсгеат 596 рВаСеЪте 602 сиггепЮа1еТ1те() 200 рВа1еТ1теЕс111 200 рВВ2 667 чЭеЬизО 74, 761 Ч0е1е1еА11() 100 РВе8кгор\У1д§е1: 574 901а! 175, 181 8е1Мо1сЬе8У1$|Ые() 181 зесМоГсЬТаг^егО 181 8ебУгарр1п8() 181 уа1иеСЬап§ес!() 182 РВ1а1о§ 508 ассерС() 514 АссерГед 407, 509, 514 ехес() 512,514 ШеО 510 18М(м1а1() 509 Ке]ес(ес1 509,514 ге]ес1е<1() 514 8есМос1а1() 509 $Ьсж() 510 (ДОг 581,587 аЬ8о!и1еРасЬ() 589 сс!() 587 саиро 587 соип1() 588 сштегН() 587, 589 сштетРаШО 234 дпуе8() 587 еп1гу1пГоЬ181() 588 еп1гуЬ181() 588, 591 ех1818() 587 1юте() 587 такеАЪ8о1и1е() 587 ткШг() 587 гепате() 587 гтсйг() 588 гоо1() 587 8е1Р1кег() 591 8е18оПт§() 592 (етрРаЖО 587. рВиМоде! 223, 232 рВотАПг 653 8е1Уа1ие() 658 рВотВоситеш аррепаСЫ1с1() 656 сгеа1еАипЬи!е() 655, 658 сгеа!еЕ1етепЮ 655,658 сгеа(еТех(Мос1е() 655, 658 <1оситешЕ1етепС() 655 зегСоМехЦ) 655 1о81пп§() 656 рВотЕ1етеп! 653 РВотМоНе 653 рВотТех! 653 рВоиЫеУаНёасог 201 рВга§ 480 $е1М1теВаи() 487 5е1Р1хтар() 480 8СаП() 480 рЭга^ЕгИегБует 273,482 РВга§ЬеауеЕуеп! 273 рВга^МоуеЕуеп! 273, 482 рВгорЕуепс 274 цЕциаЦ) 100 РЕггогМеава^е 530 рЕуеп! 261,278 ассерЮ 261,274 Ассе881Ы1куВе8спрйоп 280 Ассе881ЬШ1уНе1р 280 Ассе881Ъ11куРгераге 279 АсСюпАсШес! 279 Ас1юпСЪап§е<1 279 АсНопКетоуед 279 АсйуайопСЬап^е 279 АррВсайопАсйуаГед 280 АррНсайопВесйуаСеё 280 АррНса1юпРопССЬап§е 279 АррНса11опЕауои1В1гес0опС11ап§е 275 АррНсайопРа1еиеСкап^е 280 Арр11сайопХУ1пдо\у1сопСЬап§е 279 СЫШАМед 280 СЬ11аРоВ8кеё 280 СЬНаКетоуеё 280 СКрЬоагё 280 С108е 279 Соп1ех1Мепи 278 СгеаГе 279 ВеГеггеаВе1е1е 280 Вез1гоу 279 Вга^Етег 280 Вга^Ьеауе 280 Вга^Моуе 280 Вга§Ке8роп8е 280 Вгор 280 ЕпаЫеёСкапёе 279 Еп1ег 279 Еп1ег\Мка151Ъ18Моае 280
РкеОреп 279 ТаЫеГКеказе 279 Роси81п 279 ТкгеааСкап§е 279 РосизОш 279 Ътег 278 РогнСкап^е 279 Тоо!ВагСкап$е 280 НИе 279 ТооШр 279 НШеТоРагеш 279 1уре() 261,278 НоуегЕтег 280 бр<1а1еЬа1ег 278 НоуегЬеауе 280 ИриаГеКециез! 278 НоуегМоуе 280 Ъзег 277, 280 1сопГ>га§ 279 ХУка^зТЫз 279 1сопТех1Скап§е 279 УУкаСзТЫзСкскед 280 1§поге() 261,274 \Укее1 279 1при(Ме(Ьсм1 278 ХУ1п<1о\уАсйуа1е 279 КеуРгезз 279, 289, 291 \У1пс1о\уВ1оске(1 279 КеуКе1еа$е 279 5У1пдо\уОеасЙУа1е 279 Ьап§иа§еСкап§е 279 \У1пс1о\у1сопСкап§е 279 ЬауоиЮиесНопСЬап^е 279 ХУ1пс1о\у8СаГеСкапйе 279 ЕауоШКециезс 278 \У1пс1оу/Гк1еСкап§е 279 Ьеауе 279 \У1пдо\у11пЫоскес1 279 ЬеауеХУЬаиТккМоде 280 \У1пЕуеп1АсС 280 Ъоса1еСкап§е 279 20гёегСкап§е 280 МепиЬагУрс1а1ес1 280 ЧРа1а1() 74 Мо&йеёСЬап^е 279 РРЕТСН0 721 МоизеВиНопОЫСНск 278 (}Р11е 581,584 МоизеВиПопРгезз 278, 286 с1озе() 554 МоизеВиПопКеказе 278 ех1з1з() 584 МоизеМоуе 278 ШеЫатеО 584 МоизеТгаскш^Скап^е 279 Ли8к() 584 Моуе 279 ореп() 552 Мопе 278 8е1Мате() 584 Раш1 279 (ЗРПеОваке 515 РакиеСкап^е 280 5е1Ех18кпеП1гес1огу() 515,517 Рагеп1АЬои1ТоСкап§е 280 беЮрепРПеЫатеО 515,552 РагетСкап§е 279 §е1ОрепРПе№те$() 515 Рокзк 278 ее18ауеРИеМате() 515,553 РокзкКециез! 280 (}РПе1пГо 581,592 РиегуХУка1зТк18 280 аЬ8о1и1еР11еРаСк() 592 <№ 279 ЬазеИатеО 592 Кезхге 279 сотр!е1е8иН1х() 592 8коПси1 279 сгеа1ед() 592 8коПси1Оуегпде 280 ШеМатеО 592 8ко\* 279 Г11еРаЙ1() 592 8кои/ТоРагегП 279 18О1Г() 592 8Ьо\у\У1пдо\уКецие81 280 15ЕхесиСаЪ1е() 593 8оскАс1 280 1вР11е() 592 8рееск 280 18Н1ааеп() 593 8га1изТ1р 279 1$Кеа4аЫе() 593 8Гу1е 279 гзЗутЫпкО 592 81у1еСЬап5е 279 1зУУп1еаЫе() 593 ТаЫе(Моуе 279 1а81М(к11йеа() 592 ТаЫе1Ргезз 279 (азсКеасЦ) 592
0Р11е8у81ет\Уа1скег 594 дРШО 100 цРтд() 100 ОРосизЕуепГ 265 ОРот 356 рРопСОаСаЬазе 357 0РопЮ1а1о§ 518 ОРопНпГо 357 ОРопсМеГпса 357 ОРгате 123 Вох 123 с!га\ургате() 415 Н1ипе 123 1ЧоРгате 123 Рапе! 123 Р1ат 123 На1$е4 123 зе1Ргате() 186 8е1Ргате81у1е() 123,414 зейЫпе^УШто 124,186,414 8е[МИЫпе\У1с11Ь() 124 8ипкеп 123 УЬте 123 ТОпРапе! 123 ОРф 647 ОСЬ 386 06ЬСо1огтар 386 ОСЬСопСех! 386 ОСЬРогта! 386 0СЬР1хе1ВиЙег 386 ОбЕАУШДО 386 сотех!() 386 ГогтаК() 386 пШакгебЦ) 387 ратЮЦ) 387 Я§1С1еагСо1ог() 389 ге812е6Ь() 387 ООгасИеп! 5еССо1огА1() 360 ООгарЫсзАттабоп 367 ОбгарЫсзЕШрзеКет 365 06гар№с$1сет 362 дга§Еп(егЕуеп1() 370 Цга^МоуеЕуепсО 370 ЦгорЕуепЦ) 370 Ыс1е() 365 рат1() 365 го(а(е() 365 5са1е() 365 8е(ЛссерСВгор$() 370 $е1ЕпаЫе() 365 $е1Ма1пх() 365 8е1Ро8() 365 8ееТгап8Гогт() 378 8Ьеаг() 365 8Ьо\уО 365 Сгап81а1е() 365 ОСгарЫсзкетСгоир 367 06гарЫс8Р]хтаркет 367 0СгарЫс8Ро1у§опкет 365 ОСгарЫс8Ргоху\У1д§еС 377 ОСгарЫсзНесгкет 365 8еСВги8Ь() 368 8е1Реп() 368 ООгарШсзЗсепе 362 аскШетО 364 1СетА1() 364 Пет8() 364 06гарЫс88сепеЕуеп1 370 06гарЬ1С8$1тр1еТех1кет 365 0ОгарЫс8§у§кет 367 ООгарЫс8У1е5У 362, 368 сепГегОп() 364 8е(Ма1пх() 365 8е18сепе() 368 8110^0 368 Чбгееп() 302 ОСпд 768 ООпдЕауои! 129, 137 аскПУМвеЮ 142 8е1Со181ге1сЬ() 137 8е1Ко\у81ге1с11() 137 зе18раст§() 137, 138 ОСгоирВох 170 дНазкО 96 0На8к<К,Т> 96 ОНавккегаЮг 81 ОНВох 768 ОНВохЬауоиГ 133, 134, 136 0Неа«1егУ1е5У 225 ОНИеЕуеМ 274 ОНкр 648 01ВА8Е 667 01сопУ1е\у 770 01та§е 340 сопуегТоРогтаК) 341 Гогта1() 341 РогтаиАКСВ32 341 Рогта1_АКОВ32_Ргетик1ркед 341 Рогтас1пс1ех8 341
РогтаС_1пуа1Ш 340 РогтаСМопо 340 РогтасМопоЬЗВ 341 РогтаГ_К6В32 341 1ПУепР1хе15() 344 1оа<1() 342 пмггогеёО 347 р1хе1() 342 зауе() 342 5са1е<1() 344 $сап1лпе() 341 8е(Со1ог() 343 8е1Р1хе1() 343 Р1та§еЮР1и§т 680 Р1приЮ1а1о§ 519 р1приГЕует 261, 265, 272 ц1п51а11М5§Напд1ег() 74 фпСУаИсЫог 201 рЮОешсе’ 455, 581 Аррепй 582 а(Епс!() 583 с1о&е() 583 ёегСЬагО 583 18Ореп() 584 18&еас1аЫе() 584 1$\УгйеаЫе() 584 1ЧоЮреп 582 ореп() 583 орепМойеО 583 рО8() 583 геаё() 583, 584 геадАПО 583, 585 геасЮаГа() 583 геаЛлпеО 583 КеадОп1у 582 НеасВУгйе 582 8еек() 583 81хе() 583 Тех1 583 Тптсаее 583 ПпЬиЯегеа 582 \уп1е() 583, 584 у/п1еОа(а() 583 \УгкеОп1у 582 рПетОе1е§а1е 228 сгеаСеЕШюгО 230 8есЕс11ГогЭа1а() 230 зеГМодеЮаСа() 230 Р11:ет8е1ес1юпМос1е1 225 сиггепсСЬапеесК) 227 сиггеп(Со1итпСЬап§е<1() 227 сиггеп1Яо^СЬап§ес1() 227 8е1ес1() 227 8е1ес1еШги1ехе8() 227 8е1есНопСЬап§е(1() 227 рКеуЕуеп! 262,265,291 рЬаЬе! 148, 188 1ткАс11УаГе<1() 153 зегАН^тепЦ) 148 8е1АИ§птеп1() 272 8есВис1с1у() 152,186,202 8е(Моу1е() 148, 380 зесЫитО 179 8есОрепЕх1ета11лпк8() 154 зегИхтарО 148, 152, 577 аегТехЦ) 148, 150, 272, 276, 482, 605 рЬауоие 129 рЬауоиШет 129 РЬСОМшпЬег 157 Вт 158 Оес 158 Шзр1ау() 159 Р11Ы 158 Р1а1 158 Нех 158 Осс 158 Оийте 158 оуегПо\у() 157 зееВтМодеО 158 зеГВесМоёеО 158 8с1НехМо<1е() 158 8ееМос1е() 158 8ееМитВ1§й8() 157 зеЮсСМодеО 158 8е18е§тет81у1е() 141, 157 8ее8та1Юес1та1Ро1п1() 157 уа1иеСИап§ес1() 159 рЫЬгагу 678, 680 рЬте 299 рЬтеагбгаШепе 316 рипеЕдЛ 184 с!еаг() 645 сору() 186 ст() 186 18Рес1оАуаПаЫеО 187 1$11гк1оАуаПаЫе0 187 тахЕеп§сЬ() 186 Ра58\уогс1 184 раз1е() 187 гейо() 187
геШтРгеазесЮ 184 $е(ЕскоМо(1е() 184 5е1МахЬепёй1() 186 8есКеасЮп1у() 184 $е(ТехС() 184,645 зеГУакёагогО 200 СехГ() 184 1ехсСкап§ес10 184 шк!о() 187 <21лпеР 299 <2Ипкес1Е18(<Т> 90 0ЫпкесПл8(кега1ог 81 (}Е18Г<Т> 89 а!() 90 тоуе() 89 гетоуеР1г$1() 89 гетоуеЬазсО 89 8\уар() 89 1акеА1() 89 1акеР1Г8С() 89 ГакеЬазЦ) 89 го8е(() 90 Со8кИл81() 90 1оУес1ог() 90 ОЫзСВох 770 ОИзскегаСог 81 ГтдИехЦ) 82 Гтс1Ргеую118() 82 Ьа5№х1() 81 ИазРгеуюизО 81 пехЦ) 81 реекМехс() 81 реекРгеуюизО 82 ргеуюизО 81 СоВаск() 81 ЮРгопЮ 81 9Ь1$(У1ем/ 224 ТорТоВоиот 209 $е1Р1о>у() 209 $ес8е1ес11ОпМоде() 211 <21л8ЛУ1две1 204, 249, 770 аскШет() 204 с!еаг() 204 сштеп1кет() 206 тзегШетО 205 твегШепкО 205 кетСкап^есК) 207 ПетСПскеёО 207 кетОоиЫеСНскес1() 207 1Гст8е1ес(лопСЬапйес1() 207 ИетАУШдоО 205 8е1ес(есП1ет8() 206 8еШет\У1с1§ег() 205 зойкетзО 206 (}Ы81\У1ф*е11(ет 204 9Ьоса1е 497 дЬо\уегВоипё() 100 <2Ма1п\Ут<1о\у 539 адёВоск\У1<1§е1() 545 аййТоо1Ваг() 544 сеп1га!\У1(1ееС() 541 тепиВагО 539 8есСепГга1\У1(1§е1() 541,556,561 8СаШ8Ваг() 541 Ятаке 62, 64, 68 (?Мар<К,Т> 94 РМаркегаСог 81 ()МАХ() 75 ОМсйАгеа 556 са8са^е8иЬ\У1гк1ом/$0 556 8е1Нопхоп1а18сго11ВагРо11су() 561 8е1Уегйса18сгоПВагРоВсу() 561 $иЬ\У1П(1о\уИ81() 556 ййе8иЬ\Утдо\У8() 556 (}Ме(11аОЬ]ес1 455 ОМепи 498, 570 а&1Асйоп() 166, 502 аск18ерага1ог() 502 с!еаг() 565 ехес() 506 аеГСкескесК) 502 8е1ТеагОГГЕпаЫес1() 504 1п§§егес1() 506 9Ме88а§еВох 524 АЬоП 526 аЬоигОК) 529 Сапсе1 526 Спкса1 526 спйсаЦ) 528 ВеГаик 526 Езсаре 526 ехес() 526 1§поге 526 1пГогта1юп 526 тГогтагюпО 527 Ыо 526 МоАП 526 МоВикоп 526 МоТсоп 526 Ок 526
риезкоп 526 <2М-фаил 495 Ке(гу 526 рМе1\УогкРго1осо1 771 зеШииопТехСО 526 <2ОЬ}ес1 38, 49, 55, 284 зеНсоп() 526 Ъ1оск51§па18() 51 8е11сопР1хтпар() 526 сЫМО 274 5е(Тех1() 526 сЫМЕуепЦ) 274 АУагшп^ 526 сЫШгепО 57 \уаппп§() 527 соппесЦ) 50, 54, 182, 512, 537 Тез 526 соппесЦ) 142 УезАП 526 сизГотЕуешО 277 РМе(аОЬ]ес1 57 <1е1еСеЬа1ег() 637 рМ1тейа1а с!15соппес1() 54 ЬазРогтаЦ) 482, 489 дитрОЪ]есг1пГо() 57, 74 зесСо1огВа1а() 476 йитрОЬ]'ес1Тгее() 57 зеЮаГаО 477,483 еуепС() 277 зеГНГтЦ) 476 суепсГ1ксг() 284,285,291,292 8еС1та§еОа1а() 476,483 ГтдСЫШО 57 зеСТехЦ) 476 ГтдСЫШгепО 57 зегШз() 476 ткеп&О 58 иг1з() 482 тзГаНЕуепсПкегО 283, 285, 290 рМ1те8оигсе 475 кИГПтегО 604 (?М1М() 75 тоуеТоТЪгеа<1() 617 рМодеПпйех 230 оЬ]ес!Нате() 55, 74 рМокйкук 419 рагет() 56 рМоизеЕует 354 зепс!ег() 49 рМоизеЕуеп! 259,286 зе1ОЬ]есСМате() 55, 769 ЬиПоп() 267, 286 зе1Рагет() 55 ЬиИопзО 267 з€апТ1тег() 603 §1оЬа1Роз() 267 €Ьгеас1() 617 21оЪа1Х() 267 ИтегЕуепЦ) 273, 604 ё!оЬа1У() 267 С1тег1<1() 604 роз() 267 1г() 492 х() 267 цо^ес1_саз1<Т>() 39,686 УО 267 (?ОЬ]ес1Ызг 57 рМоизеЕуем 547 ООС1 667 РМоуеЕует 274 РСЮВС 667 (}МоУ1е 379 рРатЮеуюе 310, 311,402 ЫоКиптпё 380 рРа1пЮеУ1сеМеСпс8 769 Раизес! 380 (2Рат1Еп§1пе 694 Киптп§ 380 рРатГег 311 зеСРаизес1() 379 ЪевтО 312,348 зе15реес!() 379 Сотро8к1опМоде_8оигсеОуег 3 з1ап() 379 скахуЕШрзеО 348,408 з!а1е() 380 с1га\у1та^е() 343, 348 $Сор() 379 {кашЬтеО 408 0МиШНазЬ<К,Т> 97 ёга^РаЛ() 330 рМиШМар<К,Т> 96 йга>уР1СШге() 326 РМи1ех 627 с!га>уР1хтар() 823 (ЗМШехЬоскег 629 <1га\уРо1уЕ1пе() 321 0МУ$0Ь 667 с1га\уКес1() 360,408
агаад/ТехЦ) 358, 359,408, 415 епд() 312,348 ткРготО 348 гезе1Ма1пх() 824 гезюге() 313,327 го1а€е() 327 зауе() 313,327 зса1е() 327 зеШпиЬО 314,408 зеССПрРаЛО 331 зегСНрКесЦ) 331 8есСНрКе§юп() 331 зеССотрозкюпМодеО 333 зесРопсО 358,408 зе(Ма1пх() 329 зе1Реп() 313,314,408 . $е(Кеп(1егНт1() 318,348 зкеаг() 327 (гап$1а1е() 327, 824 У1еуфоП() 408 (}Рат1егРа111 330 рРатсЕуеШ 261, 266, 311 гесф 311 ге§1оп() 266,311 рРа1е«е 252 Асйуе 253 Вазе 253 Вп^ЫТех! 253 Виной 253 ВиНопТех! 253 йагк 254 ОкаЫед 253 253 Н1§Ы1ёЬ(е(Пех1 253 1пасйуе 253 1Л2Ы 253 Ыпк 253 Ь1пкУш1е<1 253 М1ё 254 Мк№&№ 254 зеШгизЬО 255 зегСо1ог() 173,255 8кас1о>у 254 Техг 253 АУтскну 253 \Утс1о\уТех1 253 (}Реп 313 (}Р1с1иге 326 рР1сШгеЕогтаСР1и$1п 680 (}Р1хтар 349 сгеа1еНеип8НсМазк() 354 (1еСаикЕ>ерй1() 349 ага\уР1хтар() 349 6П() 824 §гаЬХУ1пдо\у() 577 1оаН() 152,349 го1а1е() 824 зауе() 349 8са1еа() 577 зегМазкО 351,352 9Р1хтарСасЬе 350 Гт<1() 350 1пзеп() 350 (2Р1а811яие$Су1е 419 рР1и§1пЬоаёег 682, 686 тз1апсе() 682, 686 ип1оад() 682 (}Ро1Ш 295 15ГЧи1)0 296 тапЬаиапЕеп^ШО 297 гх() 296 зегХ() 296 зегТ() 296 х() 296 УО 296 (2РО1МР 295 рРо1у§оп 300 <2Ро1у$опР 300 <2РппЮ1а1ое 402,407, 517 (^РпШег 402 АО—А9 403 аЬоП() 404 ВО—В10 403 С5Е 403 Со1ог 403 СоттЮЕ 403 ОЬЕ 403 Ехесийуе 403 РоПо 403 6гаузса1е 403 Ьапёзсаре 402 Ьед^ег 403 Ье§а1 403 Ьекег 403 пе\уРа^е() 407 РсИРогтаС 403 РогСгак 402 зеССо1огМоде() 403 8еЮосМате() 403 зе1РготТоО 403
зе1МтМах() 407 $е1МитСор1е$() 403 зе1Опета6оп() 402 зеЮи1ри1Я1еМате() 403 зеЮифигГоппаЦ) 403 зе1Ра$е8ке() 403 ТаЫок! 403 РРгосезз 610 СгазИЕхк 610 еггог() 610 ехк81аГ1з() 610 йтзИесК) 610 МогтаШхк 610 геа<1АП81апс1аг(1Епог() 610 геайАПЗДапЦагйОиЕриК) 610 геадуКеад() 610 геадуКеас181апс1агс1Еггог. () 610 геас1уКеаёЗДап4аг(1Ои(ри1() 610, 613 з1аП() 610, 613 з1аПед() 610 9Рго§геззВаг 154 гезеЦ) 157 зеЮпепСайопО 154 8е1Рго2ге88() 182 зеСКапдеО 157 зе1Уа!ие() 157 ()Ргобге$8В1а1о8 521 сапсе1её() 521 гезе1() 523 зе1Аи1оС1озе() 523 зегАиЮКезегО 523 8е1М1П1титВига(1оп() 521 8е1Рго§е88() 521 зе1ТоГа1$1ер8() 521 зе1\Утс1о\уТк1е() 522 шазСапсекдО 522 (2Р5(2Ь 667 ОРизЬВиНоп 144, 162 сНскедО 514,537 зегЯа1() 165 зе(Мепи() 166 О(?иеие<Т> 92 ОКаШа10гаШеп( 317 ОКесС 117,298 Ье1еЬ1() 299 зеШе^ЬЦ) 299 зе181ге() 299 зеПУМШО 299 зе!Х() 298 зеМ) 298 81ге() 298 \лчскЬ() 299 х() 298 УО 298 ОКесГЕ 298 ЯКеёО 302 ОКе^Ехр 104, 202 (}Ке§1оп 332 ОКез1геЕуепГ 275 302 ЧКёЬ() 302 ЧКеЬа() 302 цКоипд() 75 О5спр(Соп1ех1 817 ()8спрСЕп§1пе 784,817,818 еуа!иа(е() 784,818 пе\урОЪ]ес1() 784,818,826 р8спр1Еп§1пеВеЬи§§ег 835 08спргУа1ие 784,817 1зЕггог() 818 зегРгореПуО 784 (}8сгоПАгеа 124 <28сго11Ваг 175, 179 зИдегМоуеёО 176 уа1иеСЬап§е(10 176,413 ()8етарЬоге 631 (28езз1опМапа&ег 471 а11омг1п1егас1юп() 471 сапсеЦ) 471 8е1Кез1апН|т() 471 (28екТ> 97 О8еи1п§з Ье§т6гоир() 464, 470 епдбгоирО 464, 469 геадЕпСгуО 469 гетоуеО 464 зе!Уа1ие() 464 уа1ие() 464 \угкеЕп1гу() 470 <28ко>уЕуепс 275 981ёпа1Маррег 559 081ге 297 Ье1§Ь1() 297 1зМи11() 297, 299 гке1§Ь1() 297 пу1сШ1() 297 зса!е() 297 зе1Не1еЫ() 297 зе1\М1с11Ь() 297 297
0$1ге6пр 541 <281геРо11су 411 <2811(1ег 175, 177,453 МоТ1ск8 177 8е1Т1ск1п1егуа1() 177 зеГПсктагкзО 179 яеСПскРозйюпО 177 Т1ск8АЬоуе 177 Т1ск8Ве1о\у 177 Т1ск8ВоШ8|(1е8 177 <28оске1 643 ц8оП() 100 <28оПР1кегРгохуМос1е1 246 (}8оип(1 443,444 18Душ1аЫе() 444 18Е1гм8ке(1() 443 1оор8Кета1пт§() 443 р!ау() 443 8е(Ьоор8() 443 8Гор() 443 <28р1пВох 8е1ВиПоп8утЬо18() 199 8е1РгеГ1х() 198 8е(Кап§е() 198 8е18реаа1Уа1иеТех1() 198 8е18иГГ1х() 198 8еГУа1ие() 198 8ейУгаррт$() 198 51ерЕ)о\уп() 198 81ерИр() 198 уа1ие() 198 уа1иеСкап§е(1() 198 Р8р1а8к8сгееп 548 йтзкО 550 8ко\у() 549 8Но\уМе88а^е() 550 РЗрПиег 145 рЗдЮаГаЬазе 666 ас1(10аСаЬа8е() 667 1а8ГЕггог() 668 ореп() 668 8е1Оа1аЬа&еМате() 668 8есНо8(Кате() 668 8е1118егНате() 668 <?8цЮпуег 666 О8дЮпуегСгеаЮг<Т*> 666 рЗяЮпуегСгеаЮгВазе 666 98цЮпуегР1и§1п 666, 680 98ц1Еггог 666, 668 18УаП6() 675 1ех1() 668 Р8ч1Пе1(1 666 08чПпс1ех 666 (ЭДИТЕ 667 989ЫТЕ2 667 98д^иегу 666, 668 Ыпс1Уа1ие() 669 ехес() 668 ЙГ81() 668 1а8(() 668 пехс() 668 ргерагеО 669 ргеУ1ои8() 668 гесопЦ) 672 8еек() 668 51хе() 669 <28ц1()иегуМос1е1 666 08ц1Кесогд 666, 672 98д1Ке1а1:юпа1ТаЫеМос1е] 666 08ц1Ке5ик 666 (}8д1ТаЫеМо(1е1 666 ОпР1е1(1Скап8е 673 ОпМагша18иЬтк 673 ОпКо\уСкапёе 673 геуейАПО 673 зесЕёкЗй-аГезуО 673 зиЬткАПО 673 ц81аЫе8ой() 100 (281аск<Т> 91 О81аскес!\У^е1 122 асШУУШееЮ 122 ЫехОЮ 123 8еГСиггеп11пс1ех() 122 $е1СиггегцУ/1с1§е(() 122 ОЗитйагёкетМоде] 222 ОЗШизВаг 545 адс1Регтапеп1\У1(1§е1() 546 аскПУШЕеЦ) 546, 547 с1еаМе58а§е() 545 гетоуе\У1ф;е1() 546 8ко^Ме85а§е() 545, 556, 561 102 аррепё() 103 аг§() 669 с!еаг() 770 сопийпзО 202 Г1пд() 770 Гт<1Яеу() 770 тс!ехОЕ() 770 18Етр1у() 103, 552 (авЙпёехОГО 770
1еп§Л() 103 гш11 770 питЬег() 103 питЬег() 276 гетоуе() 506 гер!асе() 103 зегМитО ЮЗ зр1й() 591 1оЕ<жег() 103 Со11ррег() 103 (}81ппе1л81 104 Р81пп§1л81М(м1е1 222 081у1е 418 дга\мСотр1ехСоп1го1() 424 с1га\уСоп1го1() 424 (1га\уРптк1уе() 424 ро11зк() 429 8га(е_Мои8еОуег 230 ипроПзЬО 430 (?81у1еРас1огу 424, 682 081у1еОрНопУ1е\уКет 230 (}81у1еРа1п1ег 433 081у1еР1и§1п 680,681 р8у§Кеп(1егег 381,383 08уёМс1§е1 381 ц8\уар() 100 <?8утахН1еЫ1§Ь1ег 188, 190, 193 Ы§ЬН§ЫВ1оск() 190 ргеуюи5В1оск8иге() 196 8е1Сиггет8га(е() 196 ()8у8СетТгау!соп 568,570 8е1Сотех1Мепи() 568 зеИсопО 568,573 8е1Тоо1Т|р() 568, 572 8Ьо\мМе88а§е() 568, 573 Л 33 АК^пВонот 149 АП^пСетег 149, 272 АП§пНСепГег 149 АП§п1и8й(у 149 АН^пЬеЙ 149 А11$пК1§1н 149 АН^пТор 149 АП^пУСешег 149 АИМоёЮег 261 Агго\уСиг8ог 119 АзсепсИп^Огдег 206 АиЮСоппесйоп 49, 617 Васк§гоипс1Со1огКо1е 236 ВВ1а§Раиегп 315 Ыаск 306 В1апскСигзог 121 Ыие 306 С1о8ес1Напс1Сиг5Ог 121 со1огО 306,351,352 со1ог1 306,351,352 Соп1са16гаЛеп1Раиет 315 Соп1го1Мо<11(1ег 261 СгоззСигзог 119 СгоззРаиет 315 суап 307 дагкВ1ие 307 ёагкСуап 307 дагкОгау 306 дагкОгееп 307 <1агкМа§еп1а 307 дагкКеё 307 <1агкУе11о\м 307 ОазкОоЮойлпе 313 Оа8ЬГ)о1Ьте 313 ПазЫлпе 313 ВесогаНопЯо1е 236 Е>еп8е1 Ранет 314 Ое5сепс11П§Огс1ег 206 О1аёСго58Раиет 315 В1гес(СоппесНоп 49 В18р1ауЯо1е 236 Оо1Ьте 313 ЕО1а§РаНет 315 НасСар 314 Роп1Ко1е 236 РогЬ|ск1епСиг8ОГ 120 §гау 306 §гееп 306 Нопгота! 175 НогРаНет 314 НэеатСигаог 120 1§погеА8рес1Ка1ю 297, 344 18ООа1е 599 КеерА8рес1Вайо 297 КеерА8рес1Ка6о 344 КеерА8рес(Ка1юВуЕхрапс1т§ ЬеИВипоп 268 Н^ЬЮгау 306 ЫпеагОгасйетРаиет 315 Ьоса1еОаГе 599 та§еп1а 307 М1дВиНоп 268 ИоВгияЬ 314 МоВиНоп 268
ЫоМоШПег 261,289 пользовательский интерфейс 745 ЫоРеп 313 сокрытие и отображение кода 749 ОрепНапдСигзог 121 типы проектов 741 РотОп^НапёСигзог 120 91 Оезфпег 696, 739, 742, 747 9иеиес1Соппесйоп 49 91 Неф 739 КаШа16га<Неп1Раиет 315 91Ьш§ш81 491,494 геё 306 91 ЗспрС ВфЫВиКоп 268 Аггау 810 ИоипёСар 314 еуа!() 804 ЗЫйМосййег 261 18ЫаМ() 804 81геА11Сигзог 120 рагзеР1оа1() 790, 804 81геВО1а@Сигзог 120 рагзе1т() 790, 804 812еЕВ1а8Сиг8ог 120 исключительные ситуации 800 81геНогСигзог 120 ключевое слово 785 81хеУегСигзог 120 комментарии 786 8оНаЬте 313 массив 810 8оНаРаПет 314 наследование 805 8р1йНСигзог 120 объект 804 ЗрШУСигзог 120 объект Ваге 811 ЗциагеСар 314 объект 61оЪа1 807 8уз1етЕоса1еОа1е 599 объект МитЬег 807 Тех1Со1огЯо1е 236 объект 81пп$ 808 ТехЮа(е 599 отладка 833 ТехЮопГСНр 359 переменная 786 ТехГЕхрапдТаЬз 359 подстрока 809 ТехсЗЬоиМпетошс 359 преобразование регистра строк 808 Техг81п§1еЫпе 359 преобразование типа 790 ТехШгеРаиегп 315 регулярные выражения 809 Тех(У/огё\Угар 359 строка 788 Тоо1Т1рВо1е 236 тип данных 788 НрАгго^Сигзог 119 условный оператор 796 УегРаПегп 315 функция 801 УеП1са1 154, 175 цикл 798 \УА_Пе1е1еОпС1озе 562 9138ирроП 772 5УА_Ноуег 229, 429 9ТаЫе 770 ХУайСигзог 120 9ТаЫеУ1е\у 224, 673 ХУЬаГзТЫзСигзог 120 9ТаЫе\У1(феГ 212, 249 5УЬа1ТЫзКо1е 236 зе^СеПМсфеЮ 212 шЫсе 306 $еШеш() 213 ХУ1Пс1оху81ау8ОпТорН1п€ 115 9ТаЫе\У1д8е11(ет 212 уе11ош 307 9ТаЬ\У1сфе: :А88181ап1 771 адс1ТаЬ() 217 :АззИап1 740 5е1Сиггеп11п<1ех() 216 : СгеаЮг 738, 740, 747, 756 зеПаЬЕпаЪЫО 216 Ьосасог 752 9Тагп81дгтп 378 автоматическое дополнение кода 750 9с9оге 742 автоформатирование 754 9Тср8егуег 634 отладчик 761 9Тср8оске! 643 подсветка синтаксиса 748 91ВеЬи§Мз§ 74 поиск и замена 750 ОТИ8 667
(уГешрогагуРПе 586 (УГезГ: аск!Со1шпп() 720 кеуСНскз() 723 пе>уКо\у() 720 0ТЕ8Т_МА1К() 717, 722 (УГех1Вго>У8ег 535 Ьаску/агс1() 537 ЪасЬуап1АуаПаЫе() 537 ГопуагсЦ) 537 ГопуагдАуаПаЫеО 537 Ьоте() 537 $е1$еагскРаО1() 538 8е18оигсе() 538 (УГех^СодесРШет 680 СУГехгСигзог 187 рТехЮосшпепг 188 ОТехОк 187 с!еаг() 187 сору() 187 си!() 187 дезе1ес1() 187 <1осшпеп(() 188 ГшсЦ) 188 тзеПНйпЦ) 188 1пзепР1а1пТех1() 188 раз1е() 187 геёоАуаПаЫеО 188 8е1ес(АП() 187 зе1есйопСкап§е<1() 187 зеЮосшпепЦ) 188 зе1Н1ш1() 187, 188, 190, 506 8С1Р1ашТех1() 147,187,552 5е1Р1ашсТех1() 188 зеГВеас1Оп1у() 187 1ех1() 187 1ех1Скап§ес1() 187 1ех1Сиг8ог() 187 ЮР1ашТех1() 554 шкк>АуаПаЬ1е() 188 гоош1п() 188 хоотОиЦ) 188 (УГех(8сгеат 595 геадАПО 552, 595 геадЫпеО 595 5е1Кеа1№тЬегРгес181оп() 595 ()1Ра(а1М8§ 74 ОЮш 742 (уТЪгеас! 619 ехес() 603, 617 ехк() 617 рпогкуО 616 цип() 617 гип() 615 8е1РНоп1у() 616 81ай() 615,622 из1еер() 622 (УПте 600 айдМЗесзО 601 адд$ес8() 601 сштеп1Т1те() 601 е!ар8ес!() 601 {гот51лп§() 601 Ьоиг() 601 ттисе() 601 т$ес() 601 зесопдО 601 8е1НМ8() 601 8Гап() 601 1о81пп§() 601,639 СУПтег 605 18Ас11Уе() 606 зеИпГегуаЦ) 606 8)п§1е8Ьо1() 606 810р() 606 йтеоиЦ) 606 (уПтегЕуеп! 273 ОсМз^Туре 74 ОТооШаг 542 а&1Ас1юп() 542 а&18ерагасог() 545 543 ОТооШох 217 жкШешО 217 соип!() 217 сиггетСЬапёедО 217 !п$егШет() 217 гетоуеИетО 217 ОТгапзГогт 378 8са1е() 378 1гап81а1е() 378 (}Тгап81а1ог 491,495 <ЗТгееУ1е\у 224 (УГгееМдееС 209,249 кетСПскедО 211 кетОоиЫеСНскедО 211 кет8е1ес110пСкап§е(1() 211 8е(Неас1егЬаЬе18() 210 8еШетБхрапде<1() 210 8е18ой1п§ЕпаЬЫ() 211 (уГгееХУМ^егкет 209 О18ув 381
ОМТоок 710 (У^агшпеМзЕ 74 ОшскТипе 455 (ДЛЬоайег 710 ЯШсотргеззО 88,585 ц11ррегВошк!() 100 <21М 455,482, 730 (ЗШОрегаЮг 771 РУа11с1а1ог 200 9Уа1иеЫз1 770 (ЗУапапГ 106 Со1пС() 241,672 Ю81пп§() 672 1уре() 107 Суре№те() 107 1уреТо№те() 107 уа!ие<Т>() 241 РУВох 768 (УУВохЬауоиС 133, 135, 136 ОУессог<Т> 87 (ЗУесСогПегаЮг 81 (?УЕЯ1РТ() 723 (ЗАУаИСопсШюп 629 д\Уатш$() 74 (2\УеЬН181огу 731 Ьаск() 731 сап6оВаск() 737 сап6оРопуагд() 737 с!еаг() 731 ГопуагсЦ) 731 (2\УеЬН1$Гогукет 731 (ЖеЬУ1е\* 728,729, 731, 732 1оа4() 730, 736 1оадР1П1зНед() 732 1оас1Ргойге55() 732,735 зСор() 735 (?\УЬее1Еуеп1 272 ЬиПопз() 272 с1ека() 272 §1оЬа1Роз() 272 роз() 272 114, 173,262 зеГГаЬОгёегО 145 асйуа(е\Утс1ои'() 510 Ьаск§гоипс1Яо1е() 173 с!озе() 274 с1о$еЕуеп1() 274,470,571 сотехсМепиЕуепЦ) 506 (1га§Еп1егЕуеп1() 273,482 ёга§ЬеауеЕует() 273 (1га§МоуеЕуепЮ 273 НгорЕуепЦ) 274,482 етегБуепЦ) 272 еуепС() 262 ГосизГпЕуепК) 265 ГосизОтЕуетО 265 беоте!гу() 117 ЫёЬс() 116,574 Ые() 114,274 ЫдеЕует() 274 вЕпаЫедО 116 кеуРгеззЕуетО 262 кеуНеказеЕуепЦ) 262 1еауеЕуеп1() 272 тои5еГ)оиЫеС11скЕуеп(0 116, 267, 268 тоизеМоуеЕуепЮ 116, 267, 272, 477, 479,489, 547 тои$еРге$$Еуеп1() 116, 260,267,272, 277, 477,479 тоизеКеказеЕуепЦ) 116, 267,272 тоуе() 117 гпоусЕусгНО 274 ратГЕуепЮ 266, 310,415, 823 ра1е«е() 252 ро&() 116 га1$е() 510 герат!() 266, 415, 824 геыхеО 117,140, 269,276 геыгеЕуепЦ) 275 $е(АссерЮгор8() 482,488 $е1АПпЪи1е() 229 $е1Аи1оР111Васк§гоипс1() 117 зеГВаск^гоипдМоёеО 769 5е(Сиг5ог() 122 $е1ЕпаЫе(1() 116,537 $е1Р1хес181ге() 823 5е1Роп1() 357 аеЮеотейуО 114, 117 $е<Магё1п() 124 зесМазкО 352 зесМЫтитАУккЬО 157 8есМои$еТгаск1П&() 267 $еЮЬ)ес1Мате() 485 8есРа1е«е() 117, 173,255 &е1Ра1еиеВаск$гоипдСо1ог() 769 $е1Ра1еЦеВаск^гоипс!Р1хтар() 769 5е1Ра1еиеРоге§гоипдСо1ог() 769 $есР1хтар() 353 5ес5су1е() 419,421 $ес81у1е$Ьее€() 433 5еГТоо1Т1р() 533 8е1\У1пйом/Р1а28О 115
8е1\Ут(1о\у1соп() 562 8е(\УЫо\уТп1е() 116,485, 526, 562 8Ьо\у() 114, 140, 266, 269, 275, 391 зко^ЕуепЦ) 275 811о^Ри118сгееп() 391 81хе() 116 ыгеНтСО 410 ыгеРоНсуО 410 81у1е() 424 ирда1е() 266 \укее!Еуеп1() 272 116,574 уу!п1с1() 694 х() 116 хПЕуепЦ) 695 У() П6 рАУ!П<кж$81у1е 419,429 (ЭМгап! 523 □У/1хагс1Ра$е 523 0Хт1Соп1еп1Нагк11ег 659 ОХтЮеГаикНапШег 659 0Хт181тр1еКеа(1ег 658, 660 Н КАО 696 КаШо ВиКоп 168 Кегк1епп§ сопСех( 384 КСВ 300,301 8 8аГап 727 8АХ 658 8са1аЫе УесСог бгарЫсз 381 801 550 8еек8П(1ег 453, 458 8кагес1(1а1а 107 8т§1е ОоситепС УУтйоуу 1п1егГасе 550, 566 81п§1е8ко( 606 8куре 568 8расег 702 8р1а$к 8сгееп 548 8(}Ь 663 81ер1пЮ 762 8герОи! 763 ЗсерОуег 762 8ТЬ 77, 84 8У6 381.727 зу81а( 634 8у$1етТгау 567 ТеагоЙ'щепи 503 ТЬгеад заГеГу 628 То§§1е ЬиПоп 163, 444 Тоо! Т1р 532 Тор-1еуе1 \УЙ1§е1 114 ТКАМ8ЬАТЮМ8 493 Тп81а1е ВиНоп 167 Т8-файл 492, 494 1уреоГ() 789 ц Ытсоде 595 11КЬ 476 V УИеоР1ауег 453 УМеоМс^еГ 452,459 У18иа1 С++ 450 УЬС 451 Уо1ите81к1ег 453, 458 ТО ^аСсЬе8 764 \УАУ 443 \УеЬ 725 5УеЬКк 726 \УеЬ-браузер 725 \УеЪ-клиент 725 У/ЬаГ8 1Ы8 533 \У1пс1оу/8 725 ХУтскжз АР1 42, 694 \У1п<1о\У8 ГЧсИерас! 550 ХУогкагоипс! 777 \Угаррег 781 \УУ81>УТ6 696 X ХВМ 337 ХНТМЬ 727 Х1пе 450 ХМЬ 649, 727 ХР1хтар 339 ХРМ 337, 339, 349
А Интерактивные действия 281 Активное состояние 253 Алгоритмы 99 Анимация 379 Атрибут файла 593 Интерактивный отладчик 756 Интернационализация 491 Интернет 725 Интерфейс 683 Итератор 80 Б встилеЗТЬ 82 встилеЗауа 81 Баг 69 База данных 663 Буфер обмена 474 Бэкэнд 450 константный 81, 84 К Кисть 314 В Клавиатура 262 Класс обертки 781 Вектор 86 Взаимная блокировка 630 Вид и поведение 35,417 Виджет 113, 114 верхнего уровня 114 Виджет экрана 574 Воздушная подсказка 532 Время 600 Всплывающее меню 165, 498 Выпадающий список 214 Клиент-сервер 633 Ключ 463 Кнопка 140, 161 Кнопка, нажимающаяся 162 Кнопка-выключатель 164 Кнопка-флажок 166 Компоновка виджетов 128 Конический градиент 317 Контейнер 78 Контекст воспроизведения 384 Г Контекст рисования 310 Контекстное меню 498, 504 Гарнитура шрифта 356 Главное окно приложения 539 Градиент 316 Контролер 201 Контрольная точка 72, 763 Кэш изображений 350 д Л Дата 598 Двойная буферизация 266 Действие 541 Диалог мастера 523 Диалоговое окно 507 Док 545 Линейный градиент 316 Логические ошибки 760 М МЫ 556 Маска прозрачности 351 3 Масштабирование 328 Матрица моделирования 390 Завершение программы 569 Закладки 215 Закрытие окна виджета 470 Закулисное хранение 117 Матрица проектирования 390 Медиаисточник 452 Медиаплеер 455 Медиапоток 452 Медиаузел 452 И Медиафайл 452,453 Менеджеры компоновки 129 Иерархический список 209 Индикатор прогресса 154 Меню 498 Меню верхнего уровня 498
Метаобъектный компилятор 44, 66 Механизм неявных общих данных 253 Многодокументный оконный интерфейс 550, 566 Многопоточность 614 Множество 97 Модальный диалог 509 Модули 01 31 Мультимедиа 449, 452 Мышь 119,266,351 Мьютекс 627 Ползунок 177 Полоса прокрутки 179 Помощь 755 Поток 613 Предопределение 457 Представление 364 Проект 740 Процесс 609 Прямоугольник 298 Р Н Рабочий стол 35 Радиальный градиент 317 Надпись 148 Настройки приложения 463 Неактивное состояние 253 Недоступное состояние 253 Немодальный диалог 510 Разделитель 145 Размер 297 Рамка 123 Регулярное выражение 104,202 Режим совмещения 333 Ресурс 67 О Рисование 266 Область оповещений 567 Обнаружение столкновений 362 Общее использование данных 107 Однодокументный оконный интерфейс 550, 566 Однострочное текстовое поле 184 Окно заставки 548 Окно переменных 764 Окно сообщения 524 Операционная система 471 Отладчик 69 Отрывное меню 498, 503 Отсечение 331 Очередь 92 Ошибки времени исполнения 760 Ошибки компоновки 759 С Сглаживание 384 Селектор 433 Семафор 630 Сжатие данных 88,585 Сигнал 39, 45, 259 Сигналы 45 Синтаксические ошибки 758 Синхронизация 627 Система помощи 535 Системный реестр 463 Скос 328 Словарь 94 Слот 39,45, 48, 259 . Событие 259, 283, 369,604, 622 П Сокет 632 Сопоставление сигналов 559 Палитра 305 Панель инструментов 217, 542 Перевод 492 Переключатель 168 Перемещение 327 Перетаскивание 475 Перо 313 Плоская кнопка 163 Поворот 328 Поиск 101 Сортировка 100 Список 89, 204 выпадающий 214 иерархический 209 Сравнение 101 Стек 91, 140 Стек виджетов 122 Стив Балмер 727 Стиль 417 встроенный 419
Строка 102 Строка состояния 545 Сцена 363 Счетчик 198 Т Таблица 212 Таймер 39, 603, 605, 608 Тамм, Игорь 450 Текст 102 Тест: графического интерфейса 722 модульный 715 передача данных 719 создание 715 Тестирование 714 Техника сглаживания 318 Точка 295 Трассировка 761 У Указатель мыши 119 Установщик 181 Утечка памяти 625 Ф Файл: атрибуты 593 воемя изменения 592 время создания 592 компоненты пути 592 наблюдение за изменениями 594 размер 593 Файл проекта 62, 68 Фильтр событий 39,283 Фокус 265 Фон 117 Функции обратного вызова 41 X Хэш 96 ц Цепочки вызовов 765 Ч Что это 533 Ш Шаблон 740 Шрифт 356 Э Электронный индикатор 140, 157 Элемент 365
014.5 Профессиональное Платформенно-независимая реализация приложений — это уже сегодняшний и завтрашний день программной индустрии. И эта книга станет вашим путеводителем будущее программирование на |тьВ1УТШ> Макс Шлее (Мах 8сП1ее} закончил Университет прикладных наук в городе Кайзерлаутерн (Германия). Разработчик программного обеспечения в фирмах ТН0М80А1, Сгазз УаНеу и 0|дйа1Н1тТес11по1оду УУеКег^асН, эксперт в области объектно-ориентированного проектирования, специализирующийся на С++ и 01 Создатель программы ХЕгатег, позволяющей использовать фрейм-технологию в разработке программного обеспечения, увлеченно занимается проектами программирования в области графики, звука и анализа финансовых рынков. Является автором ряда статей, научных докладов на международных конференциях по генеративному программированию пользовательского интерфейса. Автор книг «01. Профессиональное программирование на С++», «014. Профессио- нальное программирование на С++». В свободное время увлекается фотографией сочиняет музыку. В ПОДЛИННИКЕ 0У0 содержит исходные тексты описанных в книге примеров, библиотеку 0(4.5 и интегрированную среду разработки 01 Сгеа1ог для Шюбошз. Моих и Мас 08 X Книга подробно знакомит с библиотекой 014.5, являющейся не только средством для создания пользовательских интерфейсов, но и позволяющей разрабатывать приложения практически любой сложности. Недаром 01 широко используется многими известными организациями и компаниями, такими как АООВЕ, АМО. В08СН, САМОМ, С18С0 8У8ТЕМ8,018МЕУ, 1МТЕ1-, 1ВМ, РАМА80М1С, РЮМЕЕВ, РН1ИР8, ОВАСЬЕ, НР. 60061-Е, МА8А, МЕС, 8АМ80М6, 81ЕМЕМ8, 8ОМУ, ХЕВ0Х,Х11_1МХ,УАМАНАидр. Если вы хотите идти в ногу со временем, то вам без этой книги просто не обойтись, поскольку она является исчерпывающим пособием по написанию 014 программ на С++ Библиотека 01 не ограничивая ваш выбор и творческие возможности, делает кроссплатформенное программирование действительностью. Она предоставляет пользователям то, что им нужно — быстрые программы которые выглядят и работают должным образом. А разработчикам программ — конструкции, позволяющие создавать меньшии код и при этом получать лучший результат. Благодаря этому программирование становится интереснее вне зависимости, создаете вы коммерческий продукт или проект с открытым исходным кодом (Орел Боигсе). Маттиас Эттрих, основатель КОЕ БХВ-Петербург 190005, Санкт П %<урт, Иэм^млпк кхй пр., 29 Е-та!Р та1к ги 1п1ете1 зЬу ги Тел (812) 251-42 44 Факс (812) 320-01-79 1пГогтаМоп тизЪ Ье Ггее