/
Автор: Шлее М.
Теги: компьютерные технологии общие вопросы лингвистики, литературы и филологии программирование программное обеспечение язык программирования c++
ISBN: 5-94157-566-1
Год: 2005
Текст
Макс Шлее
i
И НИ И
С5
ПРОФЕССИОНАЛЬНОЕ
ПРОГРАММИРОВАНИЕ
НА
• Создание приложений под Windows
и Linux
• Программирование графики
баз данных, сети, таймера,
многопоточности, XML
• Более 80 завершенных программ
+ CD-ROM
. . i ®
УДК 681.3.068+800.92С++
ББК 32.973.26-018.1
Ш68
Шлее М.
Ш68 Qt. Профессиональное программирование на C++. — СПб.:
БХВ-Петербург, 2005. — 544 с: ил.
ISBN 5-94157-566-1
Книга посвящена разработке программ под Windows и Linux на C++
с использованием библиотеки Qt версии 3.2. Подробно рассмотрены
возможности, предоставляемые этой библиотекой, и описаны особенности,
выгодно отличающие ее от других библиотек. Книга содержит
исчерпывающую информацию о классах Qt и практические рекомендации по их
применению, проиллюстрированные на большом количестве подробно
прокомментированных примеров. Компакт-диск содержит исходные
примеры и библиотеку Qt для Windows и Linux.
Для программистов
УДК 681.3.068+800.92С++
ББК 32.973.26-018.1
Группа подготовки издания:
Главный редактор Екатерина Кондукова
Зам. главного редактора Игорь Шишигин
Зав редакцией Григорий Добин
Редактор Андрей Смышляев
Компьютерная верстка Ольги Сергиенко
Корректор Зинаида Дмитриева
Дизайн серии Инны Тачинои
Оформление обложки Игоря Цырульникова
Зав. производством Николай Тверских
Лицензия ИД № 02429 от 24.07.00. Подписано в печать 21.01.05.
Формат 70x100V,e. Печать офсетная. Усл. печ. л. 43.86.
Тираж 3000 экз. Заказ № 772
"БХВ-Петербург", 190005. Санкт-Петербург. Измайловский пр.. 29.
Санитарно-эпидемиологическое заключение на продукцию
№ 77.99.02.953.Д.006421.11.04 от 11.11.2004 г. выдано Федеральной службой
по надзору в сфере защиты прав потребителей и благополучия человека.
Отпечатано с готовых диапозитивов
в ГУЛ "Типография "Наука"
199034. Санкт-Петербург, 9 линия. 12
ISBN 5-94157-566-1 ° Шлее м- 2005
С Оформление, издательство "БХВ-Петербург", 2005
Оглавление
Предисловие 12
Введение 14
От автора , 14
Структура книги 15
Благодарности 22
ЧАСТЫ. ОСНОВЫ QT 25
Глава 1. Обзор иерархии классов Qt 26
Первая программа на Qt 26
Иерархия классов Qt 28
Класс Qt 28
Классы интерфейса пользователя 29
Классы для работы с графикой 32
Класс приложения 33
Резюме 34
Глава 2. Философия объектной модели 35
Механизм сигналов и слотов 36
Сигналы 40
Слоты 42
Соединение объектов 43
Разъединение объектов 49
Организация объектных иерархий 49
Свойства объектов 52
Резюме 53
Глава 3. Работа с Qt 54
Установка Qt 54
Установка Qt в ОС Windows 54
Установка Qt в ОС Linux 55
4 Оглавление
Работа с qmake 57
Рекомендации для проекта с использованием Qt 60
Метаобъектный компилятор МОС 61
Структура Qt проекта 62
Методы отладки 63
Глобальные определения Qt 64
Использование Qt в Microsoft Visual Studio 65
Расцветка синтаксиса 65
Резюме 67
ЧАСТЬ П. ЭЛЕМЕНТЫ УПРАВЛЕНИЯ 69
Глава 4. С чего начинаются элементы управления 70
Класс QWidget 70
Размеры и координаты виджета 73
Установка фона виджета 74
Установка курсора мыши 76
Рамки 77
Стек виджетов 78
Виджет прокрутки 79
Резюме 81
Глава 5. Элементы отображения 83
Надписи 83
Индикатор прогресса выполнения 88
Электронный индикатор 91
Резюме 93
Глава 6. Кнопки 94
С чего начинаются кнопки. Класс QButton 94
Кнопка нажатия 94
Флажки 98
Переключатели 100
Группировка кнопок 101
Класс QGroupBox. 101
Класс QButtonGroup 102
Резюме 106
Глава 7. Элементы управления для установки значений 107
Класс QRangeControl 107
Ползунок 107
Полоса прокрутки 109
Установщик 112
Резюме ИЗ
Глава 8. Элементы выбора И5
Иерархические списки И5
Простой список 118
Оглавление 5
Выпадающий список 120
Элемент показа иконок 122
Таблицы 123
Закладки 125
Виджет инструментов 127
Резюме 128
Глава 9. Элементы ввода 129
Однострочное текстовое поле 129
Редактор RichText 131
Многострочное текстовое поле 135
Счетчик 136
Элемент ввода даты и времени 138
Проверка ввода 139
Резюме 141
Глава 10. Управление автоматическим размещением элементов 142
Лейауты, основанные на QFrame 143
Класс QHBox 143
Класс QVBox 144
Класс QGrid 145
Лейаут-менеджеры 146
Вложенные размещения 147
Табличное размещение 149
Порядок следования табулятора 154
Разделители 155
Резюме 156
Глава 11. Цветовая палитра для элементов управления 158
Резюме 162
Глава 12. Элементы со стилем 163
Встроенные стили 164
Создание собственных стилей 169
Резюме 173
ЧАСТЬ III. СОБЫТИЯ И ВЗАИМОДЕЙСТВИЕ С ПОЛЬЗОВАТЕЛЕМ.... 175
Глава 13. События 176
Перезапись специализированных методов обработки событий 178
События клавиатуры 178
Событие обновления контекста рисования. Класс QPaintEvent 181
События мыши 182
Событие таймера. Класс QTimerEvent 187
События перетаскивания drag&drop 187
Остальные классы событий 188
Методы enterEventQ и leaveEventQ 192
6 Оглавление
Перезапись метода eventQ 192
Резюме 195
Глава 14. Фильтры событий 196
Резюме 199
Глава 15. Искусственное создание событий 200
Резюме 203
ЧАСТЬ IV. ГРАФИКА И ЗВУК 205
Глава 16. Введение в компьютерную графику 206
Классы геометрии 206
Точка 206
Двумерный размер 208
Прямоугольник 209
Цвет 211
Цветовая модель RGB 211
Цветовая модель HSV 212
Представления цвета 213
Палитра 213
Класс QColor 214
Работа с цветовой моделью RGB 214
Работа с цветовой моделью HSV 215
Предопределенные цвета 215
Резюме 217
Глава 17. Контекст рисования 218
Класс QPainter. 219
Перья и кисти 220
Рисование 222
Запись команд рисования 229
Трансформация систем координат 230
Отсечения 232
Подавление эффекта мерцания 234
Резюме 235
Перья и кисти 220
Рисование 222
Запись команд рисования 229
Трансформация систем координат 230
Отсечения 232
Подавление эффекта мерцания 234
Резюме 235
Глава 18. Растровые изображения 236
Форматы графических файлов 236
Формат BMP 236
Оглавление 7
Формат GIF 237
Формат PNG 237
Формат JPEG 237
Формат ХРМ 237
Контекстно-независимое представление 239
Класс Qlmage 239
Класс QlmagelO 244
Контекстно-зависимое представление 245
Класс QPixmap 246
Класс QBitmap 248
Класс QPixmapCache 248
Прозрачность 248
Прозрачность с использованием класса QPixmap 248
Прозрачность с использованием класса Qlmage 252
Резюме 252
Глава 19. Работа со шрифтами 254
Отображение строки 256
Резюме 258
Глава 20. Работа с изображениями холста 259
Резюме 266
Глава 21. Анимация 267
Резюме 269
Глава 22. Работа с OpenGL 270
Основные положения OpenGL 271
Классы библиотеки Qt для работы с OpenGL 272
Реализация OpenGL-программы 272
Разворачивание OpenGL-программ во весь экран 277
Графические примитивы OpenGL 277
Вывод растровых изображений 281
Трехмерная графика 283
Резюме 288
Глава 23. Вывод на печать 289
Резюме 295
Глава 24. Разработка собственных элементов управления 296
Резюме 303
Глава 25. Звук 304
Воспроизведение звука 304
Проверка возможности воспроизведения 305
Пример программы, воспроизводящей звук 306
Резюме 309
S Оглавление
ЧАСТЬ V. СОЗДАНИЕ ПРИЛОЖЕНИЙ 311
Глава 26. Сохранение настроек приложения 312
Управление сеансом 320
Резюме 322
Глава 27. Буфер обмена и перетаскивание 323
Буфер обмена 323
Перетаскивание 324
Реализация Drag 325
Реализация Drop 328
Резюме 330
Глава 28. Интернационализация приложения 331
Подготовка приложения к интернационализации 331
Утилита LUPDATE 332
Программа Qt Linguist 333
Утилита LRELEASE. Пример программы, использующей перевод 334
Резюме 336
Глава 29. Создание меню 337
Анатомия меню 337
Отрывные меню 342
Контекстные меню .' 343
Резюме 345
Глава 30. Диалоговые окна 346
Правила создания диалоговых окон 346
Класс QDialog 347
Модальные диалоговые окна 348
Немодальные диалоговые окна 348
Создание собственного диалогового окна 349
Стандартные диалоговые окна 353
Диалоговое окно выбора файлов 353
Диалоговое окно настройки принтера 354
Диалоговое окно выбора цвета 355
Диалоговое окно выбора шрифта 356
Диалоговое окно ввода 357
Диалоговое окно прогресса 358
Диалоговое окно с закладками 360
Диалоговое окно мастера 361
Окна сообщений 362
Окно информационного сообщения 365
Окно предупреждающего сообщения 365
Окно критического сообщения 366
Окно сообщения About 367
Окно сообщения About Qt 367
Окно сообщения об ошибке 367
Резюме 368
Оглавление 9
Глава 31. Предоставление помощи 370
Воздушная подсказка. Класс QToolTip 370
Подсказка "Что это". Класс QWhatsThis 371
Система помощи (Online Help) 373
Резюме 375
Глава 32. Панель инструментов и строка состояния 377
Панель инструментов 377
Кнопка-инструмент. 378
Строка состояния 380
Резюме 382
Глава 33. Создание приложений 383
Класс главного окна QNainWindow 383
Предшествующее окно 385
Класс действия QAction 387
Создание SDI- и MDJ-лриложений 388
SDI-приложение 388
MDI-приложение 393
Резюме 400
ЧАСТЬ VI. ОСОБЫЕ ВОЗМОЖНОСТИ QT 403
Глава 34. Процессы и потоки 404
Процессы 404
Потоки 407
Приоритеты 408
Синхронизация 411
Резюме 414
Глава 35. Дата, время и таймер 415
Дата и время 415
Класс даты QDate 415
Класс времени QTine 417
Класс даты и времени QDateTime 418
Таймер 419
Событие таймера 420
Класс QTimer. 422
Резюме 424
Глава 36. Библиотека контейнеров 426
Контейнеры данных 427
Векторы 428
Списки 429
Словари 431
Стек 433
Очередь 433
10
Оглавление
Контейнеры указателей 433
Строки 435
Регулярные выражения 437
Произвольный тип 439
Модель общего использования данных 439
Модель неявных общих данных 440
Модель явных общих данных 440
Резюме 441
Глава 37. Работа с файлами, директориями и потоками ввода/вывода 442
Ввод/вывод. Класс QIODevice 442
Работа с файлами. Класс QFile 443
Класс QBuffer. 446
Работа с директориями. Класс QDir. 446
Просмотр директории 447
Информация о файлах. Класс QFilelnfo 450
Файл или каталог? 450
Путь и имя файла 450
Информация о дате и времени 450
Получение атрибутов файла 451
Определение размера файла 451
Потоки ввода/вывода 452
Класс QTextStream 452
Класс QDataStream 453
Резюме 454
Глава 38. Программирование поддержки сети 455
Сокетное соединение 455
Модель клиент-сервер 456
Реализация сервера 457
Реализация клиента 460
Высокоуровневые классы 463
Класс QFtp 463
Класс QHttp 464
Класс QUrl 465
Резюме 466
Глава 39. Работа с XML 467
Основные понятия и структура XML-документа 468
XMLhQi 469
Работа с DOM 470
Работа с SAX 475
Резюме 479
Глава 40. Программирование баз данных 480
Основные положения SQL 481
Создание таблицы 481
Операция вставки 481
Оглавление у у
Чтение данных 482
Изменение данных 482
Удаление 482
Использование SQL в библиотеке Qt 483
Соединение с базой данных 483
Исполнение команд SQL 484
Обращение к данным при помощи курсора 486
Осведомленные элементы управления 488
Резюме 489
Глава 41. Динамические библиотеки и система расширений 490
Динамические библиотеки 490
Создание динамической библиотеки 490
Загрузка динамических библиотек 491
Расширения (plug-ins) 493
Резюме 495
Глава 42. Совместное использование Qt с платформозависимыми API 496
Совместное использование с Windows API 497
Совместное использование с Linux и KDE 499
Реализация программы в KDE 499
Резюме 501
Глава 43. Qt Designer. Быстрая разработка прототипов 502
Создание нового проекта в Qt Designer 502
Добавление виджетов 505
Размещение (лейауты) 506
Порядок следования табулятора 507
Сигналы и слоты 507
Компиляция 510
Резюме 511
ПРИЛОЖЕНИЯ 513
Приложение 1. Таблица описания формата RichText 514
Приложение 2. Таблица простых чисел 516
Приложение 3. Глоссарий 519
Приложение 4. Описание компакт-диска 523
Предметный указатель 528
Предисловие
Lets start with a fictional story. Imagine ten years ago, someone came to me
and asked, "Is it possibly to write a feature-rich graphical application, and
then compile and run this application natively on all different major operating
systems? On Linux, on UNIX, on Windows, and on the Macintosh?" Back
then — as a young computer scientist — I would probably have answered,
"No, that's not possible. *4nd if it was, the system would be very difficult to
use, and limited by the weakest platform. Better choose one platform, or write
your code several times."
A few years later I discovered Qt - and how wrong I was!
Qt makes true cross-platform programming a reality, without limiting your
choices and creativity. It gives users wnat users want: fast, native applications
that look and feel just right It gives developers what developers want: a
framework that lets us \^rite less code, and create more. A framework that
makes programming fun eigain, no matter whether we do commercial work or
contribute to Open Source projects.
Too good to be true? You don't believe me? Well, tU proof is easy. I'll pass
the word on to Max, who W/7/ tell you exactly how it's done. Max, your turn.
Before I leave, let me wish you good luck with your first Qt-steps. But be
careful, it may very well turn into a lifetime addiction. Either way, I hope you
will have as much fun using Qt as we have creating it for you.
Matthias Ettrich, the creator of the KDE
October 1st, 2004, Oslo
Предисловие У£
Давайте пофантазируем. Представим себе, что 10 лет назад кто-то
подошел бы ко мне и спросил: "Возможно ли создать
многофункциональное приложение с графическим интерфейсом, а затем откомпилировать
его и использовать во всех известных операционных системах? В Linux,
UNIX, Windows, Macintosh?" В то время я был молодым программистом
и, наверное, ответил бы: "Нет, это невозможно. А если это и было бы
возможным, то такая система оказалась бы очень трудна в обращении
и ограничена весьма малым количеством платформ. Лучше выбирайте
одну операционную систему или переписывайте свою программу столько
раз, сколько нужно".
Несколько лет спустя я открыл для себя библиотеку Qt и понял, как
был не прав!
Библиотека Qt, не ограничивая ваш выбор и творческие возможности,
делает кроссплатформенное программирование действительностью.
Она предоставляет пользователям то, что им нужно — быстрые
программы, которые выглядят и работают должным образом. А
разработчикам программ — конструкции, позволяющие создавать меньший код и
при этом получать больший результат. Благодаря этому
программирование становится интереснее вне зависимости, создаете вы
коммерческий продукт или проект с открытым исходным кодом (Open Source).
Слишком хорошо что бы быть правдой? Вы мне не верите? Ну что же,
доказать это просто. Я передаю слово Максу, который расскажет вам
подробно, как это делается. Макс, теперь твоя очередь.
Прежде чем попрощаться, позвольте пожелать вам удачи в ваших
первых шагах с Qt. И будьте осторожны — Qt может вызвать у вас
зависимость на всю жизнь. В любом случае, я надеюсь, что вам будет
также интересно работать с Qt, как нам было интересно создавать ее
для вас.
Матиас Этрих, основатель KDE
1 октября 2004, Осло
(перевод автора)
Посвящается
любимой Аленушке,
моим родителям
и семейству Гоуз (Goes)
Введение
Любая достаточно передовая технология
неотличима от магии.
Артур Кларк
От автора
Сегодня практически невозможно представить себе приложения, не
обладающие интерфейсом пользователя. Понятия Software и GUI (Graphical
User Interface) неразрывно связаны друг с другом. Библиотеки для создания
пользовательского интерфейса применяются в большом количестве
операционных систем. Начиная с Motif для ОС UNIX и заканчивая широко
известной MFC (Microsoft Foundation Classes) от Microsoft для ОС Windows.
Хотя Windows API обладает всем необходимым для создания фафического
интерфейса пользователя, но использование этих доступных "инструментов"
требует больших затрат времени и практического опыта. Даже MFC,
призванная облегчить процесс написания программ для ОС Windows, не дает
той простоты и легкости в процессе создания программ, как хотелось бы.
Поэтому, до сегодняшнего дня, разработчики по-прежнему тратят массу
времени на реализацию интерфейса пользователя. Но самый большой
недостаток, связанный с применением этих библиотек, — это платформозави-
симость.
И если вы программируете только для ОС Windows, то у вас, наверняка,
возникнет вопрос — зачем мне испытывать что-то новое? Одна из причин,
почему это стоит сделать — это реализация платформонезависимых
приложений. Платформонезависимая реализация приложений — это будущее
программной индустрии. С каждый днем она будет приобретать все более
важное значение. В самом деле, задумайтесь — зачем оставлять без
внимания пользователей ОС UNIX, только лишь по тому, что вы являетесь
программистом для ОС Windows. Приняв решение в пользу
платформонезависимых приложений, вы заметно увеличите количество пользователей (кли-
Введение
15
ентов). Выигрыш же от реализации платформонезависимых приложений
налицо: значительно сокращается время разработки, т. к. нет
необходимости писать код дважды, и что не менее важно — отпадает необходимость
знать специфику каждой из платформ, для которой пишется программа. И,
вместе с тем, заметно улучшится и качество приложений, т. к. оно будет
тестироваться на нескольких платформах, а ошибки будут исправляться
централизованным путем из одного и того же исходного кода программы.
Библиотека Qt (в дальнейшем просто — Qt) — это луч надежды для
программистов, пишущих на языке C++, вынужденных сейчас выполнять
двойную работу по реализации своих приложений для ОС Windows, Linux и
UNIX. Qt предоставляет поддержку большого числа операционных систем:
Windows, Mac OS X, Linux, Solaris, HP-UX, FreeBSD и другие клоны UNIX
с XI1. Более того, благодаря Qt Embedded — все возможности Qt доступны
также и в интегрированных системах (Embedded Systems).
Qt на сегодняшний день это зрелый продукт, широко используемый
разработчиками всего мира. Из активных пользователей Qt можно назвать такие
известные компании, как: Adobe, AT&T, IBM, NASA, Xerox и др.
В самом деле, Qt — это не только средство для создания интерфейса
пользователя, а это еще и полный инструментарий для программирования. Qt
предоставляет прекрасную поддержку для двух- и трехмерной графики
OpenGL, возможность интернационализации, использование формата XML,
библиотеку контейнеров, поддержку стандартных протоколов ввода/вывода,
классы для работы с сетью и многое другое.
Несмотря на то, что Qt изначально создавалась для языка
программирования C++, это вовсе не означает, что ее использование невозможно в других
языках. Эту библиотеку можно также использовать и с другими языками,
такими как, например, Perl, Python, С# и т. д.
И, если быть кратким, то библиотеку Qt можно было бы охарактеризовать
в трех словах: Простота + Быстрота + Мощность.
Добро пожаловать в мир Qt!
Макс Шлее
Weiterstadt
30 сентября 2004 г.
Структура книги
Книга состоит из шести частей. Первая часть посвящена основам
программирования с использованием библиотеки Qt. Вторая часть описывает
предоставляемые элементы управления и операции, которые можно проводить
с ними. Третья часть знакомит с классами событий и областью их примене-
16
Введение
ния. Четвертая часть посвящена программированию графики Пятая часть
опирается на материал, полученный в первых четырех частях, а основная ее
цель — создание приложений, имеющих пользовательский интерфейс.
Шестая часть описывает такие возможности Qt, как программирование много-
поточности, таймера, работа с файлами, использование контейнерных
библиотек, сетевое профаммирование, работа с XML, профаммирование баз
данных, создание динамических библиотек, совместное использование Qt
с платформозависимыми функциями и методы быстрой разработки
прототипов приложений.
Часть I. Основы Qt
Основная задача этой части — описать новый подход при профаммирова-
нии на Qt.
Глава 1. Обзор иерархии классов Qt
Первая глава вводная, знакомящая с иерархией классов Qt, а также
реализацией первой профаммы.
Глава 2. Философия объектной модели
Во вторую главу входит подробное описание механизма сигналов и слотов,
организация объектов в иерархии, свойства объектов.
Глава 3. Работа с Qt
Третья глава описывает подготовку средств для разработки приложений с
помощью Qt. Инсталляция самой библиотеки и настройка Microsoft Visual
Studio. Процесс создания проектных файлов, которые можно переработать
на любой платформе в соответствующие make-файлы. Описание методов и
средств отладки приложений.
Часть II. Элементы управления
Задача второй части — описание элементов, из которых строятся
пользовательские интерфейсы. Эта часть дает также навыки фамотного и
обоснованного применения данных элементов.
Глава 4. С чего начинаются элементы управления
Четвертая глава вводит понятие "виджет", как синоним элемента
управления. Описание трех классов, от которых наследуются все элементы
управления. Описание самых важных методов этих классов, таких как изменение
размера, местоположения, цвета и др. Описание, как управлять из виджета
изменением изображения указателя мыши. Знакомство с классом, который
способен показывать в отдельно взятый момент времени только лишь один
из содержащихся в нем виджетов.
Введение
17
Глава 5. Элементы отображения
Пятая глава описывает элементы управления, не принимающие
непосредственного участия в действиях пользователя и служащие только для
отображения информации. В группу этих элементов входят: надписи, индикатор
процесса выполнения и электронный индикатор. Подробно разбираются
основные особенности этих виджетов.
Глава 6. Кнопки
В этой главе, после описания основных возможностей базового класса
кнопок, рассматривается каждый тип кнопок в отдельности: кнопки нажатия,
флажки, переключатели. Делается акцент на особенности их применения.
Описываются классы для группирования кнопок.
Глава 7. Элементы управления для установки значений
Седьмая глава описывает виджеты, позволяющие производить установку
значений, не требующих большой точности. Описание каждого из
элементов этой группы в отдельности: ползунок, полоса прокрутки, установщик.
Глава 8. Элементы выбора
Восьмая глава знакомит с самой большой группой виджетов. В нее входят
такие элементы, как списки, таблицы, закладки, инструменты и др.
Глава 9. Элементы ввода
Девятая глава приводит описание группы виджетов, представляющих собой
фундамент для ввода пользовательских данных. Проводится детальное
рассмотрение каждого из виджетов этой группы: однострочные и
многострочные текстовые поля, счетчик, элемент ввода даты и времени.
Рассматривается использование класса QVaiidator для предотвращения неправильного
ввода пользователя.
Глава 10. Управление автоматическим размещением элементов
Десятая глава описывает классы для размещений (Layouts), позволяющие
управлять различными вариантами размещения виджетов на поверхности
другого виджета. Знакомство с классом разделителя QSpiitter. Пример
написания программы калькулятора.
Глава 11. Цветовая палитра элементов управления
Одиннадцатая глава описывает процесс изменения цветов как для каждого
виджета в отдельности, так и для всех виджетов приложения.
Глава 12. Элементы со стилем
В двенадцатой главе описывается механизм, позволяющий изменять
внешний вид приложения и его поведение (Look&Feel). Знакомство со встроен-
18
Введение
ными стилями, их демонстрация, а также описание механизма создания
своих собственных стилей.
Часть III. События и взаимодействие с пользователем
Цель третьей части — подробно ознакомить с тонкостями применения
событий при программировании на Qt.
Глава 13. События
В тринадцатой главе разъясняется необходимость сосуществования двух
моделей, связанных с оповещением, — сигналы/слоты и события. После этого
следует описание целого ряда классов событий для мыши, клавиатуры,
таймера и др. Отдельно рассматривается каждый из методов, предназначенных
для получения и обработки этих событий.
Глава 14. Фильтры событий
Четырнадцатая глава знакомит с очень мощным механизмом, дающим
возможность объекту фильтра осуществлять перехват управлением событиями.
Это позволяет объектам классов, унаследованных от QObject, реализовать,
например, один класс фильтра и устанавливать его в нужные объекты, что
значительно сэкономит время на разработку, т. к. отпадет необходимость
наследования или изменения класса, если при этом преследуется цель
только перезаписать методы для обработки событий.
Глава 15. Искусственное создание событий
В пятнадцатой главе рассказывается о возможностях производить события
искусственным образом, что может оказаться очень полезным, например,
для проведения имитации ввода пользователя.
Часть IV. Графика и звук
Задача четвертой части — познакомить с разнообразием возможностей,
связанных с программированием компьютерной графики. Также затрагивается
тема реализации приложений со звуком.
Глава 16. Введение в компьютерную графику
Шестнадцатая глава описывает основные классы для геометрии,
необходимые, прежде всего, для рисования. Дается понятие цвета и палитры.
Глава 17. Контекст рисования
Семнадцатая глава описывает перья и кисти, трансформации систем
координат, отсечения и двойную буферизацию, как средство подавления
нежелательного эффекта мерцания. Примеры рисования различных фафических
примитивов от точек до полигонов.
Введение
19
Глава 18. Растровые изображения
Восемнадцатая глава содержит подробное описание двух классов для
растровых изображений: QPixmap и Qimage. Описание преимуществ подобного
разделения растровых изображений на два класса. Функциональные
возможности этих классов. Введение понятия "прозрачность". Поддерживаемые
графические форматы.
Глава 19. Работа со шрифтами
Девятнадцатая глава демонстрирует использование шрифтов.
Глава 20. Работа с изображениями холста
Двадцатая глава описывает иерархии классов QCanvas, предоставляющих
собой интерфейс рисования высокого уровня. Эти классы можно применять
там, где необходимо предоставить пользователю возможность
манипулировать большим количеством графических изображений, например, в качестве
спрайтов для компьютерных игр.
Глава 21. Анимация
Двадцать первая глава содержит описание класса QMovie, предназначенного
для отображения анимированных изображений в GIF- и MNG-форматах.
Глава 22. Работа с OpenGL
Двадцать вторая глава описывает совместное использование этой
библиотеки с Qt, в основном, в качестве дополнительного средства для вывода
трехмерной графики. Подробно рассматриваются классы Qt, созданные для
поддержки OpenGL. Небольшое знакомство с возможностями самого
OpenGL: примитивы, проекции, пикселы и изображения, трехмерная
графика, текстура и дисплейные списки.
Глава 23. Вывод на печать
Двадцать третья глава рассказывает о возможностях, связанных с выводом
на печатающее устройство: использование принтера в качестве контекста
рисования, настройка параметров печати и многое другое.
Глава 24. Разработка собственных элементов управления
Двадцать четвертая глава описывает факторы, которые необходимо
учитывать при создании собственных виджетов. Например, какой из классов
взять за базовый и какие методы нуждаются в перезаписи.
Глава 25. Звук
В двадцать пятой главе описываются возможности, предоставляемые Qt для
работы со звуком.
20
Введение
Часть V. Создание приложений
В пятой части описываются все необходимые составляющие для реализации
профессиональных приложений.
Глава 26. Сохранение настроек приложения
В двадцать шестой главе объясняется механизм сохранения измененных
пользователем настроек, для их восстановления при дальнейших загрузках
приложения. Также описывается механизм управления сеансом, сохранение
настроек и документов в тех случаях, когда пользователь, завершая сеанс
работы с операционной системой, оставил приложение открытым, изменив
его настройки или не записав измененный документ.
Глава 27. Буфер обмена и перетаскивание
В двадцать седьмой главе демонстрируются возможности обмена данными
между разными приложениями посредством буфера обмена и
перетаскивания (drag&drop).
Глава 28. Интернационализация приложения
Двадцать седьмая глава описывает технику, связанную с
интернационализацией и локализацией создаваемых приложений.
Глава 29. Создание меню
Двадцать девятая глава описывает процесс создания меню разных типов:
строка меню, выпадающие меню, отрывные меню, контекстное меню, а
также описание ускорителей, предназначенных для быстрого доступа к
отдельным пунктам меню.
Глава 30. Диалоговые окна
В тридцатой главе вводятся понятия модальных и немодальных диалоговых
окон. Описываются стандартные диалоги для выбора файлов, шрифтов,
цвета и др. Объясняется применение простых диалогов сообщений.
Создание собственных диалоговых окон
Глава 31. Предоставление помощи
В тридцать первой главе рассмотрены различные варианты помощи с
методами их реализации.
Глава 32. Панели инструментов и строка состояния
Тридцать вторая глава описывает технику создания панелей инструментов
для приложений и использования строк состояния.
Глава 33. Создание приложений
Тридцать третья глава знакомит с анатомией главного окна приложения и
описывает возможности класса для главного окна приложения QMainwindow.
Введение
21
Приведен пример создания полноценного приложения текстового редактора
сначала как SDI, а затем MDI.
Часть VI. Особые возможности Qt
Задача шестой части — подробно ознакомить с теми возможностями Qt,
которые не обязательно связаны с программированием графики и
пользовательского интерфейса, но очень важны, т. к. предоставляют профаммисту
набор функциональных возможностей практически на все случаи жизни, и,
тем самым, позволяют добиться полной платформонезависимости.
Глава 34. Процессы и потоки
Тридцать четвертая глава рассказывает о назначении процессов. Описывает
использование многопоточности для параллельного выполнения задач,
необходимые для этого классы и методы. Совместное использование данных и
сложности, связанные с этим. Введение понятия "мьютекс" (mutex).
Использование синхронизации, описание класса QWaitcondition. Введение
понятия семафора, как обобщение мьютекс.
Глава 35. Дата, время и таймер
Тридцать пятая глава описывает область назначения и применения
таймеров. Знакомство с классами, предоставляющими информацию о текущей
дате и времени, а также предоставляющими методы для работы с ними.
Глава 36. Библиотека контейнеров
Тридцать шестая глава приводит описание классов, которые в состоянии
хранить в себе элементы различных типов данных и манипулировать ими.
Объясняются причины разделения этих классов на две фуппы, контейнеры
данных и контейнеры указателей на данные. Описание механизма "общих
данных", даюшего возможность экономично и эффективно использовать
ресурсы. Описание каждого контейнерного класса в отдельности: списки,
словари, хэш-таблицы и др. Описание класса строк QString и мощного
механизма для анализа сфок, именуемого "регулярное выражение".
Знакомство с классом QVariant, объекты которого способны содержать в себе данные
разного типа.
Глава 37. Работа с файлами, директориями и потоками ввода/вывода
В фидцать седьмой главе описываются возможности, предоставляемые Qt
для чтения и записи файлов, а также для просмофа директорий и
получения подробной информации о файлах. Завершается глава примерами
реализации профаммы, осуществляющей поиск файлов в заданной директории.
Глава 38. Программирование поддержки сети
Тридцать восьмая глава знакомит с базовым классом QSocket, позволяющим
реализовывать как TCP-клиенты, так и серверы. После этого рассматрива-
22
Введение
ются более специализированные в применении классы: QFtp — дающий
возможность доступа к данным через FTP, QHttp — представляющий собой
реализацию клиентской части HTTP-протокола, а также класс,
обеспечивающий прозрачный Доступ К сети — QUrlOperator.
Глава 39. Работа с XML
Тридцать девятая глава содержит краткий вводный курс в очень
популярный формат для хранения и обмена данными XML. Анализ преимуществ и
недостатков различных способов представления данных XML-документа,
SAX и DOM. После небольшого введения в DOM объясняется, как можно
осуществлять чтение и проводить операции с узлами DOM-представления
XML-документа. Чтение при помощи SAX. Запись XML-документов.
Глава 40. Программирование баз данных
Сороковая глава содержит краткий вводный курс в базы данных. Описание
процесса соединения с базой данных и ее открытия. Класс QSqiQuery —
исполнение команд SQL, получение, удаление и вставка данных. Описывается
возможность обращения к данным при помощи курсора, как способ для
более простого проведения операцией с данными.
Глава 41. Динамические библиотеки и системы расширений
Сорок первая глава рассказывает, как объединить общеиспользуемый код в
отдельные динамические библиотеки. Описывается процесс создания и
загрузки динамических библиотек. Описание системы расширений (Plug-ins).
Глава 42. Совместное использование Qt с платформозависимыми API
Сорок вторая глава описывает использование Qt с платформозависимыми
функциями ОС Windows и KDE (Linux).
Глава 43. Qt Designer. Быстрая разработка прототипов
В сорок третьей главе, после небольшого описания возможностей Qt
Designer, проводится разработка приложения исключительно средствами,
предоставляемыми этой программой.
Благодарности
Автор выражает глубокую признательность своей первой наставнице в
области информатики — Татьяне Дмитриевне Оболенцевой — преподавателю
Новосибирского филиала Московского технологического университета
легкой промышленности, разбудившей в нем творческий потенциал. А также
профессора, доктора Ульриха Айзинекера (Ulrich W.Eisenecker), который
помог определиться в многообразном мире информатики.
Введение
23
Большую помощь в создании этой книги оказали самые близкие ему люди:
Шлее Алена (Schlee Olena), родители Шлее Ойген и Галина (Schlee Eugen
& Galina), сестра Гоуз Натали (Goes Nataly).
Глубокую признательность и уважение высказывает автор в адрес
издательства "БХВ-Петербург" и, конкретно, Игорю Владимировичу Шишигину за
его поддержку и сотрудничество.
Особая благодарность Матиасу Этриху (Matthias Ettrich) — основателю
KDE, за его интерес и поддержку, проявленные во время написания книги.
А также автор благодарит компанию "Трольтек" (Trolltech) за их
замечательную библиотеку, которая вдохновила его на написание этой книги.
Основы Qt
Глава 1. Обзор иерархии классов Qt
Глава 2. Философия объектной модели
Глава 3. Работа с Qt
ГЛАВА 1
Обзор иерархии классов Qt
Если вы хотите знать территорию — нужно
сначала изучить карту.
Тони Бьюзен
Первая программа на Qt
Как и заведено, в самом начале знакомства нужно поздороваться, и, чтобы
никого не оставить без внимания, лучше всего обратиться сразу ко всему
миру. Давайте для этого напишем короткую программу "Hello, World!"
("Здравствуй, Мир!"), результат выполнения которой показан на рис. 1.1.
Написание подобного рода программ стало уже традицией при знакомстве с
новым языком или библиотекой. Хотя подобный пример не в состоянии
продемонстрировать весь потенциал и возможности самой библиотеки, но
он дает представление о базовых понятиях. Данный пример позволяет
оценить объем и сложность процесса реализации программ, использующих эту
библиотеку. Кроме того, при помощи примера можно убедиться, что все
необходимое для компиляции и компоновки установлено правильно.
Не
Рис. 1.1. Окно программы "Hello, World!"
; Листинг 1.1. Файл hello.cpp
#include <qapplication.h>
#include <qlabel.h>
int main (int argc, char** argv)
{
QApplication app (argc, argv);
Глава 1. Обзор иерархии классов Qt
27
QLabel 1Ы ("Hello, World !", 0) ;
app.setMainWidget (&1Ы);
1Ы. show () ;
return app.exec();
}
В первых строках листинга 1.1 выполняется подключение заголовочных
файлов qapplication.h и qlabel.h. В Qt имя заголовочного файла всегда
соответствует имени класса, который в нем определен, с той разницей, что все
буквы должны быть маленькие. Есть, правда, исключения — в случае, когда
несколько классов находятся в одном заголовочном файле, тогда для его
имени используется имя одного из классов. В данном примере
заголовочный файл qapplication.h содержит определение класса QAppiication, a
qlabel.h — определение класса QLabel.
Разберем наш пример.
Сначала создается объект класса QAppiication, который осуществляет
контроль за всеми процессами приложения, а также управление ресурсами и
установками приложения. Любая, использующая Qt, программа должна
создавать только один объект этого класса.
Затем создается объект класса QLabel. После своего создания элементы
управления Qt, по умолчанию, невидимы и для их отображения на экране
необходимо вызвать метод show о.
Передав в метод setMainwidget () ссылку на объект класса QLabel, мы
сообщаем, что этот объект является основным управляющим элементом
приложения. Это позволяет завершить работу приложения при закрытии окна
элемента. Иначе, при закрытии элемента, приложение останется в памяти
компьютера и будет использовать его ресурсы.
( Примечание )
Если в создаваемом приложении имеется сразу несколько независимых друг от
друга элементов управления, го тогда возникают трудности при определении
главного элемента. Это затруднение продиктовано тем, что элементы будут
исполняться в качестве отдельных программ. В подобных ситуациях можно
прибегнуть к помощи сигнала (см гл. 2) lastwindowClosed (), высылаемого
объектом класса QAppiication, каждый раз при закрытии последнего окна
приложения. Соединение этого сигнала со слотом quit () будет производить выход из
приложения после закрытия последнего окна.
QObject::connect(app, SIGNAL(lastwindowClosed()),
app, SLOT(quit())
);
И наконец, в последней строке программы производится запуск
приложения. С его запуском приводится в действие цикл обработки событий, регии-
28
Часть I. Основы Qt
зованный внутри метода QAppiication:: exec о. Этот цикл передает
получаемые от системы события на обработку соответствующим объектам. По
завершению работы приложения метод QAppiication:: exec о возвращает
значение целого типа, содержащее код, информирующий о его завершении.
Иерархия классов Qt
Библиотека Qt — это множество классов, которые охватывают большую
часть функциональных возможностей операционных систем, предоставляя
разработчику мощные механизмы, расширяющие и вместе с тем
упрощающие разработку приложений. При этом не нарушается идеология
операционной системы.
В начале изучения классов новой библиотеки создается ощущение
перенасыщения из-за большого объема информации. Иерархия классов Qt имеет
четкую внутреннюю структуру, которую важно понять, чтобы уметь хорошо
и интуитивно ориентироваться в этой библиотеке.
Класс Qt
Класс Qt содержит ряд типов перечислений и констант, которые часто
применяются при программировании. Он является базовым классом для
важнейшего класса QObject при программировании на Qt. Это дает
возможность избежать использования оператора видимости :: внутри
унаследованных классов с указанием имени класса. То есть, в этих классах
необязательно писать, например, Qt::vertical, можно просто написать
vertical. Табл. 1.1 содержит некоторые классы, унаследованные
непосредственно от Qt.
Таблица 1.1. Некоторые классы, унаследованные
непосредственно от класса Qt
Класс
QBrush
QEvent
QMutex
QObject
QPainter
Заголовочный
файл
qbrush.h
qeventh
qthread.h
qobject.h
qpainter.h
Назначение
Определяет образцы заливок замкнутых
контуров, применяется в QPainter. См. гл. 17
Базис для всех классов событий. См. гл. 13
Обеспечивает синхронизируемый доступ
потоков. См. гл. 34
"Краеугольный камень" объектной модели Qt.
См. гл. 2
Набор методов для рисования в контексте
графического устройства. См. гл. 17
Глава 1. Обзор иерархии классов Qt
29
Таблица 1.1 (окончание)
Класс
QPen
QPixmap
QSemaphore
QThread
QToolTip
QWhatsThis
QWaitCondition
Заголовочный
файл
qpen.h
qpixmap.h
qthread.h
qthread.h
qtooltip.h
qwhatsthis.h
qthread.h
Назначение
Задает стиль линий для QPainter. См. гл. 17
Работа с растровыми изображениями.
См. гл. 18
Синхронизируемое выполнение потоков
См. гл. 34
Потоки. См. гл. 34
Всплывающие подсказки. См. гл. 32
Снабжение элементов управления небольшим
описанием. См. гл. 32
Ожидание потоков при наступлении
выполнении условия. См. гл. 34
Классы интерфейса пользователя
Классы элементов управления
Эти классы предоставляют "строительный материал" для создания
приложений с интерфейсом пользователя. Некоторые из этих классов приведены в
табл. 1.2.
Таблица 1.2. Некоторые классы элементов управления
Класс
I QButton
QCheckBox
QComboBox
QFrame
JQHeader
QLabel
Заголовочный
файл
qbutton.h
qcheckbox.h
qcombobox.h
qframe.h
qheader.h
qlabel.h
Назначение
Базовый класс для всех кнопок. См. гл. 6
Кнопка-флажок. Используется для установки
одного из двух состояний
включено/выключено. См. гл. 6
Выпадающий список. Дает возможность
выбора одного элемента из списка. См. гл. 8
Представляет собой прямоугольник с рамкой.
См. гл. 4
Заглавие для таблиц. См. гл. 8
Текстовое поле, не подлежащее изменению со
стороны пользователя. См. гл. 5
30 Часть I. Основы Qt
Таблица 1.2 (окончание)
Класс
QLCDNumber
QLineEdit
QListBox
QListView
QMultiLineEdit
QPopupMenu
QProgressBar
QPushButton
QRadioButton
QScrollBar
QSlider
QSpinBox
QTabWidget
QWidget
Заголовочный
файл
qlcdnumber.h
qlineedit.h
qlistbox.h
qlistview.h
qmultilineedit.h
qpopupmenu.h
qprogressbar.h
qpushbutton.h
qradiobutton.h
qscrollbar.h
qslider.h
qspinbox.h
qtabwidget.h
q widget, h
Назначение
Используется только для отображения чисел.
См. гл. 5
Отображает прямоугольную область для ввода
текста. См. гл. 9
Дает возможность выбора от одного или
нескольких элементов из списка. См. гл. 8
Предоставляет выбор в иерархической форме
от одного до многих элементов. См. гл. 8
Многострочная область для ввода текста.
См. гл. 9
Всплывающие меню. Предоставляет
возможность выбора команд. См. гл. 29
Отображение прогресса выполнения операции
в процентах. См. гл. 5
Кнопка нажатия. См. гл. 6
Кнопка переключения, предназначенная для
использования в группе. См. гл. 6
Полоса прокрутки. Используется для
отображения данных, превышающих по размерам
отведенную для них область. См. гл. 7
Ползунок, позволяющий осуществлять
настройки параметров приложения. См. гл. 7
Предоставляет пользователю доступ к
ограниченному диапазону упорядоченных чисел
См. гл. 9
Закладки, используются для смены страниц,
отображаемых в одной и той же области
См. гл. 8 I
Базовый класс для всех элементов
управления. См. гл. 4
Классы для автоматического размещения элементов
Группа классов, управляющая автоматическим размещением элементов
управления — виджетов, а также их размерами. В табл. 1.3 приводится
описание некоторых из них.
Глава 1. Обзор иерархии классов Qt 31
Таблица 1.3. Некоторые классы для автоматического размещения
Класс
QButtonGroup
QGroupBox
QGrid
QGridLayout
QHBox
QHBoxLayout
QHButtonGroup
QLayout
QSplitter
QVBox
QVBoxLayout
QVButtonGroup
Заголовочный
файл
qbuttongroup.h
qgroupbox.h
qgrid.h
qlayout.h
qhbox.h
qlayouth
qhbuttongroup.h
qlayout.h
qsplitter.h
qvbox.h
qlayouth
qvbuttongroup.h
Назначение
Предоставляет возможность группировки
отдельных кнопок вместе. См. гл. 6
Контейнер, собирающий в себе различные
виджеты. См. гл. 6
Размещает виджеты-потомки в форме
таблицы. См. гл. 10
Размещает виджеты в форме таблицы.
См. гл. 10
Размещает виджеты-потомки друг напротив
друга, слева направо. См. гл. 10
Размещает виджеты напротив друг друга,
слева направо Отличается от qhbox тем, что
не унаследован от класса QWidget.
См. гл. 10
Содержит рамку. Размещает виджеты слева
направо. См. гл. 6
Базовый класс для размещения виджетов.
См. гл. 10
Разделители. Используются для
одновременного просмотра различных частей
объектов. См. гл. 10
Размещает виджеты-потомки друг под
другом, сверху вниз. См. гл. 10
Размещает виджеты друг под другом,
сверху вниз. Отличается от QVBox тем, что не
унаследован от класса QWidget. См. гл. 10
Размещает виджеты в порядке сверху вниз.
См. гл. 6
Классы для диалоговых окон
Предопределена группа классов, реализующая стандартные диалоговые
окна, такие как выбор шрифта, цвета, открытие файлов, а также отображение
сообщений, информирующих пользователя о некоторых событиях.
Некоторые из этих классов представлены в табл. 1.4.
32
Часть I. Основы Qt
Таблица 1.4. Некоторые классы диалоговых окон
Класс
QColorDialog
QDialog
QFileDialog
QFontDialog
QMessageBox
QProgressDialog
QPrintDialog
Заголовочный файл
qcolordialog.h
qdialog.h
qfiledialog.h
qfontdialog.h
qmessagebox.h
qprogressdialog.h
qprintdialog.h
Назначение
Окно для выбора цвета. См. гл. 30
Базовый класс для диалоговых окон.
См. гл. 30
Окно выбора файла. См. гл. 30
Окно выбора шрифта. См. гл. 30
Окно сообщения. См. гл. 30
Окно прогресса. См. гл. 30
Окно настройки печати. См. гл. 30
Классы для работы с графикой
Группа классов для работы с графикой реализует отображение графических
примитивов, растровые изображения, анимацию, шрифты и др. В табл. 1.5
приводится описание некоторых из них.
Таблица 1.5. Некоторые классы для работы с графикой
Класс
QBitmap
QBrush
QCanvas
QColor
QFont
QGLWidget
Qlmage
QMovie
QPaintDevice
QPainter
Заголовочный
файл
qbitmap.h
qbrush.h
qcanvas.h
qcolor.h
qfont.h
qgl.h
qimage.h
qmovie.h
qpaintdevice.h
qpainter.h
Назначение
Класс двухцветного растрового
изображения. См. гл. 18
Класс кисти. Используется для заполнения
внутренней части фигуры. См. гл. 17
Класс холста. Применяется для создания
интерактивных изображений. См. гл. 20
Класс цвета. См. гл. 16
Класс шрифта. См. гл. 19
Класс поддержки OpenGL. См. гл. 22
Класс контекстно-независимого растрового
изображения. См. гл. 18
Класс анимации. См. гл. 21
Класс контекста рисования. См. гл. 17
Класс, производящий рисование на объекте
контекста. См. гл. 17
Глава 1. Обзор иерархии классов Qt
33
Таблица 1.5 (окончание)
Класс
QPen
QPixmap
QPrinter
Заголовочный
файл
qpen.h
qpixmap.h
qprinter.h
Назначение
Класс пера. Используется для рисования
контурных линий отображаемой фигуры.
См. гл. 17
Класс контекстно-зависимого растрового
изображения. См. гл. 18
Класс для вывода на печать. См. гл. 23
Класс приложения
QApplication — класс, с которым мы встречались в самом первом примере.
Это очень важный класс, который является стержнем любого Qt-прило-
жения. Его определение находится в заголовочном файле qapplication.h.
Объект класса QApplication представляет собой центральный контрольный
пункт всех Qt-приложений, имеющих пользовательский интерфейс. Он не
должен создаваться в приложении больше одного раза. Данный объект
используется для получения событий клавиатуры, мыши, таймера и других
событий, на которые приложение должно реагировать соответствующим
образом. Например, окно даже самого простого приложения может быть
изменено по величине или быть перекрыто окном другого приложения, на
все подобные события необходима правильная реакция.
Объект класса можно образно сравнить с сосудом, содержащим объекты,
подсоединенные к контексту операционной системы. Срок жизни объекта
класса QApplication соответствует концу работы всего приложения, и он
остается досягаемым в любой промежуток времени работы программы.
В назначение этого объекта входит:
О управление событиями между приложением и операционной системой;
О высылка событий к разным элементам управления (см. гл. 15);
П анализ аргументов командной строки;
О установка стиля приложения (см. гл. 12);
О получение указателя на объект рабочего стола (desktop);
П получение доступа к буферу обмена (см. гл. 27);
О управление глобальными манипуляциями с мышью. Регистрирует
движения мыши как в пределах окна приложения, так и за ними. Позволяет
установить интервал двойного щелчка;
П выдача предупреждающего звукового сигнала (см. гл. 25);
34
Часть I. Основы Qt
П правильное завершение работающего приложения при завершении
работы операционной системы (см. гл. 26);
П инициализация необходимых настроек приложения, например, палитры
для расцветки элементов управления (см. гл. 11).
Резюме
Библиотека Qt — это множество классов для интерфейса пользователя,
работы с графикой и т. д. Эти классы предоставляют разработчику
механизмы, расширяющие и вместе с тем упрощающие создание приложений.
Вершиной классовой иерархии является класс Qt, который содержит ряд типов
перечислений и констант, часто применяемых при
программировании.
Класс QAppiication является стержнем любого Qt-приложения. Он не
должен создаваться в приложении больше одного раза. Данный объект
используется для получения событий клавиатуры, мыши, таймера и других
событий, а также для анализа аргументов командной строки, получения доступа
к буферу обмена и т. д.
ГЛАВА 2
О
Философия объектной модели
Те, кого первое знакомство с квантовой
теорией не повергло в шок, скорее всего,
вовсе ее не поняли.
Макс Бори
Объектная модель подразумевает, что все построено на объектах.
Фактически, класс QObject — основной класс, с которого все начинается.
Большинство классов библиотеки Qt являются наследниками этого класса, что дает
им возможность с легкостью управлять объектами, унаследованными от
этого же класса. Классы, имеющие сигналы и слоты, обязаны быть
унаследованы от этого класса.
( Примечание )
При множественном наследовании очень важно учитывать, что от класса
QObject должен быть унаследован только один из базовых классов. Другими
словами, нельзя производить наследование сразу от нескольких классов,
наследующих QObject.
С Примечание )
При множественном наследовании важно также учитывать, чтобы при
определении класса имя класса QObject или унаследованного от него класса стояло
первым, для того чтобы МОС (Meta Object Compiler, метаобъектный
компилятор) мог правильно распознать его (листинг 2.1).
Г •:
1?истинг 2'1- Наследование класса
class MyClass : public QObject, public AnotherClass {
36
Часть! ОсновыQt
Класс QObject содержит в себе поддержку:
□ сигналов и слотов (signal/slot);
□ таймера;
□ механизма объединения объектов в иерархии;
□ механизма фильтрации событий;
□ свойств объектов.
Сигналы и слоты — это средства, позволяющие эффективно производить
обмен информацией о событиях, вырабатываемых объектами.
Поддержка таймера дает возможность каждому из классов, унаследованных
от класса QObject, не создавать дополнительно объект таймера. Тем самым
экономится время на разработку. Подробную информацию о таймере см.
в гл. 35.
Механизм объединения объектов в иерархические структуры позволяет
резко сократить временные затраты при разработке приложений, не заботясь
об освобождении памяти (эту операцию автоматически производит объект-
предок).
Механизм фильтрации событий позволяет осуществить перехват событий.
Объект фильтра может быть установлен в любом классе, унаследованном от
QObject, благодаря чему можно изменять реакцию объектов на
происходящие события, без изменения исходного кода класса (см. гл. 14).
Свойства объектов содержат информацию о наследовании классов,
благодаря которой можно определить, являются ли они непосредственными
наследниками или нет, а также узнать имя объекта и класса.
Механизм сигналов и слотов
Элементы графического интерфейса реагируют определенным образом на
действия пользователя и посылают сообщения. Существует несколько
вариантов такого решения.
Старая концепция функций обратного вызова (callback functions), лежащая в
основе X Window, основана на использовании обычных функций, которые
должны вызываться в результате действий пользователя. Применение такой
концепции значительно усложняет исходный код программы, делая его
менее понятным. Кроме того, отсутствует возможность производить проверку
типов возвращаемых значений, потому что во всех случаях возвращается
указатель на void. Одним из ярких представителей этой концепции является
Motif.
Важно помнить, что Motif, а также и Windows API приспособлены для
функционального программирования и с реализацией
объектно-ориентированных проектов, наверняка, появятся трудности.
Глава 2. Философия объектной модели 37
Для программирования в ОС Windows существуют специальные библиотеки
классов C++, облегчающие программирование для этой операционной
системы. Самой популярной библиотекой является Microsoft Foundation Classes
(MFC). Ее можно с большой натяжкой назвать объектно-ориентированной,
т. к. она создавалась людьми, не подозревающими о существовании самых
элементарных принципов объектно-ориентированного подхода. Одна из
самых фундаментальных заповедей объектно-ориентированного подхода это
инкапсуляция, которая запрещает оставлять переменные классов
незащищенными, но, несмотря на это, во многих MFC-классах не соблюдено это
требование. Сама библиотека MFC является надстройкой, предоставляющей
доступ к функциям ОС Windows, реализованных на языке С, что заставляет
разработчиков время от времени использовать устаревшие структуры, не
вписывающиеся в рамки концепции объектно-ориентированного подхода.
Интересно также отметить, что сама Microsoft при реализации широко
известной программы Microsoft Word не использует MFC вообще.
При использовании MFC для обеспечения связи сообщений и методов
обработки применяются специальные макросы — так называемые карты
сообщений. Они сильно загромождают исходный код программы, заметно
снижая ее читаемость.
| Листинг 2.2. Отрывок программы, реализованной с помощью MFC
class CPhotoStylerApp : public CWinApp {
public:
CPhotoStylerApp();
public:
virtual BOOL Initlnstance();
afx_msg void OnAppAbout();
afx_msg void OnFileNew();
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CPhotoStylerApp, CWinApp)
ON_CCMMAND(ID_APP_ABOUT, OnAppAbout)
ON_COMMAND(ID_FILE_NEW, OnFileNew)
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()
Подобные конструкции, показанные в листинге 2.2, неудобны для
восприятия и приводят в замешательство при проведении анализа кода программы.
Пусть многие рассказывают об удобстве использования средств автоматиче-
38
Часть I. Основы Qt
ского создания подобного кода, но созданы они были не от хорошей жизни.
Так как не продуманность самой библиотеки вынуждает разработчика, при
внесении незначительных изменений, модифицировать код самой
программы сразу в нескольких местах. Например, для того чтобы добавить в
диалоговое окно текстовое поле, необходимо провести целый ряд операций. Во-
первых, нужно создать в классе диалогового окна переменную,
предназначенную для хранения значений, вводимых в текстовом поле. Задать
идентификатор ресурса текстового поля. Затем связать идентификатор ресурса и
переменную в методе DoDataExchange () при помощи метода DDXTexto.
Посредством метода DDXTexto будет осуществляться обмен данными в
направлениях от текстового поля к переменной, и наоборот. Этим
направлением необходимо управлять, передавая в методе updateDataO значения бу-
левого типа true или false. И лишь с помощью средств автоматического
создания кода можно частично избавиться от этой проблемы, заставив
произвести все эти изменения за вас, получив взамен другие, такие как
дополнительное засорение кода программы ненужной информацией и
возможным несовпадением созданного кода с принятыми требованиями для
форматирования и нотации, утвержденными для проекта (если не используется
венгерская нотация). Поймите меня правильно, я не противник
обоснованного применения подобного рода средств, но, по моему мнению, они не
должны создаваться как средство устранения изъянов плохого дизайна
самой библиотеки.
В данной ситуации часть вины скрыта в самом языке C++. Главное понять,
что C++ не создавался как средство для написания пользовательского
интерфейса, и поэтому не предоставляет соответствующей поддержки,
делающей программирование в этой области более удобным. Например, если бы
работа по передаче событий реализовывалась средствами самого языка,
тогда отпадает необходимость в использовании подобного рода макросов. До
настоящего времени не удавалось сделать ничего подобного, именно
поэтому библиотека Qt явилась "как гром среди ясного неба". В отличие от
большинства других библиотек программирования, Qt расширяет язык C++
дополнительными ключевыми словами.
Проблема расширения языка C++ решена в Qt с помощью специального
препроцессора МОС (Meta Object Compiler, метаобъектный компилятор).
Он анализирует классы на содержание специального макроса qobject в их
определении и внедряет в отдельный файл всю необходимую,
дополнительную информацию. Процесс происходит автоматически, без
непосредственного участия разработчика. Подобная операция автоматического создания
кода не противоречит привычному процессу программирования на C++,
ведь стандартный препроцессор перед компиляцией самой программы тоже
создает промежуточный код, содержащий исполненные команды
препроцессора. Подобным образом действует и МОС, записывая всю необходимую
дополнительную информацию в отдельном файле, содержимое которого
Глава 2. Философия объектной модели
39
может быть проигнорировано. Макрос qobject должен располагаться сразу
на следующей строке после ключевого слова class с определением имени
класса. Очень важно помнить, что после макроса не должно стоять точки с
запятой. Внедрять макрос в определение класса имеет смысл в тех случаях,
когда созданный класс использует механизм сигналов или слотов, или если
ему необходима информация о свойствах.
Сигналы и слоты — это очень важная часть программирования с
использованием библиотеки Qt, позволяющая соединить вместе объекты,
несвязанные друг с другом. Каждый унаследованный от Qobject класс способен
высылать и получать сигналы. Эта особенность идеально вписывается в
концепцию объектной ориентации и не противоречит человеческому
восприятию. Представьте себе ситуацию: у вас звонит телефон и вашим
ответным действием будет снятие трубки. На языке сигналов и слотов подобную
ситуацию можно описать следующим образом: объект "телефон" выслал
сигнал "звонок", на который объект "человек" отреагировал слотом "снятие
трубки".
Использование механизма сигналов и слотов дает программисту следующие
преимущества:
О каждый класс, унаследованный от Qobject, может иметь любое
количество сигналов и слотов;
О сообщения, посылаемые посредством сигналов, могут иметь множество
аргументов любого типа;
□ сигнал можно соединять с различным количеством слотов. Высылаемый
сигнал, в этом случае, поступит ко всем подсоединенным слотам;
О слот может принимать сообщения от многих сигналов, принадлежащих
разным объектам;
О соединение сигналов и слотов можно производить в любой точке
приложения;
О при уничтожении объекта происходит автоматическое разъединение со
всеми сигнально-слотными связями. Это гарантирует, что сигналы не
будут высылаться к несуществующим объектам.
Нельзя не упомянуть и о недостатках, связанных с применением сигналов
и слотов:
О сигналы и слоты не являются частью языка C++, поэтому требуется
запуск дополнительного препроцессора перед компиляцией программы;
П отсылка сигналов немного медленнее, чем обычный вызов функции,
который производится при использовании механизма функций обратного
вызова;
О существует необходимость в наследовании класса Qobject;
40
Часть I. Основы Qt
П в процессе компиляции не производится никаких проверок: имеется ли
сигнал или слот в соответствующих классах или нет; совместимы ли
сигнал и слот друг с другом и могут ли они быть соединены вместе. Об
ошибке можно будет узнать только после запуска приложения. Вся эта
информация выводится на консоль, поэтому для того чтобы увидеть ее в
ОС Windows, в проектном файле (см. гл. 3) необходимо добавить в
секции config опцию console (для Linux никаких дополнительных
изменений проектного файла не требуется).
Сигналы
Сигналы (signals) окружают нас в повседневной жизни везде: звонок
будильника, жест регулировщика, индейский сигнальный костер. В
программировании с использованием Qt под этим понятием подразумеваются методы,
которые в состоянии производить высылку сообщений. Причиной для
появления сигнала может быть сообщение об изменении состояния
управляющего элемента, например — перемещение ползунка. На подобные
изменения присоединенный объект, отслеживающий эти сигналы, может
соответственно отреагировать, хотя совсем и не обязательно. Этот очень важный
момент говорит о том, что соединяемые объекты могут быть абсолютно
независимы друг от друга, и реализованы отдельно друг от друга. Это
позволяет объекту, высылающему сигналы, не беспокоиться о том, что
впоследствии будет происходить с этими сигналами. Объект, высылающий сигналы,
может даже и не догадываться, что их принимают и обрабатывают другие
объекты. Благодаря такому разделению можно разбить большой проект на
компоненты, которые будут разрабатываться разными программистами в
отдельности, а потом соединяться при помощи сигналов и слотов вместе.
Это делает код очень гибким и легко расширяемым, если вдруг один из
компонентов устареет или должен будет реализован иначе, то все другие
компоненты, участвующие в коммуникации с этим компонентом, и сам
проект, в целом, не изменятся. Новый компонент после разработки встанет
на место старого и будет подключен к основной программе при помощи тех
же самых сигналов и слотов. Это делает библиотеку Qt особенно
привлекательной для реализации компонентно-ориентированных приложений.
Сигналы определяются в классе, как и обычные методы, только без
реализации. С точки зрения программиста они являются только прототипами
методов, содержащихся в заголовочном файле определения класса. Всю
дальнейшую заботу о реализации кода для этих методов берет на себя МОС.
Методы сигналов не должны возвращать каких-либо значений, и поэтому
перед именем метода всегда должен стоять void.
Сигнал не обязательно соединять со слотом. Если соединения не
произошло, тогда он просто не будет обрабатываться. Подобное разделение объектов
высылающих, от объектов получающих, исключает возможность того, что
Глава 2. Философия объектной модели
41
один из подсоединенных слотов каким-то образом сможет помешать
объекту, выславшему сигналы.
Библиотека предоставляет большое количество уже готовых сигналов для
существующих элементов управления. В основном, для решения
поставленных задач хватает этих сигналов, но иногда возникает необходимость
реализации новых сигналов в своих классах (листинг 2.3).
| Листинг 2.3. Определение сигнала в классе
class MySignal {
Q_OBJECT
signals:
void dolt();
};
Обратите внимание на метод сигнала doit о. Он не имеет реализации, эту
работу принимает на себя МОС и делает примерно следующее:
void MySignal::dolt()
{
activate_signal(staticMetaObject()->signalOffset() + 0) ;
}
Из вышесказанного становится ясным, что не имеет смысла определять
сигналы как private, protected или public, т. к. они играют роль
вызывающих методов.
Выслать сигнал можно при помощи ключевого слова emit. Ввиду того, что
сигналы играют роль вызывающих методов, конструкция высылки сигнала
emit doit о, приведет к обычному вызову метода doit о. Сигналы могут
высылаться из классов которые их содержат. Обращаясь к листингу 2.3, сигнал
doit о может отсылаться только объектами класса MySignal, и никакими
Другими. А теперь следует добавить метод sendsignai (), вызов которого
заставит объект класса MySignal высылать сигнал doit ().
[Листинг 2.4. Реализация сигнала
class MySignal {
Q_OBJECT
Public:
void sendSignaK)
{
emit dolt () ;
}
42
Часть I. Основы Qt
signals:
void dolt();
};
Сигналы также имеют возможность высылать информацию, передаваемую
в параметре (листинг 2.4). Например, если возникла необходимость
передать в сигнале строку текста, то можно поступить так, как это реализовано
в листинге 2.5.
j Листинг 2.5. Реализация сигнала с параметром
class MySignal {
Q_OBJECT
public:
void sendSignaK)
{
emit sendString("Information") ;
}
signals:
void sendString(const QString&);
};
Слоты
Слоты (slots) — это методы, которые присоединяются к сигналам. По сути,
они являются обычными методами. Самое большое их отличие состоит в
возможности принимать сигналы. Как и обычные методы, они
определяются в классе как public, private или protected. Соответственно, перед
каждой Группой СЛОТОВ ДОЛЖНО СТОЯТЬ: private slots:, protected slots: ИЛИ
public slots:. Слоты могут быть и виртуальными.
( Примечание )
По данным фирмы Trolltech, соединение сигнала с виртуальным слотом
примерно в десять раз медленнее, чем с невиртуальным. Поэтому не стоит делать
слоты виртуальными, если для этого нет особой необходимости.
Правда, есть небольшие ограничения, отличающие обычные методы от
слотов. В слотах нельзя использовать параметры по умолчанию, например
slotMethod(int n = 8), И определять ИХ КЭК static.
Классы библиотеки содержат целый ряд уже реализованных слотов. Но
определение слотов для своих классов — это частая процедура. Реализация
слота показана в листинге 2.6.
Глава 2. Философия объектной модели
43
Листинг 2.6. Реализация слота
class MySlot : public QObject {
Q_OBJECT
public:
MySlot();
public slots:
void slot()
{
cout « "I'm a slot" « endl;
}
);
Внутри слота, вызовом метода sender о, можно узнать, от какого объекта
был выслан сигнал. Он возвращает указатель на объект типа QObject.
void slot()
{
cout « sender()->name() « endl;
}
В данном случае на консоль будет выведено имя объекта, выславшего
сигнал.
Соединение объектов
Соединение объектов осуществляется при помощи статического метода
connect о, который определен в классе QObject. В общем виде, вызов метода
connect {) выглядит следующим образом:
QObject::connect(const QObject* sender,
const char* signal,
const QObject* receiver,
const char* slot
);
В этот метод передаются четыре следующих параметра:
О sender — указатель на объект, высылающий сигнал;
О signal — это сигнал, с которым осуществляется соединение. Прототип
(имя и аргументы) метода сигнала должен быть заключен в специальном
макросе SIGNAL(method());
О receiver — указатель на объект, который имеет слот для обработки
сигнала;
44
Часть /. Основы Qt
О slot — слот, который вызывается при получении сигнала. Прототип
слота должен быть заключен в специальном макросе slot (method ()).
Следующий пример демонстрирует, как может быть произведено
соединение объектов в программе.
void main ()
{
QObject::connect(pSender, SIGNAL(signalMethod()),
pReceiver, SLOT(slotMethod())
);
}
Если вызов происходит из класса унаследованного от QObject, тогда
"QObject: : " МОЖНО ОПУСТИТЬ.
MyClass::MyClass() : QObject()
{
connect(pSender, SIGNAL(signalMethod()),
pReceiver, SLOT(slotMethod())
);
)
В том случае, если слот содержится в классе, из которого производится
соединение, можно воспользоваться сокращенной формой метода connect о,
опустив третий параметр, указывающий на объект-получатель. Другими
словами, если в качестве объекта получателя должен стоять указатель this,
его можно просто не указывать.
MyClass::MyClass() : QObj ect()
{
connect(pSender, SIGNAL(signalMethod()), SLOT(__ot()));
}
void MyClass::slot()
{
cout « "I'm a slot" « endl;
}
Иногда возникают такие ситуации, когда объект не обрабатывает сигнал, а
просто передает его дальше. Для этого необязательно определять слот,
который в ответ на получение сигнала (при помощи emit) отсылает свой
собственный. Можно просто соединить сигналы друг с другом. Высылаемый
сигнал должен содержаться в определении класса.
Глава 2. Философия объектной модели
45
MyClass::MyClass() : QObject()
{
connect(pSender, SIGNAL(signalMethod()), SIGNAL(mySignal()));
}
Высылку сигналов можно заблокировать на некоторое время, вызвав метод
biocksignais () с параметром true. Объект будет "молчать" до тех пор, пока
блокировка не будет снята тем же методом biocksignais {) с параметром
false.
При помоши метода signaisBiocked () можно узнать о текущем состоянии
блокировки сигналов.
Программа, показанная на рис. 2.1, демонстрирует механизм сигналов и
слотов в действии. Создается приложение, состоящее из кнопки нажатия и
надписи. При щелчке на кнопке ADD (Добавить) происходит увеличение
отображаемого значения на 1. Как только значение будет равно пяти,
произойдет выход из приложения.
ГШ-1°Н
Рис. 2.1. Программа-счетчик. Демонстрация работы механизма сигналов и слотов
[Листинг 2.7. Файл main.cpp
#include <qapplication.h>
#include <qlabel.h>
#include <qpushbutton.h>
#include "Counter.h"
//
int main (int argc, char** argv)
{
QApplication app (argc, argv);
QLabel lbl("0", 0);
QPushButton cmd("ADD", 0);
Counter counter;
lbl.show() ;
cmd.show();
QObject::connect{&cmd, SIGNAL{clicked()),
scounter, SLOT(slotlnc())
);
46
Часть I. Основы Qt
QObject::connect(&counter, SIGNAL(counterChanged(int)),
&1Ы, SLOT(setNum(int))
);
QObject::connect(&counter, SIGNAL(goodbye()),
&app, SLOT(quit())
);
QObj ect::connect(&app, SIGNAL(lastWindowClosed()),
&app, SLOT(quit())
);
return app.exec();
}
В основной функции приложения (листинг 2.7) создается объект надписи
1Ы, кнопка нажатия and и объект счетчика counter. Далее производится
соединение сигнала clicked о со слотом siotinco. При каждом щелчке на
кнопку вызывается метод siotinco, увеличивая значение счетчика на I. Он
должен быть в состоянии сообщать о подобных изменениях, чтобы элемент
надписи отображал всегда только актуальное значение. Для этого сигнал
counterChanged (int), передающий в параметре актуальное значение
счетчика, соединяется со слотом setNum(int), способным принимать это значение.
( Примечание )
При соединении сигналов со слотами, передающими значения, важно следить
за совпадением их типов. Например, сигнал, передающий в параметре
значение int, не должен соединяться со слотом, принимающим QString.
Далее соединяется сигнал goodbye о, символизирующий конец работы
счетчика, со слотом объекта приложения quit о, который осуществляет
завершение работы приложения. Наше приложение состоит из двух окон, и
поэтому нужно его завершить закрытием последнего окна. Это производится
при помощи последнего соединения сигнала lastWindowClosed () со слотом
quit () объекта приложения.
I Листинг 2.8. Файл Counter.h
#ifndef _Counter_h_
#define _Counter_h_
#include <qobject.h>
// =========„===„=======,
class Counter : public QObject {
Q_OBJECT
Глава 2. Философия объектной модели
47
private:
int m_nValue;
public:
Counter();
public slots:
void slotlncO ;
signals:
void goodbye ( ) ;
void counterChanged(int);
};
#endif //_Counter_h_
Как видно из листинга 2.8, в определении класса счетчика содержатся
два сигнала — goodbye (), сообщающий о конце работы счетчика, и
counterChanged (int), передающий актуальное значение счетчика, а также
слот siotinco, увеличивающий значение счетчика на единицу.
|ТЛистинг 2.9. Файл Counter.cpp
#include "Counter.h"
//
Counter::Counter() : QObj ect()
, m_nValue(0)
{
}
//
void Counter::slotlnc()
{
emit counterChanged(++m_nValue);
if (m_nValue ==5) {
emit goodbye();
)
}
В листинге 2.9 метод слота siotinco высылает два сигнала: counterChanged о
и goodbye о. Сигнал goodbye о высылается при значении mnvaiue равном 5.
Слот, не имеющий параметров, можно соединить с сигналом, имеющим
параметры. Это удобно, когда сигналы поставляют больше информации,
48
Часть I. Основы Qt
чем требуется для объекта, получающего сигнал. В этом случае в слоте
можно не указывать параметры, которые все равно не нужны.
MyClass:: MyClass () : QObjectO
{
connect(pSender, SIGNAL(signalMethod(int)), SIGNAL(mySignal()));
}
Если вы не уверены, что параметр сигнала пригодится в будущем, то лучше
определить слот с параметром, и внутри слота проигнорировать его. Зато
потом, когда возникнет необходимость в использовании параметра, вам не
потребуется менять прототип слота.
Бывают случаи, когда не всегда можно или нецелесообразно унаследоваться
от класса cobject. В Qt существует класс QSignai, который обычно
используется в тех случаях, когда требуется избежать наследования от класса
oobject. С его помощью можно установить соединение к слоту, но только
без параметров.
Для этого необходимо определить в классе указатель на объект класса
QSignai и создать этот объект оператором new в конструкторе. Так как
созданный объект не использует механизм объектной иерархии, то нужно
позаботиться и о его уничтожении в деструкторе. После этого нужно
реализовать в нем метод для соединения при помощи метода connect (receiver,
slotMethod). Вызов метода activated () высылает сигнал из объекта,
произведенного от класса QSignai. Пример возможной реализации приводится
в листинге 2.10.
Листинг 2.10. Реализация сигнала без наследования OTQObject
#include <qsignal.h>
class MySignal {
private:
QSignai* m_psignal;
public:
MySignal()
m_psignal = new QSignai;
-MySignal()
delete m_psignal;
void connect(QObject* pobj, const char* psz)
m_psignal->connect(pobj, psz);
Глава 2. Философия объектной модели
49
void activate()
(
// Выполнение каких-либо действий перед высылкой сигнала
m_psignal->activate();
}
};
Разъединение объектов
Если есть возможность соединения объектов, то должна существовать и
возможность их разъединения. В Qt при уничтожении объекта все
связанные с ним соединения уничтожаются автоматически, но в редких случаях
может возникнуть необходимость в уничтожении этих соединений
"вручную". Для этого существует статический метод disconnect (). Его параметры
аналогичны параметрам статического метода connect о. В общем виде этот
метод ВЫГЛЯДИТ следующим Образом: QObject::disconnect (sender, signal,
receiver, slot);.
Следующий пример демонстрирует, как может быть произведено
разъединение объектов в программе:
void main ()
{
QObject::disconnect(pSender, SIGNAL(signalMethod()),
pReceiver, SLOT(slotMethod())
);
Существуют два сокращенных, не статических варианта: disconnect (signal,
reciever, slot) И disconnect(receiver, slot).
Организация объектных иерархий
Организация объектов в иерархии снимает с разработчика необходимость
самому заботиться об освобождении памяти от созданных объектов.
Конструктор класса QObject выглядит следующим образом:
QObject(QObject* pobj = 0, const char* pszName = 0);
Первым параметром передается указатель на другой объект класса oobject
или унаследованного от него класса. Благодаря этому параметру, существует
возможность создания объектов-иерархий. Он представляет собой указатель
на объект-предок. Если в первом параметре передается значение, равное
50
Часть I. Основы Qt
нулю, или не передается вообще ничего, это значит, что у создаваемого
объекта нет предка, и он будет являться объектом верхнего уровня и
находиться на вершине объектной иерархии. Объект-предок указывается только
при создании объекта и впоследствии изменению не подлежит.
( Примечание )
Исключением является только класс QWidget и унаследованные от него классы.
Для объектов таких классов можно изменить объект-предок, вызвав метод
QWidget::reparent().
Второй параметр служит для присвоения объекту имени. При передаче
нулевого значения или опускании этого параметра объект будет безымянным.
Имя объекта не имеет особого значения, но может быть полезно при
отладке программы. Его можно менять вызовом метода setNameO, а для
получения имени объекта можно вызвать метод name о.
i Листинг 2.11. Пример создания объектной иерархии
QObject* pobjl = new QObject;
QObject* pobj2 = new QObject(pobjl, "the first child of pobjl");
QObject* pobj3 = new QObject(pobjl, "the second child of pobjl");
QObject* pobj4 = new QObject(pobj2, "the first child of pobj2");
В первой строке листинга 2.11 создается объект верхнего уровня (объект без
предка). При создании объекта pobj2 в его конструктор передается, в
качестве предка, указатель на объект pobjl. Объект pobj3 имеет в качестве
предка тоже pobjl, а объект pobj4 имеет предка pobj2. Полученная объектная
иерархия показана на рис. 2.2.
I *PQbJ1 й
*pobJ3 |
[*PQbJ4 |
Рис. 2.2. Пример объектной иерархии
При уничтожении созданного объекта (при вызове его деструктора) все
присоединенные к нему объекты-потомки уничтожаются автоматически
Эта особенность рекурсивного уничтожения объектов значительно упрошае:
программирование, т. к. не нужно заботиться об освобождении pecypcoi
памяти. Именно поэтому важно создавать объекты, а особенно объекты н<
Глава 2. Философия объектной модели
51
верхнего уровня, динамически, при помощи оператора new, иначе удаление
объекта приведет к ошибке при исполнении программы.
Предупреждены- ,3)
Одна из самых распространенных ошибок программистов на C++ при
программировании с использованием Qt— это самостоятельный контроль процесса
выделения/удаления памяти и нединамическое создание элементов
управления. При программировании с Qt важно помнить, что все объекты должны
создаваться в памяти динамически, с помощью оператора new. Исключение из
этого правила могут составлять только объекты, не имеющие предков.
Для получения информации об объектной иерархии существуют два метода:
parent о и children о. С помощью метода parent о можно определить
объект-предок. Согласно рис. 2.2, вызов pobj2->parento вернет указатель на
объект obji. Для объектов верхнего уровня этот метод вернет значение 0.
Чтобы вывести на консоль всю цепь имен объектов-предков какого-либо из
объектов, можно поступить следующим образом:
for (pobj = parent(); pobj; pobj = pobj->parent()) {
cout « pobj->name() « endl;
}
Метод children о возвращает константный указатель на список объектов-
потомков. Для приведенного выше примера вызов pobji->chiidren()
возвращает указатель на список QObjectList, содержащий два элемента:
указатели pobj 2 И pobj3.
Можно осуществлять поиск нужных объектов-потомков при помощи метода
child о. В первом параметре передается имя объекта, который нужно найти.
Во втором параметре передается имя класса, от которого был произведен
объект. Третий параметр — это булево значение, включающее (true) или
отключающее (false) режим рекурсивного поиска. Второй и третий
параметры не обязательны. Например, следующий вызов возвратит указатель на
pobj4:
pobjl->child("the first child of pobj2");
Для расширенного поиска существует метод queryListo, возвращающий
список указателей на объекты. Все параметры метода не обязательны, вызов
метода без параметров приведет к тому, что он вернет список указателей на
все объекты. В первом параметре этого метода передается имя класса, во
втором — имя объекта. Третий параметр управляет включением режима
регулярного выражения, по умолчанию он равен true. Четвертый параметр
Управляет режимом рекурсивного поиска и по умолчанию тоже равен true.
Следующий вызов возвратит список указателей на все объекты, имя
которых начинается с букв th:
QObjectList* plist = pobjl->(0, "th*");.
52
Часть I. Основы Qt
Свойства объектов
Каждый объект, произведенный от класса QObject или от унаследованного
от него класса, располагает структурой данных, называемой метаобъектной
информацией. В ней хранится информация о сигналах, слотах и о самом
классе. Например, вызов метода classNameo возвращает имя класса в
строке типа const char*.
Для получения информации о наследовании классов существует метод
inherits (const char*). Он возвращает значение true, если класс объекта
унаследован от указываемого в этом методе класса, либо false, если он сам
произведен от данного класса.
Метод isA(const char* psz) сравнивает передаваемое имя класса со своим
собственным и возвращает значение типа booi. Например, выражение:
if (obj->isA("MyClass")),
будет эквивалентно выражению:
if (!strcmp(obj->className(), "MyClass"))
В следующем примере вызов обоих методов вернет значение true:
QObject* pobj
pobj->inherits("QObject");
pobj->isA("QObject") ;
Для отладки программы полезен метод dumpobjectinfoo. Он показывает
информацию, относящуюся к объекту, такую как:
П имя объекта;
П класс, от которого был произведен объект;
П сигнально-слотные соединения;
П объект предка;
П объекты потомков.
Вся эта информация поступает в стандартный поток вывода stdout.
При отладке можно воспользоваться методом dumpObjectTreeo,
предназначенным для отображения объектов потомков в виде иерархии.
При помощи статического метода QObject: robjectTrees о можно получить
список на все объекты, находящиеся в памяти. Например, следующая
программа выведет на консоль имена всех объектов, созданных приложением.
const QObjectList* plist = QObject:tobjectTrees();
QObjectList it(*plist);
for(; it.current 0; ++it) {
cout « it.current()->name() « endl;
}
Глава 2. Философия объектной модели 53
Резюме
В этой главе мы узнали о сущности сигналов и слотов. Это достойная
альтернатива используемым на сегодняшний день средствам для реализации
связей между компонентами графического интерфейса пользователя,
позволяющая значительно повысить читабельность программного кода.
Сигналы и слоты могут быть соединены друг с другом, причем сигнал
может быть соединен с большим количеством слотов. Слот, в свою очередь,
тоже может быть соединен со многими сигналами. В случае, когда слот не
делает ничего, кроме отправки полученного сигнала дальше, то можно
вообще обойтись без него, для этого нужно просто соединить сигналы друг с
другом. Методы сигналов должны быть обозначены в определении класса
специальным словом signals, а слоты — slots. При этом слоты являются
обыкновенными методами C++ и в их определении могут находиться
модификаторы public, protected, private. Реализацию кода для сигналов берет
на себя МОС. Высылка сигнала производится при помощи ключевого слова
emit. Класс, содержащий сигналы и слоты, должен быть унаследован от
класса oobject или от класса, унаследованного от этого класса. Сигнально-
слотные соединения всегда можно удалить, воспользовавшись методом
disconnect о, но это нужно крайне редко, т. к. Qt автоматически
уничтожает при удалении объекта все его сигнально-слотные соединения.
Соединение объектов производится при помощи статического метода QObject::
connect().
QObject — класс, по сути, являющийся основным классом при
программировании с использованием Qt. Конструктор класса QObject имеет два
параметра, первый используется для создания объектных иерархий, а второй для
присвоения объекту имени. Свойства объектов важны, т. к. позволяют
получать информацию о классе и объекте, в процессе исполнения программы.
Все объекты класса QObject или унаследованных от него классов должны
создаваться динамически, оператором new, и об освобождении ресурсов
памяти программист может не беспокоиться.
Так как концепт сигналов и слотов, а так же информацию о
наследственности невозможно было реализовать средствами самого языка C++, был
создан специальный препроцессор, называемый МОС (метаобъектный
компилятор), задачей которого является создавать для заголовочных файлов
дополнительные ерр-файлы, подлежащие компиляции и присоединению их
объектного кода к исполняемому коду программы. Для того чтобы МОС
мог распознать классы, нуждающиеся в подобной переработке, этот класс
Должен содержать макрос qobject.
ГЛАВА 3
Работа с Qt
Чтобы узнать истину, нужно узнать причины.
Фрэнсис Бэкон
Прежде чем начать изучение и перейти к следующим главам, необходимо
установить библиотеку на ваш компьютер.
Установка Qt
Для работы нужна сама библиотека Qt и компилятор C++. Процесс
установки библиотеки зависит от целевой платформы.
Установка Qt в ОС Windows
Проще всего Qt устанавливается в ОС Windows. Для этого нужно просто
запустить программу инсталляции install.exe. Программа установки Qt
производит настройку некоторых параметров Visual Studio, делающих работу
более удобной, например расцветка синтаксиса ключевых слов Qt. Для
Microsoft Visual Studio .NET производится установка дополнительных
кнопок на панели инструментов.
В процессе установки программа сделает соответствующие изменения в
области переменных среды системы. Вы можете убедиться в этом, посмотрев
переменные среды path и qtdir — Start | Control Panel | System | Advanced |
Environment Variables (Пуск | Панель управления | Свойства системы |
Дополнительно | Переменные среды), которые должны включать в себя пути к
этой библиотеке.
Для компиляции демонстрационных программ перейдите в директорию
библиотеки /examples и запустите компилятор командой nmake.
Глава 3. Работа с Qt
55
С Примечание )
Для работы с Qt в ОС Windows не обязательно иметь Microsoft Visual C++. В
качестве альтернативы можно использовать компилятор C++ от Borland. Его
бесплатную версию вы можете загрузить со страницы http://www.borland.com
/products/downloads/.
Установка Qt в ОС Linux
Qt входит в поставку большинства вариантов ОС Linux. Но если у вас все-
таки появилась необходимость в установке, то нужно провести ее, назначив
себе права доступа суперпользователя (superuser). Для этого запустите shell и
дайте команду su (switch user ID — изменение идентификатора
пользователя):
su <password>
Теперь можно скопировать архив, содержащий исходные файлы библиотеки
в каталог /usr/lib (или в любой другой, удобный вам).
ср qt-xll-free-3.3.2.tar.gz /usr/lib
Перейдите в данный каталог при помощи команды:
cd /usr/lib
Распакуйте содержимое архива:
tar xzvf qt-xll-free-3.3.2.tar.gz
В результате будет создан каталог /qt-xll-free-3.3.2, содержащий весь
необходимый исходный код библиотеки.
Затем необходимо модифицировать profile либо login, в зависимости от
используемого дистрибутива, следующим образом:
QTDIR=/usr/lib/qt-xll-fгее-З.3.2
PATH=$QTDIR/bin:$РАТН
MANPATH=$QTDIR/doc/man:$MANPATH
LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
export QTDIR PATH MANPATH LD_LIBRARY_PATH
L Примечание ^
Если вы используете tcsh, то setenv должно предшествовать определению
QTDIR. Например: setenv QTDIR /usr/lib.
Чтобы сразу воспользоваться внесенными изменениями, дайте команду:
source [местонахождение] имя_профайла
56
Часть I. Основы Qt
После этого вы можете проверить переменную среды qtdir. Для этого дайте
команду echo $qtdir, и если все сделано правильно, то на консоли должен
появиться путь к Qt.
Вся дальнейшая установка базируется на программе configure, которая
находится в каталоге /qt-xll-free-3.3.2. Эта программа анализирует локальную
систему и определяет, имеется ли все необходимое для успешной
компиляции библиотеки. Программа конфигурации может быть запущена с
различными опциями, некоторые из которых перечислены в табл. 3.1. Для
получения полного списка опций просто запустите программу configure с
опцией -п.
Таблица 3.1. Некоторые опции конфигурационной программы configure
Опция
-release
-debug
-shared
-static
-no-gif
-qt-gif
-qt-zlib
-system-zlib
-Dstring
-Istring
-Lstring
-Rstring
-qt-sql-<driver>
-plugin-sql-<driver>
Назначение
Компиляция и компоновка с выключенным режимом
отладки. Эта опция определена по умолчанию
Компиляция и компоновка с включенным режимом
отладки
Создать библиотеку динамически (libqt.so). Эта опция
определена по умолчанию
Создать библиотеку статически (libqt.a)
Не предоставлять поддержку для чтения GIF. Эта опция
определена по умолчанию
Предоставить поддержку для чтения формата GIF
Использовать встроенную поддержку zip-архивов. Эта
опция определена по умолчанию
Использовать поддержку zip-архивов, предоставляемую
системой
Добавить для компиляции и компоновки директиву
препроцессора
Добавить путь к заголовочным файлам
Добавить путь к файлам библиотек
Добавить путь к файлам динамических библиотек
Задействовать поддержку SQL-драйвера. По умолчанию
ни один из драйверов не включен. Возможные значения
для <driver>: mysql, odbc и psql
Задействовать поддержку SQL-драйвера в качестве
системы расширения (plug-in), которая будет подключаться
при старте самой программы
Глава 3. Работа с Qt
57
Таблица 3.1 (окончание)
Опция
-no-sql-<driver>
-qt-style-<style>
-no-style-<style>
-no-thread
-thread
-no-stl
-stl
-h
-no-imgfmt-<format>
-no-nas-sound
-system-nas-sound
Назначение
Исключить внутреннюю поддержку SQL-драйвера
Сделать один из стилей стилем по умолчанию.
Возможные значения для <style>: cde, compact, motif, motifplus,
platinum, sgi и windows
Исключить внутреннюю поддержку одного из стилей
Создать библиотеку без поддержки многопоточности. Эта
опция определена по умолчанию
Создать библиотеку с поддержкой многопоточности
Откомпилировать без поддержки STL (Standard Template
Library, стандартная библиотека шаблонов)
Откомпилировать с поддержкой STL. Эта опция
определена по умолчанию
Напечатать информации о возможных опциях
Исключить внутреннюю поддержку одного из
графических форматов. Возможные значения для <format>: png,
jpeg и mng
Не создавать поддержку NAS для звука
Использовать NAS libaudio, предоставляется
операционной системой
Давайте установим Qt с поддержкой мультипоточности. Для этого дадим
следующую команду из каталога /usr/lib/qt-x l l-free-3.3.2.
/configure -thread
После окончания процесса конфигурации библиотеку необходимо
откомпилировать. Для этого запустите gmake. По завершении компиляции, можно
запустить из директории /examples некоторые демонстрационные
программы, иллюстрирующие функциональные возможности Qt.
Работа с qmake
"и один программист не будет каждый раз задавать для компиляции своей
программы параметры для компоновки и передавать пути к библиотекам
вручную". Гораздо удобнее создать make-файл (makefile), который возьмет
На себя всю работу по настройке компилятора и компоновщика.
58
Часть I. Основы Qt
Создание make-файлов вручную требует от их создателя опыта и понимания
протекающих процессов компоновки приложения. Раньше техника
создания подобных файлов являлась неотъемлемой частью программирования,
но теперь многое изменилось. И дело совсем не в том, что структура make-
файлов стала проще, скорее наоборот — она стала сложнее. Просто
появились специальные утилиты — генераторы, которые выполнят эту работу
за вас.
Предыдущие версии Qt содержали утилиту tmake, которая позволяла
довольно просто создавать make-файлы. Она изначально была реализована на
языке Perl, и это накладывало на нее дополнительные ограничения — для
использования необходимо было наличие интерпретатора языка Perl на
компьютере. Это, в свою очередь, снижало способность переносимости
данной утилиты. По этой причине и была создана новая утилита qmake,
которая вошла в поставку начиная с версии 3.0. Примечательно, что новая
утилита так же хорошо переносима, как и сам Qt. Утилита qmake полностью
освобождает программиста от использования tmake, перенимая все ее
возможности. Благодаря этому старые конфигурационные файлы для tmake
могут интерпретироваться ей без проблем. Таким образом, тем, кто уже
успел привыкнуть к tmake, вовсе не обязательно изучать новый синтаксис.
Программа qmake интерпретирует файлы проектов, которые имеют
расширение pro и содержат различные параметры. Создать из такого файла make-
файл совсем не трудно, для этого нужно просто дать команду
qmake -о Makefile file.pro
В qmake существует возможность создания файлов с расширением dsp
(Developer Studio Project), которые могут быть загружены в Visual Studio.
Это можно сделать при помощи команды:
qmake -о file.dsp -t vcapp file.pro
Опция -t является так называемой шаблонной опцией и заставляет qmake
использовать параметр vcapp, невзирая на то, что написано в самом файле
проекта. Шаблоны нужны для определения типа проекта. Например, при
создании make-файла для приложения нужно задать в файле проекта опцию
template = app, а при создании библиотеки template должен быть равен lib.
Значения vcapp и vclib имеют смысл только для работы в ОС Windows.
Допускается передавать опции в командной строке qmake:
qmake -о Makefile "TEMPLATE=vcapp" file.pro
Табл. 3.2 содержит некоторые опции файла проекта. Полный список опций
можно получить в официальной документации Qt, поставляемой вместе с
самой библиотекой.
Глава 3. Работа с Qt
59
Таблица 3.2. Некоторые опции для файла проекта
1 Опция
[headers
["sources
[forms
Tlexisources
[ YACCSOURCES
TARGET
CONFIG
DESTDIR
DEFINES
INCLUDEPATH
DEPENDPATH
DEF_FILE
RC_FILE
RES_FILE
Назначение
Передается список созданных заголовочных файлов
Передается список созданных файлов реализации (с расширением
срр)
Передается список файлов с расширением ui. Эти файлы
создаются программой QtDes gner и содержат описание интерфейса
пользователя в формате XML
Lex — это программа для синтаксического анализа. Используется
для написания компиляторов
Yacc (Yet another Compiler Compiler) — утилита для создания
компиляторов
Передается имя приложения. Если данное поле не заполнено, то
название программы будет соответствовать имени проектного
файла
Задает опции, которые должен использовать компилятор
Задает путь, куда будет помещен готовый исполняемый модуль
Здесь можно передать опции для компилятора. Например, это
может быть опция помещения отладочной информации для debuger в
исполняемый модуль
Путь к каталогу, где содержатся заголовочные файлы. Этой опцией
можно воспользоваться в случае, если уже есть готовые
заголовочные файлы и вы хотите использовать их в текущем проекте
В данном разделе указываются зависимости, необходимые для
компиляции
Файл определения модуля, указывает, является ли файл DLL-
библиотекой или ЕХЕ-файлом. Используется только для ОС
Windows
Файл ресурсов в ОС Windows. Содержит растровые изображения,
меню, диалоговые окна и др. Используется только для ОС Windows
Откомпилированный файл ресурсов (присоединяется к
исполняемому коду программы). Используется только для ОС Windows
Проектный файл Qt может выглядеть следующим образом:
TEMPLATE = арр
HEADERS += filel.h \
file2.h
SOURCES += main.cpp \
filel.cpp \
file2.cpp
60
Часть I. Основы Qt
TARGET = file
CONFIG += qt warn_on release
В первой строке задается тип программы, в данном случае это приложение
(template = app). Во второй строке, в опции headers, перечисляются все
заголовочные файлы, принадлежащие проекту. В опции sources
перечисляются все файлы реализации проекта. Опция target определяет имя
программы, config определяет опции, которые должен использовать компилятор в
соответствии с подсоединяемыми библиотеками. Например, в нашем случае:
П Qt указывает, что это Qt-приложение и используется библиотека Qt;
П wamon означает, что компилятор должен выдавать как можно больше
предупреждающих сообщений;
П release указывает, что приложение должно быть откомпилировано в
окончательном варианте, без отладочной информации.
Как видно из вышеприведенного примера, программе qmake не требуется
много информации, т. к. она опирается на файл локальной конфигурации,
который определен системной конфигурацией. Такой конфигурационный
файл очень важен еще потому, что один и тот же вызов qmake приведет к
созданию разных make-файлов, в зависимости от того, на какой платформе
он был вызван. Это один из очень важных шагов в сторону платформной
независимости самих проектных файлов.
Рекомендации для проекта
с использованием Qt
При реализации файлы классов лучше всего разбивать на две отдельные
части. Часть определения класса храните в заголовочном файле с
расширением h, а реализацию класса — в файле с расширением срр. Важно
помнить, чтобы в заголовочном файле с определением класса содержалась
директива препроцессора #ifndef. Смысл этой директивы состоит в том,
чтобы избежать конфликтов, если один и тот же заголовочный файл будет
включаться в исходные файлы более одного раза.
#ifndef _MyClass_h_
#define _MyClass_h_
class MyClass {
};
#endif //_MyClass_h_
Как правило, по традиции, заголовочный файл носит имя содержащегося
в нем класса. В целях более быстрой компиляции, в заголовочных файлах,
Глава 3. Работа с Qt
61
для указателей на типы данных используется предварительное объявление
для типа данных, а не прямое включение посредством директивы #inciude.
В начале определения класса содержится макрос qobject для МОС.
class MyClass : public QObject {
QjDBJECT
public:
MyClass();
};
Основная программа должна быть реализована в отдельном файле, который
является, как бы, "стартовой площадкой" приложения и общепринято
давать этому файлу имя main.cpp. Это удобно еще и потому, что проект может
состоять из сотен файлов и в этом случае найти отправную точку всего
проекта не составит труда.
Соблюдение этих требований может сослужить хорошую службу. Проектам
свойственно со временем расширяться и поэтому неплохо с самого начала
придерживаться определенной структуры, чтобы потом чувствовать себя в
своих и чужих проектах, придерживающихся Qt структуры, "как рыба в воде".
Метаобъектный компилятор МОС
Мешаобъектный компилятор (МОС — Meta Object Compiler). По сути дела,
этот инструмент является не компилятором, а препроцессором, который
исполняется в процессе компиляции приложения, создавая, в соответствии
с определением класса, дополнительный код на C++. Это происходит из-за
того, что определение сигналов и слотов в исходном коде программы
недостаточно для компиляции. Сигнально-слотный код должен быть
преобразован в код, понятный С++-компилятору. Код сохраняется в файле с
прототипом moc_<filename>.cpp.
'\ , Внимание!' ?j
Созданные moc-файлы не стоит включать с помощью команды препроцессора
#include "mocj^roccpp" в конец основного файла. Лучше, если они будут
отдельно откомпилированы и подсоединены компоновщиком к основной
программе.
Если вы работаете с файлами проекта, то о существовании МОС можете и
Не догадываться, ведь в этом случае управление МОС автоматизировано.
Для создания moc-файла "вручную" можно воспользоваться следующей
командой:
°с "О raoc_proc.cpp proc.h
После ее исполнения МОС создаст дополнительный файл moc_proc.cpp.
62
Часть I. Основы Qt
Для каждого класса, унаследованного от QObject, MOC предоставляет
объект класса, унаследованного от QMetaObject. Объект этого класса содержит
информацию о структуре объекта, например, сигнально-слотные
соединения, имя класса, структуру наследования.
Структура Qt проекта
Мы рассмотрели утилиты для Qt проекта по отдельности. Теперь давайте
соберем их вместе, чтобы понять их взаимодействие. Структура Qt проекта
очень проста, помимо файлов исходного кода на C++, обычно имеется
файл проекта с расширением pro. Из этого файла вызовом qmake создается
make-файл. Этот make-файл содержит в себе все необходимые инструкции
для создания готового исполняемого модуля (рис. 3.1).
Проектный файл
Исходные файлы
moc_file.obj jfl file.obj [
(Объектный код Г I
-Н LINK
Исполняемый модуль | file.exe |
Рис. 3.1. Схема создания исполняемого модуля
В make-файле содержится вызов МОС для создания дополнительного кода
C++ и необходимых заголовочных файлов. Все исходные файлы
компилируются компилятором C++ в файлы объектного кода, которые затем
объединяются компоновщиком link в готовый исполняемый модуль
Глава 3. Работа с Qt
63
Методы отладки
Благодаря платформонезависимости Qt, разработчик может для отладки
своих программ использовать любой из полюбившихся ему отладчиков,
например: ddd, gdb, а так же отладчик, встроенный в Microsoft Visual Studio.
Qt предоставляет также макросы и функции для отладки, при помощи
которых можно встраивать в саму программу различного рода проверки и
вывод тестовых сообщений.
В заголовочном файле qglobal.h содержатся определения двух макросов:
q assert о и q_check_ptr (). Макрос q_assert () принимает, в качестве
аргумента, значение булевого типа и выводит предупреждающее сообщение,
если это значение не равно true. Макрос qcheckptr () принимает указатель и
выводит предупреждающее сообщение, если переданный указатель равен 0.
Значение, равное 0, для указателя означает, что он либо не был
инициализирован, либо операция по выделению памяти прошла неудачно.
Qt предоставляет глобальные функции qDebugo, qwamingo и qFataio,
которые также определены в заголовочном файле qglobal.h. Их применение
похоже на функцию printf о. Как и в printf о, в эти функции передаются
форматированная строка и различные параметры. В Microsoft Visual Studio
вывод этих функций производится сразу в окно отладчика, а в ОС Linux —
в стандартный поток вывода ошибок.
( Примечание ^
В заголовочном файле qglobal.h определены глобальные функции debug О,
warning () и fatal (), которые полностью идентичны функциям qDebug (),
qWarning() И qFatal().
С Примечание j
Вызов функции qFataio после вывода сообщения сразу завершает работу
всего приложения.
Если вдруг потребуется перенаправить поток вывода сообщения, тогда
нужно создать и установить свою собственную функцию для управления
выводом. Устанавливается она при ПОМОЩИ функции qlnstallMsgHandler (). ЭТОЙ
Функции, в качестве аргумента, перелается адрес на функцию,
управляющую сообщениями и имеющую следующий прототип:
v°id(*)(QtMsgType type, const char *rasg);
Первый аргумент представляет собой тип сообщения, принимающего одно
Из Значений перечисления QtMsgType: QtDebugMsg, QtWarningMsg ИЛИ
^tFatalмsg. Второй аргумент — это указатель на само сообщение.
Для облегчения процесса отладки рекомендуется присваивать всем объектам
иМена. Таким образом, эти объекты можно будет всегда найти, вызвав ме-
64
Часть I. Основы Qt
тод паше о. Это позволит в процессе работы программы воспользоваться
методом dumpobjectinfoo класса QObject, который позволит отобразить
внутреннюю информацию объекта.
Для отладки можно так же воспользоваться установкой фильтра событий в
объект класса QAppiication, в этом случае данный фильтр будет являться
наипервейшим объектом, получающим и обрабатывающим события всех
объектов приложения (см. гл. 14).
Глобальные определения Qt
Qt содержит в заголовочном файле qglobal.h некоторые макросы и функции,
которые могут быть очень полезны при написании программ.
Макросы qmax(a, b) и QMiN(a, b) используются для определения
максимального и минимального из двух переданных значений.
п - QMAX(3, 5); // п = 5
Макрос QABS(a) возвращает абсолютное значение.
n = QABS(-5); // n = 5
Функция qRound () округляет передаваемое число до целого
int n = qRound(5.2); // n = б
int n = qRound(-5.2); // п = -6
В табл. 3.3 приведен список типов Qt, которые можно использовать при
программировании.
Таблица 3.3. Таблица целых типов Qt
Тип Qt
Q_INT8
QJJINT8
Q_INT16
Q_UINT16
Q_INT32
Q_UINT32
Q_INT64
Q_UINT64
С++-эквивалент
signed char
unsigned char
short
unsigned short
int
unsigned int
long
unsigned long
Размер (бит)
8
8
16
16
32
32
64
64
Глава 3. Работа с Qt
65
Использование Qt в Microsoft Visual Studio
В этой среде программирования для написания программ на C++
используется Microsoft Visual C++. Есть два варианта для использования Microsoft
Visual C++ при программировании с использованием Qt.
Первый самый быстрый. Суть его состоит в том, чтобы создать при помощи
утилиты qmake файл проекта с расширением dsp (Developer Studio Project),
который может быть сразу загружен в Microsoft Visual C++. Этот файл
создается из файла проекта Qt следующим образом:
qmake -о MyProject.dsp -t vcapp MyProject.pro
Суть второго способа состоит в том, чтобы создать в Microsoft Visual C++
проект, базирующийся на make-файле. Для этого создайте новый проект,
имеющий имя make-файла. Запустите Microsoft Visual C++ и выберите
команду меню File | New (Файл | Создать), затем выберите вкладку Projects
(Проекты) и выберите элемент Makefile (Make-файл) (рис. 3.2).
Proiects I Wort jaces j Olher scuments |
$Jwn32 Static Lbrary
U3 ATL COM AppWizard
ИЗ jster Resource Type Wizard
J53 custom AppWizard
R3 Database Pioiect
H^DevStudio Add in Wizard
Щ Extended Stored Proc Wizard
МИ ~4PI Extension Wizard
Ы Makefile
Ы; MFC ActiveX ControlWizard
У^"СС AppWizard (dH)
Щ С AppWizard (exe)
Пч New Database Wizard
hfj Utility Proiect
И Win32 Application
□Win32 Console Application
R) Win32 Dynamic Link Library
ВЕЗ
Prefect name.
im
(CAengineVTest
'J
"■ Cxeate new workspace
JLJ
DK |
Рис. З.2. Диалоговое окно New
Make-файл для этого проекта впоследствии будет создаваться с помощью
Программы qmake. Важно, чтобы этот файл имел расширение так.
Расцветка синтаксиса
*1ля программиста важна поддержка расцветки синтаксиса редактором. Это
способствует более удобному восприятию исходного кода программы. К со-
66
Часть I. Основы Qt
жалению, макросы Qt не являются ключевыми словами языка C++ и
поэтому редактором Microsoft Visual C++ они выделяться не будут. Это можно
исправить, но для этого нужно сообщить Microsoft Visual C++ о
дополнительных ключевых словах. Поместите в один каталог с msdev.exe
(исполняемый файл этого редактора) текстовый файл usertype.dat, в котором будут
перечислены ключевые слова Qt, в каждой строке по слову.
( Примечание )
Если вы используете версию Qt выше 2-й, то программа установки проделает
всю эту работу в процессе инсталляции сама.
Этот файл может выглядеть следующим образом (листинг 3.1).
Листинг 3.1. файл usertype.dat
Q_OBJECT
Q_PROPERTY
Q_ENUMS
emit
TRUE
FALSE
SIGNAL
SLOT
signals:
slots:
После этого нужно выбрать необходимый цвет для новых слов, лучше, если
он будет совпадать с цветом, определенным для ключевых слов. Для этого
Рис. 3.3. Диалоговое окно Options
Глава 3. Работа с Qt
67
выберите в Microsoft Visual C++ команду меню Tools | Options
(Инструменты | Опции), в появившемся диалоговом окне выберите закладку Format
(формат). После этого выберите в группе Colors (Цвета) элемент User
Defined Keywords (Слова, определенные пользователем) и установите
нужные цвета для Foreground (Буквы) и Background (Фон) (рис. 3.3).
резюме
В этой главе мы узнали — как установить Qt, какие опции используются
для тонкой настройки этой библиотеки в ОС Linux, как выглядит типичный
проект с использованием Qt и какие процессы протекают за кулисами
создания готового, исполняемого программного модуля. Познакомились с
утилитой qmake, берущей на себя всю работу по созданию make-файлов из
файлов проекта для любой платформы. Более подробно ознакомились с
препроцессором МОС, который при запуске создает для заголовочных
файлов дополнительный код поддержки сигналов и слотов. Рассмотрели
возможности для отладки программ Qt, а также специальные макросы,
предназначенные для этих целей. Провели небольшой экскурс в глобальные
определения Qt и рассмотрели возможности применения Microsoft Visual C++
для работы с проектами Qt.
ЧАСТЬ II
Элементы управления
Глава 4.
Глава 5.
Глава 6.
Глава 7.
Глава 8.
Глава 9.
Глава 10.
Глава 11.
Глава 12.
С чего начинаются элементы управления
Элементы отображения
Кнопки
Элементы управления для установки значений
Элементы выбора
Элементы ввода
Управление автоматическим размещением
элементов
Цветовая палитра для элементов управления
Элементы со стилем
ГЛАВА 4
С чего начинаются
элементы управления
Кто скажет, где кончается одно
и начинается другое.
Сунь-Цзы
Практически любая программа имеет интерфейс пользователя. Виджеты
(widgets) — это "строительный материал" для его создания. Виджет — это не
просто область, отображаемая на экране, а компонент, способный
выполнять различные действия, например, реагировать на поступающие сигналы
и события, посылать сигналы другим виджетам. Qt предоставляет полный
арсенал виджетов, от кнопок меню до окон диалога, необходимых для
создания профессиональных приложений. Если виджетов в Qt недостаточно, то
можно создать свои собственные, наследуя классы уже существующих
виджетов.
Иерархия, показанная на рис. 4.1, содержит классы виджетов. В этой части
находится описание большинства из них. Классы, не описанные в этой
части, МОЖНО НаЙТИ В ДРУГИХ Главах КНИГИ: QPopupMenu (СМ. гл. 29), QGLWidget
(СМ. гл. 22), QSizeGrip (СМ. гл. 32), QMainWindow (СМ. гл. 33), QMenuBar
(СМ. гл. 29), QCanvasView (СМ. гл. 20).
Класс QWidget
Класс QWidget является фундаментальным для всех классов виджетов. Он
содержит в себе массу определений, необходимых каждому из виджетов,
например, для изменения размеров, местоположения, обработки событий и
многого другого. Сам класс QWidget, как видно из рис. 4.1, унаследован от
QObject, а значит, может использовать механизм сигналов/слотов и
механизм объектной иерархии. Благодаря этому, виджеты могут иметь потомков,
которые отображаются внутри предка. Это очень важно, т. к. каждый
QObject
2 С
zz*
QPaintDevice ¥
Q Widget
^
T H QGroupBox L
QSpiitter
QHBox
Tr
] QVGroupBox If
—I QHGroupBox
QButtonGroup
QHButtonGroup l
l.'l' . . 4Д.ЦЦ L -...... ОД.Д]-Л<
QVButtonGroup h
QScrollView |;
QCanvasView }
\—\ QlconView b
H QUstBox
H QListView"
3
1
3
U
[—I QTextBrowser I;
I ' .д. - , „„..uj.^w-imv. Л
Li QTextView b
Рис. 4.1. Иерархия классов виджетов
72
Часть II. Элементы управления
виджет может служить контейнером для других виджетов, управляя их
размерами и расположением. Виджеты в контейнерах могут выступать в роли
контейнеров уже для других виджетов, и так до бесконечности. Например,
диалоговое окно содержит кнопки Ok и Cancel (Отмена), следовательно,
является контейнером. Это удобно в том случае, если виджет-предок станет
недоступным или невидимым, то тогда виджеты-потомки автоматически
примут его состояние.
Виджеты без предка называются виджетами верхнего уровня (top-level
widgets). Они имеют свое собственное окно. Все виджеты, без исключения,
могут быть виджетами верхнего уровня. Позиция виджетов-потомков внутри
виджета-предка может изменяться методом setGeometryO "вручную" или
автоматически, с помощью специальных классов размещений (layouts). Для
отображения виджета на экране вызывается метод show о, для скрытия —
метод hide ().
( Примечание )
Не забывайте, после создания виджета верхнего уровня, для того чтобы
показать его на экране, вызвать метод show (), иначе его окно и виджеты-потомки
будут невидимыми.
Класс cwidget и большинство унаследованных от него классов имеют
конструктор с тремя параметрами:
QWidget(QWidget* pwgt = 0, const char * pszName = 0, WFlags flags = 0);
Из конструктора класса QWidget видно, что не обязательно передавать в него
параметры, т. к. они равны нулю по умолчанию. А значит, созданный
виджет будет являться виджетом верхнего уровня и не будет иметь имени.
Третий параметр — тоже будет равен нулю. Он используется для задания
свойств окна, например, с его помощью можно изменить внешний вид
окна, установить режим, чтобы окно не перекрывалось другими окнами,
и т. д. Эти изменения не отражаются на виде окон других виджетов
верхнего уровня. Чтобы изменить внешний вид окна, необходимо в третьем
параметре конструктора передать значения, объединенные с Qt: :wstyie_Customize
побитовой операцией ИЛИ. Например:
QWidget wgt(0, "", Qt::WStyle_Customize | Qt::WStyle_Title |
Qt::WStyle_StaysOnTop) ;
( Примечание )
Значение Qt: :wstyle_staysOnTop не изменяет внешний вид окна, а управляет
тем, чтобы окно всегда находилось на переднем плане и не перекрывалось
другими окнами.
На рис. 4.2 изображены некоторые из вариантов применения этих
значений.
Глава 4. С чего начинаются элементы управления
73
i .^Style_NoimalBord«
РТТЯЯВШ! 1и1х|[
Qt.WStyle Title
Qt: WStyle.SysMenu
Qt WStyle_Maximize
Qt:wsiyie_Title
I М!ЯШд.|| jx||
Qt ' ■" le_Title
Qt b_S Menu
Qt:WSMe_Minimi«
I HJ.'.U.W.lDlxll
It/ >le_T'tle
С tyle SysMenu
Qt :WStyle_MinMax
r*TT!ffffggyfffWi i I л l[
Qt:' ~yle_Title
Qt. yle.SysMenu
Qt .WStyle.ContextHelp
кгвв&ш]
Q' WStyle_Title
Q " yle.SysMenu
Qt :WStyle_Tool
Рис. 4.2. Вид окон виджетов верхнего уровня
При помощи слота setcaptiono устанавливается надпись заголовка окна.
Но это имеет смысл только для виджетов верхнего уровня. Например:
wgt.setCaption("My Window");
Слот setEnabiedo устанавливает виджет в доступное (enabled) или
недоступное (disabled) состояние. Параметр true соответствует доступному, a false —
недоступному состоянию. Чтобы узнать, в каком состоянии находится
виджет, Надо вызвать метод isEnabled ().
При создании собственных классов виджетов важно, чтобы виджет был
в состоянии обрабатывать события. Например, для обработки событий
мыши необходимо перезаписать хотя бы один из следующих
методов: mousePressEvent(), mouseMoveEvent (), mouseReleaseEvent() ИЛИ
mouseDoubleClickEvent (). Также важно перезаписывать метод paintEvent (),
чтобы виджет мог перерисовывать область своего графического вывода,
например, в том случае, если эта область перекрыта другим окном. Более
подробно события мы рассмотрим в части III.
Размеры и координаты виджета
Виджет представляет собой прямоугольную область (рис. 4.3).
Рис. 4.3. Виджет в области экрана (или предка)
Существует целый ряд методов, с помощью которых можно узнать о месте
Рождения виджета и о размерах. Методы size о, height о и width о воз-
74
Часть II. Элементы управления
вращают размеры виджета. При этом если вызов height () и width () методов
вернут значения ширины и высоты целого типа, то вызов метода size о
вернет объект класса QSize (см. гл. 16), хранящий в себе сразу ширину и
высоту виджета.
Методы х() и у о и post) возвращают координаты виджета. Первые два
метода предоставляют целые значения координат по осям X и Y, а метод pos ()
возвращает объект класса QPoint (см. гл. 16), хранящий в себе обе
координаты сразу.
Метод geometry () возвращает объект класса QRect (см. гл. 16), описывающий
расположение и размеры виджета.
Расположение виджета изменяется методом move о, а его размеры —
методом resize о. Например:
pwgt->move (5, 5);
pwgt->resize(260, 330);
Изменить расположение и размеры виджета одновременно, можно вызвав
метод setGeomerty о. Первый параметр метода задает координату левого
верхнего угла виджета по оси X, второй по оси Y. Третий задает ширину, а
четвертый — высоту. Например, следующий вызов эквивалентен двум выше
приведенным вызовам move () и resize о:
pwgt->setGeometry(5, 5, 260, 330);
Установка фона виджета
Виджету можно задавать фон. Причем это может быть цвет или растровое
изображение. Для заполнения фона сплошным цветом вызывается метод
setPaietteBackgroundCoior (), а для установки растрового изображения —
метод setBackgroundPixmap (). В примере, показанном на рис. 4.4,
изображены два виджета. Фон виджета большого размера заполнен сплошным
цветом. Фон виджета меньшего размера заполнен растровым изображением.
I Листинг 4.1. Файл main.cpp
#include <qapplication.h>
#include <qwidget.h>
#include <qpixmap.h>
//
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QWidget wgtl;
Глава 4. С чего начинаются элементы управления
75
wgtl.setPaletteBackgroundColor(Qt::blue);
pwgt2->setBackgroundPixmap(QPixmap("stone.jpg"));
pwgt2->resize(100, 100);
pwgt2->move(50, 50);
wgtl.resize(200, 200);
wgtl.show();
app.setMainWidget(&wgtl);
return app.execO;
}
[Non-Commercial] - - HCHE3
A *•
Рис. 4.4. Два виджета с фоном
В листинге 4.1 создается виджет верхнего уровня wgtl. Он передается
второму виджету (указатель pwgt2) в качестве виджета-предка. Виджету-предку,
вызовом метода setPaietteBackgroundCoior (), устанавливаем сплошной цвет
фона (голубой). Это позволяет отображать виджет-потомок (указатель pwgt2)
в области виджета-предка wgtl. В виджете-потомке, вызовом метода
setBackgroundPixmap (), устанавливаем фоном растровое изображение из
файла stone.jpg. Размеры и расположение виджетов изменяются с помощью
Методов resize () И move ().
Внимание"! j
В Qt для установки цвета фона существует метод setBackgroundColor (), но
его лучше не использовать, он помечен в документации как устаревший
(obsolete). Это говорит о том, что он может быть исключен из последующих
версий библиотеки.
76 Часть II. Элементы управления
Установка курсора мыши
Класс курсора мыши QCursor определен в файле qcursor.h. Курсор мыши
представляет собой небольшую растровую картинку, информирующую
пользователя о позиции указателя мыши на экране. В зависимости от места
положения внешний вид указателя может изменяться. В большинстве
случаев это стрелка, но если она попадает на границу окна, то может
превратиться в двуглавую стрелку, информируя пользователя, что размеры окна
могут быть изменены.
Установить изображение курсора можно вызовом метода setcursoro,
передав ему одно из значений, указанных в табл. 4.1.
Таблица 4.1. Значения CursorShape класса Qt
Значение
ArrowCursor
UpArrowCursor
CrossCursor
WaitCursor
IbeamCursor
PointingHandCursor
ForbiddeCursor
WhatsThisCursor
Вид
•fc
T
+
I
i
ь
®
4f
Значение
SizeVerCursor
SizeHorCursor
SizeBDiagCursor
SizeFDiagCursor
SizeAllCursor
SplitVCursor
SplitHCursor
BlanckCursor
Вид
I
«—►
г
\
*
_t_
T"
Ф
Пустой курсор
( Примечание j
Вызов статического метода QApplication:: setOverrideCursor ()
устанавливает изображение курсора для всего приложения. Это может понадобиться,
например, для информирования пользователя о том, что приложение выполняет
интенсивную, продолжительную по времени операцию и не в состоянии
реагировать на команды. В этот момент времени все виджеты должны отображать
указатель мыши в виде песочных часов, для этого вызывается метод
QApplicatin:: setOverrideCursor (WaitCursor). После того как приложение
снова в состоянии выполнять команды пользователя, вызовом статического
метода QApplication:: restoreOverrideCursor О указателю мыши
возвращается его прежний вид.
Глава 4. С чего начинаются элементы управления
77
В классе QCursor содержится метод pos(), который возвращает текущую
позицию указателя мыши относительно левого верхнего угла экрана. При по-
моши метода setPosO можно перемещать указатель мыши.
Чтобы создать собственное изображение курсора мыши, нужны два
растровых изображения типа QBitmap. Эти изображения должны иметь одинаковые
размеры, а одно из них будет представлять собой битовую маску. В местах,
на которых маска будет иметь цвет color 1, будет нарисовано само
изображение указателя, а в местах, где маска будет иметь цвет coioro, —
изображение будет прозрачно.
Листинг 4.2. Создание изображения указателя мыши
QBitmap pix(32, 32, рс, true);
QBitmap pixMask(32, 32, pcMask, true);
QCursor cur(pix, pixMask);
pwgt = new QWidget(this);
pwgt->setCursor(cur);
В листинге 4.2 создаются объекты растровых изображений (маска pixMask и
изображение pix). В их конструкторы первыми двумя параметрами
передаются ширина и высота, равные 32 пикселам. В третий параметр передаются
указатели на данные изображения. Объекты растровых изображений
передаются, для создания курсора мыши, в конструктор класса QCursor. После
этого создается виджет (указатель pwgt), которому с помощью метода
setcursoro присваивается созданный указатель мыши.
Рамки
Класс QFrame унаследован от класса QWidget и расширяет его возможностью
отображения рамки. Этот класс является базовым для большого числа
классов виджетов (см. рис. 4.1), один из них — уже знакомый нам класс QLabei.
Стиль рамки может быть разнообразный. Устанавливается он вызовом
метода setFramestyle (), принимая значения флагов формы и флагов теней
рамки. Значения соединяются друг с другом побитовой операцией ИЛИ.
Существуют Три флага теней— QFrame: : Raised, QFrame:: Plain И QFrame::
Sunken (табл. 4.2). С их помощью достигается эффект вогнутости или
выпуклости рамки.
Для задания внешнего вида рамки можно воспользоваться одной из пяти
ОСНОВНЫХ форм (табл. 4.2): QFrame: :Box, QFrame: : Panel, QFrame: :WinPanel,
QFrame:: HLine И QFrame::VLine. ЕСЛИ нужно, чтобы рамка вообще не OTO-
"Ражалась, В метод setFramestyle () передается значение QFrame: :NoFrame.
78
Часть II. Элементы управления
Таблица 4.2. Примеры рамок
Флаги
Box I Plain
Box I Raised
Box I Sunken
Panel 1 Raised
Panel I Sunken
WinPanel | Plain
WinPanel | Raised
Вид
U
LI
D
J
ГЦ
□
J
Флаги
WinPanel | Sunken
HLine | Plain
HLine I Raised
HLine | Sunken
VLine I Plain
VLine I Raised
VLine 1 Sunken
Вид
Г
—
L
—
1
'»
[
Методом setMarginO устанавливается дистанция от рамки до содержимого
виджета, а методами setLineWidth () и setMidLinewidth () МОЖНО изменять
толщину самой рамки.
; Листинг 4.3. Создание виджета с рамкой
QLabel plbl = new QLabel(this);
plbl->setFrameStyle(QFrame::Box I QFrame::Sunken);
plbl->setLineWidth(3) ;
В листинге 4.3 создается виджет надписи, для которого вызовом метода
setFramestyie () устанавливается нужный стиль рамки, а вызовом метода
setLineWidth о — ее толщина.
Стек виджетов
Класс QWidgetstack унаследован от класса QFrame и представляет собой
виджет, который показывает в отдельно взятый промежуток времени только
одного из своих потомков.
Виджеты добавляются в стек при помощи метода addwidgeto. Он
принимает указатель на виджеты и возвращает присвоенный виджету
идентификационный номер. Удаление виджетов из стека производится вызовом метода
removewidget (), в который передается указатель на виджет.
Глава 4. С чего начинаются элементы управления
79
Передавая методу raisewidget () указатель виджета или его
идентификационный номер сразу происходит его отображение. Идентификационный
номер виджета можно узнать вызовом метода id о. В этот метод передается
указатель на виджет.
Виджет прокрутки
Виджет прокрутки QScroiiview унаследован от класса QFrame и
предоставляет собой окно для просмотра только части информации. Этот виджет может
размещать виджеты потомков, а если размеры одного из них превысят
размеры окна просмотра, то автоматически появляются вертикальная и/или
горизонтальная полосы прокрутки. С их помощью можно перемещать части
виджета в видимую область. Добавить виджеты-потомки можно при
помощи метода addchiido, передав в него указатель виджета. В этом методе
можно указать так же и координаты местоположения виджета, по
умолчанию они равны нулю, что соответствует левому верхнему углу. В
дальнейшем, местоположение виджета внутри QScroiiview можно изменить при
помощи метода moveChiido. Виджет можно удалить из QScroiiview методом
removeChildO. Размещенные виджеты можно делать видимыми, с помощью
метода showChildO, И невидимыми, С ПОМОЩЬЮ метода hideChildO.
..-.J. Внимание j
Несмотря на то, что каждый виджет имеет собственные методы для
перемещения (move()), показа (show()) и скрытия (hideО), их взаимодействие внутри
QScroiiview может оказаться некорректным. Поэтому следует применять
методы QScroiiview:imoveChiId(), QScroiiview::showChiId() и QScroiiview::
hideChild().
Виджет прокрутки является совокупностью сразу нескольких виджетов,
работающих вместе. Указатели на эти виджеты можно получить методами,
Показанными на рис. 4.5. Методы verticalScrollBar() И horizontalScrollBarO
возвращают указатели на виджеты вертикальной и горизонтальной полосы
Прокрутки (QScrollBar). Методы viewport О И cornerWidget () ВОЗВра-
Щают указатели на объект класса Qwidget. Методы contentswidth () и
contentsHeight () возвращают ширину и длину содержащегося для
просмотра объекта.
Применение прокрутки иллюстрирует программа, показанная на рис. 4.6,
позволяющая перемещать части изображения в видимую область окна.
[Листинг 4.4. Файл main.cpp
♦include <qapplication.h>
♦include <qscrollview.h>
♦include <qpixmap.h>
80
Часть II. Элементы управления
// _
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QScrollView sv;
QWidget* pwgt = new QWidget(&sv);
QPixmap pix("maja.jpg");
pwgt->resize(pix.width(), pix.height());
pwgt->setBackgroundPixmap(pix);
sv.addChiId(pwgt);
sv.resize(400, 150);
sv.showO ;
app.setMainWidget (&sv);
return app.exec();
}
contentsWidthQ
> / '] о О r> ^ f '-" viewportO
5°ф г> verticalScrollBar()
|Ы ,\ 1|-1. comerWidgetQ
т
norizontalScrollBarQ
contentsHeightQ
Рис. 4.5. Структура виджета прокрутки
В листинге 4.4 создается виджет прокрутки sv. Затем создается обычный
виджет (указатель pwgt) и объект растрового изображения pix. Объект
растрового изображения инициализируется файлом maja.jpg. Размеры виджета
(указатель pwgt) приводятся в соответствие с размерами растрового
изображения вызовом метода resize о. Растровое изображение устанавливается в
Глава 4. С чего начинаются элементы управления
81
виджете методом установки фона setBackgroundPixmap (). Затем виджет
видовой прокрутки sv добавляет в свое окно виджет, имеющий фон
растрового изображения (указатель pwgt).
[Non-Commercial]-ScrollView HDD ||
I
Рис. 4.6. Пример виджета видовой прокрутки
В классе QScroiiview определены пять сигналов:
О contentsMoving(int x, int у) — высылается при перемещении
содержимого, но до того, как содержимое переместится на свою новую позицию;
О horizontalsiiderPressedO — высылается при нажатии пользователем на
горизонтальной полосе прокрутки;
О horizontalSliderReleasedO — высылается при отпускании пользователем
горизонтальной полосы прокрутки;
□ verticalSliderPressedO — высылается при нажатии пользователем на
вертикальной полосе прокрутки;
□ verticalSliderReleasedO — высылается при отпускании пользователем
вертикальной полосы прокрутки.
Резюме
В центре создания пользовательского интерфейса стоит понятие виджет
(элемент управления). QWidget является базовым для всех элементов
управления. Все, из чего состоит приложение Qt, — это объекты класса QWidget и
унаследованных от него классов.
В виджетах нет разделения между виджетами-контейнерами и просто
виджетами. Любой виджет может использоваться в качестве контейнера для
Других виджетов. К основным операциям с виджетами, помимо показа
(show) и скрытия (hide), относятся методы, позволяющие изменять их
размеры и расположение.
Виджеты верхнего уровня имеют свое собственное окно, которое можно
Декорировать, например, изменять рамку окна.
82
Часть II. Элементы управления
В каждом виджете можно устанавливать указатели мыши. Qt предоставляет
ряд предопределенных изображений, которые можно использовать для
установки. Также предоставляется возможность создавать свои собственные
указатели при помощи растровых изображений.
Класс QFrame унаследован от QWidget и представляет собой прямоугольник
с рамкой.
Класс QWidgetstack показывает в отдельно взятый промежуток времени
только одного из потомков. Этим свойством пользуются тогда, когда есть
много виджетов, и нужно чтобы только один из них был видимым в
определенное время.
Виджет прокрутки предоставляет окно для просмотра части информации.
Этот виджет применяется для отображения информации, размеры которой
превышают выделенную для нее область.
ГЛАВА 5
Элементы отображения
Видеть — значит верить, но только чувства
отражают истину.
Томас Фуллер
Элементы отображения не принимают активного участия в действиях
пользователя, они используются только для информирования его о
происходящем. Эта информация может носить как текстовый, так и графический
характер (картинки, фафики). Все элементы отображения унаследованы от
Класса QFrame.
Надписи
Виджет надписи используется для информирования о состоянии
приложения или отображения поясняющего текста. Он представляет собой
текстовое поле, текст в котором не подлежит изменению со стороны пользователя.
Информация, отображаемая этим виджетом, может изменяться только
самим приложением. Например, приложение может сообщить пользователю
об изменении своего состояния, но пользователь не может изменять эту
информацию, непосредственно в самом виджете. Класс виджета надписи —
QLabel, определен в заголовочном файле qlabel.h.
Виджет надписи унаследован от класса QFrame и может иметь рамку.
Отображаемая им информация может быть текстового, фафического или
анимационного характера. Для ее передачи используются методы setTexto,
setPixmapO И setMovie().
Расположением текста можно управлять при помощи метода set Alignment о.
Метод использует большое количество флагов, некоторые из которых
указаны в табл. 5.1. Обратите внимание на то, что значения не пересекаются и
это позволяет комбинировать их друг с другом с помощью логической
операции | (ИЛИ). Наглядным примером служит значение AlignCenter, состав-
Ленное ИЗ значений AlignVCenter И AlignHCenter.
84
Часть II. Элементы управления
Таблица 5.1. Значения флагов Alignment Flags класса Qt
Константа
AlignAuto
AlignLeft
AlignRight
AlignHCenter
AlignJustifу
AlignTop
AlignBottom
AlignVCenter
AlignCenter
Значение
0x0000
0x0001
0x0002
0x0004
0x0008
0x0010
0x0020
0x0040
AlignVCenter | AlignHCenter
Описание
Расположение текста слева.
Для Арабских Эмиратов —
справа
Расположение текста слева
Расположение текста справа
Центровка текста по горизонтали
Растягивание текста по всей
ширине
Расположение текста вверху
Расположение текста внизу
Центровка текста по вертикали
Центровка теста по вертикали и
горизонтали
Как видно из рис. 5.1, виджет надписи может отображать не только
обычный текст, но и текстовую информацию в формате RTF (RichText Format)
(в дальнейшем — RichText). Виджет надписи автоматически распознает ее.
Сам формат представляет собой подмножество языка HTML (см.
приложение 1).
гтс
■mm
QLabel
l.
2
3
Bold
21
One
Two
Three
|H|H|i ||— I il I
- Richtext Demo j
List
Font Style
Italic,' 1гИог1'п
22
Table
23 24
Рис. 5.1. Отображение информации виджетом надписи в формате RichText
Глава 5. Элементы отображения
85
Листинг 5.1. Файл main.cpp
#include <qapplication.h>
#include <qlabel.h>
tt
int main (int argc, char** argv)
{
QApplication app(argc, argv);
QLabel lbl("<Qt>"
"<Hl><CENTER>QLabel - Richtext Demo</CENTERX/Hl>"
"<H2><CENTER>List</CENTERXH2>"
"<OL><LI>One</LI><LI>Two</LI><LI>Three</LI></OL>"
" <H2 ><CENTER>Font Style< /CENTERXH2 > "
"<FONT COLOR=RED>"
<B>Bold</B>, <I>Italic</I>, <U>Underline</U>"
"</FONT>"
"<H2><CENTER>Table</CENTERX/H2>"
"<TABLE>"
<TR BGCOLOR=#f00000>"
<TD>1,1</TDXTD>1,2</TDXTD>l, 3</TDXTD>l, 4</TD>"
</TR>"
<TR BGCOLOR=YELLOW>"
<TD>2,1</TDXTD>2,2</TDXTD>2, 3</TDxTD>2, 4</TD>"
</TR>"
<TR BGCOLOR=#00f000>"
<TD>3,1</TDXTD>3, 2</TDXTD>3, 3</TDXTD>3, 4</TD>"
</TR>"
"</TABLE>"
"</Qt>",
1;
app.setMainWidget (&1Ы);
1Ы. show () ;
return app.exec();
В листинге 5.1 при создании виджета надписи 1Ы в первом параметре
конструктора передается текст, находящийся в формате RichText. Его можно
предать и после создания этого виджета, при помощи метода setTexto.
второй параметр конструктора равен 0, что делает его виджетом верхнего
Уровня, т. е. виджетом, имеющим свое собственное окно.
86
Часть II. Элементы управления
Следующий пример, показанный на рис. 5.2, демонстрирует возможность
отображения виджетом надписи, информации графического характера
[Non-Commercial]-... _ ОБЗ
Рис. 5.2. Отображение графической информации виджетом надписи
Листинг 5.2. Файл main.cpp
#include <qapplication.h>
#include <qpixmap.h>
#include <qlabel.h>
// _
int main (int argc, char** argv)
{
QApplication app(argc, argv);
QPixmap pix;
pix.load("mira.jpg");
QLabel lbl(O);
lbl.resize(pix.size());
lbl.setPixmap(pix);
app. setMainWidget (&1Ы) ;
lbl.show();
return app.exec();
}
Как видно из листинга 5.2, сначала создается объект растрового
изображения QPixmap. После этого, вызовом метода load (), в него загружается файл
Глава 5. Элементы отображения
87
mirajpg- Следующим шагом является создание самого виджета надписи —
объект 1Ы класса QLabei. Затем, вызовом метода resize о, его размеры
приводятся в соответствие с размерами растрового изображения. И,
наконец, вызов метода setPixmapO устанавливает в виджете само растровое
изображение.
При помощи метода setBuddyO виджет надписи может ассоциироваться с
любым другим виджетом. Если текст надписи содержит знак &, то символ,
перед которым он стоит, будет подчеркнутым. При совместном нажатии
этого символа с клавишей <Alt> фокус перейдет к виджету, установленному
методом setBuddyO. Пример, показанный на рис. 5.3, демонстрирует это.
II HWIi.liWM-K'M|
р£
&де: ]
In d|
Рис. 5.3. Использование метода setBuddy ()
[Листинг 5.3. Файл main.cpp
£L.,... ..
#include <qapplication.h>
#include <qvbox.h>
#include <qlabel.h>
#include <qlineedit.h>
tinclude <qspinbox.h>
//
int main (int argc, char** argv)
{
QApplication app(argc, argv);
QVBox vbx;
QLabei* plblName = new QLabei("&Name:", &vbx);
plblName->setBuddy(new QLineEdit(&vbx));
QLabei* plblAge = new QLabei("&Age:", &vbx);
plblAge->setBuddy(new QSpinBox(l, 140, 1, &vbx));
app.setMainWidget (&vbx);
vbx.show();
return app.exec();
>
Часть II. Элементы управления
В листинге 5.3 виджет vbx, произведенный от класса qvbox, является видже-
том верхнего уровня, т. к. его конструктор по умолчанию присваивает
указателю на виджет предка значение 0. Этот виджет обладает способностью
размещать виджеты потомков вертикально. Первый виджет-потомок в
программе — это виджет надписи Name (Имя), в его тексте символ N
определен как символ быстрого доступа. Вторым потомком является виджет
создаваемого однострочного текстового поля. Далее, вызов метода setBuddyo
связывает передаваемый в нем указатель на виджет однострочного
текстового поля с виджетом надписи. Создание виджета надписи Age (Возраст) и
последующих — аналогичны. В конструкторе создаваемого виджета
счетчика устанавливается возрастной диапазон от 1 до 140 лет и шаг, равный 1.
В виджетах есть возможность обработки событий клавиатуры и мыши. Этим
можно воспользоваться, например, для создания гипертекстовой ссылки,
которая при нажатии вызовет определенную страницу. Подробную
информацию о событиях можно получить в части III.
Индикатор прогресса выполнения
Индикатор прогресса — это виджет, показывающий процесс выполнения
операции и заполняющийся, по мере ее выполнения, слева направо.
Полное заполнение индикатора информирует о завершении операции. Этот
виджет необходим в том случае, когда программа выполняет
продолжительные действия. Он дает пользователю понять, что программа не зависла, а
работает. Также он показывает, сколько уже проделано работы и сколько
еще предстоит сделать. Класс QProgressBar виджета индикатора прогресса
определен в заголовочном файле qprogressbar.h.
Следующий пример, приведенный на рис. 5.4, демонстрирует
использование индикатора прогресса. При нажатии кнопки Step (Шаг) производится
увеличение значения индикатора на один шаг. Нажатие кнопки Reset
(Сбросить) сбрасывает значение индикатора.
[Non-Commercial] - Progress ВО ЕЗ
■ ■■■■■И 4 .--..,, Beset ||
Рис. 5.4. Пример индикатора прогресса
Листинг 5.4. Файл main.cpp
#include <qapplication.h>
#include "Progress.h"
Глава 5. Элементы отображения
89
И
int main (int argc, char** argv)
{
QApplication appfargc, argv);
Progress progress;
app.setMainWidget(&progress);
progress.show();
return app.exec();
}
В основной программе, показанной в листинге 5.4, создается виджет
индикатора прогресса (progress) класса Progress.
[Листинг 5.5. Файл Progress.h
#ifndef _Progress_h_
♦define _Progress_h_
♦include <qhbox.h>
class QProgressBar;
// ===^======^=^==^=================================================
class Progress : public QHBox {
Q_OBJECT
private:
QProgressBar* m_j?prb;
int m_nStep;
public:
Progress(QWidget* pobj = 0, const char* pszName = 0);
Public slots:
void slotStep ();
void slotResetO;
);
*endif //_Progress_h_
» листинге 5.5 приведен файл Progress.h, содержащий определение класса
Progress, который наследуется от qhbox и является виджетом-контейнером
°Яя размещения виджетов-потомков горизонтально, слева направо. Класс
с°Держит две переменные. Первая — указатель на виджет индикатора
провеса, а вторая хранит целое число — номер шага. В классе определены два
90
Часть II. Элементы управления
слота: siotstepo и siotReset (). Первый — для наращивания шага на
единицу, а второй — для установки индикатора прогресса в нулевое положение.
| Листинг 5.6. Файл Progress.cpp
#include <qpushbutton.h>
#include <qprogressbar.h>
#include "Progress.h"
//
Progress::Progress(QWidget* pwgt/*= 0*/, const char* pszName/*= 0*/)
: QHBox(pwgt, ps zName)
, m_nStep(0)
{
mjpprb = new QProgressBar(10, this);
m_j?prb->setFixedWidth(200) ;
m_j?prb->setIndicatorFollowsStyle(false);
QPushButton* pcmdStep = new QPushButton("&Step", this);
QPushButton* pcmdReset = new QPushButton("&Reset", this);
QObject::connect (pcmdStep, SIGNAL(clickedO ), SLOT(slotstep())) ;
QObject::connect(pcmdReset, SIGNAL(clickedO), SLOT(siotReset()));
}
//
void Progress::slotStep()
{
m_j?prb->setProgress (++m_nStep) ;
)
//
void Progress::siotReset()
{
m_nStep = 0;
m_j?prb->reset () ;
}
В конструкторе класса (листинг 5.6) переменной m_nstep присваивается
значение 0. После создания объекта индикатора mjpprb и задания количества
шагов, равного 10, происходит фиксация его длины вызовом метода
setFixedSize(). Вызов метода setlndicatorFollowsStyleO С параметром
false переводит индикатор в режим отображения процентов в центре. Затем
создаются две кнопки: Step (Шаг) и Reset (Сбросить), которые соединяются
со слотами siotstepo и siotReset о. В слоте siotstepo увеличивается зна-
Глава 5. Элементы отображения
91
чение переменной mnstep на 1, и передается в слот объекта индикатора
прогресса. Слот siotReseto устанавливает переменную m_nstep равной 0 и,
вызвав слот reset о, возвращает индикатор на исходную позицию.
Электронный индикатор
Класс QLCDNumber виджета электронного индикатора определен в
заголовочном файле qlcdnumber.h. Этот виджет представляет собой набор сегментных
указателей, например, как на электронных часах. С его помощью
отображаются целые числа. Допускается использование точки, позицию которой
МОЖНО задавать между ПОЗИЦИЯМИ сегментов методом setSmallDecimalPointO,
передав ему true или false. Количество отображаемых сегментов можно
задать в конструкторе или с помощью метода setNumDigits (). В том случае,
когда для отображения числа не хватает сегментов индикатора,
производится высылка сигнала overflow ().
По умолчанию стиль электронного индикатора соответствует стилю outline,
но его можно изменять, передав в метод setsegmentstyie () одно из
следующих значений: QLCDNumber::Outline, QLCDNumber::Filled ИЛИ QLCDNumber::
Flat. В табл. 5.2 приводится внешний вид виджета для каждого из
перечисленных стилей.
Таблица 5.2. Стили электронного индикатора
Константа
Outline
Flat
Filled
Вид
1 Ш
1 ЗТ
Электронный индикатор можно включать в режиме отображения двоичной,
восьмеричной, десятеричной или шестнадцатеричной систем исчисления.
Режим отображения изменяется с помощью метода setModeO, в который
передается одно из следующих значений: QLCDNumber::Bin (двоичная СИСТе-
^Х QLCDNumber::Oct (ВОСЬМеричная), QLCDNumber: :Dec (десятичная) ИЛИ
QLCDNumber::Hex (шестнадцатеричная). Также, для смены режима
отображения МОЖНО Воспользоваться слотами setBinMode (), setOctMode (), setDecMode ()
и setHexModeO соответственно. Пример, показанный на рис. 5.5,
демонстрирует электронный индикатор, отображающий шестнадцатеричные
значения.
92
Часть II. Элементы управления
I 1Ш,Н,ШШ«.1мЫ|
! II |
Рис. 5.5. Пример электронного индикатора
1 Листинг 5.7. Файл main.cpp
#include <qapplication.h>
#include <qlcdnumber.h>
#include <qspinbox.h>
#include <qvbox.h>
//
int main (int argc, char** argv)
{
QApplication app(argc, argv);
QVBox vbx;
QLCDNumber* pled = new QLCDNumber(&vbx);
QSpinBox* pspb = new QSpinBox(Svbx);
plcd->setSegmentStyle (QLCDNumber: .-Filled) ;
plcd->setMode(QLCDNumber::Hex);
pspb->setFixedHeight(30) ;
QObj ect::connect(pspb, SIGNAL(valueChanged(int)),
pled, SLOT(display(int))
>;
app.setMainWidget (&vbx);
vbx.resize(200, 100);
vbx.show();
return app.exec();
}
В листинге 5.7 создается виджет электронного индикатора — указатель pied»
а также виджет счетчика — указатель pspb. Затем, вызовом методе
setsegmentstyie о, изменяется стиль сегментных указателей. Электронны*1
индикатор, вызовом метода setModeO и передачей значения QLCDNumber::
Hex, включается в режим шестнадцатеричного отображения. У элемента
Гпава 5. Элементы отображения 93
счетчика, методом setFixedHeight (), устанавливается неизменная высота,
равная 30. После этого, сигнал vaiuechanged () виджета счетчика
соединяется со слотом display () электронного индикатора.
резюме
В этой главе мы познакомились с элементами отображения. Виджет
надписи содержит информацию, предназначенную только для просмотра и не
подлежащую изменению со стороны пользователя. Этот виджет способен
отображать не только простой текст, но и текст в формате RTF (RichText
Format). Кроме текстовой, может отображаться и графическая информация.
Виджет индикатора прогресса — прекрасный элемент для информирования
пользователя о процессе работы приложения. Этот виджет незаменим для
иллюстрации продолжительных операций.
Виджет электронного индикатора по внешнему виду представляет собой
набор сегментных указателей, как на электронных часах. Стиль виджета
можно изменять и использовать его в различных режимах отображения
чисел—в двоичной, десятеричной, шестнадцатеричной и восьмеричной
системах исчисления.
ГЛАВА 6
Кнопки
Нажми на кнопку, получишь результат,
и твоя мечта осуществится.
Группа "Технология"
Кнопки — один из важнейших и часто встречаемых виджетов интерфейса
пользователя. Даже если программистом в коде программы не создается ни
одной, то все равно, в правом верхнем углу присутствуют кнопки,
управляющие окном приложения. Кнопка может быть нажата (on) или не
нажата (of!).
С чего начинаются кнопки. Класс QButton
Класс QButton является базовым для всех кнопок. В этом классе
реализованы методы и возможности, присущие всем кнопкам, а именно:
П setTexto и setPixmapo — задание текста надписи или растрового
изображения;
П pixmapo и texto — получение изображения или текста надписи;
П pressedo, released о и clicked о — отправление сигналов при нажатии,
отпускании и щелчке;
П group о — получение указателя на группу, в которой размещена кнопка.
В приложениях применяется три основных вида кнопок — кнопки нажатия,
флажки и переключатели.
Кнопка нажатия
Этот виджет можно встретить в любом приложении, например — кнопки
Ok или Cancel (Отмена), без которых не может обойтись ни одно
диалоговое окно. Иногда такой виджет называется командная кнопка и представляет
Глава6-
собой прямоугольный элемент управления и используется, как правило, для
выполнения определенной операции при нажатии на нее. Класс QPushButton
виджета кнопки нажатия определен в заголовочном файле qpushbutton.h.
Кнопки нажатия могут содержать текст, который можно передать в
конструкторе, в качестве самого первого параметра, или установить посредством
метода setTexto. При помощи метода setpixmapo можно установить
растровое изображение на кнопке.
Создать кнопку нажатия можно следующим образом:
QPushButton* pcmd = new QPushButton("My Button", this, "Button");
Первый параметр задает надпись кнопки, второй — указатель на виджет-
предок, а третий задает виджету имя, которое можно получить вызовом
метода name(). Можно воспользоваться и более простой формой конструктора:
QPushButton(QWidget* pwdg, const char* psz = 0);
Пример, показанный на рис. 6.1, демонстрирует различные варианты
кнопок нажатия.
О Normal Button (Обычная кнопка) соответствует обычной кнопке нажатия,
которую мы привыкли видеть в большинстве случаев. После отпускания
кнопка всегда возвращается в свое исходное положение.
О Toggle Button (Выключатель) может прибывать в двух состояниях:
нажатом или не нажатом, которые соответствуют состояниям — включено
или выключено. Логика действия этой кнопки идентична, например,
логике обычного комнатного выключателя.
П Flat Button (Плоская кнопка) является кнопкой, и по своим
функциональным особенностям идентична с обычной кнопкой нажатия.
Разница лишь во внешнем виде. Благодаря тому, что контуры этой
кнопки не видны, ею можно прекрасно воспользоваться для размещения
секрета в окне диалога.
И наконец, последняя кнопка содержит растровое изображение.
1 HiFM-lulxfl
В lor
J loggle Button
Flat Button
Рис. 6.1. Примеры кнопок нажатия
96
Часть II. Элементы управления
\ Листинг 6.1. Файл main.cpp
#include <qapplication.h>
#include <qbuttongroup.h>
# include <qpushbutton.h>
//
int main (int argc, char** argv)
{
QApplication app(argc, argv);
QButtonGroup hgr(1, QGroupBox::Horizontal, "Buttons", 0);
new QPushButtonC'&Normal Button", &hgr) ;
QPushButton* pcmdToggle = new QPushButton( "&Toggle Button", &hgr);
pcmdToggle->setToggleButton(true);
pcmdToggle->setOn(true);
QPushButton* pcmdFlat = new QPushButton("&Flat Button", &hgr);
pcmdFlat->setFlat(true);
QPixmap pix("aloena.jpg");
QPushButton* pcmdPix = new QPushButton (&hgr);
pcmdPix->resize(pix.size());
pcmdPix->setPixmap(pix);
app.setMainWidget (&hgr);
hgr.show();
return app.exec();
}
В примере исходного кода, приведенном в листинге 6.1, создается виджет
hgr Класса QButtonGroup ДЛЯ ГОрИЗОНТаЛЬНОЙ ГруППЫ КНОПОК, расПОЛОЖвННЫХ
в одну колонку. Количество колонок определяет значение, передаваемое в
первый параметр конструктора. Второй параметр конструктора — флаг
QGroupBox::Horizontal, задает группе горизонтальное положение. Третий
параметр задает заголовок группы. Четвертый задает указатель на виджет-
предок, и в данном примере он равен нулю, что делает группу виджетом
верхнего уровня, имеюшим свое собственное окно. Все виджеты
создаваемых кнопок получают в качестве виджета-предка адрес hgr.
Указатель на объект обычной кнопки Normal Button (Обычная кнопка) не
сохраняется, т. к. с этим объектом не производится дальнейших действий.
Кнопка выключателя Toggle Button (Выключатель) создается как обычная
кнопка— указатель pcmdToggle. Вызовом метода setToggieButton() с пара-
глава 6. Кнопки
97
метром true она приводится в режим выключателя. Вызов метода setono
с параметром true приводит эту кнопку во включенное состояние.
Затем создается виджет плоской кнопки Flat Button (Плоская кнопка), как
обычной кнопки — указатель pcmdFiat. Вызвав метод setFiato с
параметром true, зададим кнопке соответствующий вид.
Для создания кнопки с растровым изображением сначала создается само
растровое изображение pix, которое загружается из файла aIoena.jpg,
указанного в строке конструктора. Затем создается кнопка — указатель pcmdPix,
размеры которой приводятся в соответствие с размерами растрового
изображения вызовом метода resize о. После этого объект растрового
изображения передается в метод setPixmap () для его установки на кнопке.
При щелчке на кнопку высылается сигнал clicked о. Кнопка считается
нажатой, если пользователь щелкнул на нее кнопкой мыши или нажал на
клавишу <Enter>, при условии, что кнопка была текущей. Для того чтобы
кнопку сделать таковой, можно воспользоваться методом setDefauitO.
Существует возможность использования в кнопках нажатия всплывающего
меню (рис. 6.2). Подобные кнопки можно встретить, например, в
обозревателе Microsoft Internet Explorer. Кнопка Start (Пуск) панели задач ОС
Windows представляет собой такую кнопку. Установить меню можно с
помощью метода set Popup о, передав ему указатель на объект всплывающего
меню. Подобные кнопки могут использоваться в качестве альтернативы
выпадающему списку (см. гл. 8).
[Non Commercial]
| Iteml
terru.
Item3
I S*
Menu
в.. HDD
-
Рис. 6.2. Кнопка нажатия со всплывающим меню
[Листинг 6.2. Файл main.cpp
^include <qapplication.h>
^include <qpushbutton.h>
#include <qpopupmenu.h>
// •
mam (int argc, char** argv)
QApplication app(argc, argv);
QPushButton cmd("Menu", 0);
98
Часть II. Элементы управления
QPopupMenu* pmnu = new QPopupMenu(&cmd);
pmnu->insertltem("lteml") ;
pmnu->insertltem("ltem2");
pmnu-^insertltemC'ItemS") ;
pmnu->insertItem("&Quit", &app, SLOT(quit()));
cmd.setPopup(pmnu);
app.setMainWidget (&cmd);
cmd.show();
return app.exec();
}
В листинге 6.2 создается виджет кнопки cmd и виджет всплывающего меню
pmnu. Вызовом метода insertitemo добавляется элемент меню. Последняя
команда Quit (Выход) соединяется со слотом quit о объекта приложения.
Это позволяет пользователю завершить приложение, выбрав эту команду из
меню. Установка созданного меню в кнопке нажатия производится вызовом
метода set Popup ().
Флажки
Большинство программ предоставляют целый ряд настроек, позволяющих
изменять поведение программы. Для этих целей может быть полезным
виджет флажка, который позволяет пользователю выбирать сразу несколько
опций. Класс QCheckBox виджета кнопки флажка определен в заголовочном
файле qcheckbox.h.
( Примечание )
Если же опций больше пяти, то вместо него лучше использовать виджет списка
QListBox (СМ. 8Л. 8).
Флажок состоит из маленького прямоугольника и может содержать
поясняющий текст или картинку. При щелчке на виджете в прямоугольнике
появится отметка. Того же можно добиться при нажатии клавиши
<Пробел >, когда виджет находится в фокусе. Этот виджет устанавливается в
положение "включено" или "выключено" и является, по логике действия,
кнопкой выключателя (toggle button). В отличие от кнопки выключателя,
флажок может иметь еще и третье состояние — неопределенное (рис. 6.3)-
Пример использования такого состояния можно увидеть в диалоговом окне
Propreties (Свойства) Проводника в ОС Windows при выборе нескольких
файлов, имеющих разные атрибуты.
fnaea 6.
I ItfHMsBl
J7 N malCh ^ Box1
Г Instate Check Box
Рис. 6.З. Пример флажков
^Листинг 6.3. Файл main.cpp
^include <qapplication.h>
#include <qvbox.h>
^include <qcheckbox.h>
7/
int main(int argc, char** argv)
{
QApplication appfargc, argv);
QVBox vbx;
QCheckBox* pchkNormal = new QCheckBox("&Normal Check Box", &vbx);
pchkNormal->setChecked(true);
QCheckBox* pchkTristate = new QCheckBox("&Tristate Check Box", &vbx);
pchkTristate->setTristate(true);
pchkTristate->setNoChange();
app.setMainWidget(&vbx);
vbx.show() ;
return app.exec();
}
В листинге 6.3 создаются два флажка — указатели pchkNormal и
pchkTristate. Флажок Normal Check Box (Обычный флажок) помечается
вызовом метода setTristateO с параметром true. Флажок Tristate Check Box
(Флажок с неопределенным состоянием) переводится в режим поддержки
третьего состояния (неопределенного) передачей true в метод setTristate ().
Затем вызовом метода setNoChange () устанавливается третье состояние.
Для взаимодействия с пользователем можно воспользоваться следующими
Сигналами: clickedO, released О И pressedO, унаследованными ОТ QButton.
"ри помоши метода ischeckedo можно узнать о текущем состоянии
флажка, а вызовом метода setchecked () установить его значение.
100
Часть II. Элементы управления
Переключатели
Свое название — Radio Button, виджет получил благодаря своей схожести с
кнопками радиоприемника, на панели которого может быть нажата только
одна из кнопок. Нажатие на другую кнопку радиоприемника приводит
к тому, что в группе автоматически отключается кнопка, нажатая до этого.
Переключатель представляет собой виджет, который может находиться в
одном из двух состояний — включено (on) или выключено (ofT). Эти
состояния пользователь может устанавливать с помощью мыши или клавиши
<Пробел>, когда кнопка находится в фокусе. Класс QRadioButton виджета
переключателя определен в заголовочном файле qradiobutton.h.
Этот виджет должен предоставлять пользователю, по меньшей мере, выбор
одной из двух альтернатив. Виджеты переключателей не могут
использоваться в отдельности и должны быть сгруппированы вместе. Их
группировку МОЖНО Произвести, Например, При ПОМОЩИ КЛаССОВ: QButtonGroup,
QHButtonGroup И QVButtonGroup.
Поясняющие надписи должны быть определены для каждого,
используемого в группе переключателя, а также необходимо задать сочетания клавиш
для быстрого доступа к каждому из переключателей. Это достигается
включением в надпись символа & перед нужной буквой.
( Примечание )
Преимущество переключателей состоит в том, что все опции видны сразу, но
они занимают много места. Поэтому, если количество переключателей больше
пяти, то лучше воспользоваться виджетом выпадающего списка QComboBox
(см. гл. 8).
I №Ш -
- Colas
Г Red
I <*
Г glue
TnTxll
|
Рис. 6.4. Переключатели
! Листинг 6.4. Файл main.cpp
#include <qapplication.h>
#include <qradiobutton.h>
#include <qbuttongroup.h>
Глава 6. Кнопки
101
п
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QButtonGroup hgr(3, Qt::Vertical, "&Colors");
new QRadioButton("&Red", &hgr);
QRadioButton* pradGreen = new QRadioButton("SGreen", &hgr);
new QRadioButton("SBlue", &hgr);
pradGreen->setChecked(true);
app.setMainWidget(&hgr);
hgr.show();
return app.exec();
}
В листинге 6.4 создается вертикальная группа переключателей hgr. Первый
параметр, передаваемый в конструктор, задает количество строк, которые
будут содержать кнопки. Если переключателей окажется больше чем строк,
то автоматически будут созданы дополнительные столбцы. Объект группы
передается в качестве виджета-предка созданным переключателям: pradRed,
pradGreen и pradBiue. После этого один из переключателей устанавливается
во включенное состояние вызовом метода setcheckedo с параметром true.
В переключателях, как и в кнопках нажатия, можно установить текст или
растровое изображение. Для взаимодействия с пользователем можно
использовать унаследованные ОТ QButton сигналы: clickedO, released О И
pressedo. Узнать о текущем состоянии переключателя можно при помощи
Метода isChecked ().
Группировка кнопок
Виджеты группирования кнопок не предназначены для взаимодействия с
Пользователем. Их основная задача — повысить удобство использования и
понимание программы. Для этого очень важно, чтобы элементы интерфейса
были связаны, по принадлежности, в отдельные логические группы. Кроме
т°го, нужно помнить, что кнопки переключателей QRadioButon не могут
использоваться отдельно друг от друга и должны быть объединены.
Класс QGroupBox
Класс QGroupBox — базовый для классов группировки. Представляет собой
контейнер, содержащий в себе различные элементы управления. Он может
иМеть поясняющую надпись в верхней области, эта надпись может содер-
102
Часть II. Элементы управления
жать клавишу быстрого доступа, при нажатии на которую переводится
фокус на саму группу. Этот класс определен в заголовочном файле
qgroupbox.h.
Класс QButtonGroup
Предоставляет возможность группировки любых кнопок. Переключатели
необходимо Объединять В ГРУППЫ при ПОМОЩИ Класса QButtonGroup, а ОН
позаботится об их правильном поведении. Этот класс унаследован от
OGroupBox, что позволяет ему отображать рамку, показывающую
сгруппированные элементы. Определение класса находится в заголовочном файле
qbuttongroup.h.
Существуют два способа группировки кнопок. Суть первого состоит в том,
чтобы сделать кнопки потомками виджета группы:
QButtonGroup hgr(2, Qt::Vertical, "&Colors");
QRadioButton* pradl = new QRadioButton("&Choice 1", &hgr);
QRadioButton* prad2 = new QRadioButton ("«.Choice 2", &hgr) ;
Второй способ — добавление кнопки с помощью метода insert о:
QButtonGroup hgr(2, Qt:rVertical, "&Colors");
QRadioButton* pradl = new QRadioButton("&Choice 1", this);
QRadioButton* prad2 = new QRadioButton("&Choice 2", this);
hgr->insert(pradl);
hgr->insert(prad2);
Есть специальные варианты класса — QHButtonGroup и QVButtonBox, которые
размещают введенные элементы в горизонтальном или вертикальном
направлении. При их использовании отпадает необходимость во флагах
Qt:rVertical И Qt::Horizontal.
Приложение, показанное на рис. 6.5, представляет собой группу
переключателей, отвечающих за цвет фона. Например, выбор переключателя Red
(Красный) приведет к тому, что фон виджета верхнего уровня станет
красным. Кнопка флажка Light (Свет) управляет яркостью цвета. А кнопка Exit
(Выход) осуществляет выход из программы.
Листинг 6.5. Файл main.cpp
#include <qapplication.h>
#include "Buttons.h"
//
int main (int argc, char** argv)
{
QApplication app(argc, argv);
Глава 6. Кнопки
103
Buttons buttons;
арр.setMainWidget(&buttons);
buttons.show();
return app.exec();
}
_ □ x
up
ygra
Exit
Рис. 6.5. Пример группировки элементов
[Листинг 6.6. Файл Button.h
#ifndef _Buttons_h_
#define _Buttons_h_
tinclude <qwidget.h>
class QVButtonGroup;
class QCheckBox;
class Buttons : public QWidget {
Q_OBJECT
Private:
QVButtonGroup* m_pvgr;
QCheckBox* m_pchk;
Public:
Buttons(QWidget* pwgt = 0, const char* pszName = 0);
Public slots:
void slotButtonClicked(int nld);
};
#endif //_Buttons h_
104 Часть II. Элемвнпмты упраг»пг
Как видно из листинга 6.6, класс Buttons, объект которого исгл:пользуетг
листинге 6.5, наследуется от класса owidget и определяет переме!эенные щ D B
для виджета группы и mjpchk для флажка. Это необходимо для i того, что^*
эти переменные были доступны в слоте siotButtonciickedo. '
Листинг 6.7. Конструктор класса Buttons. Файл Buttons.cpp
Buttons::Buttons( QWidget* pwgt /*= 0*/,
const char* pszName/*= 0*/
)
: QWidget(pwgt, pszName)
{
resize(100, 150);
m_pvgr = new QVButtonGroup("QButtonGroup", this);
connect(m_pvgr, SIGNAL(clicked(int)), SLOT(slotButtonClick^tked(int))) ;
QRadioButton* pradl = new QRadioButton("fiRed", mjpvgr);
new QRadioButton("&Green", mjpvgr);
new QRadioButton("SBlue", mjpvgr);
pradl->setChecked(true);
m_pchk = new QCheckBoxC'&Light", mjpvgr);
m_pchk->setChecked(true);
mjpvgr->resize(width(), height());
slotButtonClicked(mjpvgr->id(pradl));
В конструкторе класса Buttons (листинг 6.7) создается виджет гртэуппы mjpvgr,
И его сигнал clicked (int) соединяется СО СЛОТОМ slotButtonOfiClicked(int)-
Параметр, передаваемый сигналом clicked о,— это целочисл©1енный
идентификатор кнопки, присвоенный ей при помещении в группу. ' После
создания трех переключателей (указатели pradl, prad2 и prad3) п производите
выделение первой из них вызовом метода setchecked о с пара вметром t rue-
Флажок Light (Свет) (указатель mjpchk) выделяется сразу nocjieie своего с
дания при помощи метода setchecked о. Последней из кноппок создаепЫ
кнопка нажатия Exit (Выход) (указатель pemd). Размеры вил.1ржста грУ
приводятся в соответствие с размерами окна с помощью мет<ГОда reSlZoTa
В последней строке для инициализации производится i вызов
siotButtonciickedo, в него передается идентификатор первого о перек-
теля. Метод id о класса QButtonGroup получает указатель на к:><нопку,
дящуюся в группе, и возвращает этот идентификатор.
^g^S^ 105
Г-^2Уб.8. Метод slotButt»onClicked(). Файл Buttons.cpp
•d Buttons: :slotButtonCli±cked(int nld)
QButton* pbutton = m_j»\^v-gr->find(nld) ;
int nLight - m_pchk->i^Checked () ? 150 : 80;
switch (nld) {
case 0:
setPaletteBackgrourr-idColor (Qt: :red. light (nLight)) ;
break;
case 1:
setPaletteBackgrourr-idColor (Qt: :green. light (nLight)) ;
break;
case 2:
setPaletteBackgrounodColor (Qt: :blue. light (nLight)) ;
break;
)
if (::qt_cast<QPushButt^Lon*>(pbutton)) {
qApp->quit();
)
В листинге 6.8 метод find( > при помощи идентификатора находит и
возвращает указатель на переюиючатель, который был выбран. При каждом
вызове слота проверяется сост^ояние флажка Light (Свет) для установки
переменной nLight, управляю11__1ей яркостью цвета. Всем элементам группы
Рисваивается уникальный »юмер, причем отсчет начинается с нуля. В опе-
чен ^?™1ЬсЬ производится анализ идентификатора. В нашем примере зна-
ие 0 соответствует перен<лючателю Red (Красный), 1 - переключателю
сяв m ЫЙ)' 2 ~" Wue ( Синии>- Кнопка нажатия Exit (Выход) находит-
•^атопа В единственном числе, поэтому можно обойтись без идентифи-
обхоли' ЧТОбы Узнать> было .ли осуществлено нажатие на нее. Для этого не-
111 Ука ° ПРИ помоши гл°бгальной функции qtcasto попробовать привес-
НазначеаТеЛЬ pbutton к т*^пу QPushButton. Эта функция имеет то же
^РНетн116' ЧТ° С++"опер^тоР dynamiccast. В случае неудачи функция
^Нны е значение. Ее ли этого не произойдет, то мы можем быть уве-
^^ои- нажата именно сэна, и произвести выход из программы вызовом
4Ult() из глобального объекта приложения чарр.
106
Часть II. Элементы управления
Резюме
Существует три основных типа кнопок: кнопки нажатия, флажки и
переключатели.
Кнопки нажатия используются для выполнения определенных действий.
При нажатии на кнопку высылается сигнал pressedo, а после
отпускания — сигнал reieasedo. Чаще всего используется сигнал clickedo,
который высылается, если пользователь нажал и отпустил кнопку.
Флажки часто используются в диалоговых окнах, содержащих опции.
Группа из флажков используется для одновременного выбора нескольких опций.
Возможен вариант, когда не будет выбрана ни одна из них.
Переключатели используются только в группе, в которой одновременно
можно выбрать только один из них. Тем самым, при помощи этой группы
моделируется отношение "один-ко-многим".
Виджеты группировки представляют собой контейнеры, способные
содержать различные виджеты. Их основная задача — облегчить восприятие и
работу с программой. Они объединяют элементы интерфейса в отдельные
логические группы, по принадлежности или назначению.
ГЛАВА 7
Элементы управления
для установки значений
Происходящим необходимо управлять —
иначе оно будет управлять Вами.
Бак Роджерс, вице-президент IBM
Эта группа виджетов используется, как правило, для установки значений, не .
требующих большой точности, например — настройка громкости звука,
скорости движения указателя мыши, скроллинг содержимого окна и другие
подобные действия.
Класс QRangeControl
Этот класс является базовым для всех виджетов установки, его определение
содержится в заголовочном файле qrangecontrol.h.
Для установки границ, шага и текущего значения используются методы
setRange (), setsteps (), setvalue (). Для получения текущего значения
можно вызвать метод value о. Если требуется создать свой собственный
установочный виджет, то можно унаследовать этот класс. В унаследованном
классе необходимо реализовать метод vaiuechange (), вызывающийся всякий раз
ПРИ изменении значения. Также можно переопределить методы rangechange {)
и stepchange {), которые вызываются при изменении границы и шага.
Ползунок
^от виджет позволяет довольно комфортно выполнять настройки
некоторых параметров приложения. Класс ползунка — Qsiider, определен в
заголовочном файле qslider.h. Создать объект ползунка несложно:
^Slider* psld = new QSliderd, 10, 1, 5, Qt:: Horizontal, this);
108
Часть II. Элементы управления
Первый параметр задает минимально возможное значение установки
ползунка — его нижнюю границу. Вторым параметром задается его
максимально возможное значение — верхняя граница. Третий параметр задает шаг
ползунка. Четвертый параметр задает стартовое значение, используемое для
синхронизации с другими элементами управления, работающими вместе с
ползунком. С его помощью можно сделать так, чтобы величины виджетов
совпадали друг с другом, а также он может служить для задания начального
значения при первом показе элемента. Ползунок может быть как
горизонтальным — флаг Qt: :Horizontal, ТЭК И вертикальным — флаг Qt: :Vertical.
Эти флаги передаются пятым параметром. Последним параметром
передается указатель на объект предка (this).
Класс QSiider содержит метод, управляющий размещением рисок ползунка.
Риски очень важны при отображении ползунка. Они дают пользователю
визуально более четкое представление о местонахождении и показывают
шаг. Возможные значения, которые можно передать в метод setTickmarkso,
указаны в табл. 7.1.
Таблица 7.1. Значения перечисления TickSetting класса QSiider
Константа
NoMarks
Above
Below
Both
Описание
Ползунок без рисок
Отображение рисок на верхней стороне ползунка
Отображение рисок на нижней стороне ползунка
Отображение рисок на верхней и нижней сторонах ползунка
Вид
ЕВ
m
ЕЕ
FtJt:
Не следует злоупотреблять большим количеством рисок, т. к. это приведет
к появлению сплошной серой линии и не принесет пользы.
При изменении положения ползунка высылается сгнал siiderMoved(int).
Сигнал vaiuechanged(int) идентичен с сигналом siiderMoved(int).
Высылаются они одновременно, сразу после изменения положения ползунка. Но
это можно изменить, вызвав метод setTrackingo и передав ему значение
false. При ЭТОМ сигнал valueChanged(int) будет высылаться ТОЛЬКО при ОТ-
пускании ползунка.
Если интересно узнать, отпустил ли пользователь указатель текущего
положения ползунка или держит его, то тогда можно подсоединиться к сигналам
sliderPressedO или sliderReleasedO. Приложение, изображенное на
рис. 7.1, содержит виджет ползунка и надписи, значения которых
изменяются в зависимости от положения ползунка.
Глава 7. Элементы управления для установки значений
109
pJ.l.l^1.IJ,!J['JMIMJiil^|
Рис. 7.1. Приложение, демонстрирующее работу ползунка
j Листинг 7.1. Файл main.cpp
#include <qapplication.h>
#include <qhbox.h>
|include <qslider.h>
#include <qlabel.h>
Ц
int main (int argc, char** argv)
{
QApplication app(argc, argv);
QHBox hbx;
QSlider* psld = new QSliderd, 12, 1, 3, Qt: :Horizontal, Shbx) ;
QLabel* plbl = new QLabel("3", &hbx);
psld->setTickmarks(QSlider::Below);
QObject::connect(psld, SIGNAL(valueChanged(int)),
plbl, SLOT(setNum(int))
);
app.setMainWidget (&hbx);
hbx.show();
return app.exec();
}
В листинге 7.1 создаются виджеты ползунка (указатель psld) и надписи
(указатель plbl). В конструктор ползунка четвертым параметром передается
значение 3, синхронизирующее его со значением, отображаемым при
создании надписи. Вызовом метода setTickmarkso производится установка
рисок внизу. Методом connect о производится соединение сигнала ползунка
valueChanged (int) СО СЛОТОМ наДПИСИ setNum(int).
Полоса прокрутки
Полоса прокрутки — это важная составляющая практически любого пользо-
вательского интерфейса. Она интуитивно воспринимается пользователем
и с ее помощью отображаются текстовые или графические данные, превы-
110
Часть II. Элементы управления
шающие по размерам отведенную для них область. Используя указатель
текущего положения полосы прокрутки, можно перемещать данные в
видимую область. Он показывает относительную позицию видимой части
объекта, благодаря которой можно приблизительно догадаться о размере самих
данных. Класс QScroiiBar виджета полосы прокрутки определен в
заголовочном файле qscrollbar.h.
Отдельно полосы прокрутки используются очень редко. Они встроены в
некоторые ВИДжеТЫ, например, QScrollView, QListBox, QMultiLineEdit И Т. Д.
Поэтому если вы намерены использовать класс полосы прокрутки
QScroiiBar, то не исключено, что лучшим вариантом может оказаться
использование одного из вышеперечисленных классов.
Виджет полосы прокрутки имеет минимальное и максимальное значение,
текущее значение и ориентацию. Перемещение указателя текущего
положения осуществляется с помощью левой кнопки мыши. В качестве
альтернативы можно воспользоваться нажатием на кнопки стрелок, расположенных
на концах полосы прокрутки.
Необходимые параметры можно передать в конструктор. Например:
QScroiiBar* pvsb = new QScroiiBar(0, 10, 1, 10, 0, Qt::Vertical, this);
Первый параметр задает минимальное значение для полосы прокрутки, а
второй — максимальное. Третий параметр задает шаг, используемый при
нажатии на стрелки полосы прокрутки. Четвертый параметр задает шаг для
страницы. Пятый параметр служит для задания текущего значения.
Шестой — задает ориентацию полосы прокрутки: вертикальную Qt::vertical
или горизонтальную Qt: horizontal. Установленные конструктором
значения можно изменить после создания виджета с помошью следующих
методов: setRange(), setSteps(), setValueO И setOrientation().
Сигнал siiderMoved(int) передает актуальное значение положения полосы
прокрутки и посылается при изменении пользователем указателя текущего
положения. Сигнал vaiuechanged(int) посылается одновременно с сигналом
sliderMoved(int) и также передает измененное значение полосы прокрутки.
Поведение сигнала изменяется вызовом метода setTracking (), передав ему
значение false. Это приведет к тому, что сигнал vaiuechanged(int) будет
высылаться при отпускании указателя текущего положения.
Существуют еще четыре сигнала, посылаемые при изменении значения
полосы прокрутки. Они высылаются каждый раз, вначале или по завершению
перемещения указателя текущего положения. Этими сигналами можно
воспользоваться, например, для подготовки перемещения данных:
□ nextLineO — высылается после того, как полоса прокрутки
переместилась на шаг вниз или вправо;
□ prevLineO — высылается после того, как полоса прокрутки
переместилась на шаг вверх или влево;
Глава 7. Элементы управления для установки значений 111
0 nextPageO — высылается после того, как полоса прокрутки
переместилась на шаг страницы вниз или вправо;
0 prevPageO — высылается после того, как полоса прокрутки
переместилась на шаг страницы вверх или влево.
Приложение, показанное на рис. 7.2, состоит из электронного индикатора и
полосы прокрутки. Значение, отображаемое электронным индикатором,
изменяется в зависимости от положения указателя текущего положения.
i imiJ.i.,LUjjui^TT5Txi|
Рис. 7.2. Приложение, демонстрирующее работу полосы прокрутки
I Листинг 7.2. Файл main.cpp
#include <qapplication.h>
#include <qvbox.h>
#include <qscrollbar.h>
#include <qlcdnumber.h>
//
int main (int argc, char** argv)
{
QApplication app(argc, argv);
QVBox vbx;
QLCDNumber* pled = new QLCDNumber(4, &vbx);
QScrollBar* phsb =
new QScrollBar(0, 100, 2, 10, 0, Qt::Horizontal, &vbx);
QObject::connect(phsb, SIGNAL(valueChanged(int)),
pled, SLOT(display(int))
);
app. setMainWidget (&vbx);
vbx.resize(200, 100);
vbx. show () ;
}
return app.exec();
112
Часть //. Элементы управлен^
В листинге 7.2 создаются виджеты электронного индикатора (указатель
pled) и полосы прокрутки (указатель phsb). После этого сигнал
valueChanged(int) ПОЛОСЫ Прокрутки соединяется СО СЛОТОМ display (int,
электронного индикатора, служащего для отображения значений целого ти.
па, при помощи метода connect ().
Установщик
Класс QDiai виджета установщика определен в заголовочном файле qdial.h.
Этот виджет очень похож на крутящийся регулятор громкости
радиоприемника, головку которого можно перемещать при помощи мыши или клавиш
курсора. По своим функциональным особенностям он похож на ползунок.
Разница лишь в том, что круглая форма этого виджета позволяет
пользователю после достижения максимального значения, сразу перейти к
минимальному, и наоборот. Для управления разрешением прокручивания служит
метод setwrapper (). Все методы, за исключением этого setwrapper (),
практически совпадают с методами класса Qsiider. Сигналы также совпадают с
сигналами класса Qsiider, но вместо префикса slider имеют префикс dial,
т. е., например, не sliderMovedO, a diaiMovedo. Создать виджет
установщика можно следующим образом:
QDial* pdia = new QDial(0, 100, 1, 0, &vbx);
Первый и второй параметры представляют собой минимальное и
максимальное значение установщика. Третий параметр определяет шаг для
нажатия клавиш <Page Up>, <Page Down> и клавиш курсора. Четвертый
параметр является значением для инициализации, устанавливая текущее
значение. В пятом параметре передается указатель на виджет предка. Последний,
шестой параметр, который не используется в приведенном примере, задает
имя виджета.
Приложение, представленное на рис. 7.3, содержит виджеты установщика и
индикатора прогресса. Состояние последнего зависит от местоположения
стрелки установщика.
I HJ-liH-liiliiHtiHHTPH
I lllllllllllll ГС II
Рис. 7.3. Приложение, демонстрирующее работу установщика
глава 7. Элементы управления для установки значений
113
Глистинг 7.3. Файл main.cpp
«include <qapplication.h>
^include <qvbox.h>
^include <qdial.h>
^include <qprogressbar.h>
n
int main (int argc, char** argv)
{
QApplication app(argc, argv);
QVBox vbx;
QDial* pdia = new QDial(0, 100, 1, 0, &vbx);
QProgressBar* pprb = new QProgressBar(100, &vbx);
QObject::connect(pdia, SIGNAL(valueChanged(int)),
pprb, SLOT(setProgress(int))
);
app.setMainWidget (&vbx);
vbx.show();
■ return app.exec();
}
В листинге 7.3 создаются виджеты установщика (указатель pdia) и
индикатора професса (указатель pprb). В конструкторе виджета установщика
задается диапазон значений от 0 до 100. После создания сигнал виджета
установщика valueChanged (int) соединяется при ПОМОЩИ метода connect () СО
СЛОТОМ ИНЛИКатора професса setProgress (int).
Резюме
Виджеты установки применяются в тех случаях, когда не требуется точная
Установка значений. Базовым классом для всех виджетов установки
является класс QRangeControi, который содержит основные методы для определе-
ния диапазона, шага и текущего значения.
Виджет ползунка позволяет осуществлять настройки параметров
приложения, например: настройка фомкости звука, скорости движения указателя
^Ь1Ши и т. д. Этот виджет содержит риски, дающие пользователю визуально
"олее четкое представление о шаге и месте нахождения.
°Иджет полосы прокрутки позволяет отображать текстовые или фафические
^Нные, превышающие по размерам отведенную для них область. При по-
114 Часть II. Элементы управления
мощи указателя текущего положения можно перемещать данные из
невидимой области в видимую. Этот класс обладает большим количеством
сигналов, связанных с перемещением указателя.
Виджет установщика по своим функциональным особенностям очень похож
на виджет ползунка. Разница состоит во внешнем виде, в наименовании
префиксов высылаемых сигналов и наличии метода для разрешения
прокручивания, при переходе от максимального значения к минимальному.
ГЛАВА 8
Элементы выбора
Если вам предстоит сделать выбор,
а вы его не делаете — это тоже выбор.
У. Джеймс
Иерархические списки
Виджет QListView отображает элементы списка в иерархической форме для
выбора пользователем одной или нескольких из них. Его часто применяют
для показа содержимого дисков и каталогов. Класс унаследован от
QScroiiview и в том случае, когда область отображения не в состоянии
разместить все элементы, появляются полосы прокрутки. Элементы списка
ПРОИЗВОДЯТСЯ ОТ класса QListViewItem. Оба класса QListView И QListViewItem
определены в заголовочном файле qlistview.h.
С Примечание ^
Класс QListViewItem из целей эффективности и экономии ресурсов
унаследован от класса Qt, а не от класса QWidget или QObject.
При создании объекта класса QListViewItem — элемента списка, в
конструктор можно передать до восьми значений заголовков столбцов. Если этого
Окажется недостаточно, ТО ТОГДа С ПОМОЩЬЮ метода QListViewItem:: addColumn ()
создаются дополнительные столбцы. На рис. 8.1 список содержит три
элемента. Двойной щелчок мыши на элементе Local Disk(C) (Локальный
Диск(С)) сворачивает/разворачивает вложенные элементы Program Files
(Программные файлы) и WindowsXP
15<стинг 8.1. Файл main.cpp
•include <qapplication.h>
•include <qlistview.h>
116
Часть II. Элементы управления
,1
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QListView lv;
lv.addColumn("Folders");
lv.addColumn("Used Space");
QListViewItem* plvltem =
new QListViewItem(&lv, "Local Disk(C)", "1.8GB");
new QListViewItem(plvltem, "WindowsXP", "1GB");
new QListViewItem(plvltem, "Program Files", "800MB");
plvltem->set0pen(true);
lv.resize(160, 110);
lv.show();
app.setMainWidget (&lv);
return app.exec();
}
ji iiji.i,ui.mw_iuixi||
| j Folders Used Space
[Local bisk(C) =1.8GB
11 • Program Fles 800MB
■WindovwXP 1GB
IN J^Jll
Рис. 8.1. Пример виджета списка
Для отображения структуры каталогов диска, например как в Проводнике
ОС Windows, необходимо построить объекты QListViewItem в нужном
иерархическом порядке. В листинге 8.1 создается виджет списка lv и с
помощью метода addcoiumno создаются столбцы Folders и Used Space. В
конструктор объекта первого элемента — указатель ivitem, передаются адрес iv
и два текстовых параметра. Этот элемент является предком для создаваемых
в дальнейшем элементов и представляет собой вершину иерархии. Метод
setopeno разворачивает вершину и показывает иерархию элементов.
Обратите внимание на столбцы — они представляют собой намного больше,
чем просто описание. При нажатии на заголовок столбца проводится
сортировка элементов в убывающем или возрастающем порядке. Подобную сор-
Глава 8. Элементы выбора
117
тировку можно произвести и программно, вызвав метод setsorting ().
Первый параметр этого метода представляет собой номер столбца, а второй
сообщает, в каком порядке должна производиться сортировка — в
возрастающем (true) ИЛИ убывающем (false).
По умолчанию из списка одновременно выбирается только один из
элементов. ЕСЛИ ЭТОГО недостаточно, ТО МОЖНО вызвать метод setMultiSelection()
с параметром true и тем самым установить режим множественного
выделения. Выбор элементов можно осуществлять программно, вызывая метод
setselected () и передавая в него true для вьшеления элемента или false
для снятия вьшеления. Можно блокировать выбор элемента, вызвав метод
setSelectable () С параметром false.
Чтобы "пройтись" по всем элементам виджета Qiistview, необходимо с
помощью метода firstchiido получить указатель на первый элемент, а затем,
используя метод itemBeiowO класса Qiistviewitem, получать указатели на
следующие. Значение 0 означает, что больше элементов нет. Этот метод
применим и к развернутым элементам.
QListViewItem* plvltem = plv->firstChild();
for (; plvltem; plvItem->itemBelow()) {
}
Класс QListview содержит следующие сигналы:
О сигнал seiectionchangedo сообщает об изменении выбранных элементов;
О doubleClicked(QListViewItem*) ИЛИ returnPressed(QListViewItem*)
высылаются при двойном щелчке мыши или при нажатии клавиши <Enter>
на элементе. Обычно сигналы соединяются с одним слотом, т. к. при
двойном щелчке мыши и при нажатии клавиши <Enter> должны вызвать
одно и то же действие;
■О сигнал rightButtonClicked(QListViewItem*, const QPoint&, int)
высылается при нажатии правой кнопки мыши. Второй параметр сигнала
содержит координаты, в которых произошло нажатие, третий параметр
указывает номер столбца. Этим сигналом можно воспользоваться,
например, чтобы отобразить в нужном месте контекстное меню.
Если необходимо снабдить элементы небольшими растровыми изображе-
НИЯМИ, ТО ОНИ устанавливаются С ПОМОЩЬЮ метода QListViewItem::
setPixmap(int, const QPixmap&), а текст элемента устанавливается с
помощью QListViewItem: :setText (int, const QString&). ПерВЫЙ параметр ДЛЯ
°боих методов соответствует номеру столбца.
у^ Примечание }
Элементы могут содержать не только текст и растровые изображения.
Существует возможность размещать в них объекты класса QCheckListltem. Этот класс
118
Часть II. Элементы управления
не унаследован от класса QWidget. Он предоставляет возможность
размещения в иерархическом списке кнопок (см. гл. 6), типом которых можно управлять
при помощи флагов: RadioButton, CheckBox, Controller, передавая их в
конструктор, при создании объектов класса QCheckListitem. Размещение же
в иерархических списках "настоящих" виджетов не представляется возможным.
Можно разрешить пользователю переименовывать элементы, но по
умолчанию этот режим отключен. Его можно включить, вызвав метод
QListViewItem: :setRenameEnabled(int, bool). Первым параметром В метод
передается номер столбца, который переименовывается, а вторым — булево
значение для включения или отключения режима переименования. После
того как пользователь переименует опцию, QListview сообщает об этом
посылкой сигнала itemRenamed ().
Класс QListview поддерживает технологию перетаскивания (drag&drop). Для
этого необходимо вызвать метод setDragEnabied () у каждого из элементов,
которым нужно включить поддержку перетаскивания, и передать ему
параметр true. А чтобы элемент был в состоянии принимать "сбрасываемые"
объекты, у него нужно вызвать метод setDropEnabied () и передать ему true.
Простой список
Класс QListBox — это виджет, предоставляющий пользователю возможность
выбора одного или нескольких элементов. Он унаследован от класса
QScroilview, что позволяет ему отображать полосы прокрутки. Элементы
списка могут содержать текст и растровые изображения. Чтобы добавить
элемент в список, нужно вызвать метод insertitemo класса QListBox.
Реализовано три варианта метода: для текста, растрового изображения и
объекта класса QListBoxitem. Если необходимо удалить из списка все элементы,
то для этого нужно вызвать слот clear ().
QListBoxitem — абстрактный класс для элементов списка. При его
наследовании нужно реализовать следующие методы: height о, width о и paint о •
В Qt есть готовые классы, унаследованные от QListBoxitem. Объекты этих
классов создаются не самим классом QListBox, а в методе insertitemo при
передаче в них текста или изображения. Если передано растровое
изображение, то внутри метода insertitemo создается объект класса
QListBoxPixmap, а если текст — объект класса QListBoxText.
В список можно внести сразу несколько текстовых элементов, передав
объект класса QStringList, содержащий список строк (см. гл. 36) в метод
insertStringList().
Элементы списка можно упорядочить, вызвав метод sort (). При передаче в
этот метод значения true сортировка элементов будет выполнена в
возрастающем порядке, а при false — в убывающем.
Глава 8. Элементы выбора
119
узнать, какие элементы выбрал пользователь, можно с помощью метода
QListBox: :currentitem() — он возвращает номер позиции, которую элемент
занимает в списке. Или вызвать метод seiecteditemo, который возвращает
указатель на выбранный элемент. После выбора элемента посылаются сразу
ХрИ сигнала selected О С параметрами int, const QString& И QListBox It em*.
При двойном щелчке мыши высылается сигнал doubieciickedo с
параметром QListBoxitem*. При нажатии клавиши <Enter> будет послан сигнал
returnPressed (QListBoxitem*).
Можно разрешить режим множественного выделения. Этот режим
устанавливается с помощью метода setMuitiseiectionO класса QListBox, в который
передается параметр true. В этом случае, чтобы определить, какие из
элементов были выбраны, необходимо воспользоваться циклом, в котором
будут опрашиваться все элементы. Например:
for (int i = 0; i < plb->count(); ++i) {
if (plb->isSelected(i)) {
}
}
На рис. 8.2 показан пример использования простого списка. В программе
приводится список операционных систем.
I 1Ш1Л^_1
DC
ч Linux
Windows
ша|
|
j
zl\
Рис. 8.2. Пример простого списка
[Листинг 8.2. Файл main.cpp
♦include <qapplication.h>
♦include <qlistbox.h>
♦include <qpixmap.h>
11
Xnt main(int argc, char** argv)
QApplication app(argc, argv);
QListBox lb;
120
Часть II. Элементы управления
lb.insertItem("CP/M");
lb.insertItem("DOS");
lb.insertItem(QPixmap("linlogo.jpg"), "Linux");
lb.insertItem(QPixmap("winlogo.jpg"), "Windows");
lb.insertItem (QPixmapl"os21ogo.jpg"), "OS/2");
lb.resize(130, 150);
lb.show();
app.setMainWidget (&lb);
return app.exec();
}
В листинге 8.2 создается виджет простого списка lb. Элементы добавляются
в список методом insertitemo. Три последних метода insertitemo
получают помимо текста еще и растровое изображение, передаваемое в первом
параметре.
Выпадающий список
Класс ocomboBox предоставляет пользователю возможность выбора одного
элемента из нескольких. Его функциональное назначение совпадает с вид-
жетом простого списка QListBox. Основное преимущество виджета
выпадающего списка состоит в отображении только одного, выбранного
элемента, благодаря чему для его размещения не требуется много места.
Отображение всего списка (раскрытие) происходит только на некоторый
промежуток времени, для того чтобы пользователь мог сделать выбор, после
чего список возвращается в свое исходное положение.
В качестве элемента можно добавить текст и/или картинку. Для этого
служит метод insertitemo. Можно добавить сразу несколько текстовых
элементов, передав указатель на объект класса QStringList в метод
insertStringListO. Вызвав метод 'setDuplicatesEnabled (false), МОЖНО
включить режим, исключающий из списка повторяющиеся элементы. Если
необходимо удалить все элементы выпадающего списка, тогда вызывается
СЛОТ clear ().
Чтобы узнать, какой из элементов выбрал пользователь, нужно вызывать
метод currentitemo. Можно сделать так, чтобы пользователь мог сам
добавлять элементы в список. Типичным примером является адресная строка
Проводника ОС Windows, содержащая в себе список просмотренных
ссылок. Для установки виджета в этот режим вызывается метод setEditabie ()'
передавая ему значение true. После изменения пользователем текста Bbij
бранного Элемента Высылается сигнал textChanged(const QString&) И НОВЫЙ
элемент добавляется в список.
Глава 8. Элементы выбора
121
После выбора элемента высылаются сразу два сигнала activated о — один
с параметром типа int, а другой с параметром типа const QString&.
Пример, приведенный на рис. 8.3, демонстрирует виджет выпадающего
списка. При изменении элемента список будет пополняться новым.
|пет-|пЫ|
|john 3
Paul
Geoige
Rmgo
Рис. 8.3. Пример выпадающего списка
^Листинг 8.3. Файл main.cpp
#include <qapplication.h>
#include <qcombobox.h>
//
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QComboBox cbo;
cbo.insertItem("John");
cbo.insertltem("Paul");
cbo.insertltem("George");
cbo.insertItem(VRingo");
cbo.setEditable(true);
cbo.show();
app.setMainWidget (&cbo);
return app.exec();
1
в программе, приведенной в листинге 8.3, создается виджет выпадающего
списка сьо, затем в него добавляются элементы с помощью метода
msertitemo. Вызов метода setEditable о с параметром true устанавливает
список в режим редактирования.
Примечание ^
Если при редактировании текста требуется проверять правильность ввода
информации, то для этого в виджете QComboBox следует установить объект
класса QValidator (см. гл. 9).
сг
122
Часть II. Элементы управления
Элемент показа иконок
Виджет Qiconview содержит в качестве элементов растровые изображения и
позволяет выбирать их, проводить над ними операции перемещения и
перетаскивания (drag&drop). Помимо растрового изображения элементы могут
иметь текстовое описание. Этот виджет унаследован от класса QScroiiview и
поэтому может автоматически отображать полосы прокрутки в том случае,
если не все элементы отображаются в его видимой части.
Элементом является объект класса Qiconviewitem и содержит растровое
изображение. Текстовое описание не обязательно.
( Примечание ^
Класс Qiconviewitem из соображений эффективности и экономии ресурсов
унаследован от класса Qt, а не от класса QWidget или QObject.
По умолчанию все помещаемые элементы будут заполнять виджет Qiconview
слева направо и сверху вниз. Это можно изменить с помощью метода
setArrangement (), передав ему флаг QlconView: : TopToBottom, ЧТО Приведет К
заполнению опций сверху вниз и слева направо.
Вызовом метода Qiconview: :setsortingо с параметром true включается
режим сортировки опций. Сортировка будет осуществляться каждый раз при
добавлении новых элементов.
Чтобы предоставить пользователю возможность изменять текст элемента
НУЖНО вызвать метод Qiconviewitem: : setRenameEnabled () С параметром true.
Переименование производится при нажатии клавиши <F2> или
последовательными нажатиями левой кнопкой мыши на элементе. По
завершении переименования виджет Qiconview пошлет два сигнала —
itemRenamed(Qiconviewitem*, const QString&) И itemRenamed(Qiconviewitem*) -
Сигнал rightButtonPressed(Qiconviewitem*, const QPont&) высылается прИ
нажатии правой кнопкой мыши на элементе. Последний параметр этого
метода указывает координаты курсора мыши в момент нажатия, что
применяется для отображения контекстного меню. После двойного щелчка мыши
высылается сигнал doubleClicked(Qiconviewitem*).
Пример, показанный на рис. 8.4, предоставляет пользователю выбор иконки
одной из трех операционных систем.
[ra,i^jjjjMJuix-i|]
г щ
Lir Windows О" '2 ^\
Рис. 8.4. Виджет показа иконок
Глава 8. Элементы выбора
123
[Листинг 8.4. Файл main.cpp
#include <qapplication.h>
#include <qiconview.h>
^include <qpixmap.h>
j I
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QlconView iv;
new QiconViewitem(&iv, "Linux", QPixmap("linlogo.jpg"));
new QIconViewItem(&iv, "Windows", QPixmap("winlogo.jpg"));
new QIconViewItem(&iv, "OS/2", QPixmap("os2logo.jpg"));
iv.resize(180, 80);
iv.show() ;
app.setMainWidget (&iv);
return app.exec();
}
В листинге 8.4 создается объект iv класса QlconView. Для добавления
элемента в список нужно, для начала, создать его. В программе создаются три
элемента (класс QiconViewitem). В первом параметре конструктора
указывается объект-предок. Вторым параметром передается текст надписи, а
третьим — растровое изображение.
С Примечание )
Может показаться, что класс QiconViewitem унаследован от класса QObject
или QWidget, но на самом деле он является прямым наследником класса Qt.
Таблицы
Класс отаЫе представляет собой таблицу. Поместить текст в ячейку можно
с помощью метода setTexto, а для размещения растрового изображения
Используется метод setPixmap (), передавая ему объект растрового
изображения класса QPixmap. Первый параметр методов setText () и setPixmap ()
указывает на номер строки, второй параметр — на номер столбца. Таким обра-
*°К эти параметры задают местоположение ячейки в таблице. Если текст и
Растровое изображение помещаются в одну ячейку, то изображение будет
Находиться слева от текста.
124
Часть II. Элементы управления
( Примечание )
В таблицу допускается, помимо текста и иконок, помещать и виджеты. Для
этого используется метод setCellwidget ().
При двойном нажатии левой кнопки мыши на поле ячейки она переходит в
режим редактирования, для этого используя виджет QLineEdit.
( Примечание ^
При использовании другого виджета для редактирования наследуется класс
QTabie и перезаписывается метод createEditor (). Метод возвращает виджет
для редактирования в тот момент, когда ячейка переходит в этот режим. После
того как пользователь закончит редактирование, метод setContentsFromEditorO
должен скопировать данные.
( Примечание )
Если надо поместить для редактирования виджеты QComboBox или QCheckBox,
то можно воспользоваться уже готовыми классами QComboTableitem и
QCheckTableltem.
Методы класса QTabie в большинстве совпадают с методами классов
QListView И QlconView.
Приложение, показанное на рис. 8.5, использует таблицу, состоящую из
трех столбцов и двух строк. Содержимое текста ячеек можно изменять.
[Non-Commercial] - Table ИП D
1
2
Name
Piggy
Kermil
Phone
+496313"187
+4^631 siA 81
| E-Mail |
piggy@mega de
kermrt@mega de |
Рис. 8.5. Пример использования виджета таблицы
Листинг 8.5. Файл main.cpp
#include <qapplication.h>
#include <qtable.h>
// ;
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QTabie tbl(2, 3) ;
tbl.horizontalHeaderO->setLabel(0, "Name");
tbl.horizontalHeader()->setLabel(1, "Phone");
tbl.horizontalHeaderO->setLabel(2, "E-Mail");
Глава 8. Элементы выбора
125
tbl.setText(О, О, "Piggy");
tbl.setText(0, 1, "+49 631322187");
tbl.setText(О, 2, "piggy@mega.de");
tbl.setText(1, 0, "Kermit");
tbl.setText(1, 1, "+49 631322181");
tbl.setText(1, 2, "kermit@mega.de");
tbl.resize(350, 80);
tbl.show();
app.setMainWidget (&tbl);
return app.exec();
}
В листинге 8.5 создается виджет таблицы tbl. Первый и второй параметры,
передаваемые в конструктор, задают количество строк и столбцов. Чтобы
задать заголовки столбцов таблицы, нужно сначала с помощью метода
horizontaiHeader () получить указатель на виджет заголовка QHeader , а затем
вызвать метод setLabel (). Первый параметр метода setLabel () задает номер
столбца, а второй — его надпись. Заголовки столбцов, их размеры и
расположение изменяются при помощи указателя мыши.
( Примечание )
Виджет QHeader используется в классе QListView для подписи заголовков
столбцов. В классе QTable он применяется и для заголовков строк таблицы.
Вызовом методов setText о устанавливаются текст в ячейках таблицы.
Первые два параметра определяют номер строки и столбца, а третий задает
текст ячейки.
Помимо класса QTable в библиотеке Qt имеется еще и класс QGridview для
работы с таблицами. Непосредственно от него нельзя наследовать виджеты,
т- к. он является абстрактным. Поэтому для создания собственных таблиц
е*х> необходимо унаследовать и реализовать в нем метод paintceii о.
Закладки
При чтении книг вы, возможно, используете закладки, которые помогают
^м быстро открывать книгу в нужном месте. Ситуация с компьютерными
^ладками аналогична, только с той разницей, что они используются для
"Истрой смены диалоговых окон. При выборе одной из закладок в окне бу-
** отображен закрепленный за ней виджет. Закладки могут содержать как
^Кст, так и растровое изображение.
126
Часть II. Элементы управления
Основное назначение закладок — разгрузить сложное диалоговое окно,
имеющее большое количество опций на серию логически скомпонованных
поддиалогов. Сам класс QTabwidget представляет собой комбинацию из двух
виджетов: от-аьваг и Qwidgetstack. Эти виджеты располагаются друг под
другом.
Страницы можно делать доступными и недоступными. Чтобы сделать
страницу недоступной, нужно вызвать метод setTabEnabied () и передать ему
значение false. Вызовом метода setcurrentTab () можно сделать одну из
закладок текущей.
Показанный на рис. 8.6 пример содержит три закладки: One, Two и Three.
При выборе одной из закладок происходит отображение закрепленного за
ней виджета.
I WI-liBili'i1nffl-"FT*1
One . Three I
Label 2
Рис. 8.6. Виджет закладок
I Листинг 8.6. Файл main.cpp
#include <qapplication.h>
# include <qtabwidget.h>
#include <qlabel.h>
//
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QTabWidget tab;
tab.addTab(new QLabel("Label 1", &tab), "One");
tab.addTab(new QLabel("Label 2", &tab), "Two");
tab.addTab(new QLabel("Label 3", &tab), "Three");
tab.resize(150, 80);
tab.show() ;
app.setMainWidget (&tab);
return app.exec();
}
Глава 8.
В листинге 8.6 создается виджет закладок tab. Вызовом метода асютаьо
добавляются новые закладки. В первом параметре этого метода передается
указатель на виджет, который должен отображаться при выборе закладки.
Во втором параметре передается текст закладки.
( Примечание )
Для оснащения закладки растровым изображением его нужно передать в метод
addTab () вторым параметром, а текст — третьим.
Виджет инструментов
Класс QTooiBox представляет собой закладки, расположенные вертикально.
Связанные виджеты отображаются непосредственно под ними. Текст
закладки добавляется вместе с виджетом при вызове метода additemo. Если
требуется вставить закладку на определенную позицию, то вызывается
метод insertitemo. Количество закладок можно узнать, вызвав метод count о.
Для удаления закладок реализован метод removeitemo.
Вызов метода currentindex () возвращает индекс текущей закладки. Передав
этот индекс в метод item(), можно получить указатель на закрепленный за
этой закладкой виджет.
Виджет инструментов располагает только одним сигналом currentchanged(int),
высылаемым при выборе одной из закладок.
В приведенном на рис. 8.7 примере реализована панель инструментов,
содержащая три закладки с закрепленными за ними виджетами надписей.
1 Ш.Я_
One
Two
Label 2
Thiee
Inlxl
i
Рис. 8.7. Виджет инструментов
[^ртинг 8.7. Файл main.cpp
'include <qapplication.h>
include <qtoolbox.h>
•include <qlabel.h>
//
niainfint argc, char** argv)
128
Часть II. Элементы управления
{
QApplication app(argc, argv);
QToolBox toolbox;
toolbox.addltem(new QLabel("Label 1", &toolbox), "One");
toolbox.addltem(new QLabel("Label 2", &toolbox), "Two");
toolbox.addltem(new QLabel("Label 3", &toolbox), "Three");
toolbox.resize(100, 80);
toolbox.show() ;
app.setMainWidget (&toolbox);
return app.exec();
}
В листинге 8.7 создается виджет инструментов. После этого в него
добавляются закладки с помощью метода additemo. Первым параметром методу
addltem о передается указатель на виджет. который отображается при
выборе закладки. Во втором параметре передается текст закладки.
Резюме
Элементы выбора могут содержать как текст и так растровые изображения.
Сами элементы можно выбирать с помощью левой кнопки мыши или
клавиш курсора.
Классы QListView, QListBox, QTable, QGridView И QlconView унаследованы ОТ
класса QScroliview и могут автоматически отображать полосы прокрутки,
в том случае когда элементы не помещаются.
Коротко классы этой главы можно охарактеризовать следующим образом:
□ класс QListView предназначен для отображения элементов в
иерархической форме;
□ класс QListBox удобен для выбора от одного до нескольких элементов;
□ элементы класса iconview обязаны содержать растровые изображения,
которые можно снабдить текстом;
□ класс QTable предоставляет собой таблицу. В ее ячейки можно помешать
текст, растровые изображения и виджеты;
□ класс ocomboBox позволяет выбирать только один элемент. Основное его
преимущество состоит в том, что он не требует много места;
□ класс QTabWidget позволяет разбить сложное диалоговое окно на серию
простых и тем самым делает приложение более понятным;
□ класс QToolBox по своей сути очень похож на класс QTabwidget. Разнив
состоит в вертикальном расположении закладок.
ГЛАВА 9
Элементы ввода
Человек и компьютер разные, и увы,
многие считают, что легче приспособить
человека к компьютеру, чем наоборот.
Чарльз Петцольд
Эта группа виджетов представляет собой основу пользовательского ввода и
редактирования данных — текста и чисел. Большая часть элементов ввода
может работать с буфером обмена и поддерживает технологию
перетаскивания (drag&drop), что избавляет разработчика от дополнительной реализации.
Текст можно выделять с помощью мыши, клавиатуры и контекстного меню.
Однострочное текстовое поле
Этот виджет является самым простым элементом ввода. Текстовое поле
состоит из прямоугольной области для ввода строки текста, поэтому не
следует использовать этот виджет в тех случаях, когда требуется вводить более
ОДНОЙ СТрОКИ Текста — ДЛЯ ЭТОГО ИМеЮТСЯ классы QTextEdit И QMutliLineEdit.
Класс QLineEdit однострочного текстового поля определен в заголовочном
Файле qlineedit.h.
Текст, находящийся в виджете, возвращает метод text о. Если содержимое
виджета подвергалось изменению, то высылается сигнал textchanged ().
Сигнал returnPressed () уведомляет о нажатии пользователем клавиши
^Enter>. Вызов метода setReadOnly () с параметром true устанавливает
редким, который дает возможность только просмотра текста, но не редактиро-
^ния. Если требуется инициализировать виджет, то текст можно передать
ВСЛот setText().
ь
° однострочном текстовом поле существует возможность включения режи-
Ма ввода пароля. Этот режим устанавливается вызовом метода setEchoMode ()
с Флагом Password. Установка этого приводит к тому, что вводимые
символы не отображаются, а заменяются символом *.
130
Часть II. Элементы управления
Пример программы, показанной на рис. 9.1, имеет два однострочных поля.
Одно из них установлено в режим ввода паролей. Вводимый в это поле
текст отображается в виджете надписи.
ДДШЦ#М-1"Н|
|Тор Secret II
lext-
pesJ
Password II
lr=~ 11
Рис. 9.1. Программа с однострочными полями ввода
I Листинг 9.1. Файл main.cpp
#include <qapplication.h>
#include <qvbox.h>
#include <qlabel.h>
#include <qlineedit.h>
//
int main (int argc, char** argv)
{
QApplication app(argc, argv);
QVBox vbx;
QLabel* plblDisplay = new QLabel(&vbx);
plblDisplay->setFrameStyle(QFrame::Box | QFrame::Raised);
plblDisplay->setLineWidth(2);
plblDisplay->setFixedHeight(50) ;
QLabel* plblText = new QLabel("&Text:", &vbx);
QLineEdit* ptxt = new QLineEdit(&vbx);
plblText->setBuddy(ptxt);
QObject::connect(ptxt, SIGNAL(textChanged(const QString&)),
plblDisplay, SLOT(setText(const QString&))
);
QLabel* plblPassword = new QLabel("&Password:", &vbx);
QLineEdit* ptxtPassword = new QLineEdit(&vbx);
plblPassword->setBuddy(ptxtPassword);
ptxtPassword->setEchoMode(QLineEdit::Password);
Глава 9. Элементы ввода 131
QObject::connect(ptxtPassword, -SIGNAL(textChanged(const QStringS)),
plblDisplay, SLOT(setText(const QStringS))
);
app.setMainWidget (&vbx);
vbx.showO ;
return app.exec();
}
В листинге 9.1 создается виджет надписи— указатель plblDisplay. Метод
setFrameO устанавливает нужный стиль рамки, a setLinewidth () — ее тол-
шину. В виджете надписи производится фиксация высоты с помощью
метода setFixedHeight (). Две надписи, pibiText И pibiPassword, связываются С
виджетами однострочного текстового поля методом setBuddyO. Затем
производится соединение сигналов textchanged () со слотами setText о видже-
тов надписей для отображения вводимого текста.
Количество ВВОДИМЫХ СИМВОЛОВ МОЖНО ОГраНИЧИТЬ МеТОДОМ setMaxLength (),
передав в него целое значение максимальной длины строки. Для получения
текущего максимального значения длины существует метод maxLengtho.
Класс QLineEdit предоставляет слоты для работы с буфером обмена:
О сору о — копирует выделенный текст;
О cut о — копирует выделенный текст и удаляет его из поля ввода;
О paste о— вставляет текст на место положения курсора, стирая
выделенный текст
Метод undo о отменяет последнюю проделанную операцию, а метод redo о
повторяет последнюю отмененную. Узнать — возможно ли использовать
Операции ОТМеНЫ И ПОВТОра, МОЖНО С ПОМОЩЬЮ МеТОДОВ isUndoAvailable (),
isRedoAvaiiabie (), которые возвращают булевы значения.
Правильность ввода гарантируется при помощи специального объекта
контролера (validater), пример реализации которого размещен ниже в этой
главе.
Редактор RichText
Класс QTextEdit позволяет осуществлять просмотр и редактирование текста,
Годящегося в формате RichText (RTF, RichText Format) (см.
приложение 1). Он унаследован от класса QScroiiview, что дает возможность
автоматически отображать полосы прокрутки, если текст не может быть
полностью отображен в отведенной для него области.
132
Часть II. Элементы управления
Рекомендуется всегда вызывать метод setTextFormat() для установки
нужного режима. Передача в метод флага Qt: :AutoText произведет установку
автоматического режима. Например, при вызове метода setTexto производится
анализ формата текста — обычный или RichText. Если передавать в метод
setTextFormat () флаг Qt::RichText, то считается, что передаваемый текст
находится в формате RichText и необходимость в промежуточной проверке
отпадет. Передача флага Qt: :PiainText устанавливает режим работы с
обычным текстом.
( Примечание )
После создания виджета QTextEdit целесообразно передать в метод
setTextFormat() флаг Qt::RichText, для предоставления пользователю
возможности редактировать текст в формате RichText.
Класс QTextEdit содержит методы, идентичные методам класса QLineEdit:
□ setReadOniyO — установка и снятие режима, блокировки изменения
текста;
□ setEchoMode (Password) — установка режима введения пароля;
□ text () — получение содержимого текста;
□ setText () — установка текста;
□ сору о, cut о и paste о — работа с буфером обмена;
□ setvaiidatoro — подтверждение ввода;
□ redo о или undo о — повторяет или отменяет проделанное действие;
□ seiectAiio или deselect о — выделяет или снимает выделение всего
текста;
□ clear () — очистка поля ввода;
□ сигнал textchanged (const QStrings) — высылается при изменении текста;
□ сигнал returnPressed () — высылается при нажатии на <Enter>.
( Примечание )
Ошибочно может показаться, что оба класса QLineEdit и QTextEdit
унаследованы от одного класса, определяющего вышеперечисленные методы. На самом
деле каждый из этих классов содержит свои собственные определения и
реализации этих методов.
Класс QTextEdit предоставляет команды для отмены (undo) или повтора
(redo) действий. Количество отмен и повторов равно по умолчанию 100, но
это можно изменить, передав в метод setundoDepth () нужное число. При
ВЫЗОВе СЛОТОВ undo О И redo () посылаются сигналы undoAvailable(bool) и
redoAvaiiabie(booi), сообщающие об успешном или неуспешном
проведении операции.
Глава 9. Элементы ввода 133
Класс QTextEdit способен отображать файлы с кодом на языке HTML (html-
документ), содержащие таблицы и растровые изображения.
Для помещения текста в область виджета можно воспользоваться, помимо
метода setTexto, методами insert о и appendo. При помощи метода
appendo добавленный текст не вносится в список операций, действие
которых можно вернуть с помошью слота undo (), это делает его быстрым и не
требующим дополнительных затрат памяти Вызов метода lines ()
возвращает количество строк текста, а метод find () может быть использован для
поиска и выделения в тексте заданной строки.
С Примечание }
Класс QTextEditor можно использовать совместно с классом
QSyntaxHighiighter для расцветки синтаксиса.
Содержащиеся в тексте гипертекстовые ссылки автоматически
подчеркиваются, но это можно изменить с помощью метода setLinkunderiine ().
Методы zoomino и zoomOuto предназначены для увеличения или уменьшения
размера текста, соответственно, действие этих методов не распространяется
на растровые изображения.
( Примечание )
Если вам требуется только отобразить текст в формате RichText, возможно,
будет лучше воспользоваться классом QLabel или QTextview. который
унаследован ОТ класса QTextEdit.
Для установки атрибутов текста существуют методы:
О setitaiic(booi) — устанавливает курсивный шрифт, если передано true,
а передача false снимает атрибут, если он установлен;
О setBoid(booi) — устанавливает полужирный шрифт, если передано true,
а передача false снимает атрибут, если он установлен;
О setunderiine(booi) — устанавливает подчеркнутый шрифт, если передано
true, а передача false снимает атрибут, если он установлен;
О setcoior (const QCoior&) — устанавливает текущий цвет;
О setCurrentFont (const QFonts) — устанавливает текущий шрифт.
Приведенный на рис. 9.2 пример отображает документ в формате HTML,
^кст которого можно редактировать.
[Дйстинг 9.2. Файл main.cpp
•include <qapplication.h>
•include <qtextedit.h>
•include <qstring.h>
134
Часть II. Элементы управления
I,
int main (int argc, char** argv)
{
QApplication app(argc, argv);
QTextEdit txt;
txt.setTextFormat(QTextEdit::RichText);
txt.setText(
"<HTML>"
"<BODY BGCOLOR=BLUE TEXT=WHITE>"
"<H2XCENTER>THE WINNER TAKES IT ALL</CENTER></H2>"
"<H3><CENTER>(B.Andersson/B.Ulvaeus) </CENTERXH3>"
"<FONT COLOR=YELLOW>"
"<P ALIGN=\"center\">"
"<I>"
"I don't wanna talk<BR>"
"About the things we've gone through<BR>"
"Though it's hurting me<BR>"
"Now it's history<BR>"
"I've played all ma cards<BR>"
"And that's what you've done too<BR>"
"Nothing more to say<BR>"
"No more ace to play<BR><BR>"
"The winner takes it all<BR>"
"The loser standing small<BR>"
"Beside the victory<BR>"
"That's her destiny<BR>"
"</I>"
"</p>"
"</FONT>"
"</BODY>"
"</HTML>"
);
txt.resize(250, 200);
txt.show();
app.setMainWidget (&txt);
return app.exec();
}
В листинге 9.2 создается виджет верхнего уровня txt. Вызовом метода
setTextFormat () производится установка режима работы с текстом в форма-
Глава 9. Элементы ввода
135
те RichText. Метод setText () устанавливает в виджете QTextEdit текст — код
на языке HTML.
[Non-Commercial] - TextEdit ЯП ЕЭ
THE WINNER TAKES IT A
ALL
(B ■ < sson/B.UIvaeus)
Idontw talk
About the thmgt me V» gone thtough
T • • it's hutting me
Now Л'* history
l'¥B played all ma cards
And that's what you've -too
Mot ' > more to sa
Рис. 9.2. Программа, отображающая документ в формате HTML
Многострочное текстовое поле
Класс QMultiLineEdit виджета многострочного текстового поля определен в
заголовочном файле qmultilineedit.h. По назначению он очень похож на
QLineEdit, но разрешает пользователю делать многострочный ввод.
Применяется, например, для создания простого текстового редактора. Унаследован
этот класс от QScroiiview, благодаря чему может автоматически отображать
полосы прокрутки, которые необходимы для показа текста, не
помещающегося в отведенной для него области.
С Примечание ^
С появлением Qt версии 3 класс QMultiLineEdit стал считаться устаревшим.
На смену этому классу пришел класс QTextEdit, обладающий богатыми
возможностями отображения и редактирования текста. В целях совместимости
с кодом программ, написанных с использованием более ранних версий
библиотеки Qt, класс QMultiLineEdit не был ликвидирован, а просто сменил свой
базовый класс С QTabelView на QTextEdit.
Виджет, показанный на рис. 9.3, содержит текст, который может быть
изменен пользователем.
[Non-Commercial] - Mu tUmeEdit -!□
В bo Sparhawk goblins and pixies
Snowman Willow TROLLS and the seven dwarfs
The path goes forever on
Nightwish
Рис. 9.З. Пример многострочного текстового поля
J
136
Часть II. Элементы управления
\ Листинг 9.3. Файл main.cpp
#include <qapplication.h>
#include <qmultilineedit.h>
//
int main (int argc, char** argv)
{
QApplication app(argc, argv);
QMultiLineEdit txt;
txt.append("At the grove, I met the rest-the folk of my fantasies\n"
"Bilbo, Sparhawk, goblins and pixies,\n"
"Snowman, Willow, trolls and the seven dwarfs\n"
"The path goes forever on\n\n"
"Nightwish"
);
txt.setAlignment(Qt::AlignCenter);
txt.resize(300, 100);
txt.show();
app.setMainWidget (&txt);
return app.exec();
}
В листинге 9.3 создается виджет верхнего уровня txt класса QMultiLineEdit.
Вызов метода append о добавляет передаваемый текст к содержимому поля
ввода. В нашем примере эта область до добавления была пуста. Метод
setAlignment о устанавливает режим выравнивания текста по центру.
Счетчик
Виджет счетчика, реализуемый классом QSpinBox, состоит из небольшого
текстового поля и двух стрелок для уменьшения или увеличения числовых
значений. Тем самым виджет предоставляет пользователю доступ к
ограниченному диапазону упорядоченных чисел. Все вводимые значения
проверяются, чтобы предотвратить выход за пределы установленного диапазона.
Диапазон устанавливается методом setRangeO. Можно установить
циклический режим, когда за максимально возможным значением будет следовать
минимально возможное, и наоборот. Этот режим устанавливается вызовом
метода setwrapping () с параметром true.
Значения можно устанавливать при помощи метода setvaiueo, а полу
чать — методом value (). Реализованы два метода пошагового изменения
Глава 9. Элементы ввода
137
значений: stepupo и stepDowno, которые симулируют нажатие на кнопки
стрелок. При изменении значений посылаются сразу два сигнала
valueChanged () — ОДИН С параметром ТИПЭ int, а ДРУГОЙ — С const QStrings.
Можно изменить отображение с помощью методов setprefixo и
setsuffix(). Например вызов следующих методов приведет к тому, что
число будет отображаться в скобках:
pspb->setPrefix("(-) ;
pspb->setSuffix(")");
С помощью метода setspeciaivaiueText () устанавливается текст,
независимо от числового значения, например:
pspb->setSpecialValueText("default");
Также, можно заменить изображение стрелок на символы "+" (плюс) или
"-" (минус), Передав методу setButtonSymbols () флаг PlusMinus.
На рис. 9.4 изображен пример, позволяющий выбирать и устанавливать
числа в диапазоне от 1 до 100.
[ММ-(ШЁЛ
98 MB ~]Ц|
Рис. 9.4. Пример счетчика
^Листинг 9.4. Файл main.cpp
#include <qapplication.h>
#include <qspinbox.h>
//
int main (int argc, char** argv)
{
QApplication app(argc, argv);
QSpinBox spb;
spb.setRanged, 100);
spb.setSuffix(" MB");
spb.setButtonSymbols(QSpinBox::PlusMinus);
spb.setWrapping(true);
spb.show();
app.setMainWidget(&spb);
return app.exec();
}
138
Часть II. Элементы управления
В листинге 9.4 создается виджет счетчика spb. После его создания
производится установка диапазона при помощи метода setRangeO. Вызов метода
setsuffixo с флагом PiusMinus изменяет вид стрелок счетчика на "+" и "—".
Метод setwrapping () устанавливает циклический режим.
Элемент ввода даты и времени
Эти виджеты представляют собой измененные, специально для ввода даты
или времени, счетчики. В общей сложности их три:
П класс QDateEdit — для ввода даты;
□ класс QTimeEdit — для ввода времени;
П класс QDateTimeEdit содержит в себе оба вышеперечисленных виджета.
При изменении даты или времени посылается сигнал vaiuechanged (). Для
класса QDateEdit этот сигнал передает константную ссылку на объект типа
QDate, ДЛЯ QTimeEdit — НЭ объект ТИПЭ QTime, а ДЛЯ QDateTimeEdit — объект
QDateTime.
Пример, изображенный на рис. 9.5, отображает актуальную дату и время
запуска программы, которые можно модифицировать.
|ГТтаташ^1иЫ||
(Ш 07 200ч ^2:25. i3j|
Рис. 9.5. Пример ввода даты и времени
; Листинг 9.5. Файл main.cpp
#include <qapplication.h>
#include <qdatetimeedit.h>
#include <qdatetime.h>
//
int main (int argc, char** argv)
{
QApplication app(argc, argv);
QDateTimeEdit dateTimeEdit (QDateTime: :currentzDateTime () ) ;
dateTimeEdit.show() ;
app.setMainWidget (&dateTimeEdit);
return app.exec();
}
Глава 9. Элементы ввода
139
Как видно из листинга 9.5, при создании виджета QDataTimeEdit в его
конструктор передается актуальная дата и время, возвращенные статическим
МеТОДОМ QDateTime: :currentDateTime (). Виджет ОТОбразИТСЯ на экране ПОСле
вызова метода show ().
Проверка ввода
Объект класса QVaiidator — контролер, гарантирует правильность ввода
пользователя. Для установки объекта класса QVaiidator необходимо
передать еГО В МеТОД setvaiidator (), КОТОрЫЙ СОДврЖИТСЯ В классах QComboBox,
QLineEdit и QSpinBox. Для проверки ввода целых и вещественных чисел
пользуются уже готовыми классами Qintvaiidator и QDoubievaiidator.
Создавая свой класс проверки ввода, нужно унаследовать класс от QVaiidator и
перезаписать метод validate о — в него передается вводимая строка и
позиция курсора. Метод должен возвращать следующие значения:
О QVaiidator: : Invalid — вСЛИ строка не МОЖет быть Принята;
О QVaiidator: Acceptable ИЛИ QVaiidator: :Valid — если Строка МОЖет быть
принята без изменений.
В примере, изображенном на рис. 9.6, пользователю предлагается ввести
свое имя, причем в нем не должны содержаться цифры. Это отслеживается
классом контролера, который контролирует ввод пользователя, не допуская
того, чтобы имя содержало цифры.
I 1Ш.1Я11,.иШМ.Г5Тх11
N ame (T he digits will be not accepted')
JAgnetha
Рис. 9.6. Программа, контролирующая пользовательские данные
^истинг 9.6. Класс NameValidator. Файл main.cpp
♦include <qapplication.h>
^include <qvbox.h>
^include <qlabel.h>
♦include <qlineedit.h>
♦include <qvalidator.h>
♦include <qregexp.h>
// ===„
class NameValidator : public QVaiidator {
Private:
140 Часть II. Элементы управления
public:
NameValidator::NameValidator(QWidget* parent, const char* name)
: QValidator(parent, name)
{
}
virtual State validate(QStrings str, int& pos) const
{
QRegExp rxp = QRegExpC [0-9] ") ;
if (str.contains(rxp)) {
return Invalid;
}
return Valid;
}
};
Как видно из листинга 9.6, класс Namevaiidator унаследован от класса
QValidator. В этом классе производится перезапись метода validate о,
который получает вводимый текст и позицию курсора. Внутри метода
создается объект регулярного выражения rxp (см. гл. 36) и в его конструктор
передается шаблон, представляющий собой диапазон цифр от 0 до 9. В
операторе if, вызовом метода QString::containts(), производится проверка строки
на содержание в ней заданного шаблона. В том случае если совпадение с
шаблоном найдено, метод возвращает значение invalid, сообщая, что ввод
не удовлетворяет заданным критериям, в противном случае возвращается
значение valid и ввод принимается.
j Листинг 9.7. Основная программа. Файл main.cpp
//
int main (int argc, char** argv)
{
QApplication app(argc, argv);
QVBox vbx;
QLabel* plblText =
new QLabel("&Name (The digits will be not accepted!):", &vbx);
QLineEdit* ptxt = new QLineEdit(&vbx);
NameValidator* pnameValidator = new NameValidator(ptxt, 0);
ptxt->setValidator(pnameValidator);
plblText->setBuddy(ptxt);
Гпава 9. Элементы ввода 141
арр.setMainWidget (&vbx);
vbx.showO ;
return app.exec();
}
В листинге 9.7 создается объект надписи (указатель pibiText), в теле
которого вызовом метода set Buddy () устанавливается виджет однострочного
текстового поля — указатель ptxt. После создания объекта контролера
(указатель pnamevaiidator) вызовом метода setvaiidator () производится его
установка в виджете однострочного текстового поля (указатель ptxt).
Резюме
В этой главе мы познакомились с группой виджетов, позволяющих
пользователю осуществлять ввод текста или чисел. Большая часть этих виджетов
обладает всеми необходимыми методами для работы с буфером обмена, а
также поддерживает технологию перетаскивания (drag&drop).
Класс QLineEdit предназначен для ввода одной текстовой строки. Для
многострочного ВВОДа Следует ИСПОЛЬЗОВаТЬ Класс QTextEdit ИЛИ QMultiLineEdit.
Эти классы унаследованы от класса QScroiiview, что позволяет им
отображать текст частями, если для отображения всего текста недостаточно места.
Виджет счетчика (QSpinBox) применяется для ввода числовых значений. Он
осуществляет контроль того, чтобы вводимые значения не выхолили за
пределы установленного диапазона.
Для проверки правильности ввода данных пользователем, в виджетах
классов QComboBox, QLineEdit и QSpinBox можно устанавливать объект контролера
С ПОМОЩЬЮ метода setvaiidator().
ГЛАВА 10
Управление
автоматическим размещением
элементов
Порядок и беспорядок зависят от организации.
Сунь-Цзы
Лейауты (Layouts) — классы размещений. Одна из сильных сторон Qt —
возможность использования лейаутов. Это контейнер, который после
изменения размеров окна автоматически приводит в соответствие размеры и
координаты виджетов, находящихся в нем. Хотя они и не добавляют ничего к
функциональной части самой программы, но, тем не менее, они очень
важны для отображения внешнего вида. Лейаут определяет месторасположение
различных виджетов относительно друг друга.
Конечно, можно "вручную" размещать виджеты в окнах приложения, но это
существенно усложняет разработку. Ведь в этом случае нужно отлавливать и
обрабатывать случаи, когда изменяются размеры окна приложения, для того
чтобы заново упорядочить элементы. Подобные ситуации хорошо известны
программистам на языке Visual Basic, которые вынуждены писать для этого
сложные методы.
Еще один из недостатков размещения "вручную" состоит в том, что если
пишется приложение с поддержкой нескольких языков, то слова в
различных языках имеют разную длину, а значит необходим механизм, который
мог бы в процессе работы программы динамически поправлять и изменять
размеры, координаты виджетов. Классы лейаутов библиотеки Qt выполняют
эту непростую работу за вас.
Qt предоставляет два варианта классов лейаутов:
П лейауты, основанные на QFrame;
П менеджеры лейаутов.
Глава 10. Управление автоматическим размещением элементов 143
Цейауты, основанные на QFrame
С этим типом леиаутов мы уже познакомились в примерах предыдущих
глав. Они автоматически размещают своих потомков в нужном порядке
для упорядочения виджетов необходимо создать виджет лейаута от одного
из классов: qhbox, qvbox или QGrid, передавая в их конструкторе виджет
лейаута в качестве предка.
Все классы этой группы леиаутов унаследованы от класса QFrame, позволяя
им устанавливать различную толщину и стили рамок для обрамления
потомков. По умолчанию рамка не отображается. Подробную информацию о
классе QFrame можно найти в гл. 4.
Класс QHBox
Класс qhbox упорядочивает виджеты-потомки по горизонтали слева
направо. По умолчанию между самими виджетами не остается пустот. Расстояние
между ними можно задать с помощью метода setspacing (), передав в него
требуемое значение в пикселах. Методом setMarginO, определенным в
классе QFrame, можно установить толщину рамки. Пример, показанный на
рис. 10.1 и в листинге 10.1, иллюстрирует применение методов.
setMargin() ' setSpacing()
Рис. 10.1. Размещение кнопок по горизонтали
[Листинг Ю.1. Файл main.cpp
♦include <qapplication.h>
♦include <qpushbutton.h>
♦include <qhbox.h>
//
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QHBox hbx;
hbx.setMargin(5);
hbx.setSpacing(15);
144
Часть II. Элементы управления
new QPushButtonC'A", &hbx);
new QPushButton("B", &hbx);
new QPushButton("C", &hbx);
hbx.showO ;
app.setMainWidget (&hbx);
return app.exec();
}
В листинге 10.1 создается виджет класса qhbox. Метод setMargino
устанавливает толщину рамки 5 пикселов. Метод setspacingo устанавливает между
виджетами расстояние шириной в 15 пикселов. Созданные три кнопки А, В
и С являются виджетами-потомками виджета лейаута ььх.
Вызовом метода setstretchfactoro устанавливается фактор для растяжения
виджетов. В метод передаются два параметра — указатель на сам виджет и
на фактор растяжения. Если в листинг 10.1 после создания кнопок добавить
следующие строки, это приведет к показанному на рис. 10.2 результату.
pcmdA->setMiniraumSize(40, 20);
pcmdB->setMiniraumSize(40, 20);
pcmdC->setMinimumSize(40, 20);
hbx.setStretchFactor(pcradA, 1);
hbx.setStretchFactor(pcmdB, 2);
hbx.setStretchFactor(pcmdC, 3);
Вызовы методов setMinimumSizeO изменяют минимально возможные
размеры кнопок, т. к. по умолчанию рекомендуемая ширина кнопки равна
101 пикселу.
■[Non-Commercial] - НВок НЮ ЕЗ
Рис. 10.2. Кнопки с факторами растяжений
Класс QVBox
Класс qvbox, унаследованный от qhbox, упорядочивает все виджеты-потомки
по вертикали сверху вниз. В остальном он ничем не отличается от класса
qhbox. Если заменить в листинге 10.1 имя класса qhbox на qvbox, to в
результате получится программа, показанная на рис. 10.3.
Гпава 10. Управление автоматическим размещением элементов
145
nfliWJnlxll
f_l
Рис. 10.3. Размещение кнопок по вертикали
Класс QGrid
Виджет класса QGrid располагает своих потомков в табличном порядке. При
этом все виджеты, находящиеся в одном столбце, будут иметь одинаковую
ширину, а виджеты, находящиеся в одной строке, будут иметь одинаковую
высоту. Пример, показанный на рис. 10.4, размещает четыре кнопки А, В, С
и D в таблице размером 2 на 2 ячейки.
\\ \штж\тшщ
Рис. 10.4. Размещение кнопок в табличном порядке
[Листинг 10.2. Файл main.cpp
#include <qappl ication. h>
♦include <qpushbutton.h>
♦include <qgrid.h>
//
int main(int argc, char** argv)
{'
QApplication app(argc, argv);
QGrid grid(2, Qt::Horizontal);
grid.setMargin(5);
grid.setSpacing(15);
new QPushButton("A", &grid);
new QPushButtonC'B", sgrid);
new QPushButton("C", Sgrid);
new QPushButtonC'D", &grid);
146
Часть II. Элементы управления
grid.show();
арр.setMainWidget (&grid);
return арр.exec();
}
В листинге 10.2 создается лейаут табличного размещения grid. В его
конструктор передается количество столбцов и направление заполнения. Метод
setMarginO устанавливает толщину рамки. Вызов метода setspacingO
установит расстояние в 15 пикселов между виджетами. Каждая кнопка, которой
присвоен виджет QGrid в качестве предка, будет по очереди заполнять
таблицу слева направо, сверху вниз.
( Примечание )
Если в листинге 10.2 вместо четырех кнопок разместить больше—
например 10, то тогда будет создана таблица из двух столбцов и пяти строк.
Лейаут-менеджеры
Эта группа предоставляет больше возможностей для горизонтального,
вертикального и табличного размещения. Лейаут-менеджеры способны
управлять размещением не только виджетов, но и самих лейаутов. Это позволяет
конструировать довольно сложные размещения.
Группа не наследуется от класса Qwidget, благодаря чему лейаут-менеджеры
гораздо экономнее и эффективнее в использовании, чем лейауты,
базирующиеся на классе QFrame. Фундаментом для всей группы лейаут-менеджеров
является класс QLayout. Это абстрактный класс, унаследованный сразу от
двух классов: oobject и QLayoutitem (рис. 10.5). Все классы лейаут-менед-
жеров определены в одном заголовочном файле qlayout.h.
| QLayoutitem jj | QObJect
I
QLayout fr
-\ QGridLayout fe
H QBoxLayout fc
-\ QHBoxLayout
J
4 QVBoxLayout
Jl
Рис. 10.5. Иерархия классов лейаут-менеджеров
Глава 10. Управление автоматическим размещением элементов 147
С Примечание }
При создании своего собственного лейаут-менеджера можно унаследовать
класс QLayout, реализовав методы addltem(), setGeomery () и iterator ().
ОТ QLayout унаследованы Классы QGridLayout И QBoxLayout (рис. Ю.5). Класс
QGridLayout управляет табличным размещением, a QBoxLayout наследует два
класса: QHBoxLayout и QVBoxLayout, для горизонтального и вертикального
размещения. Для горизонтального или вертикального размещения можно
воспользоваться и самим классом QBoxLayout, передав в его конструктор
вторым параметром одно из следующих значений:
О LeftToRight — горизонтальное размещение, заполнение производится
слева направо;
О RightToLeft — горизонтальное размещение, заполнение производится
справа налево;
О TopToBottom — вертикальное размещение, заполнение производится
сверху вниз;
О BottomToTop — вертикальное размещение, заполнение производится снизу
вверх.
КлаССЫ QHBoxLayout И QVBoxLayout, унаследованные ОТ QboxLayout, ОТЛИЧаЮТ-
ся от него тем, что в их конструктор не передается параметр, говорящий
о способе размещения — горизонтальный или вертикальный. Производится
горизонтальное или вертикальное размещение слева направо или сверху
вниз.
При помощи метода addwidgeto виджеты добавляются в лейаут, а с
помощью метода addLayout () можно добавлять лейаут-менеджеры.
Кроме метода setspacing (), для установки расстояния между виджетами и
лейаутами существует метод addstretch (), в который передается фактор
растяжения. Образно можно сравнить метод с пружиной, которая находится
Между виджетами и имеет различную упругость, задаваемую параметром
Фактора растяжения.
Вложенные размещения
^о способ размещения одного лейаута в другом — таким образом можно
издавать размещения практически любой сложности. Для организации
воженных размещений существует метод addLayout о. В этот метод вторым
^Раметром передается фактор растяжения для добавляемого лейаута.
Ча рис. 10.6 показан пример вложенного размещения двух лейаут-менед-
^ерОВ. В Лейаут QVBoxLayout Помещается лейаут QHBoxLayout.
148
Часть II. Элементы управления
II HiHiB+iii'iiUWiHEsEll
I » ]]
±_l * I
Рис. 10.6. Вложенное размещение
| Листинг 10.3. Конструктор Layout. Файл Layout.cpp
Layout::Layout(QWidget* pwgt/*= 0*/, const char* pszName/*= 0*/)
: QWidget(pwgt, pszName)
{
QPushButton* pcmdA = new QPushButton("A", this);
QPushButton* pcmdB = new QPushButton("B", this);
QPushButton* pcmdC = new QPushButton("C", this);
QPushButton* pcmdD = new QPushButton("D", this);
QVBoxLayout* pvbxLayout = new QVBoxLayout(this);
QHBoxLayout* phbxLayout = new QHBoxLayout();
phbxLayout->setMargin(5);
pvbxLayout->setMargin(5);
phbxLayout->setSpacing(15);
pvbxLayout->setSpacing(15);
pvbxLayout->addWidget(pcmdA);
pvbxLayout->addWidget(pcmdB);
phbxLayout->addWidget(pcmdC);
phbxLayout->addWidget(pcmdD);
pvbxLayout->addLayout(hbxLayout);
}
В листинге 10.3 приводится фрагмент программы, показанной на рис. 10.6-
Программа будет читаться гораздо лучше, если сначала создать все виджеты,
а затем объекты лейаутов. Поэтому сначала создаются виджеты кнопок,
затем объекты лейаутов. Обратите внимание на их создание — указатель tms
передается только одному из них, самому главному. Это очень важно, т. &
главный лейаут ведет учет всех переданных ему лейаутов в метоДе
addLayout (). В момент своего уничтожения он уничтожает их. Но заметьте
уничтожены будут только лейауты, а не виджеты. Главный лейаут уничто
жается виджетом, указатель которого был передан ему в конструкторе.
k^VViASS^VV
>^ч^^-^-х^Х^^г^\^-^\1
QVBoxLayout
QHBoxLayout
Глава 10. Управление автоматическим размещением элементов
149
Классы лейаутов менеджеров имеют методы для установки толщины рамки
setMarginO, в нашем примере он равен пяти и устанавливается для обоих
лейаутов. После этого производится установка расстояния между виджетами
шириной в 15 пикселов с помощью метода setspacing (). Кнопки
помещаются в лейауты вызовами метода addwidget (), в него передается указатель на
виджет. Вторым параметром можно передавать фактор растяжения, но это
не обязательно. В завершение, из объекта лейаута vbxLayout вызывается
метод addLayout (), добавляя В Него объект лейаута hbxLayout.
Табличное размещение
Для табличного размещения используется класс QGridLayout. С его
помощью можно быстро создавать довольно сложные по структуре
размещения. Таблица состоит из ячеек, позиции которых задаются строками и
столбцами. Добавить виджет в таблицу можно с помощью метода addwidget о,
передав ему позицию ячейки, в которую помещается виджет. Помимо
позиции, с помощью метода addMuitiCeiiwidgeto задается и количество ячеек,
которые будет занимать виджет. В обоих методах в последнем параметре
можно указывать выравнивание (см. табл. 5.1), например, по центру:
playout->addWidget(widget, 17, 1, Qt::AlignCenter);
Фактор растяжения устанавливается методами setRowstretch () и
setcoistretch (), но не для каждого виджета в отдельности, а для строки или
столбца. Расстояние между виджетами также устанавливается для столбцов
ИЛИ Строк Методами addRowSpacing () И addColSpacing ().
Приложение, показанное на рис. 10.7, демонстрирует применение
табличного размещения на примере калькулятора. В примере задействованы
классы стека QVaiuestack и регулярного выражения QRegExp, описанные в гл. 36.
1 |Ц,1,Н,!„1„ШДДЛПИ
ч п
8 9
4
1
_J
2
3
'
1.
7
4
1
о 1
1
8
5
2
k30
9 1
■ |
6 !
3 !
"J
'69
СЕ j
/ {
I 1
г~7)
+ |
Рис. 10.7. Программа, использующая табличное размещение
150
Часть II. Элементы управления
\ Листинг 10.4. Файл main.cpp j
#include <qapplication.h>
#include "Calculator.h"
//
int main(int argc, char** argv)
{
QApplication app(argc, argv);
Calculator calculator;
app.setMainWidget(&calculator);
calculator.setCaption("Calculator");
calculator.resize(230, 200);
calculator.show();
return app.exec();
}
В профамме, приведенной в листинге 10.4, создается виджет калькулятора
calculator. После изменения размера методом resize о вызов метода show о
отображает калькулятор на экране.
I Листинг 10.5. Файл Calculator.h
Hfndef _Calculator_h_
#define _Calculator_h_
#include <qwidget.h>
#include <qvaluestack.h>
class QLCDNumber;
class QPushButton;
class Calculator : public QWidget {
Q_OBJECT
private:
QLCDNuraber* mjplcd;
QValueStack<QString> m_stk;
public:
Calculator(QWidget* pwgt = 0, const char* pszName = 0);
Глава 10. Управление автоматическим размещением элементов 151
QPushButton* createButton(const QStnngS str);
void calculate ( );
public slots:
void slotButtonClickedO;
};
fendif //_Calculator_h_
В определении класса calculator, приведенном в листинге 10.5, содержатся
две переменные: m_picd — указатель на виджет электронного индикатора,
а также mstk — стек для проведения операций вычисления. Метод
createButton () предназначен для создания кнопок калькулятора, а метод
calculate о — для вычисления выражений, находящихся в стеке mstk. Слот
slotButtonClickedO вызывается при нажатии на любую из кнопок
калькулятора.
Листинг 10.6. Конструктор Calculator. Файл Calculator.cpp
Calculator::Calculator(QWidget* pwgt/*= 0*/, const char* pszName/*= 0*/)
: QWidget(pwgt, pszName)
{
QGridLayout* ptopLayout = new QGridLayout(this, 5, 4, 5, 5);
mjplcd = new QLCDNumber(12, this);
mjplcd->setPaletteBackgroundColor(QColor(0x6F, 0x7F, 0));
m_plcd->setSegmentStyle(QLCDNumber::Flat);
m_plcd->setMinimumSize(150, 50);
ptopLayout->addMultiCellWidget(m_plcd, 0, 0, 0, 3);
ptopLayout->addWidget(createButton("CE"), 1, 3);
QChar aButtons[4][4] = {{'7', '8', '9', '/•},
{•4«, '5\ 'б', •*•},
{•1-, '2', '3', '-•},
{'0', •.', •=', •+•}
};
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
ptopLayout->addWidget(createButton(aButtons[i][j]), i + 2, j);
}
}
152
Часть II. Элементы управления
В конструктор класса QGridLayout (листинг 10.6) вторым и третьим
параметром передается количество строк — 5, и столбцов таблицы — 4. Четвертый
параметр задает ширину рамки, равную пяти пикселам, а пятый
устанавливает расстояние между ячейками таблицы равным пяти пикселам. При
создании электронного индикатора в его конструктор первым параметром
передается количество сегментов равное двенадцати. Вызов метода
setPaietteBackgroundCoior () устанавливает индикатору горчичный цвет
фона. Флаг QLCDNumber::Flat, переданный В МвТОД setSegmentStyle (), Задает
сегментам индикатора плоский стиль. Метод setMinimumsizeO
переустанавливает минимально возможные размеры индикатора. Вызовом метода
addMuiticeiiwidget () индикатор добавляется в лейаут-ячейку, заданную
областью от строки 0 до строки 0, от столбца 0 до столбца 3. Кнопка СЕ,
после своего создания методом createButton (), помещается в лейаут методом
addwidgeto на позиции (1,3) (т. е. на пересечении первой строки и третьего
столбца). Надписи остальных кнопок содержит массив aButtons, из
которого ОНИ ИЗВЛекаЮТСЯ В ЦИКЛе, ДЛЯ ИХ СОЗДаНИЯ МеТОДОМ createButton ().
| Листинг 10.7. Метод createButton (). Файл Calculator.cpp
QPushButton* Calculator::createButton(const QStringS str)
{
QPushButton* pcmd = new QPushButton(str, this);
pcmd->setMinimumSize(40, 40);
connect(pcmd, SIGNAL(clicked()), SLOT(slotButtonClicked()));
return pcmd;
}
Метод createButton о, приведенный в листинге 10.7, получает надпись и
создает кнопку нажатия. После этого, вызовом метода setMinimumsizeO для
кнопки производится установка минимально возможных размеров и
соединение сигнала clicked О СО СЛОТОМ slotButtonClicked ().
1 Листинг 10.8. Метод calculate (). Файл Calculator.cpp
void Calculator::calculate()
{
double dOperand2 = m_stk.pop().toDouble();
QString strOperation = m_stk.pop();
double dOperandl = m_stk.pop().toDouble();
double dResult;
if (strOperation == "+") {
dResult = dOperandl + dOperand2;
}
глава Ю- Управление автоматическим размещением элементов 153
if (strOperation == "-") {
dResuit = dOperandl - dOperand2;
}
if (strOperation == "/") {
dResuit = dOperandl / dOperand2;
}
if (strOperation =="*") {
dResuit = dOperandl * dOperand2;
)
m_plcd->display(dResuit);
}
Назначение метода calculate о (листинг 10.8) состоит в вычислении
выражения, содержащегося в стеке mstk. Переменная dOperand2 получает
значение, преобразованное к типу double и снятое с вершины стека. Строковая
переменная strOperation получает символ операции. Переменная dOperandl
получает последнее значение из стека, которое также преобразуется к типу
double. В операторах if производится сравнение символов операции и,
в случае совпадения, производится нужная операция, результат которой
сохраняется в переменной dResuit. После этого, вызовом метода disiayo,
значение переменной dResuit отображается на электронном индикаторе
(указатель m_picd).
{Листинг 10.9. Метод slotButtonClicked (). Файл Calculator.cpp
void Calculator::slotButtonClicked()
(
QString stг = ((QPushButton*)sender())->text();
if (str = "CE") {
m_stk.clear();
m_plcd->display("0");
return;
}
if (str.contains (QRegExpC [0-9]"))) {
str = QString () .setNum(m_j>lcd->value()) + str;
m_plcd->display(str.toDouble());
}
else {
if (m_stk.count() >= 2) {
m_stk.push(QString().setNum(m_plcd->value()));
calculate();
154
Часть II. Элементы управления
m_stk.clear();
m_stk.push(QString().setNum (m_plcd->value()));
if (str != "=") {
m_stk.push(str);
}
}
else {
m_stk.push(QString().setNumim_plcd->value()));
m_stk.push(str);
m_plcd->display(0);
)
}
}
В слоте siotButtonCiickedo (листинг 10.9) производится преобразование
виджета, выславшего сигнал, к типу QPushButton, для присвоения
переменной str текста надписи на кнопке. Если надпись равна "СЕ", то
производится операция сброса — очистка стека и установка значения индикатора в
ноль. Если была нажата цифра, то производится ее добавление в конец
строки цифр, отображаемых индикатором, с последующей актуализацией.
При нажатии любой другой кнопки мы считаем, что была нажата кнопка
операции. Если в стеке находится менее двух элементов, то отображаемое
число и операция заносятся в стек. Иначе, в стек заносится отображаемое
значение и вызывается метод calculate о для вычисления выражения,
находящегося в стеке. После этого стек очищается с помощью метода clear о и
в него записывается значение результата, отображаемое индикатором и
следующая операция. Если выполняется операция "=", то она добавляться
в стек не будет.
Порядок следования табулятора
Пользователь может взаимодействовать с виджетами при помощи мыши и
клавиатуры. В последнем случае для выбора нужного виджета используется
клавиша табулятора — <ТаЬ>. При ее нажатии происходит переход фокуса,
согласно установленному порядку, от одного виджета к другому. Иногда
возникает необходимость в изменении этого порядка. На рис. 10.8 цифрами
изображен порядок смены фокуса с помощью табулятора. При появлении
диалогового окна с тремя кнопками фокус будет установлен на кнопке С и
после нажатия на клавишу табулятора он перейдет к кнопке В, а затем "
к кнопке А.
Изменить ПОРЯДОК МОЖНО С ПОМОЩЬЮ ГЛОбальНОЙ функции setTabOrderl'*
получающей в качестве параметров два указателя на виджеты. Следую^11
вызовы изменят порядок следования табулятора, показанный на рис. ДО- *
на порядок, показанный на рис. 10.9:
Глава 10.
ietTabOrder (А, В);
ietTabOrder (В, С);
Рис. 10.8. Порядок смены фокуса
it^tzn
Рис. 10.9. Измененный порядок
Разделители
Разделители придуманы для возможности одновременного просмотра
различных частей текстовых или фафических объектов. Они реализованы в
классе QSpiitter, определение которого находится в заголовочном файле
qsplitter.h. С помощью виджета разделителя можно располагать виджеты
горизонтально или вертикально. Между виджетами отображается черта
разделителя, которую можно передвигать при помощи мыши, изменяя тем
самым размеры виджетов.
Если существует необходимость того, чтобы виджеты разделителя были
проинформированы об изменении размеров, то тогда нужно вызывать метод
setopaquesize (), передав ему значение true.
Пример, показанный на рис. 10.10, разделяет два виджета класса QTextEdit.
Г"Т!7Ш'Ш!|!НР^11ЯГ-
[pi^ ~~~
Line2
Line3
Lne4
1 Lne5
Ьпеб
j Lne7
|Line1
Line2
Line3
Line4
Lne5
Lne6
Line7
ШЩ
zl
-d
—i
Щ
Рис. 10.10. Разделитель
[Листки
iHr 10.10. Файл main.cpp
lnclude <qapplication.h>
#Include <qsplitter.h>
lriclude <qtextedit.h>
156
Часть II. Элементы управления
И
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QSplitter spl(QSplitter::Vertical);
QTextEdit* ptxtl = new QTextEdit(&spl);
QTextEdit* ptxt2 = new QTextEdit(&spl);
ptxtl->setText("LinelXn"
"Line2\n"
"Line3\n"
"Line4\n"
"Line5\n"
"Line6\n"
"Line7\n"
);
ptxt2->setText(ptxtl->text());
spl.resize(200, 220);
spl.show();
app.setMainWidget (&spl);
return app.exec();
)
В листинге 10.10 в конструктор класса QSiitter передается флаг
QSplitter::Vertical и, тем самым, создается виджет вертикального
разделителя.
( Примечание )
Для создания горизонтального разделителя в конструктор нужно передать
значение QSplitter::Horizontal. Того же эффекта можно добиться, если
передать флаги QSplitter: horizontal И QSplitter::Vertical в МвТОД
setOrientation().
При создании виджетов класса QTextEdit в их конструктор передается адр^
виджета-предка, которым является разделитель. Установка текста в вид#е'
Tax QTextEdit ПРОИЗВОДИТСЯ С ПОМОЩЬЮ МеТОДОВ setText ().
Резюме
Лейауты при изменении размеров окна автоматически производят размер
ние виджетов в нем. Qt предоставляет две группы классов лейаутов: лейа>
("яава 10. Управление автоматическим размещением элементов 157_
фу основанные на классе Qframe, и лейаут-менеджеры. Обе группы
предоставляют горизонтальные, вертикальные и табличные способы размещения.
Зги способы можно комбинировать для создания сложных размещений.
Классы лейаут-менеджеров, в отличие от лейаутов, базирующихся на классе
QFrame, унаследованы от класса QLayout, базирующегося сразу на двух
классах QObject и QLayoutitem. Благодаря этому они более эффективны в
использовании. Лейауты предоставляют возможность установки фактора
растяжения для управления соотношением размеров между виджетами.
Реализованы методы для установки расстояний между виджетами и работы с
рамкой лейаута.
Иногда возникает необходимость в изменении минимального или
максимального размера виджета — это можно сделать с помощью методов
setMinimumSize () И setMaximumSize ().
Виджет разделителя предоставляет возможность одновременного просмотра
содержимого виджетов. При перетаскивании разделителя пользователем
происходит изменение размеров виджетов.
ГЛАВА 11
Цветовая палитра
для элементов управления
Знание некоторых принципов
легко возмещает незнание некоторых фактов.
Гельвеций
Палитра для элементов управления — это таблица, в которой содержатся
цвета, используемые виджетом при отображении на экране. Дело в том, что
цвета виджетов не определены окончательно и могут быть изменены в
любой момент передачей соответствующего цвета текста, цвета фона и т. д.
QPalette
QColorGroup
QColorGroup
I QColorGroup
Foreground, Text,.
Foreground, Text, ...
| Foreground, Text,...
.., Shadow
.., Shadow
..., Shadow
i
Active
Inactive
Disabled
Рис. 11.1. Схема цветовой палитры виджета
Каждый из виджетов содержит в себе объект палитры. Доступ к нему можно
получить с помощью метода palette () класса Qwidget. Сама палитра -"
класс QPalette — состоит из трех основных групп объектов, произведенных
от класса QColorGroup. Эти группы определяют три возможных состояния
виджета: активное (active), неактивное (inactive) и недоступное (disabled)
Каждая группа состоит из 14 цветов. Их описание приведено в табл. 11.1.
( Примечание ^
Класс QPalette использует механизм неявных общих данных, а это означав ■
что все виджеты используют ссылку на один и тот же объект палитры. И в то
Гпава 11- Цветовая палитра для элементов управления 159
случае, если палитра подвергается изменению, то виджет получает свой
собственный объект данных палитры.
Таблица 11.1. 14 цветовых перечислений класса QCoiorGroup
[флаги
Foreground
| Text
Г BrightText
ButtonText
Highlight
HighlightedText
Background
•Base
Button
I Link
LinkVisited
Light
IMidlignt
Dark
Mid
Shadow
Описание
Цвет для рисования, который находится на переднем плане.
По умолчанию этот цвет — черный
Цвет для текста. По умолчанию этот цвет — черный
Яркий цвет текста, отличающийся от цвета фона Background.
Обычно совпадает с Text. По умолчанию этот цвет — черный
Цвет текста для надписей кнопок. По умолчанию — черный
Цвет фона для выделения выбранных элементов. По
умолчанию этот цвет темно-голубой
Цвет текста для выделенных элементов. Представляет собой
контраст к цвету, заданному значением Hightlight. По
умолчанию этот цвет белый
Цвет фона. По умолчанию — светло-серый
Альтернатива цвету фона, часто используется для полей
ввода. Например, цвет фона для поля ввода
Цвет кнопки, как правило, одного цвета с фоном. По
умолчанию — светло-серый
Цвет, используемый для не посещенной гипертекстовой
ссылки. По умолчанию — голубой
Цвет, используемый для обозначения посещенной
гипертекстовой ссылки. По умолчанию — розовый
Цвет для фоновых эффектов. Должен быть светлее цвета
заданного значением Button. По умолчанию этот цвет белый
(см. рис. 11.2)
Цвет для фоновых эффектов. По умолчанию этот цвет
светлосерый (см. рис. 11.2)
Цвет для фоновых эффектов. Должен быть темнее цвета
заданного значением Button. По умолчанию этот цвет темно-
серый (см. рис. 11.2)
Цвет для фоновых эффектов. По умолчанию этот цвет средне-
серый
Цвет для фоновых эффектов. По умолчанию этот цвет черный
(см. рис. 11.2) I
160
Часть II. Элементы управления
На рис. 11.2 показано соответствие некоторых значений, из приведенных
в табл. 11.1 для виджета счетчика.
?1
Light
Shadow
Dark
Base L
^ . . ButtonText
Text1 Button
'Midlight
Рис. 11.2. Некоторые элементы палитры виджета счетчика
В примере, показанном на рис. 11.3, показан виджет счетчика с измененной
палитрой.
I 1И-1Д1Х1|
Рис. 11.3. Приложение, демонстрирующее измененную палитру виджета
| Листинг 11.1. Файл main.срр
#include <qapplication.h>
#include <qspinbox.h>
//
int main (int argc, char** argv)
{
QApplication app(argc, argv);
QSpinBox spb;
QPalette pal = spb.palette();
pal.setColor(QColorGroup::Button, Qt::red);
pal.setColor(QColorGroup::ButtonText, Qt::green);
pal.setColor(QColorGroup::Text, Qt::yellow);
pal.setColor(QColorGroup::Base, Qt::blue);
spb.setPalette(pal);
spb.show();
app.setMainWidget (&spb);
return app.exec();
{пава 1 1- Цветовая палитра для элементов управления 161
0 листинге 11.1 создается виджет счетчика spb и объект палитры pal. При
создании вызовом метода palette о объекту палитры присваивается значе-
ние палитры виджета счетчика. После этого объект палитры подвергается
изменениям с помощью серии вызовов метода setcoioro. В эти методы
передаются вместе с цветами флаги, говорящие о том, чей цвет нужно
поменять (см. табл. 11.1). Полученная палитра устанавливается в виджете
вызовом метода setPaletteU.
В листинге 11.1 с помощью метода setcoioro производится изменение
палитры сразу для всех трех состояний виджета: активного, неактивного и
недоступного. Чтобы задать цветовую палитру для каждой из трех групп в
отдельности, нужно передать 3 объекта, произведенных от класса QCoiorGroup,
Либо В КОНСТруктор QPalette(active, disabled, inactive), либо В методы
setActive (), set Disabled () И setlnactive () класса QPalette.
Можно применять не только цвет, но и образец заполнения. Правда это
имеет смысл только для заполнения площадей, т. к. при рисовании линий
образец будет проигнорирован. Применить образец заполнения для кнопки
МОЖНО при ПОМОЩИ Метода setBrush ():
pal.setBrush(QCoiorGroup::Button, Qt::CrossPattern);
Листинг 11.1 демонстрирует изменение палитры только одного виджета. На
практике это нежелательно — представьте себе приложение, все элементы
которого имеют разные цвета. Поэтому если необходимо изменить палитру
для виджетов, то лучше делать это для всех виджетов сразу,
централизованно. Для этого необходимо передать объект палитры в статический метод
QAppiication::setPaiette(). Создавать палитру желательно так, чтобы в
приложении использовалось не менее 3 и не более 7 цветов.
Листинг 11.2 показывает, как можно установить палитру для всего
приложения. При создании объекта палитры, для задания цвета кнопок и фона,
в конструктор передаются два параметра. Все остальные цвета палитры
автоматически вычисляются на основе этих двух цветов.
г-т»—
[5^тинг 11.2. Файл main.cpp
lnt main (int argc, char** argv)
QApplication app(argc, argv);
QPalette pal(Qt::green.light(200), Qt::blue.light(200));
QApplication::setPalette(pal, true);
}
162
Часть II. Элементы управления
Резюме
Из этой главы вы узнали, что цвета виджетов могут быть изменены в любой
момент времени передачей палитры. Каждый из виджетов содержит свой
собственный объект палитры, который может быть изменен. Палитра вид-
жета состоит из 3-х групп соответствующих активному, неактивному и
недоступному состояниям. В палитре могут участвовать не только цвета, но и
образцы заполнения. Палитру, используемую в приложении, можно
изменить для всех виджетов сразу, с помощью статического метода QAppiication::
setPalette().
ГЛАВА 12
Элементы со стилем
Есть многое на свете, друг Горацио,
что и не снилось нашим мудрецам.
Шекспир
Один из немаловажных аспектов при программировании с использованием
библиотеки Qt — это управление видом и поведением приложения (Look&Feel).
Ввиду того, что Qt-приложения создаются для большого числа платформ,
необходим механизм для изменения их вида и поведения. Это позволит
добиться того, чтобы его внешний вид не выбивался из колеи, будучи
запущенным в какой-либо операционной системе, и не создавалось впечатление
того, что программа на этой платформе чужая.
В Qt внешний вид компонента может быть изменен и это может быть
использовано для того, чтобы разнообразить внешний вид приложения, если
вы относитесь к той группе программистов, которая считает стандартный
вид не совсем подходящим для своих приложений. Например, при
написании компьютерной игры вы наверняка захотите большей дизайнерской
свободы, захотите придать элементам управления новый вид. К изменению
стиля можно прибегнуть и в том случае, если нужно добиться того, чтобы
ваши программы узнавали по их внешнему виду.
Конечно, можно создать классы виджетов, которые будут иметь свой
собственный облик, отличный от стандартного. Но это не совсем удобно, потому
что каждый раз придется реализовывать новые классы, для каждого нового
°блика программы и изменять исходный код. Qt предоставляет специальные
Классы стилей, позволяющие изменять внешний вид и поведение для лю-
j|bix классов виджетов. Стили программ можно изменять в процессе ее
работы, а это значит, что пользователю можно предоставить в меню целый
сПисок стилей, для того чтобы он смог выбрать для своей работы
подходящий. Стили можно создавать самому или использовать уже готовые встро-
еНные в библиотеку Qt стили.
164
Часть II. Элементы управления
Возможность реализации и использования классов стилей, независимо от
кода программы, дает большую свободу, позволяющую разделить разработку
проекта, как минимум, на две команды, которые могут работать независимо
друг от друга. Одна команда может работать над кодом самой программы, а
другая над ее дизайном. Созданные второй командой классы стилей могут
использоваться и в других Qt-проектах.
На рис. 12.1 изображена классовая иерархия стилей. Класс QStyle
представляет собой базовый класс для всех стилей. Он определяет целый ряд
методов для изменения облика и поведения всех виджетов приложения. Для
создания своего собственного стиля можно переопределить эти методы.
Класс QStyle имеет только одного потомка — класс QCommonstyie, который
является общим для всех классов стилей, определяя часто используемые
методы. Поэтому предпочтительнее наследовать именно от этого класса, а
не от QStyle.
С£
1
Ч QObJect
ч
2
QStyle
Ц QCommonStyle ~"|}
QMotiffStyle
3
Н QCDEStyle
J
| QMotifPlusStyle |t
QSGIStyle
3
QWlndowsStyle
4 QPIatlnumStyle j
Рис. 12.1. Иерархия классов стилей
Непосредственно от класса QCommonstyie наследуется целый ряд классов
стилей, называемых встроенными стилями Qt.
Встроенные стили
По умолчанию вид и поведение Qt-приложения определяются
операционной системой, на которой приложение запущено, но это можно изменить и
использовать, например, в ОС Windows встроенный Motif-стиль. Qt предос
тавляет следующие классы встроенных стилей (рис. 12.1): QwindowsStyie*
QMotifStyle, QMotifPlusStyle, QCDEStyle, QPlatinumStyle И QSGIStyle. ЭП1
Глава
12. Элементы со стилем
165
^ассы соответствуют стилю следующих платформ (операционных систем):
VVindows, Motif, CDE (Common Desktop Enviroment), Platinum
(эмулирующий стандартный стиль JavaSwing) и SGI (клон UNIX от Silicon Graphics).
для того чтобы установить стиль в приложении, нужно вызвать метод
setstyieo, передать ему объект стиля или строку с его названием:
Amplication: :setStyle (new QMotifStyle) ;
В этом случае вызывается статический метод QAppiication: :setstyieo,
которому передается динамически созданный объект стиля. Классы стилей
наследуются от класса QObject, не используя механизм объектной иерархии,
й ответственность за уничтожение созданных объектов несет класс
QAppiication. Передача в метод setstyieo объекта стиля приводит к тому,
что этот метод сначала уничтожает, с помощью оператора delete, старый
объект стиля. Поэтому нужно создавать объекты стиля непосредственно в
самом методе setstyieo, как показано в листинге 12.1, и не создавать
дополнительных указателей на эти объекты.
В Qt можно смешивать различные стили, т. е. задавать каждому из виджетов
в отдельности свой собственный стиль. Этого делать не рекомендуется, но,
тем не менее, в целях демонстрации мы проигнорируем это правило
(рис. 12.2).
[Non-Commercial] - Styles - П 13
s Motif MotifPlus- JOE Platinum
В Check Box W Check Box " Check Box Щ Check Box ^ Check Box
Rado Button V Badio Button RadloButtm Q Rad,o Button
pushBu £ushButtm 1 Push Button 1 Eush Button J
Radio Button
Push Button
Рис. 12.2. Пример отображения виджетов с различными стилями
^ртинг 12.1. Файл main.cpp
include
include
include
delude
•include
delude
delude
include
<qapplication.h>
<qgroupbox.h>
<qhbox.h>
<qpushbutton.h>
<qcheckbox.h>
<qradiobutton.h>
<qscrollbar.h>
<qslider.h>
166
Часть II. Элементы управления
И _
void add(QWidget* pwgt, const char* psz)
{
QGroupBox* phgr =
new QGroupBox(l, QGroupBox::Horizontal, psz, pwgt);
phgr->setStyle(psz);
(new QScrollBar(0, 9, 2, 9, 0, Qt::Horizontal, phgr))->setStyle(psz) ;
QCheckBox* pchk = new QCheckBox("Check Box", phgr);
pchk->setChecked(true);
pchk->setStyle(psz) ;
(new QSlider(l, 12, 1, 3, QSlider::Horizontal, phgr))->setStyle(psz);
(new QRadioButton("&Radio Button", phgr))->setStyle(psz);
(new QPushButton("&Push Button", phgr))->setStyle(psz);
}
//
int main (int argc, char** argv)
{
QApplication app(argc, argv);
QHBox hbx;
add(&hbx, "Windows");
add(&hbx, "Motif");
add(&hbx, "MotifPlus");
add(&hbx, "CDE");
add(&hbx, "Platinum");
add(&hbx, "SGI");
app.setMainWidget(&hbx);
hbx.show();
return app.exec();
}
В функции main о листинга 12.1 создается виджет hbx для горизонтального
размещения виджетов. В функцию add о передается ссылка на него и строка
с названием стиля. В самой функции создается виджет группы (указатель
phrg), в котором размещаются создаваемые виджеты. После создания для
установки стиля из каждого виджета вызывается метод setstyleo, в
который передается строка (указатель psz).
В примере, показанном на рис. 12.3, производится изменение стиля ЛЛЯ
всего приложения. Выбрать нужный стиль можно при помощи выпадаюШ^'
го списка.
глава 12. Элементы со стилем
167
SG1
о
Radio Button
Check Box
£ush Button
Рис. 12.3. Пример изменения стиля для всего приложения
i Листинг 12.2. Файл main.срр
#include <qapplication.h>
#include "MyApplication.h"
И
int main (int argc, char** argv)
{
QApplication app(argc, argv);
MyApplication myApplication;
app.setMainWidget(&myApplication);
myApplication.show();
return app.exec();
}
В основной программе (листинг 12.2) создается виджет класса
MyApplication. Определение этого класса приведено в листинге 12.3, а
реализация — в листинге 12.4.
|§стинг 12.3. Файл MyApplication.h
Kifndef _MyApplication_h_
♦define _MyApplication_h_
♦include <gvbox.h>
// ===^
class MyApplication : public QVBox {
QJDBJECT
Public:
MyApplication(QWidget* pwgt = 0, const char* pszName = 0);
168
Часть II. Элементы управления
public slots:
void slotChangeStyle(const QString& str);
};
#endif //_MyApplication_h_
Класс MyAppiication (листинг 12.3) наследуется от класса qvbox,
чтобы виджеты-потомки автоматически размешались в вертикальном
порядке, сверху вниз. Помимо конструктора, в классе реализуется слот
slotChangeStyle (const QString&), КОТОрЫЙ ВПОСЛедСТВИИ будет соединен с
виджетом выпадающего списка.
I Листинг 12.4. Файл MyApplication.cpp
#include <qapplication.h>
#include <qcombobox.h>
#include <qpushbutton.h>
#include <qradiobutton.h>
#include <qcheckbox.h>
#include <qslider.h>
#include <qspinbox.h>
#include "MyAppiication.h"
//
MyAppiication::MyAppiication( QWidget* pwgt /*= 0*/,
const char* pszName/*= 0*/
)
: QVBox(pwgt, pszName)
{
QComboBox* pcbo = new QComboBox(this);
pcbo->insertItem("Windows");
pcbo->insertItem("Motif"); N
pcbo->insertItem("MotifPlus");
pcbo->insertItem("CDE");
pcbo->insertItem("Platinum");
pcbo->insertItem ("SGI");
connect(pcbo,
SIGNAL(activated(const QString&)),
SLOT(slotChangeStyle(const QString&))
);
new QSpinBox(this);
new QSliderd, 12, 1, 3, Horizontal, this);
Глава 12. Элементы со стилем 169
new QRadioButton("&Radio Button", this);
new QCheckBox("&Check Box", this);
new QPushButton("&Push Button", this);
}
/t
void MyApplication::slotChangeStyle(const QString& str)
i
QApplication::setStyle(str);
}
В конструкторе класса MyApplication (листинг 12.4) создается виджет
выпадающего списка, в который методом insertitemO добавляются имена
стилей. После ЭТОГО СИГНал activated (const QString&), при ПОМОЩИ метода
connect (), соединяется СО СЛОТОМ slotChangeStyle (const QString&). Тем са-
мым слот получает строку со стилем, выбранную пользователем. Она
передается в статический метод setstyieo, осуществляющий установку стиля
для всего приложения.
Создание собственных стилей
Прежде чем реализовать свой собственный стиль, нужно решить, какой из
классов иерархии стилей унаследовать. Можно унаследовать и сам класс
QStyle, переопределив необходимые методы. Но это потребует гораздо
больше усилий, чем при наследовании класса, уже обладающего
необходимыми свойствами. Воспользовавшись подобным классом, вам потребуется
перезаписать лишь несколько методов, в которых будут реализованы все
Необходимые ОТЛИЧИЯ. Это МОГУТ быть методы: drawltena (), drawPrimitiveO,
drawControl(), drawControlMask(), drawComplexControl() И drawComplexCont-
roiMasko. В каждый из этих методов передается целый ряд параметров,
предоставляющих всю необходимую информацию для отображения вид-
жетов.
Пример, изображенный на рис. 12.4, реализует стиль, изменяющий вид
КНОПКИ нажатия QPushButton.
[пДЕЯ-1р1х|||
Рис. 12.4. Пример реализации стиля кнопки
170
Часть II. Элементы управления
\ Листинг 12.5. Файл main.cpp
#include <qapplication.h>
#include <qvbox.h>
#include <qpushbutton.h>
#include "CustomStyle.h"
//
int main (int argc, char** argv)
{
QApplication app(argc, argv);
QVBox vbx;
new QPushButton ("Buttonl", &vbx);
new QPushButton ("Button2", &vbx);
new QPushButton ("Button3", &vbx);
app.setMainWidget(&vbx);
app.setstyle(new CustomStyle);
vbx.resize(80, 80);
vbx.show();
return app.exec();
}
В функции main о (листинг 12.5) создаются три виджета кнопок нажатия,
произведенных от класса QPushButton. Вызов статического метода setstyieo
устанавливает СТИЛЬ CustomStyle.
Листинг 12.6. Файл CustomStyle.h
#ifndef _CustomStyle_h_
#define _CustomStyle_h_
#include <qwindowsstyle.h>
class QPainter;
/ / =„====_„===================================
class CustomStyle : public QWindowsStyle {
public:
virtual void drawControl( ControlElement element,
QPainter* ppainter,
const QWidget* pwgt,
const QRect& r.
ffiaea 12. Элементы со стилем
171
);
fendif //_CustomStyle_h_
const QColorGroups
SFlags
const QStyleOptions
) const;
colorGroup,
flag,
option
3 листинге 12.6 класс стиля customstyie наследуется от класса
QWindowsstyie. В унаследованном классе необходимо переписать метод
drawControi о, предназначенный для отображения элементов управления
(control element). Кнопка нажатия попадает в эту категорию. Первый
параметр element содержит в себе идентификатор элемента, который должен
быть нарисован, используя объект QPainter (указатель ppainter) в регионе,
передаваемом в объекте г класса QRect. Параметр pwdt представляет собой
указатель на виджет, который может быть преобразован к нужному типу.
Используемые палитры цветов передаются в параметре colorGroup
класса QCoiorGroup. В параметре flag передаются значения перечисления
QStyie::styieFiags, управляющие тем, как должен быть нарисован элемент
управления. Параметр option содержит в себе дополнительную
информацию, не несущую особой нагрузки, которая может понадобиться виджету
для отображения В нем может, например, быть передано значение толщины
линии, которое возвращает метод linewidtho, значение цвета,
возвращаемое методом color (), и т. д. Этот параметр удобно использовать в тех
случаях, если вы создаете не только свой собственный стиль, но и свои
собственные виджеты. Тогда, унаследовав класс QstyieOption, можно передавать
для своего стиля любую нужную для ваших виджетов дополнительную
информацию.
WjeniHr 12.7. Файл CustomStyle.cpp
♦include <qpainter.h>
♦include <qpushbutton.h>
♦include <qpixmap.h>
♦include "Customstyie.h"
H
v°id Customstyie::drawControi( ControlElement element,
QPainter* ppainter,
const QWidget* pwgt,
const QRectS r,
const QColorGroups colorGroup,
SFlags flag,
172 ; Часть II. Элементы управление
const QStyleOption& option
) const
{
const QPushButton* pond - ::qt_cast<const QPushButton*>(pwgt);
switch(element) {
case CE_PushButton:
ppainter->drawPixmap(r, pcmd->isDown() ? QPixmap("btnprs.bmp")
: QPixmap("btn.bmp")
);
break;
case CE_PushButtonLabel:
drawltem(ppainter,
r,
AlignCenter I ShowPref ix,
colorGroup,
pcmd->isEnabled(),
pcmd->pixmap(),
pcmd->text(),
-1,
(pcmd->isDown()) ? scolorGroup.brightText()
: scolorGroup.buttonText()
break;
default:
QWindowsStyle::drawControl(element,
ppainter,
pwgt,
r,
colorGroup,
flag,
option
);
В листинге 12.7 производится попытка преобразования указателя
переданного виджета к указателю на кнопку нажатия. В случае неудачи указатель
будет равен нулю и, при необходимости, это можно проверить. Нам этого
делать не требуется, т. к. указатель применяется в секциях оператор3
switch — СЕ PushButton И СЕ PushButtonLabel — ДЛЯ КНОПКИ нажатия. В сеК'
(пава 12. Элементы со стилем 173_
ции CEPushButton вызовом метода drawpixmapo производится отображение
самой кнопки в области, определяемой объектом г. В зависимости от того,
нажата кнопка или нет, используются два разных растровых изображения —
btnprs.bmp или btn.bmp, соответственно. Состояние кнопки проверяется с
помощью метода isDownO. Секция CEPushButtonLabel нужна для
отображения надписи на кнопке. В этой секции производится вызов метода
drawitemo, используемый для отображения текста. В этот метод
передаются, кроме известных параметров, еще и параметры, управляющие
отображением самой надписи:
О AlignCenter — для отображения надписи в середине;
О showPref ix — для подчеркивания буквы, стоящей после символа &.
Текст на кнопке задается седьмым параметром. Восьмой параметр задает
количество символов отображаемого текста — длину, значение, равное —1
говорит о том, что текст должен быть отображен целиком. В девятом
параметре, в зависимости от того, нажата кнопка или нет, передается цвет
надписи.
В секции default вызывается метод drawControlO класса QWindowsStyle ДЛЯ
того, чтобы иметь возможность отображать виджеты, не измененные
классом CustomStyle.
Резюме
Библиотека Qt предоставляет возможность использования в программах
различных стилей (Look&Feel) и их смены в процессе работы самой
программы. Стиль может устанавливаться для всего приложения и для каждого
виджета в отдельности. Как правило, один стиль устанавливается сразу всем
виджетам приложения и для этого используется статический метод
QApplication:: setStyle (}.
Каждый стиль имеет отличия во внешнем виде и поведении. В
распоряжении программиста имеются готовые стили, которые можно установить в
приложении. На основе этих классов удобно создавать свои собственные
стили. Нужно лишь унаследовать один из них и переписать методы,
необходимые для реализации отличий.
ЧАСТЬ III
События и взаимодействие
с пользователем
Глава 13. События
Глава 14. Фильтры событий
Глава 15. Искусственное создание событий
ГЛАВА 13
События
Что-то происходит,
но ты не знаешь что.
А вы знаете, мистер Джонс?
Боб Дшюн,
"Баллада худого человека"
Обработка событий лежит в основе работы каждого приложения, имеющего
пользовательский интерфейс. Событие можно охарактеризовать как
механизм оповещения приложения о происшествии чего-либо. Например,
пользователь нажал кнопку мыши или клавишу клавиатуры, и это приведет к
созданию события мыши или клавиатуры. Также, событие будет создаваться
и при возникновении необходимости перерисовки содержимого окна.
Очевидно, что основная масса событий тесно связана с действиями,
предпринимаемыми пользователем. Но есть и события, создаваемые самой
операционной системой, например события таймера. Все события помещаются
в соответствующую очередь для их дальнейшей обработки.
Но ведь если "что-то происходит", то высылаются сигналы. Зачем же тогда
нужны события?
Механизм сигналов и слотов, по сравнению с событиями, представляет
собой механизм более высокого уровня, предназначенный для коммуникации
непосредственно объектов. Например, нажатие кнопки приводит к
оповещению о происходящем всех подключенных к сигналу объектов. События
оповещают объекты о действиях пользователя общего и детального
характера, например — перемещение указателя мыши или нажатие какой-либо
клавиши клавиатуры. Другими словами, воспользовавшись стандартным'1
сигналами, вы можете сделать заключение о том, что кнопка была нажата, а
узнать координаты указателя мыши в момент ее нажатия не представляется
возможным. Для получения подобного рода информации понадобятся объ'
екты событий. В этих объектах очень часто содержится дополнительная
информация. В частности, в объекте события мыши QMouseEvent передаю^
координаты ее местоположения и код кнопки, нажатой пользователем.
tfgea
13. События
177
Использование событий становится интересным при создании собственных
риджетов, поскольку очень часто сигналы высылаются из методов
обработки событий. Например, при щелчке мыши на виджете можно из метода
обработки события mousePressEvent () ВЫСЛЭТЬ СИГНЭЛ clickedO.
( Примечание ^
Следует учитывать, в Qt все методы обработки событий определены как
virtual protected. Поэтому при перезаписи этих методов в унаследованных
классах желательно определять их как protected.
Есть еще одно отличие сигналов от событий — события могут быть
обработаны одним методом, а сигналы могут обрабатываться неограниченным
количеством соединенных с ними слотов.
Qt предоставляет целый ряд классов для различного рода событий:
клавиатуры, мыши, таймера и др. Все классы событий определены в одном
заголовочном файле qevent.h. На рис. 13.1 представлена иерархия классов
событий Qt.
"?
2
QEvent
T-|QChlldEvent \
Н QPropEvent ~\
Ц QDragMoveEvent ""fe
Ц QDragEnterEvent j|
Н QCIoseEvent к
\-\ QCustomEvent
QDragLeaveEvent"^
3
1
QFocusEvent
QHideEvent
QKeyEvent
QTimerEvent
Lj QWheelEvent
Рис. 13.1. Иерархия классов событий
178
Часть III. События и взаимодействие с пользователем
Как видно из рис. 13.1, класс QEvent является базовым для всех категорий
событий. Его объекты содержат информацию о типе произошедшего
события. Для каждого типа события имеется целочисленный идентификатор,
который устанавливается в конструкторе QEvent и может быть получен при
ПОМОЩИ метода QEvent:: type ().
Перезапись специализированных
методов обработки событий
Перезапись специализированных методов событий является самым
распространенным способом их обработки. Для того чтобы обрабатывать
определенное событие, необходимо унаследовать нужный класс и просто
перезаписать нужный метод обработки события. В этот метод передается указатель
на объект события, который содержит информацию о нем. Каждый метод
получает объект соответствующего типа, например, методы keyPressEvento
И keyReleaseEvent () ПОЛучаюТ указатель на объект класса QKeyEvent.
События клавиатуры
Класс QKeyEvent
Класс QKeyEvent содержит данные о событиях клавиатуры. С его помощью
можно получить информацию о клавише, вызвавшей событие, а также
ASCII-код символа (American Standard Code for Information Interchange,
Американский стандартный код для обмена информации), который был
отображен. Объект события передается в методы QWidget::keyPressEvento и
QWidget::keyReleaseEvent(), определенные в классе QWidget. Это событие
может вызываться нажатием любой клавиши на клавиатуре, включая
<Shift>, <Ctrl>, <Alt>, <Esc> и <F1>—<F12>. Исключение составляют
клавиша табулятора <Tab> и ее совместное нажатие с клавишей <Shift>,
которые используются методом обработки QWidget:: event () для передачи фокуса
следующему виджету.
Метод keyPressEvento вызывается каждый раз при нажатии одной из
клавиш на клавиатуре, а метод keyReleaseEvent () — при отпускании клавиши.
Если необходимо узнать, были ли совместно с клавишей нажаты клавиши-
модификаторы <Ctrl>, <Shift> и/или <Alt>, то это можно проверить с
помощью метода state ().
В случае включения режима клавишного сжатия с помощью метода
QWidget: :setKeyCompression (true) объект события будет содержать все
СИМВОЛЫ, представляющие собой нажатые клавиши с момента первого вызова
этого события.
fTgea*3-
П СЭМОМ Методе Обработки СОбыТИЯ МОЖНО ВЫЗОВОМ Метода QKeyEvent: :
key О определить, какая из клавиш инициировала его. Этот метод
возвращает значение целого типа, которое можно сравнить с константами клавиш,
оПределенными в классе Qt (табл. 13.1).
Таблица 13.1. Значения перечислений Key класса Qt
[константа
[кеу Escape
[кеу Tab
[Key Backspace
Гкеу Backspace
ГK6y_Return
[Key_Enter
Key_Insert
Key_Delete
Key_Pause
Key_Print
Key_Home
Key_End
[Key_Left
[Key_Up
[Key_Right
[Key_Down
[Key_PageUp
l2^y_PageDovm
[Key^hift
|^y_Control
[teyjut
|tey_CapsLock
|^ey_NumLock
[JeyjScrollLock
|Key_Fl
[Key_F2
Значение
0x1000
0x1001
0x1003
0x1003
0x1004
0x1005
0x1006
0x1007
0x1008
0x1009
0x1010
0x1011
Константа
Key_F3
Key_F4
Key_F5
Key_F6
Key_F7
Key_F8
Key_F9
Key_F10
Key_Fll
Key_Fl2
Key_Space
Key_NuniberSign
0x1012 | Key_Dollar
0x1013 Key_Percent
0x1014 | Key_Ampersand
0x1015 U Key_Apostrophe
0x1016 В Key_ParenLeft
0x1017 H Key_ParenRight
0x1020
0x1021
0x1023
0x1024
0x1025
0x1026
0x1030
0x1031
Key_Asterisk
Key_Plus
Key Comma
Key_Minus
Key_Period
Key_Slash
Key_0
Key_l
Значение
0x1032
0x1033
0x1034
0x1035
0x1036
0x1037
0x1038
0x1039
0x103A
ОхЮЗВ
0x0020
0x0023
0x0024
0x0025
0x0026
0x0027
0x0028
0x0029
0x002A
0x002B
0x002C
0x002D
0x002E
0x002F
0x0030
0x0031
180 Часть III. События и взаимодействие с пользователей
Таблица 13.1 (окончание)
Константа
Кеу_2
|Кеу_3
| Кеу_4
Кеу_5
Гкеу_6
Кеу_7
|Кеу_8
Кеу_9
Кеу_Со1оп
Key_Semicolon
Key_Less
Key_Equal
Key_Greater
Key_Question
Key_A
Key_B
Key_C
Key_D
Key_E
Key_F
Key_G
Значение
0x0032
0x0033
0x0034
0x0035
0x0036
0x0037
0x0038
0x0039
ОхООЗА
0x003В
ОхООЗС
0x003D
ОхООЗЕ
0x003F
0x0041
0x0042
0x0043
0x0044
0x0045
0x0046
0x0047
Константа
Кеу_Н
Key_J
Key_J
Кеу_К
Key_L
Кеу_М
Key_N
Кеу_0
Кеу_Р
Key_Q
Key_R
Key_S
Key_T
Key_U
Key_V
Key_W
Key_X
Key_Y
Key_Z
Key_Backslash
Значение
0x0048
0x0049
0x004A
0x004B
0x004C
0x004D
0x004E ^|
0x004F "1
0x0050 1
0x0051 J
0x0052
0x0053 J
0x0054 1
0x0055 1
0x0056 |
0x0057 |
0x0058 |
0x0059 j
0x005A 1
0x005C I
Класс СОбЫТИЯ Клавиатуры QKeyEvent СОДерЖИТ МеТОДЫ accept О И ignore О,
с помощью которых устанавливается или сбрасывается специальный флаг,
который необходим для дальнейшей обработки события объектом-предком-
Например, если был вызван метод ignore о, то событие клавиатуры будет
передано дальше на обработку объекту-предку.
Вызовом метода asciio можно узнать ASCII-код символа. Этот метод
может оказаться полезным в том случае, если потребуется обеспечить в видже-
те ввод с клавиатуры. Для клавиш модификаторов <Shift>, <Ctrl>, <Alt> и
<F1>—<F12> этот метод возвращает 0.
Гпава 13. События 181
Метод для обработки событий клавиатуры класса, унаследованного от
QWidget, может выглядеть следующим образом.
void MyCalss::keyPressEvent(QKeyEvent* e)
{
switch (e->key()) {
case Key_Z:
if (e->state() & ShiftButton) {
// Произвести какие-либо действия
}
else {
// Произвести какие-либо действия
}
break;
default:
QWidget::keyPressEvent(e); // Передать событие дальше
}
}
В данном примере производится проверка совместного нажатия клавиш
<Z> и <Shifl>. Для проверки статуса значения, возвращаемого методом
state о, могут использоваться значения, указанные в табл. 13.2.
Класс QFocusEvent
Когда пользователь набирает что-нибудь на клавиатуре, информацию о
нажатых клавишах может принимать только один виджет. Если виджет
установлен для приема информации с клавиатуры, то тогда говорят, что он
находится в фокусе. Объект события фокуса QFocusEvent передается в методы
обработки сообщений focusinEvento и focusOutEvent (). Этот объект не
содержит значимой информации. Основное назначение QFocusEvent —
распознать получение виджетом фокуса для того, чтобы можно было, например,
изменить его внешний вид. Эти методы вызываются в том случае, когда
виджет получает или теряет фокус. Метод focusOutEvent () вызывается при
потере фокуса, метод focusinEvento — при получении фокуса.
Событие обновления контекста рисования.
Класс QPaintEvent
Вполне возможно, вас удивит то, что некогда выведенная в окно
графическая информация вдруг исчезнет при изменении размеров окна приложения
или после перекрытия его окном другого приложения. Чтобы этого не
произошло, Необходимо ПОЛучатЬ И Обрабатывать Событие QPaintEvent. В ОбЪ-
е*те класса QPaintEvent записана информация для перерисовки всей
графической информации или же ее части. Событие возникает тогда, когда вид-
182
Часть III. События и взаимодействие с пользователем
жет впервые отображается на экране явным или неявным вызовом метода
show(). Событие перерисовки возникает в результате вызова методов
repaint () И update (). Объект события передается В метод paintEvent ().
В нем обычно реализуется отображение самого виджета. В большинстве
случаев этот метод используется для полной перерисовки виджета. Для
виджетов маленьких размеров это вполне приемлемо, но для виджетов
больших размеров рациональнее перерисовывать только отдельную область,
действительно нуждающуюся в этом. Для получения координат и размеров
такого участка вызывается метод region (). Вызовом метода contains ()
можно проверить, находится ли объект в заданном регионе или нет. Происходит
это следующим образом:
MyClass::paintEvent(QPaintEvent* pe)
{
QPainter painter(this);
QRect r(40, 40, 100, 100);
if (pe->region().contains(r)) {
painter.drawRect(r) ;
}
}
События мыши
Мышь дает возможность пользователю быстро указывать на объекты,
находящиеся на экране компьютера. С ее помощью можно проводить над
объектами различные манипуляции, которые невозможно или неудобно
выполнять с помощью клавиатуры.
Самое большое преимущество мыши перед клавиатурой состоит в том, что
указывание на предметы реального мира — это естественное действие для
человека, заложенное в нем еще с его раннего детства, чего не скажешь о
работе с клавиатурой. Мышь можно охарактеризовать как продолжение
руки человека, располагающееся в виртуальном пространстве, с помощью
которого можно производить разного рода операции. Например, указывать на
объекты, выбирать их, перемещать с одного места на другое. Реализация
событий мыши сложнее других событий, т. к. программа должна быть в
состоянии определить, какая кнопка нажата, удерживается она или нет, был
ли произведен двойной шелчок и какие клавиши клавиатуры были нажаты
в момент возникновения события.
Класс QMouseEvent
Объект этого класса содержит информацию о событии, вызванном
действием мыши. Он хранит в себе информацию о позиции указателя мыши в мо-
глава 13. События 183
мент вызова события, статус кнопок мыши и даже некоторых клавиш
клавиатуры. ЭТОТ Объект передается В методы mousePressEvent (), mouseMoveEvent (),
-OUseReleaseEvent() И mouseDoubleClickEvent().
Доетод mousePressEvent () вызывается тогда, когда произошло нажатие на
ОДНУ из кнопок мыши в области виджета. Если, нажав кнопку мыши и не
отпуская ее, переместить указатель мыши за пределы самого виджета, то он
будет получать события мыши до тех пор, пока кнопка не будет отпущена.
Зго событие будет передаваться в метод mouseMoveEvent о, а при отпускании
КНОПКИ Произойдет ВЫЗОВ метода mouseReleaseEvent ().
По умолчанию метод mouseMoveEvent () вызывается при перемещении
указателя мыши, но только при предварительном нажатии одной из кнопок
мыши. Это позволяет не создавать лишних событий во время простого
перемещения указателя мыши. Если же получать события при простом
перемещении указателя мыши, то тогда нужно воспользоваться методом
setMouseTracking () класса Qwidget, передав ему, в параметре, значение true.
Метод mouseDoubleClickEvent () вызывается при двойном щелчке кнопкой
мыши в области виджета.
Для определения местоположения указателя мыши в момент возникновения
СОбЫТИЯ МОЖНО ВОСПОЛЬЗОВатЬСЯ методами globalXO, globalYO, х() И у(),
которые возвращают целые значения. Также можно воспользоваться
методами pos () или giobaipos (). Метод pos () класса QMouseEvent возвращает
позицию указателя мыши в момент наступления события. Это позиция
относительно левого верхнего угла виджета. Если нужна абсолютная позиция
относительно левого верхнего угла экрана, то ее получают вызвав метод
globalPos ().
i метод button о, можно узнать, какая из кнопок мыши была нажата
в момент наступления события. Реализован метод state о, с помощью
которого получают состояние кнопок мыши и клавиш клавиатуры <Shift>,
<Ctrl> и <Alt> в момент наступления события. Возвращаемое значение
является битовой комбинацией LeftButton, RightButton, MidButton, NoButton,
ShiftButton, AitButton и ControlButton. Как видно из табл. 13.2, значения не
пересекаются, поэтому можно применять операцию логического ИЛИ для
Их объединения.
^Примечание )
Одновременное нажатие сразу двух кнопок мыши приведет к тому, что будут
созданы два события.
Метод mouseDoubleClickEvent () вызывает метод mousePressEvent () дважды,
т- к. двойной щелчок обрабатывается как два простых нажатия. По
умолчанию интервал двойного щелчка составляет 400 мс и для настройки этого
Интервала МОЖНО вызвать метод setDoubleClicklnterval () Класса QApplication.
184 Часть III. События и взаимодействие с пользователей
Таблица 13.2. Значения перечисленй Buttonstate
класса Qt
Константа
NoButton
LeftButton
RightButton
MidButton
MouseButtonMasк
ShiftButton
ControlButton
AltButton
KeyButtonMasк
Keypad
Значение
0x0000
0x0001
0x0002
0x0004
0x0007
0x0008
0x0010
0x0020
0x0038
0x4000
[Non-Commercial] - MousePaint ИП D
Ш
Рис. 13.2. Программа виртуальной доски для рисования
На рис. 13.2 показана программа, которая представляет собой виртуальную
доску для рисования. При нажатии левой кнопки мыши можно чертить
линии, а нажатием правой кнопки стирать старое изображение. Ее реализация
приведена в листингах 13.1 — 13.3.
| Листинг 13.1. Файл main.cpp
#include <qapplication.h>
#include "MousePaint.h"
//
int main(int argc, char** argv)
{
QApplication app(argc, argv);
MousePaint mousePaint;
грява 13. События 185
арр.setMainWidget(&mousePaint);
mousePaint.setCaption("MousePaint");
mousePaint.show();
return app.exec();
}
| Листинг 13.2. Файл MousePaint.h
fifndef _MousePaint_h_
fdefine _MousePaint_h_
linclude <qwidget.h>
finclude <qpoint.h>
linclude <qpixmap.h>
u «_=============„========================
class MousePaint : public QWidget {
Q_OBJECT
private:
QPoint m_pt;
QPixmap mjpix;
protected:
virtual void paintEvent (QPaintEvent* pe);
virtual void mousePressEvent(QMouseEvent* pe);
virtual void mouseMoveEvent (QMouseEvent* pe);
public:
MousePaint(QWidget* pwgt = 0, const char* pszName :
*endi f //_Mous ePa int_h_
В классе MousePaint определены две переменные: m_pt — для хранения ко-
°РДинат указателя мыши, m_pix — для буфера растрового изображения.
Листинг 13.3. Файл MousePaintxpp
•include <qpainter.h>
'include "MousePaint.h"
H°UsePaint: :MousePaint(QWidget* pwgt/*= 0*/, const char* pszName/*= 0*/)
: QWidget(pwgt, pszName)
186
Часть III. События и взаимодействие с пользователе^
mjpix.resize (width(), height ()) ;
mjpix.fill (white) ;
}
//
/*virtual*/void MousePaint::paintEvent(QPaintEvent*)
{
::bitBlt(this, 0, 0, &m_pix);
}
//
/♦virtual*/void MousePaint::mousePressEvent(QMouseEvent* pe)
{
if (pe->button() & QMouseEvent::RightButton) {
m_pix.fill(white);
repaint();
}
else {
QPainter painter;
painter.begin(this);
painter.drawPoint(pe->pos());
m_pt = QPoint(pe->pos());
painter.endО ;
ubitBltt&m^ix, 0, 0, this) ;
}
}
//
/*virtual*/void MousePaint:imouseMoveEvent(QMouseEvent* pe)
{
QPainter painter;
painter.begin(this);
painter.drawLine (m_pt, pe->pos());
m_pt = QPoint(pe->pos());
painter.end();
: :bitBlt(&m_j3ix, 0, 0, this);
}
В конструкторе класса размеры объекта буфера растрового изображения
приводятся, с помощью метода resize о, в соответствие с размерами вид*6'
та, после вызывается метод fill о и буфер заполняется белым цветом. Ме'
Гпава 13. События 187_
^д paintEvent() ПРОИЗВОДИТ При ПОМОЩИ ГЛОбаЛЬНОЙ фуНКЦИИ bitBlt()
копирование растрового изображения из буфера в отображаемую область.
g методе mousePressEvento производится отслеживание нажатия правой
кнопки мыши для очистки изображения
Класс QWheelEvent
Ввиду того, что в обращении можно встретить все больше мышей,
оснащенных колесиком, рекомендуется реализовывать метод для обработки
события прокрутки КОЛеса МЫШИ — QWheelEvent.
Объект класса QWheelEvent содержит информацию о событии, вызванном
колесиком мыши. Это колесико находится, в большинстве случаев, между
левой и правой кнопками. Объект события передается в метод wheeiEvento
и содержит информацию об угле и направлении, в которое было повернуто
колесико, а также о позиции указателя мыши, статусе кнопок мыши и
некоторых клавиш клавиатуры. Наряду с методами state о, post) и
giobaiPosO, которые полностью идентичны методам класса события
QMouseEvent, реализован метод delta о, с помощью которого можно узнать
угол поворота колесика мыши. Положительное значение свидетельствует
о том, что колесико было повернуто от себя, а отрицательное значение —
на себя.
Событие таймера. Класс QTimerEvent
Объект класса QTimerEvent содержит информацию о событии,
инициированном таймером. Этот объект передается в метод обработки события
timerEvent (). Объект события содержит идентификационный номер
таймера. Например, для класса, унаследованного от QWidget, метод обработки
этого события может выглядеть следующим образом:
void MyClass: .-timerEvent (QTimerEvent* e)
i
if (event->timerld() == myTimerld) {
// Произвести какие-либо действия
}
else {
QWidget::timerEventСе); // Передать событие дальше
}
События перетаскивания drag&drop
Тема drag&drop подробно рассмотрена в гл. 27. Поэтому офаничимся лишь
катким описанием классов событий.
188
Часть III. События и взаимодействие с пользователем
Класс QDragEnterEvent
Класс события QDragEnterEvent унаследован от класса QDragMoveEvent.
Объект класса содержит данные события перетаскивания. Если пользователь,
перетаскивая объект, попадает в область виджета, то вызывается метод
dragEnterEvent().
Класс QDragLeaveEvent
Объект класса QDragLeaveEvent содержит данные события перетаскивания в
том случае, если пользователь, перетаскивая объект, выходит за область
ВИДЖета И При ЭТОМ вызывается метод dragLeaveEvent (). Класс QDragLeaveEvent
идентичен классу QDragMoveEvent.
Класс QDragMoveEvent
Этот класс служит для представления данных события перетаскивания в тот
момент, когда данные находятся в области виджета. Возникновение этого
события ПРИВОДИТ К ВЫЗОВУ метода dragMoveEvent ().
Класс QDropEvent
Объект класса QDropEvent передается в метод dropEvento при отпускании
объекта в принимающей области виджета.
Остальные классы событий
Класс QChildEvent
Это событие создается в момент ввода или удаления объекта-потомка.
Объект события передается в метод chiidEvento, который определен в классе
QObject. Вызовом метода child о можно получить указатель на этот объект.
Класс QCustomEvent
Класс предназначен для реализации своих собственных типов событий.
Чтобы реализовать такое событие, нужно создать объект класса QEvent и
передать целочисленное значение в его конструктор. Это значение
представляет собой идентификатор типа события и должно быть больше 1000,
чтобы не войти в конфликт с уже определенными типами.
Если в созданном событии требуется реализовать методы, то нужно
применять другой способ. Необходимо унаследовать класс QEvent, установить ие'
лочисленный идентификатор типа и реализовать в нем нужные методы.
class MyEvent : public QEvent {
public:
Глава
13. События 189
MyEventO : t(1200) // Устанавливаем идентификатор типа
{
( Примечание }
Свои собственные события можно высылать с помощью метода QApplication::
sendEvent () и получать в методе event ().
Класс QCioseEvent
Событие создается при закрытии окна виджета. Событие может быть
вызвано пользователем или методом Qwidget::ciose(). Объект класса
QCioseEvent передается В метод cioseEvent (), В КОТОрОМ МОЖНО СПРОСИТЬ
пользователя, действительно ли он хочет закрыть приложение. Это имеет
смысл в тех случаях, когда пользователь не сохранил свои данные.
Класс события содержит методы accept о и ignore о, с помощью которых
устанавливается флаг, сообщающий о том, что получатель события согласен
закрыть окно. Например, вызов метода accept () приведет к тому, что после
него окно будет спрятано методом hide (). Вызов ignore () оставит окно без
изменений.
Класс QHideEvent
Данное событие создается при нажатии пользователем кнопки свертывания
приложения. Также, это событие может быть вызвано методом hide о,
делающим виджет невидимым. Объект события QHideEvent передается в метод
hideEvent ().
Класс QShowEvent
Событие генерируется при создании виджета и при вызове метода show о.
Объект события QShowEvent передается В метод showEvent ().
Класс QResizeEvent
Пользователь может изменять размеры окна при помощи мыши. При этом
Создается Объект события QResizeEvent. Он передается В метод resizeEventO
и содержит информацию о старых и новых размерах виджета, которые
°ЖНО ПОЛУЧИТЬ ВЫЗОВОМ методов size О И oldSizeO.
^кции на изменение размеров виджета могут быть следующими:
^ перерисовка содержимого окна;
Изменение размеров виджетов-потомков.
Пример, показанный на рис. 13.3 и приведенный в листингах 13.4—13.6
демонстрирует перезапись метода resizeEvento. В окне отображается
эллипс, размеры которого каждый раз изменяются соответственно размерам
окна.
[Noncommercial] - Resize BOD
Рис. 13.3. Демонстрация перезаписи метода res izeEvent ()
I Листинг 13.4. Файл main.cpp
#include <qapplication.h>
#include "Resize.h"
//
int main(int argc, char** argv)
{
QApplication appfargc, argv);
Resize resize;
app.setMainWidget(&resize);
resize.setCaption("Resize");
resize.show();
resize.resize(320, 240);
return app.exec();
}
В листинге 13.4 создается виджет resize, изменение окна которого приведет
к изменению размера отображаемого в нем эллипса.
Листинг 13.5. Файл Resize.h
#ifndef _Resize_h
#define _Resize_h
#include <qwidget.h>
#include <qpixmap.h>
Глава 13. События
191
/г _____________________
class Resize : public QWidget {
Q_OBJECT
private:
QPixmap mjpix;
protected:
virtual void paintEvent (QPaintEvent* e);
virtual void resizeEvent(QResizeEvent* e);
public:
Resize(QWidget* pwgt = 0, const char* pszName = 0);
);
#endif //_Resize_h_
Класс перезаписывает два метода обработки событий: paintEvento и
resizeEvenc (). Первый метод служит для обновления области графического
вывода в случае, если окно будет перекрыто другим. Второй вызывается
всякий раз, когда пользователь изменяет размеры окна.
ЫиЪтинг 13.6. Файл Resize.cpp
tinclude <qpainter.h>
#include <qbrush.h>
#include "Resize.h"
//
Resize::Resize(QWidget* pwgt/*= 0*/, const char* pszName/*= 0*/)
: QWidget(pwgt, pszName)
<
mjpix.fill(white);
setBackgroundMode(NoBackground);
)
//
/*virtual*/void Resize::paintEvent(QPaintEvent* e)
I
::bitBlt(this, 0, 0, &mj?ix);
//
virtual*/void Resize::resizeEvent(QResizeEvent* e)
QPainter painter;
192
Часть III. События и взаимодействие с пользователем
painter.begin(this);
painter.fillRect(rect(), white);
painter.drawEllipse(0, 0, e->size().width(), e->size().height());
painter.end();
m_pix.resize(e->size());
::bitBlt(&m_pix, 0, 0, this);
QWidget::resizeEvent(e);
>
Метод paintEvento производит копирование растрового изображения из
буфера в видимую область окна. В методе resizeEvent о производится
очистка видимой области всего окна. Затем метол fiiiRecto отображает
эллипс, размер которого определяет объект события. Буфер растрового
изображения приводится в соответствие с размером окна с помощью метода
resize о. Вызов глобальной функции bitBito копирует содержимое
видимой области в буфер растрового изображения.
Класс QMoveEvent
Событие QMoveEvent возникает при перемещении виджета. Для виджетов
верхнего уровня это соответствует перемещению его окна. Объект события
класса QMoveEvent передается в метод moveEvento и содержит информацию
о старых и новых координатах виджета, которые можно получить вызовом
методов pos () и oidPos ().
Методы enterEventO и leaveEventf)
Эти методы вызываются в том случае, когда указатель мыши попадает в
область виджета или покидает его. Их можно перезаписать, например, если
требуется изменить внешний вид виджета. Метод enterEvento получает
объект события типа QEvent и вызывается каждый раз, когда указатель
мыши входит в область виджета. Метод leaveEvento получает объект события
типа QEvent и вызывается тогда, когда указатель мыши выходит за пределы
области виджета.
Перезапись метода event()
Все возникающие в системе события становятся в очередь, из которой oHl
опрашиваются циклом событий, находящимся в методе exec о объекта при-
ложения QAppiication. Объект класса QAppiication посылает события те*
виджетам, которым они предназначены. Все объекты событий поступают
центральный метод event о, в котором определяется их тип и производив
гряда 13. События 193
дузов специализированных методов обработки, предназначенных для этих
событий, например, вызов метода keyPressEvent () при возникновении
события клавиатуры. Метод event о можно использовать и для тех типов
событий' для которых не существует методов обработки.
Метод event о, так же как и все остальные специализированные методы
обработки событий — виртуальный. Его можно перезаписать, но следует
делать это только в тех случаях, когда в этом есть острая необходимость,
т к. это может изрядно усложнить исходный код всей программы. Если все
события будут обрабатываться только одним методом event (), то это может
привести к появлению громоздкого метода, содержащего несколько тысяч
строк для обработки всевозможных событий. Поэтому если есть
возможность, то лучше всего перезаписывать соответствующие
специализированные методы для обработки событий.
В метод event о передается указатель на объект типа QEvent. Все события
унаследованы от класса QEvent, который содержит переменную,
представляющую собой целочисленный идентификатор типа события, с помощью
которой всегда можно привести указатель на объект класса QEvent к
нужному типу. Программа должна быть в состоянии определить в методе event о,
какое событие произошло. Для облегчения этой задачи существует метод
type о. Возвращаемое им значение можно сравнить с предопределенной
константой, что даст возможность привести это событие к правильному
типу (табл. 13.3).
Таблица 13.3. Типы событий
Константа
None
[Timer
MouseButtonPress
pouseButtonRelease
(J^useButtonDblClick
PjouseMove
P^yPress
rjYRelease
hjcusin
L^cusOut
Psr~
Ljjave
Значение
0
'
Константа
Paint
1 |Move
2 Resize
3 Create
4 Destroy
5
6
7
8
9
10
11
Show
Hide
Close
Quit
Reparent
ShowMinimized
ShowNormal
Значение
12
13
14
15
16
17
18
19
20
21
22
23
194 Часть III. События и взаимодействие с пользователе.
Таблица 13.3 (окончание)
Константа
WindowActivate
WindowDeactivate
ShowToParent
HideToParent
ShowMaximized
ShowFullScreen
Accel
I Wheel
AccelAvailable
CaptionChange
IconChange
ParentFontChange
ApplicationFontChange
ParentPaletteChange
ApplicationPaletteChange
PaletteChange
Clipboard
Speech
SockAct
AccelOverride
DeferredDelete
DragEnter
Значение
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
42
50
51
52
60
Константа
DragMove
DragLeave
prop
DragResponse
Childlnserted
ChildRemoved
LayoutHint
ShowWindowRequest
ActivateControl
DeactivateControl
ContextMenu
IMStart
IMCompose
IMEnd
Accessibility
TabletMove
LocaleChange
LanguageChange
Layout Di rect ionChange
Style
TabletPress
TabletRelease
Значение
61
62
63
64
70
71
72
73
80
81 j
82 j
83 1
84
85
86
87 |
88 J
89 J
90 J
91 _J
92 1
93 |
Перезапись метода event о может выглядеть следующим образом:
bool MyClass::event(QEvent* e)
{
if (e->type() = QEvent::KeyPress) {
QKeyEvent* keyEvent = (QKeyEvent*) e;
if (keyEvent->key() == Key_Tab) {
// Произвести какие-либо действия
return true;
}
}
fnaea 13. События 195_
if (e->type() == QEvent::Hide) {
// Произвести какие-либо действия
return true;
}
return QWidget::event(e);
1
Метод возвращает true в том случае, если событие было обработано и не
требует передачи его дальше. После чего событие удаляется из очереди
событий. При возвращении false событие будет передано дальше объекту-
предку. Если ни один из объектов-предков не сможет обработать событие,
то оно будет просто проигнорировано и удалено из очереди событий.
Резюме
Наступление события вызывается каким-либо действием со стороны,
которое может быть щелчком кнопкой мыши, изменением размеров окна и т. д.
В этой главе вы узнали, что события являются механизмом оповещения
более низкого уровня, по сравнению с сигналами и слотами. Для получения
доступа к информации о событии предусмотрен объект события, который
передается в метод обработчика события. Все методы обработчиков
событий, за исключением метода event о, относятся к группе
специализированных обработчиков. Сначала все события попадают в метод event о, из
которого производится вызов специализированных методов для обработки
соответствующих событий, например, при нажатии клавиши на клавиатуре
вызывается обработчик keyPressEvent (). В метод event о передается в
качестве аргумента указатель на объект класса QEvent, который содержит
информацию о событиях. Метод QEvent:: type () возвращает целочисленный
Идентификатор типа события.
Событие перерисовки окна возникает тогда, когда виджет был частично или
полностью перекрыт другим окном. Объект события QPaintEvent содержит
информацию об участке, который должен быть перерисован. Для обработки
события необходимо перезаписать метод paintEvent ().
События мыши обрабатываются методами: mousePressEvent (),
^ouseMoveEvent (), mouseReleaseEvent () И mouseDoubleClickEvent (). Для
определения местоположения указателя мыши можно воспользоваться методами:
9lobalX (), globalY (), х (), у (), pos () ИЛИ globalPos ().
Для обработки событий клавиатуры предназначены методы keyPressEvent ()
и keyReleaseEvent (). Метод keyPressEvent () вызывается каждый раз,
Когда пользователь нажимает на клавиатуре одну из клавиш. Метод
KeyReleaseEvent () вызывается при отпускании клавиши.
ГЛАВА 14
Фильтры событий
Тот, кто спрашивает, всегда получит ответ.
Притчи Камеруна
Как правило, событие передается тому объекту, над которым было
произведено действие, но иногда возникает необходимость их обработки в другом
объекте. В библиотеке Qt предусмотрен очень мощный механизм перехвата
событий, который позволяет объекту фильтра принимать события раньше
объекта, для которого они предназначены, и, затем, принимать решение по
своему усмотрению — обрабатывать их и/или передавать дальше.
Важно то, что установка фильтров событий происходит не на уровне
классов, а на уровне самих объектов. Это дает возможность, вместо того чтобы
наследовать класс или изменять уже имеющийся (что не всегда
представляется возможным), просто воспользоваться объектом фильтра. Для
настройки на определенные события необходимо создать класс фильтра, установив
его в нужном объекте. Все изменения поведения относительно событий
будут касаться только объектов, в которых будут установлены фильтры, и
никоим образом не затронут объекты, произведенные от того же класса.
Фильтры событий можно использовать, например, в тех случаях, когда
нужно добавить какую-либо функциональность к некоторому количеству
реализованных классов, при этом не наследуя каждый из них. После того
как класс фильтра будет реализован, его объекты можно будет
устанавливать в любых нужных объектах, произведенных от классов наследующих
QObject. Это позволит сэкономить время на реализацию, т. к. нужно писать
меньше кода, и вместе с тем значительно сократит временные затраты на
отладку программы. Разработчику больше не надо заботиться о методах
обработки событий для каждого из классов в отдельности, потому что это
будет производиться централизованно, одним классом фильтра, имеющим
силу для всех объектов, в которых он был установлен.
Чтобы реализовать класс фильтра, нужно унаследовать класс от класса
QObject и перезаписать метод eventnitero. Этот метод имеет два парамет-
гядда 14. Фильтры событий
а: первый — это указатель на объект, для которого предназначено событие,
агорой — указатель на сам объект события.
£сли этот метод возвращает значение true, то это означает, что данное
событие не должно передаваться дальше, a false говорит о том, что данное
событие должно быть передано объекту, для которого оно и было
предназначено.
Пример, показанный на рис. 14.1, демонстрирует работу фильтра, который
устанавливается в трех виджетах. Щелчок мыши на любом из них приводит
к появлению окна сообщения, информирующего об имени класса виджета.
QLineEdit
QButto
QLabel
П
□
□
X
X
J
X
ршжящ
р
il
Рис. 14.1. Программа, демонстрирующая перехват события
Листинг 14.1. Файл main.cpp
♦include <qapplication.h>
#include <qlineedit.h>
♦include <qlabel.h>
♦include <qpushbutton.h>
♦include "MouseFilter.h"
//
int main (int argc, char** argv)
{
QApplication app (argc, argv);
QLineEdit txt("QLineEdit", 0);
txt.show();
txt.installEventFilter(new MouseFilter(&txt));
QLabel lbl("QLabel", 0);
lbl.show();
lbl.installEventFilter(new MouseFilter (&1Ы)) ;
QPushButton cmd("QPushButton", 0);
cmd.show();
cmd.installEventFilter(new MouseFilter(&cmd));
198 Часть III. События и взаимодействие с пользователей,
QObj ect::connect(&app, SIGNAL(lastWindowClosed()),
&app, SLOT(quit())
);
return app.exec();
}
В функции maino, приведенной в листинге 14.1, создаются три виджета:
QLineEdit, QLabel и QPushButton. В каждом из них устанавливается, вызовом
метода instaiiEventFiiterO, объект, произведенный от одного и того же
класса фильтра событий. В конструктор создаваемого фильтра передается, в
качестве предка, непосредственно сам виджет, в котором производится
установка фильтра. Это позволит, при уничтожении виджета, автоматически
уничтожить вместе с ним и закрепленный объект фильтра.
Листинг 14.2. Файл MouseFilter.h
#ifndef _MouseFilter_h_
#define _MouseFilter_h_
#include <qobject.h>
/ / „„„„=„_„_„„„_„=_=_====„_=======„=_
class MouseFilter : public QObject {
Q_OBJECT
protected:
virtual bool event Filter(QObject*, QEvent*);
public:
MouseFilter(QObject* pobj = 0, const char* pszName = 0);
};
#endif //_MouseFilter_h_
Обратите внимание на прототип метода eventFiiterO (листинг 14.2) — он
получает не только указатель на объект события, но еще и указатель на сам
объект, для которого это событие предназначено. Объект фильтра может
сделать с этим объектом все, что ему будет угодно, вплоть до удаления.
) Листинг 14.3. Файл MouseFilter.cpp
#include <qapplication.h>
#include <qmessagebox.h>
#include "MouseFilter.h"
//
MouseFilter::MouseFilter(QObject* pobj/*=0*/, const char* pszName/*=0*/)
: QObject(pobj, pszName)
Глава 14. Фильтры событий 199
{
. )
п
jjool MouseFilter: :eventFilter(QObject* pobj, QEvent* pe)
{
if (pe->type() = QEvent::MouseButtonPress) {
if (((QMouseEvent*)pe)->button() == LeftButton) {
QMessageBox::information(0, "Class Name", pobj->className ());
return true;
>
}
return false;
)
В листинге 14.3 метод eventnitero отслеживает событие типа QEvent::
MouseButtonPress, соответствующее нажатию одной из кнопок мыши. В том
случае если событие относится к этому типу, то производится
преобразование указателя на объект события к указателю типа QMouseEvent. Затем
вызывается метод button о класса QMouseEvent, чтобы узнать, какая из
кнопок мыши была нажата. Если была нажата левая кнопка, то в
информационном окне сообщения выводится имя класса виджета, возвращается true
и событие дальше не передается. В остальных случаях возвращается
значение false и событие передается дальше.
В объектах возможна установка сразу нескольких фильтров, а очередность
их исполнения будет выглядеть следующим образом — последний
установленный фильтр будет применяться первым.
Существует возможность глобальной установки фильтра событий, т. е.
фильтра, который будет действовать на все объекты приложения. Для этого
НУЖНО вызвать метод QApplication::installGlobalFilter(). ЭТОТ фильтр бу-
Дет наипервейшим фильтром, получающим и обрабатывающим события
всех объектов приложения, т. е. прежде чем его получают сами объекты или
их фильтры событий. Данный метод может пригодиться при отладке
приложения.
Резюме
Иногда возникает необходимость обрабатывать события в другом объекте.
В Qt предусмотрен очень мощный механизм перехвата событий, который
позволяет без наследования классов изменять реакцию объектов на
события. Тем самым экономится время на реализацию и отладку программы,
"тобы реализовать класс фильтра, нужно унаследовать класс от класса
Object И перезаписать метод eventFilterO.
ГЛАВА 15
Искусственное
создание событий
Компьютеры бесполезны.
Они могут только давать вам ответы.
Пабло Пикассо
Иногда возникает необходимость в событиях, созданных искусственно.
Например, при отладке вашей программы, для того, чтобы имитировать
возможные действия пользователя, который будет работать с ней.
Для генерации события можно воспользоваться одним из двух статических
методов класса QApplication — sendEvent() ИЛИ postEvent (). Оба Метода ПО-
лучают, в качестве параметров, указатель на объект, которому посылается
событие, и адрес объекта события. Разница между ними состоит в том, что
sendEvent о отправляет событие без задержек, тогда как postEvent о
помещает его в очередь.
Рассмотрим это на примере приложения, имеющего поле ввода и
имитирующее нажатие пользователем клавиш от <А> до <Z> (рис. 15.1).
[Non-Commercial] -EventSun- НОВ |
User input: mBCDEFGHwKLM nuPURSTUVVA'tZ i
Рис. 15.1. Программа, имитирующая ввод пользователя
; Листинг 15.1. Файл main.cpp
#include <qapplication.h>
#include <qlineedit.h>
//
int main (int argc, char** argv)
Гпава 15. Искусственное создание событий 201
{
QApplication app (argc, argv);
QLineEdit txt("User input: ", 0);
txt.show();
txt.resize(280, 20);
app.setMainWidget (&txt);
int i;
for (i =0; i < Qt::Key_Z - Qt::Key_A + 1; ++i) {
int n = 65 + i;
QKeyEvent e(QEvent::KeyPress, Qt::Key_A+ i, n, 0, QChar(n));
QApplication::sendEvent(&txt, &e) ;
}
return app.exec();
}
В исходном коде, приведенном в листинге 15.1, создается объект txt класса
QLineEdit, который будет выступать в качестве поля ввода. В цикле
происходит создание событий типа QKeyEvent. Первый параметр, передаваемый
конструктору, задает тип события клавиатуры (здесь он соответствует
событию нажатия клавиши клавиатуры QEvent::Keypress), а второй задает саму
клавишу, которая была нажата. Третий указывает на ее представление в
ASCII-коде (в примере это число начинается с 65 и увеличивается).
Четвертый указывает на клавиши-модификаторы, которые могли быть совместно
нажаты (в нашем случае это значение равно 0). Пятый параметр — это текст
в формате Unicode, генерируемый нажатием этой клавиши, который в
нашем примере создается из ASCII-кода.
Не всегда есть возможность модификации объектов событий. При
совместном создании искусственных событий с фильтрами можно осуществить
подмену самих объектов событий. Более подробно фильтры событий
рассмотрены в гл. 14. В качестве показательного примера использования
подобного перехвата события с целью его подмены можно назвать изменение
назначения клавиш клавиатуры. Так как класс события клавиатуры не
обладает методами, позволяющими его модифицировать, то каждое сообщение
клавиатуры можно получить в объекте фильтра и затем подменить его
Другим.
В следующем примере происходит подмена клавиши <Z> на клавишу <А>
(рис. 15.2). Таким образом, нажатие пользователем клавиши <Z> повлечет
за собой отображение в поле ввода буквы А.
[I tiJ.l.lJ.'.,i..'JJJMh—rn5Txl|
Рис. 15.2. Программа, демонстрирующая подмену события клавиатуры
202
Часть III. События и взаимодействие с пользователем
! Листинг 15.2. Файл main.cpp
#include <qapplication.h>
#include <qlineedit.h>
#include "KeyFilter.h"
//
int main (int argc, char** argv)
{
QApplication app (argc, argv);
QLineEdit txt(O);
txt.show();
KeyFilter* pFiiter = new KeyFilter(&txt);
txt.installEventFilter(pFiiter);
app.setMainWidget (&txt);
return app.exec();
}
В листинге 15.2 после создания объекта класса QLineEdit и вызова метода
show о производится создание объекта фильтра событий клавиатуры pFiiter,
в конструктор которого в качестве объекта-предка передается адрес на
однострочное текстовое поле txt. После этого в нем производится установка
СОЗДанНОГО фильтра При ПОМОЩИ метода installEventFilter ().
г •■■ •■• •> t
) Листинг 15.3. Файл KeyFilter.h
#ifndef _KeyFilter_h_
#define _KeyFilter_h_
#include <qobject.h>
#include <qapplication.h>
class KeyFilter : public QObject {
Q_OBJECT
protected:
bool eventFilter(QObject* pobj, QEvent* pe)
{
if (pe->type() = QEvent::KeyPress) {
if (((QKeyEvent*)pe)->key() = Key_Z) {
QKeyEvent e(QEvent::KeyPress, Qt::Key_A, 65, 0, "A");
QApplication::sendEvent(pobj, &e);
rnaga 15. Искусственное создание событий 203
return true;
}
}
return false;
}
public:
KeyFilter(QObject* pobj = 0, const char* pszName = 0)
: QObject(pobj, pszName)
{
}
>;
lendif /7_KeyFilter_h_
В методе eventFiiter о (листинг 15.3) отслеживается идентификатор
события QEvent::KeyPress, который соответствует событию нажатия клавиши
клавиатуры. В дальнейшем объект события преобразовывается к типу
QKeyEvent, для того чтобы иметь возможность вызова метода key (), который
определен в этом классе и позволяет получить код нажатой клавиши. Затем
создается и высылается новое событие нажатия клавиши <А>. После этого
метод возвращает true, и это означает, что событие дальше передаваться не
должно. Если событие, переданное в параметрах метода eventFiiter о, не
удовлетворяло двум поставленным условиям, то этот метод вернет значение
false, и, тем самым, событие будет передано дальше.
Резюме
В этой главе мы узнали о возможности создания события искусственным
образом из самой программы. Для этого можно воспользоваться методом
QRpplication::sendEvent(). При совместном использовании с механизмом
Фильтров событий можно осуществлять подмену объектов событий.
ЧАСТЬ IV
Графика и звук
Глава 16.
Глава 17.
Глава 18.
Глава 19.
Глава 20.
Глава 21.
Глава 22.
Глава 23.
Глава 24.
Глава 25.
Введение в компьютерную графику
Контекст рисования
Растровые изображения
Работа со шрифтами
Работа с изображениями холста
Анимация
Работа с OpenGL
Вывод на печать
Разработка собственных элементов управления
Звук
ГЛАВА 16
Введение
в компьютерную графику
...как будто волшебный фонарик
освешает изнутри образы на экране...
Т. С. Элиот. Песнь любви Альфреда Профрока
Графика — одна из наиболее быстро развивающихся отраслей
компьютерной индустрии. Известно, что 75% информации человек воспринимает
визуально, поэтому графика — это важнейший компонент для взаимодействия
человека с компьютером.
Для программирования компьютерной графики часто используются такие
классы геометрии, как: точки, двумерные размеры, прямоугольники, а
также специальные классы для хранения цветовых значений.
Классы геометрии
Эта группа ничего не отображает на экране. Ее основное назначение
состоит в задании расположения и размеров объектов.
Точка
Задает точку в двумерной системе координат. В двумерной системе
координат точка обозначается парой чисел X и Y, где X — горизонтальная, a Y —
вертикальная координаты. Класс QPoint, определяющий точку, содержит
методы, позволяющие проводить с координатами различные операции,
например, сложение и вычитание с координатами другой точки. При
сложении/вычитании точек производится попарное сложение/вычитание их
координат X и Y. Пример, проиллюстрированный на рис. 16.1, складывает две
точки ptl и pt2.
глава 16. Введение в компьютерную графику
Qpoint ptldO, 20);
Qpoint pt2(20, 10);
Qpoint pt3; //(0, 0)
pt3 * Pt1 + Pt2'
10 +
10 20 30 X
H—I ! >
Pt2
20+ +
pti
30 +
yT
pt3
Рис. 16.1. Создание и сложение точек
207
Объекты точек можно умножать и делить на числа. Например:
QPoint pt(10, 20);
pt *= 2; // pt = (20, 40)
Для получения координат точки X и Y реализованы методы х () и у ().
Изменяются координаты точки с помощью методов setxo и setYO.
Можно получить ссылки на координаты точки, для того чтобы изменять их
значения по отдельности. Например:
QPoint pt(10, 20);
Pt.rx() += 10; // pt = (20, 20)
Объекты точек можно сравнивать друг с другом при помоши операторов ==
и !«. Например:
QPoint pti (10, 20);
QPoint pt2(10, 20);
Ъо0^- b = (pti = pt2); // b = true
Если необходимо проверить, равны ли координаты X и Y нулю, то
вызываемся метод isNuiio. Например:
QpOint pt; // (0, 0)
1)001 b = pt.isNulK); // b = true
^год manhattanLength () возвращает сумму абсолютных значений коорди-
На*Хи Y. Например:
208 Часть IV. Графика ищ,п
QPoint pt(10, 20);
int n = pt.manhattanLengthO; // n = 10 + 20 = 30
Возвращаемое значение является грубым приближением к формуле
yJX2 + У2 . Этот метод был назван в честь улиц Манхеттена, расположенных
перпендикулярно друг к другу.
( Примечание )
Qt предоставляет класс QPointArray, унаследованный от QArray<QPoint>, для
хранения массива точек. Этот класс часто используется для задания вершин
полигона.
Двумерный размер
Класс QSize служит для хранения размеров. По своей структуре он очень
похож на QPoint, т. к. также хранит две величины, над которыми можно
проводить операции сложения/вычитания и умножения/деления.
Класс QSize, как и QPoint, предоставляет операторы сравнения ==, ! = и
метод isNuiio, возвращающий true в том случае, если высота и ширина
равны нулю.
ДЛЯ ПОЛучеНИЯ ШИрИНЫ И ВЫСОТЫ ВЫЗЫВаюТСЯ МеТОДЫ width О И height ().
Изменить их можно с помошью методов setwidtho и setHeighto. При
помощи методов rwidtho и rheighto получают ссылки на значения ширины
и высоты для того, чтобы изменять и получать их значения. Например:
QSize size (10, 20);
int n = size.rwidth()++; // n = 11; size = (11, 20)
Помимо этих методов, класс предоставляет метод scale о, позволяющий
производить изменение размеров оригинала согласно переданному ему в
первом параметре размеру. Второй параметр этого метода управляет
способом изменения размера (рис. 16.2):
□ scaieFree — изменяет размер оригинала на переданный в него размер;
□ ScaieMin — новый размер будет находиться в пределах переданного
в метод scale о размера и заполнять его площадь насколько это будет
возможно;
□ ScaieMax — новый размер может находиться за пределами переданного
в метод scale о, заполняя всю его площадь.
Согласно рис. 16.2, изменение размеров sizel, size2 и size3 может
выглядеть следующим образом:
QSize sizel(320, 240);
sizel.scale(400, 600, QSize::ScaieFree); // sizel = (400, 600)
fflgga 16. Введение в компьютерную графику 209
QSize size2(320, 240);
size2.scale(400, 600, QSize::ScaleMin); // size2 = (400, 300)
QSize size3(320, 240);
size3.scale(400, 600, QSize::ScaleMax); // size3 = (800, 600)
Оригинал
sizel f
/ -
size2 w
\
size3\
-
;
1
ScaleFree
ScaleMin
ScaleMax
Рис. 16.2. Изменение размеров оригинала
Прямоугольник
Класс QRect служит для хранения координат прямоугольных областей.
Задать прямоугольную область можно двумя способами.
Первый способ задает область с помощью двух точек, передаваемых в
конструктор, при этом первая является координатами верхнего левого угла, а
вторая — правого нижнего угла. Например, показанную на рис. 16.3 прямо-
Угольную область можно задать следующим образом:
QPoint ptl(10, 10);
QPoint pt2(30, 20);
^Rect r(ptl, pt2);
Для получения координат первой точки вызываются методы left о и top о,
а Для получения координат второй точки — righto и bottomo. Изменять
210
Часть IV. Графика и звук
ЭТИ велИЧИНЫ МОЖНО С ПОМОЩЬЮ МеТОДОВ setLeftO, setTopO, setRightO И
setBottomo. Для получения ссылок на эти координаты нужно
воспользоваться методами rLeft(), rTop(), rRight() И rBottom().
Рис. 16.3. Задание прямоугольной области двумя точками
Реализованы методы для работы с объектами класса QPoint. Для получения
первой точки нужно вызвать метод topLeft (), а для получения второй —
bottoitiRight о. Для изменения значений точек можно воспользоваться
методами setTopLeftO И setBottoraRightO.
Второй способ задает прямоугольную область объектом точки и размером.
Область, приведенная на рис. 16.4, создается при помощи следующих строк:
QPoint pt(10, 10);
QSize size (20, 10);
QRect r(pt, size);
1 0 10 20 30 X
■ I 1 W
10-
20-
30-
Y>
1
- (
Y=10
r
1 1 r
X=10
Width=20
Height=10
Рис. 16.4. Задание прямоугольной области точкой и размером
Получить координаты X или Y можно при помощи методов х() или уО-
Для их изменения нужно воспользоваться методами setx () и setY ().
fnaga *6. Введение в компьютерную графику
211
размер получают с помощью метода size о, который возвращает объект
класса QSize. Или просто вызвать методы, возвращающие составляющие
части размера— его ширину width о и высоту height о. Изменить размер
можно методом setsizeo, а каждую его составляющую— методами
setWidth() И setHeight().
Цвет
g Qt поддерживаются две цветовые модели — RGB (Red, Green, Blue) и HSV
(Hue, Saturation, Value). Цветовая модель — это спецификация в трехмерной
системе координат, которая задает все видимые цвета.
Цветовая модель RGB
Теоретически, человеческий глаз может воспринимать одновременно около
10 тысяч различных цветовых оттенков. Наиболее чувствителен глаз к
зеленому цвету, после него следует красный, а затем синий. На этих трех цветах
и построена модель RGB (Red, Green, Blue — Красный, Синий, Зеленый).
Пространство цветов задает куб, длина ребер которого равна 255 (рис. 16.5).
Синий (glue)
Синий (0, 0, 255)
Пурпурный (255,
Черный (0, 0, 0)
Голубой (0. 255, 255)
I
Белый (255, 255, 255)
Зеленыи| (0, 255, 0)
1 ;—► Зеленый (Green)
Красный (255, 0, 0) X, Желтый (255, 255, 0)
Красный (Red)
Рис. 16.5. Цветовая модель RGB
212
Часть IV. Графика изву^
Как видно из рис. 16.5, цвет задается сразу тремя параметрами. Первый
параметр задает оттенки красного, второй — зеленого, а третий — оттенок
синего цвета. Диагональ куба, идущая от черного цвета к белому — это
оттенки серого цвета. Диапазон каждого из трех значений может изменяться в
пределах от 0 до 255, при 0 -
полное присутствие.
полное отсутствие оттенка цвета, а 255 — его
Цветовая модель HSV
Модель HSV (Hue, Saturation, Value — Оттенок, Насыщенность, Значение)
не смешивает основные цвета при моделировании нового цвета, как в
случае RGB, а просто изменяет их свойства. Это очень напоминает принцип,
используемый художниками для получения новых цветов — подмешивая к
чистым цветам белую, черную или серую краски.
Пространство цветов этой модели задается пирамидой с шестиконечным
основанием, так называемый Нехсопе (рис. 16.6).
П оттенок (Hue) — это "цвет" в общеупотребительном смысле этого слова,
например: красный, оранжевый, синий и т. д. Он задается углом в
цветовом круге, изменяющимся от 0 до 360 градусов;
Синий
Ось значений (Value)
^^L Насыщенность (Saturation)
1 >^--^Оттенок (Hue)
_fr--0_
Голубой (180°, 255, 255) j Зеленый (120°, 255, 255)
(240° 255 25Ш ^Белый^Ю}, 0, 255)^ — - ^
(240,255.255) — ^yr- ^ Желтый (60°, 255,255)
Пурпурный (300°. 255, 255)^
1
Черны
Красный (0°, 255, 255)
^\Серый (0°, 0, X)
>
л (0°. 0. 0)
Рис. 16.6. Цветовая модель HSV
Глава 16. Введение в компьютерную графику
213
0 насыщенность (Saturation) представляет собой составляющую белого
цвета в оттенке. Значение насыщенности может изменяться в диапазоне от О
до 255. Значение 255 соответствует полностью насыщенному цвету,
который не содержит оттенков белого. Частично насыщенный оттенок
светлее, например, красный оттенок с насыщенностью, равной 128,
соответствует розовому оттенку;
О значение или яркость (Value) — определяет интенсивность цвета. Цвет с
высокой интенсивностью является ярким, а с низкой — темным.
Значение этого параметра может изменяться в диапазоне от 0 до 255.
Представления цвета
Представление цвета TrueColor позволяет точно отобразить нужный цвет
(оттенок) или с очень хорошим приближением. В RGB данные для каждого
пиксела представляют собой комбинации красного, зеленого и синего
цветов, которые включают в себя все представления, имеющие больше восьми
бит на пиксел. Число битов на пиксел называют глубиной цвета. Например,
очень распространено представление с глубиной цвета в пятнадцать
бит, которое способно адресовать 32 768 цветов. Шестнадцатибитовое
представление адресует 65 536 цветов, а двадцатичетырехбитовое — уже
16 777 216 цветов.
( Примечание )
Может показаться, что 24-битовое представление является избыточным, т. к.
максимальное количество цветов, которое способен различить человеческий
глаз, ограничено 19 битами (6 бит на красный, 7 бит на зеленый и 6 бит на
синий цвета). Но технически было целесообразнее округлить каждую из трех
составляющих до байта (3 х 8 = 24 бит).
Представление TrueColor снабжается дополнительным байтом, который не
содержит информации о цвете, но содержит информацию о прозрачности
пикселя. Таким образом, такое представление цвета, вместе с информацией
о прозрачности, будет равно тридцати двум битам (24 + 8 = 32).
Палитра
Палитра представляет собой ограниченное (в большинстве случаев
числом 256) количество цветовых значений. Цветовые значения адресуются при
помощи индексов. Сами индексируемые цветовые значения можно задавать
свободно. На рис. 16.7 отображается пиксел, имеющий значение цвета
*GB(200, 75, 13), адресуемое индексом 3.
214
Часть IV. Графика и звук
Индексы
палитры
Палитра
0
>
1
2 i 3 i
4
f T V T У
RGB(0,112,0) RGB{37,100,7)j RGB(10,8,4) |rGB(200.75.13)| RGB<21.123,12
Л
L
"7Г
г 2
256
▼
RGB(30,11280)|
Рис. 16.7. Отображение пиксела, имеющего цвет, указанный в палитре
Класс QColor
С помощью этого класса можно сохранять цвета моделей RBG и HSV. Его
определение находится в заголовочном файле qcolor.h. Объекты класса
QColor возможно сравнивать при помоши операторов == и !=, а также
присваивать и создавать копии.
Работа с цветовой моделью RGB
Для создания цветового значения RGB нужно просто передать в
конструктор класса QColor три параметра. Первым параметром передается значение
красного цвета, вторым зеленого, а третьим — синего. Например:
QColor colorBlue(0, 0, 255);
Получить из объекта QColor каждое цветовое значение возможно с помощью
методов red (), green () И blue ().
Для записи значений RGB необходимо воспользоваться структурой данных
QRgb. Эта структура состоит из четырех байтов и полностью совместима
с 32-битовым представлением. Эту структуру можно создать с помощью
функции qRgb() или qRgba (), передав в функцию параметры красного,
зеленого и голубого цветов. Но можно присваивать переменным структуры QR9b
и 32-битовое значение цвета. Например, синий цвет устанавливается сразу
несколькими способами:
гядва 16. Введение в компьютерную графику 215
лядЬ rgbBluel = qRgba(0, 0, 255, 255); //С информацией прозрачности
лцдЬ rgbBlue2 = qRgb(0, 0, 255);
QRgb rgbBlue3 = OxOOOOFFOO;
ЦрИ ПОМОЩИ фуНКЦИЙ qRed(), qGreenO, qBlueO И qAlphaO МОЖНО ПОЛУЧИТЬ
значения цветов и информацию о прозрачности.
Значения типа QRgb можно передавать в конструктор класса ocoior или
в меТОД setRgb ():
QRgb rgbBlue = OxOOOOFFOO;
QCoior colorBluel(rgbBlue);
QCoior colorBlue2();
colorBlue2. setRgb (rgbBlue);
Также можно получать значения структуры QRgb из объектов класса QCoior
вызовом их метода rgb ().
Работа с цветовой моделью HSV
Значение цвета в модели HSV устанавливается в конструкторе следующим
образом:
QCoior color(233, 100, 50, QCoior: :Hsv) ;
Установку значения цвета также можно произвести с помощью метода
QCoior: :setHsv():
color.setHsv(233, 100, 50);
Для того чтобы получить цветовое значение в цветовой модели HSV, нужно
передать в метол getHsvO адреса трех целочисленных значений. Следующий
пример устанавливает RGB-значение и получает в трех переменных его
HSV-эквивалент:
QCoior color (100, 200, 0);
ir*t h, s, v;
c<>lor.getHsv(&h, &s, &v);
Предопределенные цвета
0 табл. 16.1 приведены константы именованных цветов, предопределенных
Qt. Они представляют собой палитру, состоящую из семнадцати цветов,
конечно, этих цветов недостаточно для получения фотореалистичных изо-
Рэжений, но, тем не менее, они очень удобны на практике в тех ситуациях,
к°гда требуется отображать основные цветовые значения.
216 Часть IV. Графика и am
( Примечание )
В общей сложности именованных цветов 19. Две, не приведенные в таблице
константы, colorO и colorl, используются для рисования двухцветных изо.
бражений.
Таблица 16.1. Цвета из перечисления класса QCoi0r
Константа
black
white
darkGray
gray
lightGray
red
green
blue
cyan
magenta
yellow
darkRed
darkGreen
darkBlue
darkCyan
darkMagenta
darkYellow
Значение в RGB
(0, 0, 0)
(255, 255, 255)
(128, 128, 128)
(160, 160, 164)
(192, 192, 192)
(255, 0, 0)
(0, 255, 0)
(0, 0, 255)
(0, 255, 255)
(255, 0, 255)
(255, 255, 0)
(128, 0, 0)
(0, 128, 0)
(0, 0, 128)
(0, 128, 128)
(128, 0, 128)
(128, 128, 0)
Цвет
Черный
Белый
Темно-серый
Серый
Светло-серый
Красный
Зеленый
Синий
Голубой
Пурпурный
Желтый
Темно-красный J
Темно-зеленый J
Темно-синий
Темно-голубой
Темно-пурпурный
Темно-желтый J
Класс QCoior предоставляет методы light о и dark о, с помощью которых
можно получать значения цвета, делая основное значение светлее или
темнее. Эти методы не изменяют объект основного цвета, а создают новый-
Для этого текущий цвет в модели RGB преобразуется в цвет модели HSV и
умножает компоненту ее "Значение" (Value) на фактор, переданный в этот
метод, а затем преобразует полученное значение обратно в модель RGb-
Сделать красный цвет немного темнее можно следующим образом:
QCoior color = Qt::red.dark(160);
гпява 16. Введение в компьютерную графику 217
резюме
графика играет важную роль в приложениях и ее можно встретить
практически во всех серьезных программных продуктах.
Qt предоставляет ряд классов геометрии, необходимых при создании про-
пзамм с графикой. Объекты класса QPoint хранят в себе координаты X и Y,
описывающие расположение точки на плоскости. Класс размера Qsize
Предназначен для хранения значений ширины и высоты. Класс QRect
объединяет в себе величины, хранящиеся в объектах классов QPoint и Qsize.
Qt поддерживает две цветовые модели RGB и HSV. RGB — это
распространенная цветовая модель, в которой любой цвет получается в результате
смешения цветов красного, зеленого и синего цветов. Цвет в модели HSV
задается тремя параметрами — оттенком (Hue), насыщенностью (Saturation)
и значением (Value).
Представление цвета TrueColor дает возможность получить любой нужный
цвет. В него входят все представления, имеющие более 8 бит.
Палитра — это массив, в котором каждому возможному значению пиксела
ставится в соответствие значение цвета.
Класс QColor предназначен для хранения цветовых значений и
предоставляет множество полезных методов, с помощью которых можно
конвертировать цветовые значения из RGB в HSV и наоборот, сравнивать их, делать
светлее или темнее.
ГЛАВА 17
Контекст рисования
Вкусовых ощущений только пять,
но вкусовых сочетаний так много,
что никому не суждено познать их все.
Сунь-Цзы
Контекст рисования можно представить себе как поверхность для вывода
фафики. QPaintDevice — это основной, абстрактный класс для всех классов
объектов, которые можно рисовать.
Класс QPaintDeviceMetrics служит для предоставления информации о
контексте рисования объектов, произведенных от классов, показанных на
рис. 17.1. Эта информация включает в себя высоту, ширину (в пикселях или
миллиметрах), глубину цвета и т. д. Например:
QPrinter prn;
QPaintDeviceMetics pdm(&prn);
cout « "paper width:" « pdm.widthMMO
« "paper height:" « pdm.heightMMO ;
| QPaintDevice
H QPicture
H QPrinter "
h QWldget
| QPixmap
H QBitmap
| QCanvasPixmap
Рис. 17.1. Иерархия классов контекста рисования
Г пава 17. Контекст рисования
Класс QPainter
Класс QPainter определен в заголовочном файле qpaimer.h и является
исполнителем команд рисования. Он содержит методы для рисования линий,
прямоугольников, окружностей и др. Рисование производится на всех
объектах классов, унаследованных от класса QPaintDevice (рис. 17.1). Это
означает, что отображенное контекстом рисования одного объекта может быть
отображено и контекстом другого.
QPrinter
<
Виджет
QWidget
^ J
QPixmap
QBitmap
QCanvasPixmap
<$jf
V
J. IK
QPainter
Рис. 17.2. Объект класса QPainter и контексты рисования
Чтобы использовать объект QPainter, необходимо передать ему адрес
объекта, на котором должно производиться рисование (рис. 17.2). Этот адрес
можно передать как в конструктор, так и с помощью метода QPainter::
begin о. Смысл метода begin о состоит в том, что он позволяет рисовать на
одном контексте несколькими объектами класса QPainter. При
использовании метода begin о нужно, по окончании работы с контекстом, вызвать
метод QPainter: :end(), чтобы рассоединить установленную этим методом связь
с контекстом рисования, давая другому объекту возможность рисования
(пример 17.1).
^"Ример 17.1. Рисование двумя объектами QPainter в одном контексте
fainter painterl;
«painter painter2;
^interl.begin(this);
Команды рисования
^interl.end();
220
Часть IV. Графика и звук
painter2.begin(this) ;
// Команды рисования
painter2.end();
Из соображений эффективности все команды рисования поступают в
очередь. По окончании работы с объектом QPainter, при его уничтожении или
вызове метода end о, происходит автоматическое исполнение всех
невыполненных команд, оставшихся в очереди. Если требуется очистить очередь,
исполнив все команды, находящиеся в ней, то следует вызвать метод
Painter::flush().
Перья и кисти
Перья и кисти — это основа для программирования графики с
использованием библиотеки Qt. Без них не получится вывести даже точку на экран
Перо
Перо применяется для рисования контурных линий фигуры. Атрибуты
пера — цвет, толщина и стиль. Установить новое перо можно с помощью
метода QPainter:: set Реп (), передав В него Созданный объект Класса QPen.
Можно передавать и предопределенные стили пера, указанные в табл. 17.1.
Таблица 17.1. Некоторые значения из перечисления Penstyle класса Qt
Константа
NoPen
SolidLine
DashLine
DotLine
DashDotLine
DashDotDotLine
Значение
0
1
2
3
4
5
Вид (толщина = 4)
Толщина линии является значением целого типа, которое передается в
метод QPen: :setwidtho. Если значение равно нулю, то это не означает, что
линия будет невидима, а говорит о том, что она должна быть изображена
как можно тоньше. Если необходимо, чтобы линия не отображалась
вообще, то тогда устанавливается стиль NoPen. Зачем же нужно перо, которое не
рисует? Но бывают и такие случаи, когда и пустое перо пригодится. Напри*
мер, может понадобиться вывести четырехугольник серого цвета без
контурной линии. Цвет пера задается с помощью метода QPen: :setcoior(b
гря&17. Контекст рисования 221_
который передается объект класса QCoior. Следующий пример создает перо
фасного цвета, толщиной в три пиксела и со стилем — штрих Объект пера
устанавливается в объекте QPainter вызовом метода setPen (). Например:
gpainter painter(this);
oainter.setPen(QPen(red, 3, QPen::DashLine));
Стили для концов линий пера устанавливаются методом setCapstyie (),
передавая В Него ОДИН ИЗ флагов: FlatCap, SquareCap ИЛИ RoudCap. Также МОЖНО
устанавливать стили и для переходов одной линии в другую — методом
set JoinStyle (), передав В него Miter Join, BevelJoin ИЛИ RoindJoin. Эти НЭ-
стройки будут видны только на толстых линиях.
Кисть
Кисть используется для заполнения непрерывных контуров, таких как
прямоугольники, эллипсы и многоугольники. Класс кисти QBrush определен
в заголовочном файле qbrush.h. Кисть задается двумя параметрами — цвет и
образец заливки.
Установить кисть можно вызовом метода QPainter: :setBrush(), передав в
него созданный объект класса QBrush или один из предопределенных
шаблонов, указанных в табл. 17.2. Если заполнение не нужно, то тогда в метод
QPainter: :setBrush() следует передать флаг NoBrush.
Таблица 17.2. Перечисление Brushstyle класса Qt (выборочно)
Вид Константа
Константа
NoBrush
SolidPattern
DenselPattern
°епзе2Pattern
^nseSPattern
Dense4Pattern
Dense5Patter n
Deftse6Pattern
Значение
0
1
2
3
4
5
6
7
Dense7 Pattern
HorPattern
VerPattern
CrossPattern
BDiagPattern
FDiagPattern
DiagCrossPattern
CustomPattern
Значение Вид
8
9 ==
- mi
11 W
12
13
14
24
222 Часть IV. Графика из^у!
Следующие строки устанавливают красную кисть с горизонтальной штри^
ховкой:
QPainter painter(this);
painter.setBrush(QBrush(red, HorPattern));
Если в этой таблице не нашлось подходящей кисти, то можно создать свою
собственную — для этого существует стиль CustomPattem (табл. 17.2). Этот
стиль нельзя передавать в обычный конструктор, т. к. требуется задать само
растровое изображение. Для этой цели существует специальный
конструктор, в который вместе со значением цвета передается и растровое
изображение. Объекты растрового изображения производятся либо от класса
QPixmap, либо ОТ класса QBitmap (СМ. 2/7. 18). Для объектов Класса QBitmap ИЛИ
QPixmap, имеющих глубину цвета 1 бит, пикселы со значением 1 будут
окрашены в цвет, передаваемый первым параметром конструктора. Например:
QBrush brush(red, QBitmap("pattern.bmp"));
Для объектов QPixmap с большей глубиной цвета переданное в конструктор
значение цвета будет проигнорировано. Для заполнения будет
использоваться только содержащееся в нем растровое изображение без изменения
цветов. Следующий пример, показанный на рис. 17.3, демонстрирует
применение объекта кисти.
QPixmap pixC'fruits. jpg") ;
painter.setBrush(QBrush(black, pix));
painter.drawEllipse(0, 0, 300, 150);
[Non-Commercial] - QBrush Demo ВО E3
Рис. 17.3. Заполнение эллипса фоном растрового изображения
Рисование
Рисование фигур — задача несложная, ведь для этого не требуется вычи
лять расположение каждого выводимого пиксела, т. к. уже имеется иелЫ
ряд методов для вывода практически всех геометрических фигур И моЖН •
например, просто вызовом метода drawRecto отобразить на экране
прямоугольник.
рисование точек
Для отображения точек применяется только перо. Пример 17.2 отображает
на экране восемь точек (рис. 17.4).
[Пример 17.2. Вызов метода drawPointO
i^
QPainter painter(this);
int n = 8;
for (int i = 0; i < n; ++i) {
float f Angle = 2 * 3.14 * i / n;
int x = 50 + cos(fAngle) *
int у = 50 + sin(fAngle) *
painter.drawPoint(x, у);
}
40;
40;
Рис. 17.4. Рисование точек
Рисование линий
Для рисования линии, из одной точки в другую, реализован метод
drawLineo, в который передаются координаты начальной (xl, yl) и
конечной (х2, у2) точек (пример 17.3, рис. 17.5). В этот метод можно передавать и
объекты класса QPoint.
[Пример 17.3. Вызов метода drawLineO
Quainter painter(this);
Painter.drawLine(10, 10,
Метод moveToo устанавливает актуальное положение для точки, из которой
°УДет проведена линия. Метод lineToo проводит линию и перемещает ак-
''Уальное положение в ту точку, в которую была проведена линия (пример 17.4,
РИс. 17.6). Таким образом можно нарисовать любой многоугольник.
224
Часть IV. Графика и звук
I IMI-Ialxl
О
Рис. 17.5. Линия Рис. 17.6. Соединение точек линиями
Пример 17.4. Вызов методов moveTo () и 1хпеТо()
QPainter painter(this) ;
int n = 8;
for (int i = 0; i < n; ++i) {
float f Angle = 2 * 3.14 * i / n;
int x = 50 + cos(fAngle) * 40;
int у = 50 + sin(fAngle) * 40;
if (!i) {
painter.moveTo(x, y);
continue;
}
painter.lineTo(x, y) ;
}
Метод drawPoiyLine () проводит линию, которая соединяет точки,
передаваемые в первом параметре. Второй параметр задает индекс точки, с
которой начинает проводиться линия. Третий параметр задает количество точек,
которые должны быть соединены. Первая и последняя точки не
соединяются. Пример 17.5 показывает, как можно построить фигуру, идентичную
фигуре, показанной на рис. 17.6.
Пример 17.5. Вызов метода drawPolylineQ
QPainter painter(this) ;
int n = 8;
QPointArray a(n);
for (int i = 0; i < n; ++i) {
float fAngle =2 * 3.14 * i / n;
int x = 50 + cos(f Angle) * 40;
int у = 50 + sin(f Angle) * 40;
a.setPoint(i, x, y);
}
painter.drawPolyline(a, 0, n) ;
I lffl-inlxl
fnaga 17- Контекст рисования
225
^[еТОД drawLineSegments (const QPointArrayfi, int index = 0, int nlines = -1)
ойСует некоторое количество линий по точкам, содержащимся в объекте
gpointArray. ЛИНИИ прОВОДЯТСЯ между точками index И index+1, index+2
йТ. Д- Пример 17.6, результат которого отображен на рис. 17.7,
демонстрирует этот метод в действии.
Пример 17.6. Вызов метода drawLineSegmentO
QPainter painter(this);
int n = 8;
QPointArray a(n) ;
for (int i = 0; i < n; ++i) {
float fAngle = 2 * 3.14 * i / n;
int x = 50 + cos (fAngle)
int у = 50 + sin(f Angle)
a.setPoint(i, x, y);
}
painter. drawLineSegments (a) ;
40;
40;
Рис. 17.7. Рисование линий методом drawLineSegment ()
Рисование сплошных прямоугольников
Прямоугольник — очень распространенная геометрическая фигура —
посмотрите вокруг, ведь прямоугольные предметы окружают нас везде Qt
содержит два метода для рисования прямоугольников без контура: fiiiRect ()
и eraseRecto. Их внешний вид задается только кистью. В метод fiiiRecto
передаются пять параметров. Первые четыре параметра задают координаты
X и Y, размеры (ширина, высота) прямоугольника. Пятый параметр задает
кисть,
ь
° Метод eraseRect () передаются только четыре параметра, задающие
позицию и размеры прямоугольной области. Для заполнения используется
установленный в виджете цвет фона. Таким образом, вызов этого метода экви-
валентен вызову fiiiRecto, с передачей в пятом параметре значения,
возвещаемого методом paietteBackgroundcoior о. Пример 17.7 иллюстрирует
в^зов методов fiiiRecto и eraseRect о. Результат показан на рис. 17.8.
226
Часть IV. Графика изву^
Пример 17.7. Вызов методов fillRectO и eraseRectO
QPainter painter(this);
QBrush brush(red, QBrush::Dense4Pattern);
painter.fillRect(10, 10, 100, 100, brush);
painter.eraseRect(20, 20, 80, 80);
Рис. 17.8. Прямоугольники
Рисование заполненных фигур
Для рисования фигур также применяются методы, использующие перо QPen
и кисть QBrush. Если требуется нарисовать только контур фигуры, без
заполнения, ТО ДЛЯ ЭТОГО В методе QPainter: : setBrush () нужно установить
значение стиля кисти QBrush: :NoBrush. Аналогично можно поступить и
следующим образом — в методе QPainter: :setPen() устанавливается стиль пера
QPen: .-NoPen, т. е. нарисовать фигуру без контурной линии.
Метод drawRect () рисует прямоугольник. В него передаются следующие
параметры: координаты верхнего левого угла, ширина и высота. В этот метод
можно передать и объект класса QRect (пример 17.8, рис. 17.9).
j Пример 17.8. Вызов метода drawRect ()
QPainter painter(this);
painter.setBrush(QBrush(red, QBrush::DiagCrossPattern));
painter.setPen(QPen(blue, 3, QPen::DashLine));
painter.drawRect(QRect(10, 10, 110, 70));
rwMimz
(■:-;
|X-,
fciSi.^.
— n
l9±*l\
-i^i
Рис. 17.9. Рисование прямоугольника
Глава
17. Контекст рисования
Метод drawRoundRect () рисует прямоугольник с закругленными углами. За-
^угленность углов достигается с помощью четвертинок эллипса. Последние
^а параметра метода задают, насколько сильно должны быть закруглены
углы в направлениях осей координат X и Y соответственно. При
присвоении параметрам нулевых значений углы не будут закруглены. При
присвоении им значения 100 прямоугольник превратится в эллипс. Прямоугольную
область можно задавать объектом класса QRect (пример 17.9, рис. 17.10).
Пример 17.9. Вызов метода drawRoundRect ()
QPainter painter(this);
painter.setBrush(QBrush(green));
painter.setPen(QPen(black));
painter.drawRoundRect(QRect(10, 10, 110, 70), 30, 30);
Рис. 17.10. Рисование прямоугольника с закругленными углами
Метод drawEiiipse () рисует заполненный эллипс, размеры и расположение
которого задаются прямоугольной областью (пример 17.10, рис. 17.11).
; Пример 17.10. Вызов метода drawEllipse()
QPainter painter(this);
Painter.setBrush(QBrush(green, QBrush::CrossPattern))
Painter.setPen(QPen(red, 3, QPen::DotLine));
Painter.drawEiiipse(QRect(10, 10, 110, 70));
Рис. 17.11. Рисование эллипса
228
Часть IV. Графикаизй
Метод drawchord () рисует хорду. Размеры и расположение задаются пряк^
угольной областью, а отображаемая часть — двумя последними параметра,
ми, представляющими собой величины углов. Углы задаются одной шестнад,
цатой градуса. Начальная и конечная точки будут соединены прямой лини-
ей. Предпоследний параметр задает начальный угол. При положительных
значениях двух последних параметров (углов) начальная точка перемещается
вдоль кривой эллипса против часовой стрелки. Стартовое значение верхней
точки дуги равно 16x90°, нижней — 16x270°. Последний параметр задает
угол, под которым кривые должны пересекаться (пример 17.11, рис. 17.12).
Пример 17.11. Вызов метода drawChordO
QPainter painter(this);
painter.setBrush(QBrush(yellow));
painter.setPen(QPen(blue));
painter.drawChord(QRect(10, 10, 110, 70), 45 * 16, 180
16);
Рис. 17.12. Рисование хорды
В мире деловой графики пользуются спросом круговые диаграммы. Этот
тип графика очень удобен для представления статистических данных. Метод
drawPieo рисует часть эллипса. Начальная и конечная точки соединяются
с центром эллипса (пример 17.12, рис. 17.13)
! Пример 17.12. Вызов метода drawPieO
QPainter painter(this);
painter.setBrush(QBrush(black, QBrush::BDiagPattern));
painter.setPen(QPen(blue, 4));
painter.drawPie(QRect(10, 10, 110, 70), 90 * 16, 270 * 16);
Cb
Рис. 17.13. Рисование круговой диаграммы
/7jae£.
17. Контекст рисования
229
Метод drawPoiygon () рисует заполненный полигон, последняя из заданных
вершин которого будет соединена с первой (пример 17.13, рис. 17.14).
fПример 17.13. Вызов метода drawPoiygon ()
gpainter painter(this);
a^nter.setBrush(QBrush(lightGray));
painter.setPen(QPen(black));
int n = 8;
QPointArray a (n) ;
for (int i = 0; i < n; ++i) {
float fAngle = 2 * 3.14 * i / n;
int x = 50 + cos(fAngle) * 40;
int у = 50 + sin(fAngle) * 40;
a.setPoint(i, x, y);
}
painter.drawPoiygon (a) ;
Рис. 17.14. Рисование полигона
Запись команд рисования
Класс QPicture предоставляет возможность протоколирования команд
класса QPainter. С его помощью команды можно даже записывать в отдельные
Файлы (называемые метафайлами), а потом загружать их снова, чтобы
повторить ранее проделанные действия. Эти действия можно перенаправлять
и на другие контексты рисования, например — принтер или экран. В при-
МеРе 17.14 производится запись одной команды рисования в файл
ttyline.dat.
L~.?.MMeP 17-Ч4. Запись команд рисования
Picture pic;
fainter painter;
230 Часть IV. Графика изву^
painter.begin(&pic)
painter.drawLine(20, 20, 50, 50);
painter.end()
if (!pic.save(„myline.dat")) {
qDebugC'can not save the file");
}
Пример 17.15 демонстрирует загрузку команд из файла и их исполнение в
другом контексте. Для отображения в другом контексте используется метод
drawPicture (). Первый параметр этого метода устанавливает позицию, с
которой начнется рисование. Во втором параметре передается объект класса
QPicture.
| Пример 17.15. Загрузка и отображение команды в другом контексте рисования |
QPicture pic;
if (!pic.load("myline.dat")) {
qDebugC'can not load the file") ;
}
QPainter painter;
painter.begin(this)
painter.drawPicture(QPoint(0, 0), pic);
painter.end()
Трансформация систем координат
Класс QPainter предоставляет очень мощный механизм трансформации
координат для отображения объектов. Это позволяет осуществлять просмотр
изображения в повернутом, масштабированном, смещенном и скошенном
виде (рис. 17.15).
Каждая точка в двумерной системе координат описывается двумя
координатами. Трансформация одинаково действует сразу на все точки графического
объекта. Для трансформации в классе QPainter определены следующие
методы: translate о, scale о, rotate о, shear о. Трансформации можно
комбинировать. Порядок следования отражается на конечном результате.
Например, если сначала провести операцию скоса, а затем операцию поворота
и снова скоса, то тогда результат будет отличаться от результата,
полученного в результате двух операций скоса, а затем операции поворота. Трансфер"
мацию отражения можно произвести следующим образом:
painter.scaled, -l);
painter.rotate(180);
17. Контекст рисования
231
Перемещение
ш
Масштабирование
Оригинал S
\/f
Поворот Скос
Рис. 17.15. Различные трансформации
Qt предоставляет методы save () и restore () для сохранения и
восстановления состояний объектов класса QPainter. Это очень удобно при получении
указателя на объект класса QPainter извне. Можно с помощью метода
save о сохранить его состояние, а затем проводить с ним разного рода
трансформации. По окончании трансформации вызов метода restore о
вернет объект класса QPainter в исходное состояние. Например:
pPainter->save () ;
pPainter->translate(20, 40);
pPainter->restore();
Перемещение
Часто требуется переместить изображение в другое положение на экране.
Класс QPainter предоставляет для этого метод translate о, в который
передаются два целочисленных параметра. В первом параметре передается
значение перемещения по оси X, во втором — по оси Y. Положительные
значения первого параметра перемещают объект вправо, а отрицательные —
*лево. Положительные значения второго параметра смещают объект вниз, а
0тРицательные — вверх. Например, следующий вызов производит
перемещение всех рисуемых объектов вправо на 20 и вниз на 10 пикселов:
«Painter painter;
Painter.translate(20, 10);
232
Часть IV. Графика иэву
Метод scale о изменяет размер изображения в соответствии с двумя
множителями для ширины и высоты, передаваемыми в него. Значения меньще
единицы производят уменьшение, большие — увеличение объекта. Напри,
мер, после следующего вызова ширина всех рисуемых объектов увеличится
в полтора раза, а их высота уменьшится наполовину:
QPainter painter;
painter.scale(1.5, 0.5);
Поворот
Одной из основных операций в графике является поворот изображения на
определенный угол. Для поворота класс QPainter содержит метод rotate о, в
который передается значение типа double, задающее угол в градусах. При
положительных значениях поворот осуществляется по часовой стрелке, а
при отрицательных — против. Следующий вызов приведет к изображению
рисуемых объектов повернутыми (по часовой стрелке) на 30 градусов:
QPainter painter;
painter.rotate(30.0) ;
Скос
Этот вид трансформации важен в компьютерной графике. Он реализуется в
классе QPainter методом shear(). Первый параметр задает сдвиг по
вертикали, а второй — по горизонтали. Следующий вызов осуществляет сдвиг по
вертикали вниз:
QPainter painter;
painter.shear(0.3, 0.0);
Отсечения
Отсечения ограничивают вывод графики определенной областью
(многоугольником или эллипсом). Если производится попытка рисования за этими
пределами, то оно будет невидимым. Установка области отсечения
производится С ПОМОЩЬЮ методов setClippRect () И setClipRegionO. МеТОД
setciipRect () устанавливает прямоугольную область отсечения. Пример 17.16
демонстрирует отсечение фигуры эллипса прямоугольной областью.
Результат показан на рис. 17.16.
Гпрймер 17.16. Отсечение прямоугольной областью
^painter painter(this);
painter.setClipRect(0, 0, 100, 100);
oainter.setBrush(QBrush(green));
painter.setPen(QPen(black, 2));
painter.drawEllipse(0, 0, 200, 100);
Рис. 17.16. Отсечение прямоугольной областью
Более сложные области отсечения устанавливаются методом QPainter::
setciipRegion (). В качестве параметров в область передается объект класса
QRegion. В конструкторе класса можно задать область в виде
прямоугольника'или эллипса. Например, следующий вызов создаст прямоугольную
область с координатами (10, 10), шириной и высотой равными 100:
QRegion region(10, 10, 100, 100);
Область отсечения, в виде эллипса, создается следующим образом:
QRegion region (10, 10, 100, 100, QRegion::Ellipse);
Создаваемый эллипс будет иметь координаты (10, 10), ширину и высоту,
равные 100.
В качестве области отсечения можно использовать и полигон, передав в
конструкторе массив точек. Например:
QPointArray а(0, 100, 100, 100, 100, 0, 0, 0) ;
QRegion r(a);
Объекты класса QRegion можно комбинировать друг с другом при помощи
Методов unite (), intersect () и subtract (), благодаря чему можно создавать
Довольно сложные области. Метод unite о возвращает область, полученную
в результате сложения двух областей. Метод intersect о возвращает об-
Ласть, полученную в результате пересечения двух областей. Метод
subtract о возвращает область, полученную в результате вычитания двух
областей. Например:
QRegion regionK 10, 10, L00, 100);
Region region2(10, 10, 100, 100, QRegion::Ellipse);
234 Часть IV. Графика ищ,^
QRegion region3 = regionl.subtract(region2);
painter.setClipRegion(region3);
Подавление эффекта мерцания
Рисование сложного объекта может привести к появлению нежелательного
эффекта мерцания. Оно возникает вследствие отображения пикселов
разного цвета определенное количество раз за очень короткий промежуток
времени. В Qt существует три основных правила для подавления этого
нежелательного эффекта.
Правило первое
В конструкторе виджетов устанавливайте флаг WNoAutoErase. Он говорит Qt
о том, что не нужно стирать изображение до того, как будет вызван метод
обработки события перерисовки paintEvent (). Для установки этого флага
нужно вызвать из конструктора метод setwFiagsn и передать в него
значение WNoAutoErase.
Правило второе
Отключите в виджете режим рисования фона. Для этого нужно вызвать
метод setBackgrqundMode () И передать ему флаг NoBackground.
( Примечание )
Это правило следует применять в тех случаях, когда фон не нужен. Вы берете
на себя контроль над рисованием всей области виджета
Правило третье
Последнее правило — это использование техники двойной буферизации.
Двойная буферизация представляет собой очень распространенное и
простое решение проблемы подавления эффекта мерцания. Суть заключается в
формировании изображения в невидимой области и последующем его
помещении в видимую область за один раз. Для этого используются два
контекста рисования. Один из них — видимый (виджет), другой — невидимый
(объект класса QPixmap). Невидимый и видимый контексты должны быть
одинакового размера. Все изображения формируются в невидимом и
перебрасываются в видимый контекст. Эту переброску удобнее осуществить в
методе обработки событий перерисовки paintEvent о. Переброска
осуществляется С ПОМОЩЬЮ метола QPainter: : drawPixmap () ИЛИ Глобальной фуНКЦИИ
bitBlto. Рекомендуется использовать последнюю функцию— bitBitO-
В примере 17.17 показана возможная реализация метода обработки события
перерисовки с использованием двойной буферизации.
.ymgg 17. Контекст рисования 235
^Пример 17.17. Реализация метода перерисовки paintEventO
/•virtual*/void MyWidget:rpaintEvent(QPaintEvent* pe)
{
Qpixroap pix(size());
QPainter painter(&pix);
// Операции рисования
::bitBlt(this, 0, 0, &pix);
)
резюме
Объект класса QPainter осуществляет рисование на объектах классов,
унаследованных ОТ класса QPaintDevice. Для рисования класс QPainter ПрвДОС-
тавляет перо и кисть. Перо используется для рисования дуг, линий и
контуров замкнутых фигур. Кисти используются для заполнения внутренней
части фигуры. Класс QPainter собирает в себе целый ряд методов для
рисования точек, линий, эллипсов. Все команды рисования можно
запоминать в объектах класса QPicture, этот класс позволяет также сохранять
команды в файлах и считывать их оттуда.
Класс QPainter предоставляет возможность проведения геометрических
преобразований, таких как перемещение, поворот, масштабирование и скос.
Отсечения используются для ограничения вывода графики заданной
областью. Класс QRegion служит для задания областей, которые могут иметь
очень сложные формы.
Эффект мерцания возникает в том случае, когда пикселы за короткие
промежутки времени перерисовываются разными цветами. Использование
механизма двойной буферизации, установка флага WNoErase и отключение
режима рисования фона подавляют этот эффект.
ГЛАВА 18
Растровые изображения
Рисунок расскажет больше тысячи слов.
Древняя китайская мудрость
Растровые изображения представляют собой набор чисел, задающих цвет
пикселов. Пикселы — это "клеточки", формирующие графический образ на
устройстве вывода. Глаз человека в обычных условиях не способен
различать отдельные клетки, поэтому его мозг синтезирует общую картину,
соединяя их.
Форматы графических файлов
Растровые изображения можно как записывать в файлы, так и загружать из
них, имеющих различный формат представления данных. Qt поддерживает
следующие растровые форматы: PNG, BMP, ХВМ, ХРМ, PNM, JPEG,
MNG, GIF, PNM, PBM, PGM и PPM. Далее приводится описание
некоторых форматов.
Формат BMP
Формат BMP (Microsoft Windows Bitmap). Растровый формат для ОС
Windows, который используется для хранения практически всех типов
растровых данных и поддерживает любые разрешения экрана. Файлы этого фор"
мата почти всегда хранятся в несжатом виде и поэтому занимают
сравнительно много места. Структура формата BMP тесно связана с интерфейсом
прикладного программирования API для ОС Windows. Поэтому формат
BMP никогда не рассматривался как переносимый и не использовался Д^я
обмена растровыми изображениями между операционными системами, но с
поддержкой этого формата в Qt все изменилось — он стал платформонеза-
висимым.
/7*2!
18. Растровые изображения 237
формат GIF
формат GIF (Graphics Interchange Format), произносится как "джиф" — это
^рмат обмена графическими данными. Это один из самых популярных
«астровых форматов в Интернете. Основное преимущество этого формата
состоит в высокой степени сжатия без потерь, что достигается применением
алгоритма сжатия LZW (Lempel-Ziv-Welch, Лемпель-Зив-Велч). Также GIF
поддерживает и анимацию. Одним из недостатков является поддержка
только 8-битовой глубины цвета, а также требование лицензионных отчислений
за каждую программу, использующую LZW-алгоритм.
формат PNG
формат PNG (Portable Network Graphics), произносится как "пинг" —
переносимая сетевая графика. Этот формат разработан как альтернатива GIF в
пику его юридическим сложностям, связанными с требованиями оплаты
при использовании. Неофициальная трактовка названия PNG — "PNG's
Not GIF" ("PNG — это не GIF"). Подавляющее большинство Web-браузеров
поддерживают этот формат. Он не сложен в реализации и по своим
функциональным возможностям даже превосходит GIF. PNG распространяется
бесплатно, что позволяет избежать бремени лицензионных платежей и
патентных сборов. Так же, как и у GIF, в этом формате обеспечивается
сжатие данных без потерь и поддерживается прозрачность.
( Примечание )
В качестве альтернативы для анимированных файлов формата GIF можно
использовать формат MNG, хранящий в себе серии изображений формата PNG .
Формат JPEG
Формат JPEG получил свое название от Joint Photographic Experts Group —
объединенная экспертная группа по фотографии и произносится как "джи-
пег". Этот формат разрабатывался с 1991 по 1993 г., после чего был
стандартизирован. Под аббревиатурой скрыто название организации, разработав-
Шей стандарт и метод сжатия. Его отличительная особенность — это очень
высокая степень сжатия, но с потерей информации, поэтому файлы такого
Формата используются, в основном, для сжатия фотографических
изображений, т. к. на них редко можно увидеть небольшие погрешности.
Формат ХРМ
Jp*M (XPixMap) — это распространенный в системе XII (UNIX) формат.
Мы привыкли к тому, что изображение хранится в виде двоичной
информации, но в формате ХМР данные хранятся в виде исходного кода на язы-
238 Часть IV. Графика ищ,н
ке С, который можно вставлять в свои программы. Это позволяет превра,
тить обычный текстовый редактор в инструмент для создания и изменения
растровых изображений. Формат можно использовать для любых разрешу
ний экрана и 24-битовой глубины цвета.
С Примечание ^
Ввиду того, что формат неэкономичен в отношении занимаемого объема, его
лучше использовать для небольших по размеру растровых изображений.
В левой части рис. 18.1 показано содержимое файла в формате ХРМ, пред.
ставляющее собой код на языке С, в которой определен массив, хранящий
растровые данные в формате ХРМ. В правой части рисунка показано само
растровое изображение.
/* хрм */
static const char* image_xpm[] =
{
/* width height ncolors
charsjperjpixel */
"16 16 4 1",
" с #000000",
". с #848200",
"+ с #848284",
"@ с #d6d3ce",
••eeeeeeeeeeeeeeee",
"@ @«,
"@ . ееееееее e +",
"@ . ввевввве +n,
"@ . ввевввве . +-,
"в . евевевев . +",
-в . ввевввве . +",
"в . евевевев . +",
-в .. .. +",
пе +",
"в .. . +-,
"@ .. ее . +••,
и@ .. ее . +",
"@ .. ее . +",
■•ее +-,
"@е++++++++++++++",
};
Рис. 18.1. Файл в формате ХРМ
В первой строке ХРМ-файла стоит комментарий /*хрм*/, сообщающий о
формате файла (рис. 18.1). За ним следует константный указатель на массив
imagexpm, задающий само растровое изображение. Первые два целых числа
в строке задают ширину и высоту растрового изображения в пикселах
(в нашем случае размер равен 16x16 пикселов). Третье, целое число, задает
количество цветов (в примере оно равно 4). Четвертое число задает количе-
гпйва 18. Растровые изображения 239
схво знаков на пиксел и в нашем случае оно составляет один знак.
Следующие четыре значения задают цветовую палитру из четырех цветовых
значений. Символ отделяется от цветового значения буквой с, которая
является сокращением для слова color (цвет).
( Примечание ^
Можно вместо буквы с использовать букву s, что дает возможность применения
символических имен для цветов. Так, например, можно, указав после этого
символа None, задать символ для прозрачного цвета.
Как видно из рис. 18.1, для черного цвета используется символ пробела, для
коричневого — точка, для темно-серого — плюс, а для светло-серого — @.
Далее следует расположение пикселов в матрице растрового изображения.
Контекстно-независимое представление
Контекстно-независимое представление не зависит от возможностей
графической карты вашего компьютера и, следовательно, от графического
режима, установленного в вашей операционной системе. Данные растрового
изображения помещаются в обычный массив, что дает возможность очень
эффективного обращения к каждому из пикселов в отдельности, а также
позволяет эффективно производить операции записи и считывания файлов
растровых изображений.
Класс Qlmage
Qimage является основным классом для контекстно-независимого
представления растровых изображений. Его определение находится в заголовочном
файле qimage.h. Максимальная ширина и высота хранимого растрового
изображения ограничена 32 767 пикселами. Данные растрового изображения
хранятся построчно, в объектах класса Qimage, в каждой строке пикселы
Расположены слева направо. Таким образом, например, при использовании
глубины цвета в один бит каждый байт хранит в себе восемь битовых
значений. Если количество бит ширины изображения не делится на восемь, то
последний байт строки будет содержать биты, значения которых можно
проигнорировать, а следующая строка начнется с нового байта. Qimage
содержит массив указателей, указывающих на начало каждой строки
изображения. Вызовом метода scanLineO можно получить адрес строки, номер
Которой соответствует переданному в этот метод значению. Имеет смысл
Передавать в него только индексы строк, лежащие в диапазоне от нуля до
высоты растрового изображения, уменьшенного на единицу.
Чтобы создать объект этого класса, необходимо передать в конструктор ши-
РИну, высоту и глубину цвета растрового изображения. Например, следую-
240 ЧастьIV. Графикаиэвуп
щая строка создает растровое изображение шириной 320 пикселов, высотой
200 и глубиной цвета 32 бит.
Qimage img(320, 240, 32);
Содержимое для объекта Qimage считывается из файла, хранящего растровое
изображение. Имя файла проще всего передать в конструктор, при создании
объекта класса Qimage. В том случае, если данный графический формат
поддерживается Qt, то растровые данные будут загружены и автоматически
установится его ширина и высота. Например:
Qimage img("lisa.jpg");
В конструктор можно передавать указатель на массив данных формата
ХРМ. Например, согласно рис. 18.1, загрузка растровых данных будет
выглядеть следующим образом:
#include "image_xpm.h"
Qimage img (image_xpm) ;
В качестве альтернативы для считывания можно воспользоваться методом
loado. Его несомненное достоинство состоит в том, что с его помощью
можно загружать растровые изображения в любой момент времени. В
первом параметре передается имя файла, во втором — формат. Формат
обозначается строкой типа char*, принимающей возможные строковые значения:
GIF, BMP, JPG, ХРМ, ХВМ или PNG. Если во втором параметре вообще
ничего не передавать, то класс Qimage произведет попытку распознать
графический формат самостоятельно. Например:
Qimage img;
img.load("lisa.jpg");
При помощи метода save о можно записывать растровое изображение из
объекта класса Qimage в файл. Первым в этот метод передается имя файла, а
затем графический формат, в котором он должен быть записан. В данном
случае формат передается в обязательном порядке. Например:
Qimage img(320f 240, 32, QColor::blue);
img.save("blue.jpg", "JPG");
( Примечание )
Ввиду того, что фирма Unisys имеет патент на метод сжатия LZW,
используемый форматом GIF, то в некоторых странах создание GIF-файлов является
нелегальным без соответствующей лицензии. По этой причине Qt не
предоставляет возможность записи в формат GIF.
Класс Qimage просто незаменим, в плане эффективности, когда нужно
изменить или получить цвета пикселов растрового изображения RGB-зна-
Глава 18. Растровые изображения 241
цение пиксела с координатами X и Y можно получить с помощью метода
pixel(х, у). Для записи RGB значений используется структура данных QRgb
(см- гд- 16). Например:
QRgb rgb = img.pixel(250, 100);
С Примечание ^
Метод pixel Index О возвращает индекс палитры пиксела с координатами X
и Y. Этот метод работает только для растровых изображений, имеющих
глубину цвета 8 бит.
Установить новое RGB-значение пикселу с координатами X и Y можно
вызовом метода setPixel (х, у, rgb).
С Примечание }
Для растровых изображений, имеющих глубину цвета 8 бит. значение rgb будет
соответствовать индексу палитры.
Например:
QRgb rgb = qRgb(200, 100, 0);
img.setPixel(20, 50, rgb);
Объект класса Qimage может быть отображен при помощи метода
QPainter: :drawimage() в контексте рисования, передав ему в первых двух
параметрах позицию левого верхнего угла, с которого будет осуществлен
вывод. Перед тем как отобразить объект Qimage на экране, метод
drawimageo преобразует его в контекстно-зависимое представление (объект
класса QPixmap). В листинге 18.1 показан вывод изображения с позиции
(0, 0) (рис. 18.2).
I Листинг 18.1. Вывод растрового изображения
QPainter painter(this);
Qimage imgC'lisa. jpg");
painter. drawlmage (0, 0, img) ;
Если нужно вывести только часть растрового изображения, то необходимо
Указать эту часть в дополнительных параметрах метола drawimageo.
Следующий листинг 18.2 отображает участок растрового изображения,
задаваемый координатой (30, 30) и имеющий ширину 110, а высоту 100 пикселов
(Рис. 18.3).
[Листинг 18.2. Вывод части растрового изображения
QPainter painter(this);
Qbtiage imgC'lisa. jpg") ;
Winter.drawlmage(0, 0, img, 30, 30, 110, 100);
242
Часть IV. Графика извук
|i 1Ш1ал.ц]м_=тйг5|
%г~'
Рис. 18.2. Вывод объекта Qlmage
в контексте рисования
пшш
Рис. 18.3. Вывод части объекта Qlmage
в контексте рисования
Вызвав метод invert Pixels о и передав в него значение булевого типа,
можно управлять инвертированием пикселов. В листинге 18.3
проиллюстрирована эта возможность, а результат отображен на рис. 18.4.
[Non-Commercial] - Qlmage Demo
BDE3
V
Рис. 18.4. Инвертирование значении пикселов
Листинг 18.3. Инвертирование пикселов
QPainter painter(this);
Qlmage img("lisa.jpg");
{пава 18. Растровые изображения
oainter.drawlmage(О, 0, img);
jjng. invert Pixels (true) ;
painter.drawlmage(img.width(), 0, img) ;
При помощи метода scale о можно получать новое растровое изображение
с измененными размерами. Действие флагов, управляющих изменением
размеров — ScaieFree и ScaieMin, соответственно, подробно рассмотрено в
2/1, 16. Листинг 18.4 демонстрирует возможности изменения размеров
растрового изображения, результаты которых показаны на рис. 18.5.
Рис. 18.5. Изменение размеров растрового изображения
[Листинг 18.4. Изменение размеров
QPainter painter(this);
Olmage imgl("lisa.jpg");
Painter.drawlmage(0, 0, imgl);
Qlmage img2 =
imgl.scale(imgl.width() / 2, imgl.height(), Qlmage::ScaieFree);
Winter.drawlmage(imgl.width(), 0, img2);
244
Часть IV. Графика и звук;
Qimage img3 =
imgl.scale(imgl.width(), imgl.height() / 2, Qimage::SealeFree);
painter.drawlmage(0, imgl.height(), img3);
Qimage img4 =
imgl.scale(img.width() /2, imgl.height(), Qimage::ScaleMin);
painter.drawlmage(imgl.width(), imgl.height(), img4);
Класс Qimage предоставляет возможность горизонтального и вертикального
отражения растрового изображения. Для этого в метод mirror о необходимо
передать первым параметром булево значение, управляющее
горизонтальным отражением, а вторым — булево значение, управляющее вертикальным
отражением. Метод mirror о не изменяет растровое изображение объекта,
из которого он был вызван, а создает новый. Листинг 18.5 производит
вертикальное и горизонтальное отражения. Результат показан на рис. 18.6.
Листинг 18.5. Отражение изображения
QPainter painter (this);
Qimage imgClisa.jpg");
painter.drawlmage(0, 0, img);
painter.drawlmage(img.width(),
0, img.mirror(true, true)
[Non-Commercial] - Qimage Demo
-П*
^
Рис. 18.6. Отражение изображения
Класс QlmagelO
Этот класс определен в заголовочном файле qimage.h и реализует чтение и
запись растровых изображений различных форматов, предоставляет в°3'
(пава 18. Растровые изображения 245
цожность расширения количества. При помощи статического метода
defineiOHandier () устанавливаются собственные функции для чтения и
записи растровых файлов. После установки эти функции автоматически
будут ИСПОЛЬЗОВаны При вызове методов Qlmage::load() И QImage::save().
Например, функции для чтения и записи не поддерживаемого в Qt формата
рСХ можно установить так, как это показано в листинге 18.6.
Рлистинг 18.6. Установка функций считывания и записи формата PCX
L-—-•'• J
void readPCX(QlmagelO* pimgio)
{
// Код для чтения PCX-файла
}
void writePCX (QlmagelO* pimgio)
{
// Код для записи PCX-файла
}
QlmagelO: :defineiOHandier ("PCX",
•70xA/0x5/0xl/0x8",
0,
readPCX,
writePCX
);
Первый параметр, передаваемый в статический метод def ineiOHandier (), —
это строка, идентифицирующая сам формат (см. листинг 18.1). Она нужна
ДЛЯ задания ВТОРОГО Параметра методов Qlmage: : save О И Qlmage: : load () И
представляет собой строку регулярного выражения (см. гл. 36),
описывающую сигнатуру растрового формата. Третий параметр статического метода
def ineiOHandier () задает флаг формата строкового типа. На данный момент
в Qt определен только один флаг т для графического файла, находящегося в
текстовом формате (соответствует формату, подобному ХРМ). Для
двоичных файлов нужно передавать нуль. Пятый и шестой параметры задают
адреса функций для считывания и записи растровых файлов. В этих функциях
^я работы с данными вызывается метод ioDeviceO из передаваемого
указателя на объект QlmagelO.
контекстно-зависимое представление
^Нтекстно-зависимое представление позволяет отображать растровые
изображения на экране гораздо быстрее, чем контекстно-независимое, т. к. оно
Не требует проведения дополнительных преобразований. К объектам кон-
246 Часть IV. Графика и звук
текстно-зависимого представления можно применять все методы класса
QPainter. Большинство процессоров графических карт обладают
способностью отображать графические примитивы, например, линии, полигоны и
поверхности, не используя при этом основного процессора, благодаря чему
очень сильно ускоряются графический вывод и работа самой программы.
Класс QPixmap
Этот класс унаследован от класса контекста рисования QPaintDevice. Его
определение находится в заголовочном файле qpixmap.h. Объекты класса
содержат растровые изображения, не отображая их на экране. Поэтому они
очень часто используются в качестве промежуточного буфера для
рисования. То есть если требуется нарисовать сложное изображение, то его
сначала рисуют в объекте класса QPixmap, а потом, при помощи объекта класса
QPainter или глобальной функции bitBito, копируют в видимую область
(см. гл. 17). Это позволяет избежать нежелательного эффекта мерцания.
Для создания объекта этого класса в его конструктор нужно передать
ширину, высоту и глубину цвета. Глубина цвета должна быть равна только
одному из следующих значений: 1, 8, 15, 16 или 32 битам. Это значение
зависит от графической карты, т. к. она может поддерживать в один промежуток
времени только один графический режим. Если для значения глубины цвета
в конструкторе не передавать вообще ничего, то в создаваемом объекте
класса QPixmap автоматически будет установлено актуальное значение
графического режима. Это значение можно узнать вызовом статического
метода QPixmap: :defaultDepth(). Например:
QPixmap pix(320, 240);
Объект класса QPixmap можно создать, передав в его конструктор объект
класса Qimage. Например:
Qlmage img(320, 240, 32, QColor::blue);
QPixmap pix(img);
Есть возможность передать файл растрового изображения прямо в
конструктор. Например:
QPixmap pixC'forest. jpg") ;
QPixmap, как и класс Qimage, предоставляет возможность загрузки ХРМ-
данных прямо в конструкторе.
#include "image_xpm.h"
QPixmap pix(image_xpm);
Объекты класса QPixmap содержат не сами данные, а их идентификаторы*
с помощью которых они могут обратиться к системе. Поэтому прямой ПРС~
fnagg *& Растровые изображения
247
,-yn к каждому пикселу в отдельности очень медленный. Чтобы изменить
всего один пиксел, необходимо проделать следующие операции:
QPixmap pix(200, 200);
QPainter painter(&pix);
painter.setPen(QColor(0, 255, 0));
painter.drawPoint(100, 50) ;
В распоряжении класса QPainter имеются методы для считывания и записи
графических изображений— load о и save о. При проведении этих
операций все равно будет осуществляться промежуточное конвертирование из
объекта класса Qimage или в него.
Объект класса QPixmap отображается в видимой области вызовом метода
QPainter::drawPixmap(). Листинг 18.7 демонстрирует два разных варианта
вызова метода drawPixmap (). В первом варианте указана только позиция,
с которой нужно осуществить вывод. Во втором — вывод задается
прямоугольной областью, в которой должно отображаться растровое изображение.
Результат показан на рис. 18.7.
Листинг 18.7. Вывод растрового изображения
QPainter painter(this);
QPixmap pix("forest.jpg");
painter.drawPixmap (0, 0, pix) ;
QRect r (pix. width () ,
painter. drawPixmap (r,
0, pix.width() / 2, pix.height());
pix) ;
[Non-Commercial]
i
- Qimage D..
*>
BD
'
X )
Рис. 18.7. Разные способы отображения объекта класса QPixmap
248
Часть IV. Графика и звук
Класс QBitmap
Класс QBitmap унаследован от QPixmap и определен в заголовочном файле
qbitmap.h. Объекты класса предназначены для хранения растровых изобра-
жений, обладающих глубиной цвета, равной одному биту. Это позволяет
хранить изображения, имеющие только два цвета: coioro и coiori. Значения
их определены в классе QCoior. Такое изображение будет называться
двухуровневым (bi-level). Класс используется в основном для хранения форм
указателей мыши и масок прозрачности.
Класс QPixmapCache
Этот класс реализует кэш для объектов класса QPixmap. Все операции
производятся с глобальным объектом кэша, поэтому все методы этого класса
определены как статические. С помощью метода insert о можно поместить
объект класса QPixmap с ключом строкового типа в кэш. В том случае, если
растровое изображение было считано из файла, то в качестве строки можно
использовать его имя. Передавая ключ в метод findo, можно получить
внесенное растровое изображение из кэша. В кэш имеет смысл помещать
растровые изображения, часто используемые в программе, чтобы избежать
считывания из файла при каждом обращении к ним.
Прозрачность
Оба класса, QPixmap и Qimage, предоставляют возможность делать пикселы
растрового изображения прозрачными.
Прозрачность
с использованием класса QPixmap
В объектах класса QPixmap добиться прозрачности можно при помощи
специальной маски. Для установления маски необходимо вызвать метод
QPixmap: rsetMaskо. Размеры маски и растрового изображения, к которому
она применяется, должны быть одинаковы. Маски создаются в объектах
класса QBitmap. Для их создания можно воспользоваться объектом класса
QPainter, при этом для прозрачного пиксела необходимо использовать ивет
coioro, а для непрозрачного пиксела — colon. Если маска не установлена,
то все пикселы растрового изображения будут непрозрачны. Маски следУе
использовать в случаях острой необходимости, т. к. это существенно
снижает быстроту вывода растрового изображения. Листинг 18.8 демонстрируй
вышеизложенное. В качестве маски используется надпись, вид которой
задается размером и шрифтом. Результат показан на рис. 18.8.
глава 18. Растровые изображения
249
fЛистинг 18.8. Шрифт с фоном
U-^-
ORect r(0, 0, nWidth, nHeight);
QPixmap pix(nWidth, nHeight);
QPainter painter;
painter.begin(&pix) ;
painter.drawPixmap(r, QPixmap("water.jpg"));
painter.end();
QBitmap bmp(nWidth, nHeight);
Ixip.fill (colorO) ;
painter. begin (&bmp) ;
painter.setPen(QPen(colorl));
painter.setFont(QFont("Times", 75, QFont::Bold));
painter.drawText(r, AlignCenter, "Draw Text");
painter. end () ;
pix.setMask(bmp);
[Non-Commercial] - Draw Tent BD E3
Рис. 18.8. Шрифт, фон которого заполнен растровым изображением
Класс QWidget, как И класс QPixmap, Содержит метод setMask (). С его
ПОМОЩЬЮ можно установить маску и сделать окно виджета не квадратным, а
любой другой формы. Результат работы программы, показанный на
Рис. 18.9, демонстрирует эту возможность.
Листинг 18.9. Файл main.cpp
include <qapplication.h>
include <qpixmap.h>
♦include "Window.h"
//
lnt main(int argc, char** argv)
QApplication app(argc, argv);
Window win(0, 0, Qt::WStyle_Customize | Qt::WStyle_NoBorder);
250
Часть IV. Графика и звук
QPixmap pix("unixoids.png");
if (pix.mask()) {
win.setMask(*pix.mask()) ;
}
win.setBackgroundPixmap(pix);
win.show();
app.setMainWidget(&win);
return app.exec();
Рис. 18.9. Пример нестандартного окна программы
В листинге 18.9 создается объект класса window, в конструкторе которого
необходимо передать флаги wstyieCustomize i styieNoBorder, чтобы
избежать отображения декораций окна, предоставляемых системой, например —
заголовка и рамки. В объект pix загружается файл растрового изображения.
( Примечание ^
Графические форматы, например GIF, PNG и ХМР, могут содержать
прозрачность. При их считывании в конструкторе или методом load () автоматически
создается маска, которую при необходимости можно установить.
В операторе if вызовом метода mask о производится проверка на наличие
маски в растровом изображении. В случае наличия она устанавливается в
ВИДЖете методом setMaskO. После ЭТОГО ВЫЗОВОМ метода setBackgroundPixmap О
в виджете устанавливается фон изображения.
Глава 18. Растровые изображения
251
| Листинг 18.10. Файл Window.h
fifndef _Window_h_
fdefine _Window_h_
finclude <qwidget.h>
// :
class Window : public QWidget {
QJOBJECT
private:
QPoint mjptPosition;
protected:
virtual void Window::mousePressEvent(QMouseEvent* pe)
{
m_j>tPosition = pe->pos();
}
virtual void Window::mouseMoveEvent(QMouseEvent* pe)
{
move (pe->globalPos () - m_j>t Posit ion) ;
}
public:
Window(QWidget* pwgt = 0, const char* pszName = 0, WFlags flags = 0)
: QWidget(pwgt, pszName, flags)
{
}
};
tendif //_Window_h_
Ввиду того, что наше окно не имеет ни заголовка, ни рамки, нам нужно
Предоставить возможность его перемещения. В листинге 18.10 определен
Класс Window, перезаписывающий методы событий МЫШИ mousePressEvent ()
и mouseMoveEvent (), необходимые для изменения расположения окна на эк-
^не. Переменная m_ptPostion нужна для хранения координат указателя
мЬ1Ши относительно начала окна виджета.
^ЗСС QPixmap содержит метод createHeuristicMask(). ЭТОТ метод Позволяет
°3давать маски, исходя из растрового изображения. Для этого берутся
знания пикселов четырех углов изображения и все пикселы с этим значени-
** Устанавливаются прозрачными. Это может быть удобным для растровых
*°РМатов, не поддерживающих прозрачность.
252
Часть IV. Графика и звук
Прозрачность
с использованием класса Qimage
Класс Qimage использует для прозрачности альфа-канал (alpha channel).
Значение прозрачности может лежать в пределах от 0 до 255, что обеспечивает
более мягкие переходы от прозрачного цвета к непрозрачному.
По умолчанию в объектах класса Qimage альфа-канал не используется, т. к
это снижает быстродействие всей программы. Чтобы использовать альфа-
канал, нужно вызвать метод setAlphaBuffer () с параметром true, но прежде
чем это сделать, его необходимо проинициализировать нужными
значениями. Для этого используется функция qRgbao (см. гл. 16). Созданный цвет с
альфа-значением устанавливается в растровом изображении вызовом метода
Qimage::setPixel ().
Для отображения объекта Qimage с альфа-каналом на экране необходимо,
при помощи метода QPixmap:: convertFromimage (), преобразовать его в
объект типа QPixmap. Если преобразование прошло удачно, то метод возвращает
true, иначе — false. Из-за того, что маска объекта QPixmap предоставляет
только два возможных состояния прозрачности пиксела — видимый или
невидимый, альфа-значения меньшие 128 получат значение 0.
Для получения эффекта мягкой прозрачности необходимо использовать два
объекта класса Qimage. В первый объект класса Qimage записывается
содержимое области, в которой должно быть отображено растровое изображение.
Второй объект класса, содержащий альфа-канал, будет наложен на первый,
а результат наложения будет скопирован в заданную область. Таким
образом можно добиться эффекта полупрозрачности.
Резюме
Qt поддерживает большое число форматов файлов растровых изображений.
В их число входят такие популярные форматы, как BMP, GIF, PNG, JPEG
и ХРМ. Формат ХРМ применяется в основном для отображения иконок.
Основное отличие форматов ХРМ и ХВМ от других состоит в том, что он
содержит исходный код на языке С.
Классы для хранения растровых данных подразделяются в Qt на
контекстно-зависимые и контекстно-независимые. Основным, для контекстно-
независимого представления, является класс Qimage. Объекты класса Qimage
способны содержать и изменять данные, находящиеся в том графическом
режиме, который не поддерживается графической картой. Независимость о
контекста позволяет очень эффективно проводить получение и изменен!'
значений отдельных пикселов, а также проводить операции считывания
сохранения файлов. Класс Qimage содержит ряд методов, позволяющих пр°
Глава 18. Растровые изображения
253
водить с пикселами различного рода операции, такие как отражение
расовых изображений, изменение их размеров, инвертирование и др. Класс
Qimageio управляет считыванием и записью изображений в файлы. С его
помощью можно самому написать поддержку для любого растрового фор-
щата, добавив свой собственный код для чтения и записи файлов этого
формата в функции.
Основным представителем контекстно-зависимого представления является
класс QPixmap. Объекты этого класса отображаются гораздо быстрее
объектов класса Qimage. На объектах класса QPixmap можно рисовать при помощи
класса QPainter линии, эллипсы, прямоугольники и т. д. Эти объекты
имеют глубину цвета, равную глубине цвета текущего режима графической
карты. Унаследованный от QPainter класс QBitraap предоставляет возможность
для хранения двух цветных растровых изображений.
Оба класса обладают возможностью задавать пикселам прозрачность.
QPixmap предоставляет прозрачность в жесткой форме — либо пиксел
прозрачен, либо нет. Класс Qimage обладает альфа-каналом, с помощью
которого возможно создание мягкой прозрачности.
ГЛАВА 19
Работа со шрифтами
Размышляющий найдет, что черта делает
буквы, буквы — слог, а слоги — слово,
следовательно, слог был прежде слова,
буква прежде слога, и черта прежде буквы.
Карл Эккартсгаузен.
"Ключ к таинствам натуры"
Шрифт имеет очень древнюю историю. Согласно историческим данным
первый буквенный знак появился более восьми тысяч лет назад. На
протяжении тысячелетий буквы создавались вручную. Процесс печати был
изобретен в Китае примерно две с половиной тысяч лей назад. И вот
произошла вторая революция и теперь мы видим их на экранах наших мониторов.
Эта глава посвящена масштабируемым шрифтам (гарнитурам). Гарнитура
шрифта это его внешний вид (рисунок шрифта). Масштабируемая
гарнитура — это идеальное математическое описание шрифта. Она позволяет
отображать его на экране без искажений и выводить на печать. Проведение
соответствующих преобразований берут на себя специальные функции
растеризации, которые преобразуют математическое представление шрифта для
его последующего отображения в растровую матрицу. Эти функции
вызываются неявно и не накладывают на разработчика дополнительных
временных затрат при разработке.
В Qt класс QFont является основным для работы со шрифтом. Объект этого
класса задается целым рядом параметров:
П семейство шрифта;
П размер;
П толщина — нормальная или полужирная;
П отображаемые знаки;
П стиль — нормальный или наклонный.
rnaea ^- Работа со шрифтами
При передаче объекта класса QFont в метод QWidget: :setFont о последний
устанавливает в виджете шрифт, который будет использоваться при его
0^>бражении. Если требуется установить один шрифт для всего
приложения, то объект класса QFont нужно передать в статический метод
«application:: setFont ().
Qt содержит дополнительные классы для поддержки работы со шрифтом:
QFontDatabase, QFontlnfo И QFontMetrics.
Класс QFontDatabase предоставляет информацию обо всех
проинсталлированных в системе шрифтах. Для их получения вызывается метод families о,
который возвращает список шрифтов в объекте класса QstringList.
Класс QFontlnfo служит для получения информации о конкретном шрифте.
Вызовом методов family о можно узнать семейство шрифта. Методы
italic О и bold о возвращают значения булевого типа, информирующие
о стиле (наклонности и жирности) шрифта.
Класс QFontMetrics предоставляет информацию о характеристиках шрифта,
показанных на рис. 19.1.
leftBearingO
левое пространство
буквы
* rightBearingO
правое пространство
буквы
charWidth()
width()
ширина строки
descent() lineSpacing()
Высота подстрочного элемента ' пространство строки
Рис. 19.1. Характеристики шрифта
Предавая в конструктор класса QFontMetrics объект класса QFont, можно
^ОЛучать его характеристики. Методы leftBearingO И rightBearingO ВОЗВра-
256 Часть IV. Графика и зву*
щают в пикселах левое и правое пространство буквы. Метод linespacingo
возвращает расстояние между базовыми линиями. Передав в метод
charwidtho строку и позицию символа, узнают его ширину. Чтобы узнать
размер всей строки, ее нужно передать в метод widtho. Высота
возвращается методом height о, в который не требуется ничего передавать. Например:
QFontMetrics fm(QFont("Courier", 18, QFont::Bold));
QString str = "String";
cout « "Widht:" « fm.width(str)
« "Height:" « fm.height();
Для получения высоты надстрочного и подстрочного элемента шрифта
необходимо вызвать методы ascent о и descent (). Высота надстрочного
элемента — это высота символа над его базовой линией шрифта (включая
диакритические знаки), а высота подстрочного элемента — это высота
символа под базовой линией шрифта.
Вызовом метода boundRect (), передав в него строку, можно получить объект
прямоугольной области, необходимой для отображения текста строки. Этот
метод удобно использовать для определения геометрии текста, до начала его
отображения.
Отображение строки
В объектах класса QPainter методом QPainter: :setFont о устанавливаются
объекты класса QFont. В классе QPainter имеются различные варианты
метода drawTexto для отображения текста с установленным шрифтом:
П drawText(int x, int у, const char* psz, int nLen = -1) — отображает
текст psz. Координату левого края текста задает параметр х, а параметр
у задает координату основной линии. Параметр nLen задает количество
символов, которые должны быть отображены. По умолчанию параметр
равен — 1, это означает, что должны отображаться все символы;
П drawText(const QPoint& pt, const char* psz, int nLen = -1) — ОТЛИЧаеТ-
ся от вышеприведенного метода тем, что в первом параметре вместо х и
у передается объект точки QPoint.
Пример 19.1, результат которого показан на рис. 19.2, отображает на экране
строку текста "Draw Line".
Пример 19.1. Вызов методов setFont () и drawText ()
QPainter painter(this);
painter.setFont(QFont("Times", 25, QFont::Normal) );
painter.drawText(10, 40, "DrawText");
гмва 19. Работа со шрифтами
I Ш.1ЛА1..1..иШ-1м1х||
Draw Text
Рис. 19.2. Отображение строки
Класс QPainter предоставляет методы для более тонкой настройки
отображаемого текста:
Q drawText(int x, int у, int w, int h, int nTf, const char* psz,
int nLen = -l) — отображает часть текста psz в заданной параметрами х,
у, w и h прямоугольной области. С помошью параметра nTf (text format)
можно повлиять на размещение и отображение текста. Значение этого
параметра получается посредством комбинации друг с другом значений,
указанных в табл. 5.1 и 19.1, с помощью логической операции | (ИЛИ).
Параметр nLen управляет максимальным количеством отображаемых
знаков, если параметр равен —1, то отображается строка целиком;
О drawText (const QRectfi rl, int nTf, const char* psz, int nLen = -1) —
этот метод отличается тем, что прямоугольная область задается одним
параметром QRect.
Таблица 19.1. Перечисления TextFiags класса Qt
Константа
SingleLine
DontClip
ExpandTabs
ShowPrefix
Worcffireak
Значение
0x0040
0x0080
0x0100
0x0200
0x0400
Описание
Игнорирует знаки новой строки (знак \п)
Гарантирует, что в том случае, если текст будет
выступать за пределы, он не будет подрезан
Замещает знаки табуляции \t равносильным
пространством
Знак & не будет отображаться, а получит специальное
назначение. Следующий за ним символ будет
подчеркнут и получит клавишу быстрого доступа
Автоматически производит перевод строки, если строка
выходит за пределы заданного прямоугольника
Пример 19.2 выводит строку текста по центру. Результат приведен на
Рис. 19.3. Выводимая строка не в состоянии поместиться полностью в
прямоугольной области задаваемой переменной г, поэтому осуществляется
переход на новую строку посредством передачи флага wordBreak. Метод
drQwRect() вызывается для отображения границ прямоугольной области.
258
Часть IV. Графика извун
\ Пример 19.2. Вызов методов setFontO, drawRectO и drawTextO
QPainter painter (this);
QRect r = QRect(0, 0, 120, 200);
painter.setFont(QFont("Times", 25, QFont::Normal));
painter.drawRect(r);
painter.drawText(r, AlignCenter | WordBreak, "Draw Text");
I IWiH-Mxl
Draw-
Text
Рис. 19.3. Отображение строки
Резюме
Шрифты используются для вывода текста на контексте рисования. Они
задаются высотой, шириной и названием. QFont является основным классом
шрифта. При помощи класса QFont info можно получить информацию о
семействе шрифта. Класс QFontDataBase предоставляет информацию о
шрифтах, проинсталлированных в системе. Класс QFontMetrics дает информацию
о целом ряде характеристик шрифта, например о его высоте, ширине букв
и др.
Класс QPainter содержит метод для установки шрифта, а также ряд методов,
позволяющих отображать текст различным образом.
ГЛАВА 20
работа
с изображениями холста
А вот и тропинка. Она приведет меня
прямо наверх... Но как она кружит!
Прямо штопор, а не тропинка.
Льюис Кэрролл. "Атса в Зазеркалье"
При программировании графики и игр мы зачастую имеем дело с массой
двигающихся объектов, перекрывающих друг друга. Все они должны быть
отображены без эффектов мерцания. И это представляет собой нелегкую
задачу для разработчика. Три основных класса для работы с холстом —
QCanvas, QCanvasview и QCanvasitem — предоставляют решение этой
проблемы (рис. 20.1).
QCanvasRectangle h
QCanvasPojygon Ь
H QCanvasSpline \j
Рис. 20.1. Иерархия классов для работы с изображением холста
Be,
* классы для работы с холстом определены в одном и том же
заголовочном файле qcanvas.h. Их взаимодействие друг с другом вкратце можно опи-
260 Часть IV. Графика изву^
сать следующим образом. Класс QCanvas является контейнером для объектов
классов, унаследованных ОТ QCanvasItem, а класс QCanvasView — ЭТО ВИДжет
(унаследованный от класса QScrollview), используемый для показа содер.
жимого объекта класса QCanvas.
Класс QCanvas является классом более высокого уровня, чем QPainter.
Объект класса QCanvas не отображает виджет каждый раз при перемещении, а
делает это только в определенные промежутки времени. Как только объект
переместился, объект класса QCanvas запоминает его новую позицию. Перед
перерисовкой объект разделяет всю свою область на подобласти и
производит анализ, чтобы узнать, в какой из них были произведены перемещения.
После этого найденные подобласти будут скопированы на экран, тем самым
будет подавлен неприятный эффект мерцания.
Классы для работы с холстом представляют собой классический шаблон
разработки модель-представление (Model-View), где объекты класса QCanvas
являются документами, содержащими в себе объекты, произведенные от
унаследованных от QCanvasItem классов (рис. 20.1). Эти объекты являются
данными без графического представления. Подобное отделение данных от
их графического представления позволяет отображать один и тот же
документ (QCanvas) в различных представлениях (QCanvasview).
Класс QCanvasItem не позволяет создавать объекты, т. к. он является
абстрактным. Для создания объектов можно воспользоваться следующими
классами: QCanvasSprinte, QCanvasText, QCanvasEllipse, QCanvasLine,
QCanvasRectangle, QCanvasPolygon И QCanvasSpline. Эти классы представляют
собой различные геометрические фигуры, текст, а также спрайты
(перемещающиеся анимационные изображения). Если нужно создать класс своей
собственной формы, то, в большинстве случаев, лучше унаследовать
абстрактный класс QCanvasPolygonalitem. В унаследованном классе необходимо
перезаписать виртуальные методы drawshapeo и areaPointsO.
( Примечание j
В метод drawshapeo передается указатель на объект QPainter, который
можно использовать для рисования формы.
| Листинг 20.1. Файл main.cpp
#include <qapplication.h>
#include <qcanvas.h>
//
int main(int argc, char** argv)
{
QApplication app(argc, argv);
rnagg 20. Работа с изображениями холста 261
QCanvas canvas(200, 150);
QCanvasView cv(&canvas);
QCanvasRectangle* pCanvasRect = new QCanvasRectangle(&canvas);
pCanvasRect->setPen(Qt::black);
pCanvasRect->setBrush(Qt::green);
pCanvasRect->setSize(120, 80);
pCanvasRect->setX(20);
pCanvasRect->setY(20);
pCanvasRect->show();
canvas.update();
app.setMainWidget(&cv);
cv.show();
return app.exec();
}
В листинге 20.1 после создания объекта приложения арр производится
локальное создание объектов классов QCanvas и QCanvasView. Виджет cv
получает при создании адрес объекта canvas. Элементу холста (указатель
pCanvasRect) в качестве предка передается объект класса QCanvas, чтобы
поместить его в этот объект. Вызов метода setPenO устанавливает черный
цвет пера для контурной линии формы. Кисть предназначена для заливки
фона формы и устанавливается в зеленый цвет вызовом метода setBrush().
Метод setsizeo задает размеры прямоугольной области. При помощи
методов setxo и setYO устанавливаются координаты расположения элемента
холста. После этого вызовом метода show о элемент становится видимым, и
вызывается метод update о объекта QCanvas для актуализации изображения
холста. Последний вызов метода show о отображает виджет класса
QCanvasView на экране (рис. 20.2).
[Non-Commercial] - С. ВИН ЕЗ
Рис. 20.2. Отображение элемента холста
262 Часть IV. Графика изву^
Элементы холста могут перемещаться независимо друг от друга. Пример
изображенный на рис. 20.3, иллюстрирует эту возможность. В примере за-
действованы следующие элементы холста: прямоугольник (QCanvasRectangie)
ЭЛЛИПС (QCanvasEllipse), ЛИНИЯ (QCanvasLine) И текст (QCanvasText).
[Non-Commercial] - CanvasView ВП ЕЗ
ы
II
\Move us with your mouse II
i A
Рис. 20.3. Элементы холста, позиции которых можно изменять при помощи мыши
Листинг 20.2. Файл main.cpp
#include <qapplication.h>
#include <qcanvas.h>
#include "CanvasView.h"
//
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QCanvas canvas(300, 300);
CanvasView cv(& canvas);
QCanvasRectangie* pCanvasRect =
new QCanvasRectangie(20, 20, 90, 90, &canvas);
pCanvasRect->setPen(Qt::black);
pCanvasRect->setBrush(Qt::green);
pCanvasRect->show();
QCanvasText* pCanvasText =
new QCanvasText("Move us with your mouse", &canvasb
pCanvasText->setX(150);
pCanvasText->setY(120);
pCanvasText->show();
глява 20. Работа с изображениями холста 263
QCanvasEllipse* pCanvasEllipse =
new QCanvasEllipse(80, 80, &canvas);
pCanvasEllipse->setX(120);
pCanvasEllipse->setY(80);
pCanvasEllipse->setBrush(Qt::blue);
pCanvasEllipse->show();
QCanvasLine* pCanvasLine = new QCanvasLine(&canvas);
pCanvasLine->setPoints(130, 80, 150, 150);
pCanvasLine->setPen(QPen(Qt::red, 2));
pCanvasLine->show();
canvas.update();
app.setMainWidget(&cv);
cv.showO ;
return app.exec();
}
Структура программы, показанной в листинге 20.2, идентична структуре
программы листинга 20.1. Разница состоит лишь в большем количестве
элементов холста и в использовании вместо класса ocanvasview своего
собственного класса, именуемого canvasview, позволяющего перемещать
элементы холста при помощи мыши.
Листинг 20.3. Файл CanvasView.h
lifndef _CanvasView_h_
idefine _CanvasView_h_
iinclude <qcanvas.h>
// «==«=^====^============================================
class Canvasview : public QCanvasView {
^.OBJECT
Private:
QCanvasItem* m_pCanvasItem;
int m_xOffset;
int m_yOffset;
Protected:
virtual void contentsMousePressEvent (CMouseEvent* pe);
virtual void contentsMouseMoveEvent (CMouseEvent* pe);
virtual void contentsMouseReleaseEvent(CMouseEvent* pe);
264 Часть IV. Графика и звук
public:
CanvasView(QCanvas* pcanvas, QWidget* pwgtParent = 0);
};
#endif //_CanvasView_h_
Для реакции на события мыши в классе Canvasview, унаследованном от
QCanvasView, Требуется Перезаписать методы contentsMousePressEvent()5
contentsMouseMoveEvent() И contentsMouseReleaseEvent() (ЛИСТИНГ 20.3).
Внимание! j
Так как класс унаследован от QScrollview, то перезапись методов
mousePressEvent(), mouseMoveEvent() и mouseReleaseEvent() будет
получать неправильные координаты.
Переменная m_pCanvasitem служит для хранения указателя на элемент
холста, при выборе его мышью. Переменные m_xOffset и myOffset хранят
относительные координаты указателя мыши от позиции расположения
элемента холста.
Листинг 20.4. Конструктор Canvasview. Файл CanvasView.cpp
Canvasview::Canvasview(QCanvas* pcanvas, QWidget* pwgtParent/*=0*/)
: QCanvasView(pcanvas, pwgtParent)
, m_pCanvasItem(0)
{
}
В листинге 20.4 конструктор инициализирует значение указателя на элемент
холста значением нуль, чем сообщает, что ни один из элементов не выбран.
) Листинг 20.5. Метод contentsMousePressEvent (). Файл CanvasView.cpp
/*virtual*/void Canvasview::contentsMousePressEvent(QMouseEvent* pe)
{
QCanvasItemList list = canvas()->collisions(pe->pos());
if (!list.count()) {
return;
}
mjpCanvasItem = * (list.beginO ) ;
m_xOffset = (int) (pe->x() - m_pCanvasIten\->x());
m_yOffset = (int) (pe->y() - m_pCanvasIten\->y 0 );
fnasa 20. Работа с изображениями холста 265
Лосле нажатия на одну из кнопок мыши произойдет вызов метода
contentsMousePressEvent (), приведенный в листинге 20.5. Чтобы узнать,
какой из элементов холста оказался в области указателя мыши, необходимо
сначала получить методом canvas о указатель на объект класса ocanvas.
Потом передать координаты события в метод collisions о. Этот метод
возвращает список типа QCanvasitemList, попавших под координаты указателя
МЫШИ элементов холста.
( Примечание ^
Обычно метод collisions о применяется для определения столкновений
элементов холста.
В операторе if производится проверка списка на наличие в нем элементов.
В том случае, если список элементов пуст, производится выход, иначе —
указателю m_pCanvasitem присваивается первый элемент списка, и
производится установка относительных координат mxof f set и m_yOf fset.
^ ,
! Листинг 20.6. Метод contentsMouseMoveEvent (). Файл CanvasView.cpp
/♦virtual*/void CanvasView::contentsMouseMoveEvent(QMouseEvent* pe)
1
if (m_pCanvasItem) {
m_pCanvasItem->setX(pe->x() - m_xOffset);
m_pCanvasItem->setY(pe->y() - m_yOffset);
canvas()->update();
}
}
Если пользователь перемещает указатель мыши, то вызывается метод
contentsMouseMove () (листинг 20.6). При перемещении указателя мыши
необходимо вызовом методов setxo и setYO актуализировать положение
элемента холста. После этого нужно получить указатель объекта ocanvas и
вызвать метод update о для обновления актуальным изображением всех
отображающих его виджетов (ocanvasview). В нашем случае он один.
[Листинг 20.7. Метод contentsMouseReleaseEvent (). Файл CanvasView.cpp
*virtual*/void CanvasView::contentsMouseReleaseEvent(QMouseEvent* pe)
contentsMouseMoveEvent(pe);
m_pCanvas!tem = 0;
266 Часть IV. Графика иэву^
Метод contentsMouseReieaseEvento вызывается сразу после отпускания
кнопки мыши. В этом методе происходит передача объекта события дальше
на обработку методу contentsMouseReieseEvent (). После этого указатель на
элемент холста снова принимает нулевое значение, сигнализирующее о том
что ни один из элементов холста не выбран (листинг 20.7).
Резюме
Применение изображения холста идеально для приложений, в которых
должно содержаться много графических элементов, которыми управляет
пользователь, например, игры. Классы для работы с изображением холста —
QCanvas, QCanvasView, QCanvasitem и унаследованные от него, представляют
собой очень сильную комбинацию для работы с двумерной графикой.
Взаимодействие этих классов друг с другом отвечает шаблону разработки
модель-представление, что позволяет показывать один и тот же объект класса
QCanvas В разных ВИДЖетах класса QCanvasView.
Класс QCanvas можно представить как плоскость, на которой можно
размещать элементы. Несмотря на то, что того же эффекта можно добиться и с
виджетами, т. к. они тоже обладают способностью отображать своих
потомков, тем не менее, разница заключается в том, что работа с изображением
холста позволяет более эффективно использовать процессор и память
компьютера, а также предоставляет механизм для определения столкновений.
Элементы изображений холста (QCanvasitem) должны помещаться в объект
класса QCanvas. Класс QCanvas не унаследован от cwidget, поэтому не
предоставляет возможности получения событий, но при помощи класса
QCanvasView можно легко устранить этот недостаток.
Классы, унаследованные от QCanvasitem, представляют собой различные
геометрические формы, а также текст и анимированные изображения.
Для создания своих собственных форм нужно унаследовать класс
QCanvasPolygona1Item.
ГЛАВА 21
Анимация
Зри в корень.
Козьма Прутков
Понятие анимации происходит из ранних лет кино. Само слово переводится
с латыни как оживление неподвижных предметов. Принцип анимации тот
же, что используется в играх с "движущимися" картинками. При быстрой
смене последовательности изображений создается иллюзия движения.
Обычные мультипликационные фильмы состоят из целой серии рисованных
картинок, в которых последовательно и незначительно изменяются позиции
объектов относительно друг друга.
Для создания анимации можно воспользоваться классом QPixmap, показывая
изображения одно за другим. Но лучше воспользоваться уже готовым
классом QMovie, который выполнит эту работу за вас. Объекты класса хранят в
себе анимацию и могут возвращать отдельные изображения в объектах
класса QPixmap или Qimage. Этот класс поддерживает форматы MNG и GIF.
L Примечание ^
Из-за сложностей лицензионных соглашений графику в формате GIF лучше
перерабатывать в формат PNG, а анимации в формате GIF — в формат MNG.
Для таких преобразований можно воспользоваться бесплатной программой
SlowView, которую вы найдете на сайте www.slowview.at. Эта программа
разработана Николаусом Бреннингом и не уступает, по своим функциональным
возможностям, известной ACDSee.
п
° Конструктор класса QMovie передается имя анимационного файла.
Проигрывание анимации начнется сразу после создания объекта. В этом классе
определены также конструктор копии и оператор присваивания.
^уВнимание • ' j
Этот класс использует модель явных общих данных, поэтому при копировании
не происходит создания нового объекта и они будут являться ссылками на один
и тот же общий объект. Для создания объекта, содержащего копию данных,
нужно вызвать метод detach () (см. гл. 36).
268 Часть IV. Графика изву^
На проигрывание анимации можно влиять следующими методами: pause о
unpauseo, setspeedo, stepO и restart о. Метод pause о приостанавливает
проигрывание анимации, метод unpauseo возобновляет проигрывание. Ме-
тод stepO устанавливает шаг проигрывания, принимает параметр типа int
Вызов метода QMovie:: restart о перезапускает воспроизведение сначала.
Информацию о статусе проигрывания можно получить при помощи
методов paused о, running о и finished о, которые возвращают значения булево-
го типа. Метод paused () сообщает, что проигрывание было приостановлено.
Метод running о говорит, что анимация находится в состоянии
проигрывания, а метод finished о сигнализирует о завершении при проигрывании
анимации.
Для того чтобы узнать количество кадров анимационного файла, нужно
вызвать frameNumber (). Если есть необходимость в получении растрового
изображения актуального кадра, то следует вызвать метод framePixmap () или
f ramelmage (), которые ВОЗВращают ССЫЛКИ на Объекты QPixmap ИЛИ Qlmage.
Класс QLabel содержит метод setMovie о, с помощью которого можно
устанавливать объекты анимации. Пример, показанный на рис. 21.1,
иллюстрирует эту возможность.
[Noncommercial] - Movie НО ЕЗ
THIH I MOTION
Рис. 21.1. Показ анимации
Листинг 21.1. Файл main.cpp
#include <qapplication.h>
#include <qmovie.h>
#include <qlabel.h>
гялва 21. Анимация
269
•nt rnain(int argc, char** argv)
i
QApplication app(argc, argv);
QLabel lbl(O);
QMovie mov ("mot ion. rang ");
lbl.setMovie(mov);
lbl.setBackgroundMode(Qt::NoBackground);
lbl.setFixedSize(328, 270);
lbl.show();
app.setMainWidget (&1Ы) ;
return app.exec();
}
Как видно из листинга 21.1, после объекта приложения (арр) создается вид-
жет надписи 1Ы. Следующим создается анимационный объект mov, который
инициализируется файлом test.mng. Вызовом метода setMovie ()
анимационный объект устанавливается в виджете надписи. Метод setBackgroundMode ()
производит отключение режима заполнения фоном для подавления эффекта
мерцания. Метод setFixedSizeO устанавливает неизменяемые размеры
окна, виджета надписи.
Резюме
Анимация появилась сравнительно недавно. Анимационные файлы состоят
из определенного количества неподвижных изображений, при
последовательном отображении которых создается иллюзия движения. Основным
классом для анимации является класс QMovie. Он обладает рядом методов
Для управления проигрывания анимации. Проще всего установить объект
анимации методом setMovie () в виджете надписи.
ГЛАВА 22
Работа с OpenGL
Реальность воображаема,
а воображаемое — реально.
В. Соло
Трехмерная графика — несомненно, одна из самых захватывающих тем в
программировании, которая существует с ранних стадий развития
компьютерной техники. На протяжении продолжительного времени она
применялась исключительно в рамках правительственных проектов и проектах
разработки симуляторов полета. С недавнего времени трехмерная графика
вышла за рамки научной деятельности и превратилась в целую индустрию,
включающую в себя такие сферы, как анимация (от спецэффектов до
компьютерных игр), кино, виртуальная реальность, медицина и многое другое.
С каждым годом все больше людей задействованы в этой области.
OpenGL — это стандарт для двумерной и трехмерной графики, введенный
компанией Silicon Graphics в 1992 году. На сегодня это уже устоявшийся
стандарт и все вносимые в него изменения делаются с учетом гарантий
нормальной работы ранее написанного кода. Сама же библиотека может
быть создана кем угодно, главное, чтобы она отвечала спецификации,
установленной стандартом. С точки зрения программиста, библиотека OpenGL
представляет собой множество команд для создания объектов и проведения
очень сложных операций от сглаживания (Anti-Aliasing) до наложения
текстур. Для ее использования достаточно усвоить несколько простых правил,
и это даст возможность реализации замечательных программ.
Хотя OpenGL является платформонезависимой, все равно для
использования OpenGL-программ на разных платформах потребуется провести ряД
преобразований кода программы, для осуществления привязки контекста
воспроизведения (rendering context) с оконной системой платформы.
Использование OpenGL в "оправе" библиотеки Qt освобождает разработчиков от
каких-либо изменений исходного кода, что обеспечивает для OpenGL-np°"
грамм абсолютную платформонезависимость.
глава 22. Работа с OpenGL 271_
о Qt OpenGL можно использовать в качестве альтернативы классам
Qpainter или QCanvas, но самое большое ее преимущество состоит в
возможности работы с трехмерной графикой. Трехмерная графика — это
истинная мощь библиотеки OpenGL.
Основные положения OpenGL
Библиотека OpenGL не является объектно-ориентированной библиотекой.
Поэтому в библиотеке разработчик имеет дело только с функциями,
переменными и константами. Имена всех функций OpenGL начинаются с букв
gi, а констант — gl_. В имена функций входят суффиксы, говорящие о
числе и типе, передаваемых параметров. Например, прототип функции
gicolor3f о говорит о том, что в нее должны передаваться три значения с
плавающей точкой. Поэтому, при описании функций в OpenGL, чтобы не
повторяться, принято вместо числа передаваемых аргументов и их типа
ставить символ *. Например, общий вид для вышеупомянутой функции будет
выглядеть следующим образом: gicoior*o. Из этого примера становится
ясным, что речь идет не об одной функции, а о целой серии функций
начинающихся с gicoior. В табл. 22.1 указаны суффиксы и типы,
используемые в OpenGL.
Таблица 22.1. Суффиксы и типы OpenGL
Суффикс
b
s
[ i
L f
d
|__ ub
us
ui
(gl_
L v
Тип OpenGL
GLbyte
GLshort
GLint
GLfloat
GLdouble
GLubyte
GLushort
GLuint
GLenum
С++-эквивалент
char
short
int
float
double
unsigned byte
usnigned short
unsigned int
enum
Описание
Байт
Короткое целое
Целое
С плавающей точкой
С плавающей точкой двойной
точности
Байт без знака
Короткое целое без знака
Целое без знака
Перечисление
Массив из п параметров
Суффикс v говорит о том, что функция принимает массив. Передадим
массив из трех значений с плавающей точкой в функцию gicoior3fv
следующим образом:
Afloat a[] = {l.Of, O.Of, O.Of}
9lColor3fv(a);
272 Часть IV. Графина изву^
Классы библиотеки Qt
для работы с OpenGL
Все классы Qt для поддержки OpenGL определены в заголовочном файле
qgl.h. Определены следующие пять классов:
□ qgl — определяет пространство имен и содержит некоторые константы
для работы OpenGL;
□ QGLWidget — унаследован от классов Qwidget и qgl. Его основное
назначение — соединение OpenGL с виджетом;
□ QGLFormat — класс для хранения настроек OpenGL. В объектах этого
класса можно устанавливать различные режимы и передавать их в
объекты класса QWidget при ПОМОЩИ метода set Format (). Метод QGLWidget::
format () возвращает текущий объект настроек;
□ QGLContex — представляет собой контекст OpenGL (набор переменных
состояния). QGLWidget создает объект этого класса автоматически. Если
есть необходимость переключения между несколькими контекстами, то
можно создать объект класса и передать его в метод QGLWidget::
setcontext (). Для получения текущего контекста вызывается метод
QGLWidget::context();
О QGLCoiormap — класс используется для индексирования цвета и зависит от
используемого цветового режима.
Реализация OpenGL-программы
Чтобы воспользоваться возможностями, предоставляемыми библиотекой
OpenGL, необходимо унаследовать класс QGLWidget. Класс организует
соединение с функциями библиотеки OpenGL. В унаследованном от QGLWidget
классе необходимо, по меньшей мере, перезаписать три виртуальных
метода: initiaiizeGLO, resizeGLo и paintGLO. Эти методы определены в классе
QGLWidget как virtual protected.
Метод initiaiizeGLO вызывается сразу после создания объекта Он нужен
для проведения инициализаций, связанных с OpenGL. Этот метод
вызывается, если объекту, унаследованному от класса QGLWidget, присваивается
контекст OpenGL.
Назначение метода resizeGL(int width, int height) схоже с назначением
метода обработки события изменения размера resizeEvento. Этот метод
вызывается при изменении размеров объекта, произведенного от класса
наследующего QGLWidget. В параметрах метода передаются актуальные размеры
виджета.
гпава 22. Работа с OpenGL 273_
Назначение метода paintGLO схоже с назначением метода обработки
события перерисовывания paintEvent (). Этот метод также вызывается в случаях,
когда требуется заново перерисовать содержимое виджета Такое
происходит, например, после вызова метода resizeGLO.
fla рис. 22.1 показано приложение, которое отображает четырехугольник,
вершины которого окрашены в разные цвета, подвергающиеся
сглаживанию.
[Non-Commer- \-„ _ □ х
Рис. 22.1. Сглаживание цветов вершин четырехугольника
Мистинг 22.1. Файл OGLQuad.pro
TEMPLATE = арр
CONFIG += qt opengi warn_on release
HEADERS = OGLQuad.h
SOURCES = OGLQuad.cpp \
main.cpp
TARGET = OGLQuad
Обратите внимание на опцию проектного файла config (см. гл. 3), в которой
Указан параметр opengi, что нужно для того, чтобы во время компоновки
программы подсоединялась библиотека OpenGL (листинг 22.1).
F-V;-
Митинг 22.2. Файл main.cpp
♦include <qapplication.h>
♦include "OGLQuad.h"
//
lnt main(int argc, char** argv)
274
Часть IV. Графика и 3bVu.
{
QApplication app(argc, argv);
OGLQuad oglQuad;
app.setMainWidget(&oglQuad);
oglQuad.resize(200, 200);
oglQuad.showO ;
return app.exec();
}
Программа, приведенная в листинге 22.2, создает объект класса OGLQuad,
унаследованного от класса QGLwidget.
1 Листинг 22.3. Файл OGLQuad.h
#ifndef _OGLQuad_h_
#define _OGLQuad_h_
#include <qgl.h>
class OGLQuad : public QGLWidget {
Q_OBJECT
protected:
virtual void initializeGL( );
virtual void resizeGL (int nWidth, int nHeight);
virtual void paintGL ( );
public:
OGLQuad(QWidget* pwgt = 0, const char* pszName = 0);
};
#endi f //_OGLQuad_h_
В определении класса OGLQuad необходимо перезаписать три метода:
initializeGL(), resizeGL() И paintGL() (ЛИСТИНГ 22.3).
i ]
| Листинг 22.4. Конструктор OGLQuad. Файл OGLQuad.cpp J
OGLQuad::OGLQuad(QWidget* pwgt/*= 0*/, const char* pszName/*= 0*/)
: QGLWidget(pwgt, pszName)
{
}
f/iaga 22. Работа с OpenGL 275
Конструктор не имеет реализации, он просто передает дальше указатель на
объект предка pwgt и имя объекта pszName конструктору наследуемого класса
(листинг 22.4).
[Листинг 22.5. Метод initialized, (). Файл OGLQuad.cpp
/*virtual*/void OGLQuad::initializeGL()
{
qglClearColor(black);
}
В листинге 22.5 методом qglClearColor () устанавливается цвет для очистки
буфера изображения.
С Примечание ^
В OpenGL цвет очистки устанавливается при помощи функции glciearColor ().
Использование метода qglClearColor () вместо этой функции дает
возможность передавать цвета в объектах класса QColor.
Листинг 22.6. Метод resizeGL (). Файл OGLQuad.cpp
/♦virtual*/void OGLQuad::resizeGL(int nWidth, int nHeight)
{
glMatrixMode(GL_PROJECTION);
glLoadldentity();
glViewport(0, 0, (GLint)nWidth, (GLint)nHeight);
glOrtho(0, 100, 100, 0, -1, 1);
}
В листинге 22.6 функция glMatrixMode () устанавливает матрицу
проектирования текущей матрицей. В OpenGL существуют две матрицы,
применяющиеся для преобразования координат. Первая — матрица моделирования
(modelview matrix), служит для задания положения объекта и его
ориентации, а вторая — матрица проектирования (projection matrix), отвечает за
выбранный способ проектирования. Способ проектирования может быть либо
Параллельным, либо перспективным. В нашем случае способ
проектирования не играет никакой роли. Вызовом функции glLoadldentity ()
устанавливая текущая матрица в единичную матрицу.
Метод resizeGL о — это самое удобное место, чтобы установить видовое ок-
Jjo (viewport). Видовое окно устанавливается вызовом функции giviewPort ().
~Но представляет собой прямоугольную область в пределах окна виджета
(°кно в окне). Для нашего случая видовое окно равно размеру виджета. Со-
^ошение сторон видового окна задается параметрами функции giorthoo.
276 Часть IV. Графика изву^
Первый и второй параметры задают положения левой и правой отсекающИх
плоскостей. Третий и четвертый параметры задают положения верхней и
нижней отсекающих плоскостей. Пятый и шестой параметры задают
положения передней и задней отсекающих плоскостей.
| Листинг 22.7. Метод paintGL (). Файл OGLQuad.cpp
/♦virtual*/void OGLQuad::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT [ GL_DEPTH_BUFFER_BIT);
glBegin(GL_QUADS);
glColor3f(l, 0, 0);
glVertex2f(0, 100);
glColor3f(0, 1, 0);
glVertex2f(100, 100);
glColor3f(0, 0, 1);
glVertex2f(100, 0);
glColor3f(l, 1, 1);
glVertex2f(0, 0) ;
glEnd();
}
Перед формированием нового изображения нужно методом giciearo
очистить буфер изображения (glcolorbufferbit) и буфер глубины, служащий
для удаления невидимых поверхностей (gldepthbufferbit). Для очистки
буфера изображения будет использован цвет, установленный методом
qglColor().
Единица информации OpenGL — вершина. Из вершин строятся сложные
объекты. При их создании нужно дать указание, каким образом они должны
быть соединены друг с другом. Способом соединения вершин управляет
метод giBegino, в нашем примере флаг glquads говорит о том, что на
создаваемых вершинах должен быть построен четырехугольник (листинг 22.7)-
При помощи функции gicoior* () задается текущий цвет. Он
распространяется на вызовы функции givertex*o, задающей расположение вершин.
Функции задания вершин должны всегда находиться между giBeginO и
giEndo. Каждая вершина имеет свой цвет, т. к. по умолчанию в OpenGL
включен режим сглаживания цветов, что приводит к созданию радужной
окраски области прямоугольника. Режимом сглаживания цветов управляет
функция gishadeModelo. При передаче в нее флага glflat отключается Ре"
жим сглаживания, а передача флага glsmooth включает его.
f/iaga 22. Работа с OpenGL 277
разворачивание OpenGL-программ
go весь экран
faK как класс QGLwidget унаследован от Qwidget, то он обладает всеми
свойствами, присущими этому классу. Развернуть программу на полный экран
очень просто — для этого нужно заменить вызов метода show () на вызов
метода showFuiiscreen (). Вызов метода приведет к тому, что виджет
верхнего уровня перекроет своим окном все остальные окна и займет всю область
экрана. Это очень удобно, т. к. можно отлаживать программу в маленьком
окне, а когда она будет готова, просто поменять show () на showFuiiscreen ().
Производительность работы программы OpenGL в полноэкранном режиме
полностью зависит от драйвера видеокарты.
Графические примитивы OpenGL
OpenGL предоставляет средства для рисования графических примитивов,
таких как точки, линии, ломаные и полигоны, которые задаются одной или
несколькими вершинами. Для этого необходимо передать список вершин
Программа, показанная на рис. 22.2, отображает различные фигуры,
построенные на одних и тех же вершинах.
[Non-Commercial] - OGLDraw НЮ ЕЗ 1
осо
|с < I
Рис. 22.2. Отображение фигур, построенных на одних и тех же вершинах
МЙистинг 22.8. Файл main.cpp
"include <qapplication.h>
•include "OGLDraw.h"
278
Часть IV. Графика и за^
int main(int argc, char** argv)
{
QApplication app(argc, argv);
OGLDraw oglDraw;
app.setMainWidget(&oglDraw);
oglDraw.resize(400, 200) ;
oglDraw.show();
return app.exec();
}
В листинге 22.8 в основной программе создается виджет oglDraw,
произведенный ОТ Класса OGLDraw.
I Листинг 22.9. Файл OGLDraw.h
#ifndef _OGLDraw_h_
#define _OGLDraw_h_
#include <qgl.h>
/ / _«___=_______=_«____^^
class OGLDraw : public QGLWidget {
Q_OBJECT
protected:
virtual void initializeGL( );
virtual void resizeGL (int nWidth, int nHeight);
virtual void paintGL ( );
public:
OGLDraw(QWidget* pwgt = 0, const char* pszName = 0);
void draw(int xOffset, int yOffset, GLenum type);
};
#endif //_OGLDraw_h_
Класс OGLDraw наследуется от класса QGLwidget и перезаписывает три его
виртуальных метода: initializeGLO, resizeGL () И paintGL () (ЛИСТИНГ 22.9)-
В классе определен метод draw о, в который передаются координаты х и у»
с которых нужно начинать строить фигуру. Тип фигуры задается третьим
параметром.
Глава 22. Работа с OpenGL
279
(Листинг 22.10. Конструктор OGLDraw. Файл OGLDraw.cpp
OGLDraw::OGLDraw(QWidget* pwgt/*= 0*7, const char* pszName/*= 0*/)
: QGLWidget(pwgt, pszName)
{
}
В листинге 22.10 приведен конструктор класса OGLDraw, который не
выполняет в своем теле никаких действий, а просто передает указатель на объект
предка и имя виджета в конструктор класса QGLWidget.
fTTTK " "
[Листинг22.11. Метод initializeGL(). Файл OGLDraw.cpp
/♦virtual*/void OGLDraw::initializeGL()
{
qglClearColor(white);
}
В методе initialized, о устанавливается белый цвет для очистки буфера
изображения (листинг 22.11).
[Листинг 22.12. Метод resizeGLO. Файл OGLDraw.cpp
/*virtual*/void OGLDraw::resizeGL(int nWidth, int nHeight)
{
glMatrixMode(GL_PROJECTION);
glLoadldentity();
glViewport(0, 0, (GLint)nWidth, (GLint)nHeight);
glOrtho(0, 400, 200, 0, -1, 1);
}
В листинге 22.12 действия метода resizeGLO аналогичны действиям
одноименного метода из листинга 22.6 и отличаются лишь параметрами,
передаваемыми В метод glOrtho().
[Листинг 22.13. Метод paintGLO. Файл OGLDraw.i
срр
^virtual*/void OGLDraw::paintGL ()
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
draw(0, 0, GL_POINTS);
draw(100, 0, GL LINES);
280 Часть IV. Графика иэв^
draw(200, 0, GL_LINE_STRIP);
draw(3 0 0, 0, GL_LINE_LOOP);
draw (0, 100, GL_TRIANGLE_STRIP) ;
draw(100, 100, GL_POLYGON);
draw(200, 100, GL_QUADS);
draw(300, 100, GLJTRIANGLES);
}
В методе paintGLO после очистки буфера изображения и буфера глубины
производится вызов серии методов draw о (листинг 22.13). В этот метод
передаются координаты х и у, с которых будет начинаться рисование фигуры,
тип которой передается третьим параметром. Тип glpoints говорит, что
отображаться должны только точки. При построении фигуры типа gllines
каждая пара вершин задает отрезки, которые, как правило, не соединяются
друг с другом. Тип gllinestrip задает ломаную линию. Этот тип
используется в основном для аппроксимации кривых. Если требуется получить
замкнутый контур, то нужно указать одну и ту же вершину в качестве
начальной и конечной. Тип gllineloop задает ломаную линию, последняя
точка которой соединяется с первой. Тип gltrianglestrip задает
треугольники с общей стороной. Каждая последующая вершина комбинируется
с двумя предыдущими и определяет очередную ячейку. Тип gl_polygon
задает многоугольник. При построении фигуры типа glquads каждые четыре
вершины задают четырехугольник. При построении фигуры типа
gl_triangles каждые три вершины задают треугольник.
Листинг 22.14. Метод drawGL (). Файл OGLDraw.cpp
void OGLDraw::draw(int xOffset, int yOffset, GLenum type)
{
int n = 8;
glPointSize(2);
glBegin(type);
glColor3f(0, 0, 0);
for (int i = 0; i < n; ++i) {
float f Angle = 2 * 3.14 * i / n;
int x = (int)(50 + cos(fAngle) * 40 + xOffset);
int у = (int)(50 + sin(fAngle) * 40 + yOffset);
glVertex2f(x, y);
}
glEndO;
}
i?iaga 22. Работа с OpenGL
Метод drawGLO осуществляет построение фигуры заданного типа с позиции,
согласно переданным координатам. Для задания размеров точки служит
функция giPointsizeo, в нашем примере ее размер устанавливается равным
2. функция gicoior*o устанавливает всем вершинам черный цвет. Вызов
функции givertex* о задает расположение вершин (листинг 22.14).
Вывод растровых изображений
OpenGL не предоставляет никакой поддержки для форматов растровых
изображений, поэтому для получения растровых данных и их вывода в
контексте OpenGL нужно использовать класс Qimage. Этот класс предоставляет
поддержку для подавляющего большинства известных форматов (см. гл. 18).
OpenGL предоставляет фуНКЦИЮ glDrawPixels (nWidth, nHeight, format,
type, ppixeis), с помощью которой можно вывести изображение из
массива пикселов на экран. Параметры nwidth и nHeight задают размеры
копируемого изображения. Третий параметр — format, задает формат данных
пиксела, значения которого могут быть следующими: glrgb, gl rgba,
gl_red, glgreen, gl_blue или gl_alpha. Четвертый параметр — type, задает
тип каждого из записываемых значений и может быть: glunsignedbyte,
gljbyte, gl_bitmap, gl_int или glfloat. Последний параметр ppixeis — это
указатель типа void на массив значений пикселов. Изображение выводится
с позиции, установленной функцией giRasterPosM). Пример 22.1
выводит на экран растровое изображение, содержащееся в файле birds.jpg.
После загрузки изображения оно преобразуется, с помощью метода
convertToGLFormat (), в приемлемый формат расположения цветов для
OpenGL. При помощи методов width о и height о передаются размеры
растрового изображения. Метод bits о возвращает указатель на данные
растрового изображения.
п
JHpiiMep 22.1. Вывод растрового изображения
Qimage img = Qimage ("birds. jpg") . convertToGLFormat () ;
9lRasterPos2f (0, 0) ;
9lDrawPixels (img. width (),
img.height(),
GL_RGBA,
GLJJNSIGNED_BYTE,
img.bits()
);
агРузка растровых данных в текстуру выполняется аналогичным образом,
^ЬКО с Использованием функции glTexImage2D(target, level, component,
282
Часть IV. Графика извуи
nWidth, nHeight, border, format, type, ppixels). Где первый параметп
target — зарезервирован для будущего использования. Второй параметр
level — используется только в том случае, если задается сразу несколько
разрешений текстуры и при одном разрешении этот параметр должен быть
равен нулю. Третий параметр component — изменяется в пределах от 1 д0 4
Им устанавливается выбор RGBA-компонент. Значение 1 выбирает R,
значение 2 — R и А, значение 3 выбирает RGB, а значение 4 — RGBA. Пара-
метры nWidth и nHeight задают размер текстуры. Шестой параметр border -.
задает ширину рамки и обычно он равен нулю. Так как параметры размера
текстуры должны соответствовать следующей формуле:
2П + 2 х border,
где п — целое число, то в примере 22.2 производится вызов метода scale о,
изменяющего размеры растрового изображения до 256x256 пикселов, а
последний параметр задает тип каждого из записываемых значений.
Последним параметром в функцию giTeximage2D() передается указатель на массив
растровых данных.
Qt & OpenGL
НПО
Рис. 22.3. Вывод растрового изображения
Пример 22.2. Загрузка растровых данных в текстуру
Qlmage imgTemp = Qlmage("birds.jpg").convertToGLFormat0;
Qlmage img = imgTemp.scale(256, 256, Qlmage::ScaleFree);
glTextImage2D(GL_TEXTURE_2D,
0,
4,
img.width() ,
img.height() ,
rnagg 22- Работа с OpenGL 283
О,
GL_RGBA,
GL_UNSIGNED_BYTE,
img.bits()
);
Помимо возможностей вывода растровых изображений в буфер, Qt
предоставляет возможность считывания растровых изображений из буфера в
объект Класса Qlmage. ДЛЯ ЭТОГО НУЖНО ВОСПОЛЬЗОВЭТЬСЯ методом QGLWidget::
grabFrameBuf f er ().
Трехмерная графика
На рис. 22.4 показан результат работы программы, отображающей пирамиду
в трехмерном пространстве, поворот которой относительно осей X и Y
осуществляется при помощи мыши.
I 1Ши.ШЛА11^ПН|х1
Рис. 22.4. Пирамида
уЛйЬтинг 22.15. Файл main.cpp
♦include <qapplication.h>
♦include "OGLPyramid.h"
//
lnt main(int argc, char** argv)
QApplication app(argc, argv);
OGLPyramid oglPyramid;
app.setMainWidget(SoglPyramid);
284
Часть IV. Графика и звук
oglPyramid.resize(200, 200);
oglPyramid.show();
return app.exec();
}
В основной программе создается OpenGL-виджет от класса OGLPyramid
(листинг 22.15).
I Листинг 22.16. Файл OGLPyramid.h
#ifndef _OGLPyramid_h_
#define _OGLPyramid_h_
#include <qgl.h>
// _===_=^===============^=^===^=^==^======================
class OGLPyramid : public QGLWidget {
Q_OBJECT
private:
GLuint m_nPyramid;
GLfloat m_xRotate;
GLfloat m_yRotate;
QPoint m_j3tPosition;
protected:
virtual void initialized. ( ) ;
virtual void resizeGL (int nWidth, int nHeight);
virtual void paintGL ( );
virtual void mousePressEvent(QMouseEvent* pe );
virtual void mouseMoveEvent (QMouseEvent* pe );
GLuint createPyramid (GLfloat fSize = l.Of );
public:
OGLPyramid(OWidget* pwgt = 0, const char* pszName = 0);
};
#endif //_OGLPyramid_h_
В конструкторе класса OGLPyramid определена переменная mnPyramid,
которая будет хранить в себе номер дисплейного списка объекта пирамиды
(листинг 22.16).
( Примечание )
Дисплейные списки позволяют выделить конкретный набор команд, запомни
его и вызывать всякий раз, когда в нем возникает необходимость. Этот ме*
22.
низм очень похож на вызов обычных функций с той лишь разницей, что для
запуска команд дисплейного списка необходимо знать уникальный номер
присвоенным ему.
Переменные этого класса mxRotate, myRotate нужны для хранения углов
поворота по осям X и Y. Переменная m_j>tPosition хранит координату
указателя мыши в момент нажатия. Кроме перезаписи трех методов,
унаследованных от QGLWidget, перезаписываются методы обработки события мыши
mousePressEvent () и mouseMoveEvent () для осуществления поворота
пирамиды. Метод createPyramid () создает дисплейный список для отображения
объекта пирамиды.
Листинг 22.17. Конструктор OGLPyramid. Файл OGL.Pyramid.cpp
OGLPyramid::OGLPyramid(QWidget* pwgt/*= 0*/, const char* pszName/*= 0*/)
: QGLWidget(pwgt, pszName)
, m_xRotate(0)
, m_yRotate(0)
{
}
Задача конструктора класса OGLPyramid состоит в инициализации
переменных для поворота и передаче указателя на виджет предка конструктору
наследуемого класса QGLWidget (ЛИСТИНГ 22.17).
| Листинг 22.18. Метод initializeGL(). Файл OGLPyramid.cpp
/*virtual*/void OGLPyramid::initializeGL()
{
qglClearColor(black);
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_FLAT);
m_nPyramid = createPyramid(1.2f);
}
С помощью метода qglClearColor () устанавливается черный цвет очистки
"Уфера изображения (листинг 22.18). Функция glEnable о устанавливает ре-
*ИМ разрешения проверки глубины фрагментов. Режим сглаживания цветов
П° умолчанию разрешен, поэтому его необходимо отключить, передав в
Функцию gishadeModeO флаг glflat, иначе боковые грани пирамиды будут
"Меть радужную окраску. Вызов метода createPyramid () создаст дисплейный
йИсок для пирамиды и вернет номер, который будет присвоен переменной
VnPyramid. Параметр, передаваемый в этот метод, задает размеры самой
ПиРамиды.
286
Часть IV. Графика изву*
\ Листинг 22.19. Метод resizeGL(). Файл OGL.Pyramid.cpp
/♦virtual*/void OGLPyramid::resizeGL(int nWidth, int nHeight)
{
glViewport(0, 0, (GLint)nWidth, (GLint)nHeight);
glMatrixMode(GL_PROJECTION);
glLoadidentity() ;
glFrustum(-1.0, 1.0, -1.0, 1.0, 1.0, 10.0);
}
Функция giviewPorto устанавливает видовое окно, размерами равными
окну виджета (листинг 22.19). Функция giMatrixModeo устанавливает матрицу
проектирования текущей. Вызов функции glLoadidentity () присваивает
матрице проектирования единичную матрицу. Функция giFrustumo задает
так называемую пирамиду видимости. Первый и второй параметры задают
положения левой и правой отсекающих плоскостей. Третий и четвертый
параметры задают положения верхней и нижней отсекающих плоскостей.
Пятый и шестой параметры задают положения передней и задней
отсекающих плоскостей (эти значения должны быть положительными и отсчиты-
ваться от центра проецирования вдоль оси Z). При помощи пятого и
шестого параметров устанавливается значение перспективы.
{ Листинг 22.20. Метод paintGL (). Файл OGL.Pyramid.cpp
/*virtual*/void OGLPyramid::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT I GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadidentity();
glTranslatef(0.0, 0.0, -3.0);
glRotatef(m_xRotate, 1.0, 0.0, 0.0);
glRotatef(m_yRotate, 0.0, 1.0, 0.0);
glCallList(m_nPyramid);
}
После очистки буфера изображения устанавливается матрица
моделирования, служащая для задания положения объекта и его ориентации
(листинг 22.20). Функция glLoadidentity () присваивает матрице моделирования
единичную матрицу. Функция glTransiateo сдвигает начало системы
координат по оси Z на —3. Функция giRotateo производит поворот системы
координат вокруг осей X и Y на угол, задаваемый переменными mxRotate и
fngga 22. Работа с OpenGL 287
„jyRotate. Передача в функцию gicaiiListo номера дисплейного списка
пирамиды отобразит ее.
Г^истинг 22.21. Метод mousePressEvent(). Файл OGLPyramid.cpp
/♦virtual*/void OGLPyramid:rmousePressEvent(QMouseEvent* pe)
{
rojptPosition = pe->pos();
}
При нажатии пользователем кнопки мыши переменной mjptPosition будет
присвоена координата ее указателя (листинг 22.21).
| Листинг 22.22. Метод mouseMoveEvent(). Файл OGLPyramid.cpp
/♦virtual*/void CiGLPyramid: :mouseMoveEvent (QMouseEvent* pe)
{
m_xRotate += 180 * (GLfloat) (pe->y() - m_j3tPosition.y ()) / height ();
m_yRotate += 180 * (GLfloat)(pe->x() - mj?tPosition.x()) / width();
updateGL();
m_ptPosition = pe->pos();
}
В методе обработки события перемещения мыши производится вычисление
углов поворота для осей X и Y. Вызов метода updateGL о обновляет,
согласно новым углам поворота, изображение на экране. Переменной
mjptPosition присваивается актуальная координата указателя мыши
(листинг 22.22).
1нг 22.23. Метод createPyramidO. Файл OGLPyramid.cpp
GLuint OGLPyramid::createPyramid(GLfloat fSize/*=1.0f*/)
{
GLuint n = glGenLists(l);
glNewList(n, GL_COMPILE);
glBegin(GL_TRIANGLE_FAN);
qglColor(green);
glVertex3f(0.0, fSize, 0.0);
glVertex3f(-fSize, -fSize, fSize);
glVertex3f(fSize, -fSize, fSize);
qglColor(yellow);
glVertex3f(fSize, -fSize, -fSize);
288
Часть IV. Графика изву^
qglColor(blue);
glVertex3f(-fSize, -fSize, -fSize);
qglColor(white);
glVertex3f(-fSize, -fSize, fSize);
glEnd();
glBegin(GL_QUADS);
qglColor(red);
glVertex3f(-fSize, -fSize, fSize);
glVertex3f(fSize, -fSize, fSize);
glVertex3f(fSize, -fSize, -fSize);
glVertex3f(-fSize, -fSize, -fSize);
glEnd();
glEndList();
return n;
}
Функция giGenLists () возвращает первый свободный номер для
идентификации дисплейного списка (листинг 22.22). Этот номер передается в
функцию giNewListo. Второй параметр— glcompile — говорит о том, что
команды должны быть только запомнены. Все команды, находящиеся
между функциями giNewListo и glEndList о, помещаются в соответствующий
дисплейный список. Тип gl_triangle_fan задает треугольники с общей
вершиной, которая идет первой в списке. Следующие две вершины задают
треугольник. Затем каждая последующая вершина с предыдущей задает
следующий треугольник. Для типа фигуры glquads каждые четыре вершины
задают четырехугольник.
Резюме
OpenGL прост в изучении и долговечен в качестве стандартного API. Это
уже устоявшийся стандарт, действующий на протяжении 12 лет. Библиотека
Qt предоставляет классы для поддержки OpenGL. Для того чтобы
воспользоваться ею, следует перезаписать три метода в унаследованном от QGLWidget
классе: initializeGLO, resizeGLO И paintGLO.
OpenGL можно использовать в качестве альтернативы классу QPainter, но
его преимущество заключается в возможности работы с трехмерной
графикой. Единица информации OpenGL — вершина. На основании вершин
можно создавать довольно сложные объекты. Для того чтобы запустить
программу в полноэкранном режиме, нужно заменить в основной програмМе
ВЫЗОВ метода show () на метод showFullScreen ().
Совместное использование OpenGL и класса Qimage позволяет загружать
в буфер данные разных растровых форматов (JPEG, GIF, BMP и т. д.)-
ГЛАВА 23
Вывод на печать
Начать пользоваться новым методом
на практике легче, чем выверить его;
причем, чем точнее метод, тем с большей
осторожностью его надо использовать.
Р. Ф. Бейлс
В большинстве случаев приложения должны предоставлять пользователю
возможность вывода на печать. Это обстоятельство может испугать многих
разработчиков и причиной тому является ряд проблем. Например,
различные возможности принтеров, разница в отображении шрифтов на экране и
принтере, а также платформозависимые различия в программировании
принтеров. Qt берет на себя решение большинства проблем, существенно
облегчая задачу разработчиков.
В Qt класс QPrinter является основным для вывода на печать. Благодаря
тому, что этот класс унаследован от QPaintDevice, вывод на печать
аналогичен выводу на экран. Для вывода на принтер могут применяться те же
методы класса QPainter, что и для всех остальных контекстов рисования.
Класс принтера QPrinter обладает большим числом настроек, чем все
остальные классы, унаследованные от класса QPainterDevice. Можно,
например, устанавливать размер листа, количество копий и т. д. Qt предоставляет
Диалоговое окно печати, в котором пользователь сам производит
необходимые настройки. Это окно реализовано в классе QPrintDiaiog (см. гл. 30). Но
можно производить эти настройки и программно. Например:
^ при помощи метода setorientationo можно задать различные
расположения страницы, передав флаг QPrinter:: Port rait для горизонтального
расположения или QPrinter: landscape для вертикального расположения;
а метод setNumCopies () получает значение целого типа и устанавливает
количество копий;
а с помощью метода setFromToO можно задать диапазон страниц для
печати;
290 Часть IV. Графика изву^
О метод setcoiorMode () управляет цветным и черно-белым режимами
печати. Для цветного режима нужно передать в метод флаг QPrinter:: Color, a
ДЛЯ черно-белого — QPrinter::Grayscale;
О вызовом метода setPagesizeo изменяется размер листа. В метод нужно
передать одно из значений, указанных в табл. 23.1.
Таблица 23.1. Перечисления PageSize класса QPrinter
Константа
| АО
I M
А2
A3
А4
А5
Аб
А7
А8
А9
Размер (мм)
841x1189
594 х 841
420 х 594
297 х 420
210x297
148x210
105x148
74x105
52 х 74
37 х 52
Константа
во
В1
В2
ВЗ
В4
В5
Вб
В7
В8
В9
Размер (мм) | Константа
1030x1456 |вю
728x1030 1с5Е
515x728
364x515
257 х 364
182x257
128x182
91 х 128
64x91
CommlOE
DLE
Executive
Folio
Ledger
Legal
Letter
45x64 | Tabloid
Размер (мм)
32x45
163x229
105x241
110x220
191 x254
210x330
432x279
216x356
216x279
279x432
Вместо печати на принтер можно перенаправить вывод в файл. Для этого
необходимо передать путь и имя файла методу setoutputFiieNameO. Если в
метод передать пустую строку, то перенаправление вывода в файл будет
проигнорировано и вывод будет произведен на печатающее устройство. Имя
файла, в который будет производиться вывод, можно установить методом
setDocName ().
Чтобы отменить операцию печати, нужно вызвать метод abort о, который
вернет значение булевого типа, сигнализирующее об успехе выполнения
операции отмены.
Пример, показанный на рис. 23.1, представляет собой простой вывод на
печать. При нажатии на кнопку Print (Печать) откроется диалоговое окно
настроек принтера, и после подтверждения будет осуществлен вывод
картинки на печать.
| Листинг 23.1. Файл main.cpp
#include <qapplication.h>
#include <qpushbutton.h>
Г/£5£
23. Вывод на печать
Include <qvbox.h>
Include "Printer.h"
/f
int ntain(int argc, char** argv)
I
QApplication app(argc, argv);
QVBox vbx;
Printer* pprinter = new Printer(&vbx);
QPushButton* pcmd = new QPushButton("&Print", &vbx);
QObject::connect(pcmd, SIGNAL(clicked()),
pprinter, SLOT(slotPrint())
);
app.setMainWidget(&vbx);
vbx.resize(250, 320);
vbx.show();
return app.exec();
J Itl.lJJ.L.I.IJJJLMJJUJJM
Г У
PrmtV/Test
1 / \
'".. boo.
-jnlxil
\J
1
Рис. 23.1. Вывод на печать
в Программе, приведенной в листинге 23.1, создаются виджеты принтера —
Указатель pprinter, а также кнопки нажатия — указатель pcmd. Сигнал
kicked о виджета кнопки соединяется со слотом siotPrinto виджета
пРИнтера.
292 Часть IV. Графика^*
\ Листинг 23.2. Файл Printer.h - - """"]
-J
#ifndef _Printer_h_
#define _Printer_h_
#include <qwidget.h>
class QPrinter;
class QPaintDevice;
class Printer : public QWidget {
Q_OBJECT
private:
QPrinter* m_pprinter;
protected:
virtual void paintEvent(QPaintEvent* pe );
void draw (QPaintDevice* ppd);
public:
Printer(QWidget* pwgt = 0, const char* pszName = 0);
virtual -Printer();
public slots:
void slotPrint();
};
#endif //_Printer_h_
Определение класса Printer, приведенное в листинге 23.2, содержит
указатель на объект контекста принтера QPrinter. Метод draw о получает
указатель на контекст рисования. В классе определен деструктор — чтобы
освободить динамически вьщеляемую память для объекта принтера. Слот
slotPrint о вызывается для выполнения вывода на печатающее устройство.
| Листинг 23.3. Конструктор и деструктор. Файл Printer.cpp
//
Printer::Printer( QWidget* pwgt /*= 0*/,
const char* pszName/*= 0*/
)
: QWidget(pwgt, pszName)
Гпава 23. Вывод на печать 293
[
setPaletteBackgroundColor(white);
m_pprinter = new QPrinter;
)
n
/♦virtual*/Printer::-Printer()
t
delete mjpprinter;
)
В ЛИСТИНГе 23.3, В КОНСтруКТОре, Вызывается МеТОД setPaletteBackground ()
для установки белого цвета фона и динамически создается объект
принтера — указатель mjpprinter. Этот объект необходимо по завершению работы
программы удалить, и лучше всего это сделать в деструкторе.
[.Листинг 23.4. Метод paintEvent (). Файл Printer.cpp
fcUiii; .;
/♦virtual*/ void Printer::paintEvent(QPaintEvent* pe)
{
draw(this);
)
В методе обработки события перерисовки, приведенном в листинге 23.4,
вызывается метод draw о и в него, в качестве контекста рисования,
передается указатель this.
гЛистинг 23.5. Метод slotPrint (). Файл Printer.cpp
void Printer::slotPrint()
<
m_pprinter->setMinMax(1, 1);
if (m_pprinter->setup(this)) {
draw (mjpprinter) ;
}
)
Методом setMinMaxO устанавливается диапазон страниц, которые можно
Печатать (листинг 23.5). В нашем случае это всего одна страница, поэтому
^от диапазон устанавливается от 1 до 1.
имечание )
После того как в диалоговом окне будут произведены все необходимые
изменения, можно вычислить количество страниц для печати при помощи значений,
294 Часть IV. Графика изву^
возвращаемых методами toPage () и f romPage (), отняв от первого второе и
прибавив единицу. Для нашего примера это выглядит следующим образом:
int nPages = pprinter->toPage() - pprinter->fromPage() + 1;
Вызов метода setup () откроет окно диалога, дающее пользователю
возможность выбрать нужный принтер и настроить опции печати. При нажатии на
кнопку ОК метод возвращает true, иначе — false. После этого объект
QPrinter полностью готов к использованию и его указатель mjpprinter
передается методу draw ().
( Примечание )
Чтобы получить для рисования следующий лист и напечатать на нем. нужно
вызвать метод newPage () и передать указатель mjpprinter в метод draw ().
| Листинг 23.6. Метод drawPrint (). Файл Printer.cpp
void Printer::draw(QPaintDevice* ppd)
{
QPainter painter(ppd);
QPaintDeviceMetrics pdm(ppd);
QRect r(0, 0, pdm.widthO, pdm.height());
painter.drawRect(r);
painter.drawLine(0, 0, pdm.width(), pdm. height());
painter.drawLine(pdm.width(), 0, 0, pdm. height());
painter.setPen(red) ;
painter.drawEllipse(r);
painter.setPen(blue);
painter.setFont(QFont("Times", 20, QFont::Normal));
painter.drawText(r, AlignCenter, "Printer Test");
}
В метод drawo, приведенный в листинге 23.6, передаются указатели на
контекст рисования. Подавляющее большинство принтеров не в состоянии
использовать для печати всю площадь листа, поэтому, чтобы избежать вывода
на недоступные для принтера места, нужно опросить прямоугольную
область вывода, используя объект класса QPaintDeviceMetrics — указатель pdm-
Этот объект инициализируется контекстом, передаваемым методу draw О •
Исходя из полученных размеров в контексте рисования, с помощью
методов drawRect (), drawLine (), drawEllipse () И drawTextO отображаются прЯМО'
угольник, линии, эллипс и текст. Методами setPeno производится установ-
гряваЯЗ. Вывод на печать 295
Ф перьев, имеющих различные цвета. Метод setFonto устанавливает
Шрифт A™ выводимого текста.
резюме
рЛаГОДарЯ ТОМУ, ЧТО КЛаСС QPrinter унаследован ОТ Класса QPaintDevice,
ВЫВОДИТЬ информацию на печатающее устройство так же просто, как рисовать
gg на экране. Объекты класса принтера QPrinter могут быть настроены
пользователем при помощи специального диалогового окна или
программно, вызовом целого ряда методов. Класс QPainDeviceMetrics предоставляет
информацию о прямоугольной области, в пределах которой можно
осуществлять вывод в контексте. Это очень важно, т. к. размеры области вывода
зависят от моделей принтеров.
ГЛАВА 24
Разработка собственных
элементов управления
Карта — это не территория,
имя — это не сам объект.
Альфред Коржибски
Создание собственных виджетов — задача несложная. Прежде всего нужно
хорошо подумать над тем, какой из виджетов классовой иерархии Qt
обладает большим количеством необходимых качеств, для того чтобы
использовать его в качестве базового. Это поможет существенно сэкономить время
при разработке нового виджета. Например, реализация виджета счетчика,
способного принимать числа в шестнадцатеричной системе исчисления,
может ограничиться наследованием класса QSpinBox и реализацией
конструктора нового виджета (пример 24.1), создав и установив в нем объект
контролера (validator) (см. гл. 9) вызовом метода setvaiidator ().
Пример 24.1. Класс HexSpinBox
class HexSpinBox : public QSpinBox {
Q_OBJECT
public:
HexSpinBox(QWidget* pwgt, const char* psz) : QSpinBox(pwgt, psz)
{
QRegExp rxp("[0-9A-Fa-f]+");
setvaiidator(new QRegExpValidator(rxp, this));
setRange(0, OxFFFF);
}
};
Пример 24.1 представляет собой частный случай. Далее следует список Ре~
комендаций для общего случая, пункты которого могут быть проигнорир0"
ваны, если в них нет необходимости.
гпява 24. Разработка собственных элементов управления 297
л Произведите перезапись методов обработки событий, на которые должен
реагировать НОВЫЙ ВИДЖет. ЭТО МОГУТ быть: paintEvent (), resizeEvent (),
mousePressEvent (), mouseReleaseEvent () И др.
0 Подумайте, будет ли создаваемый класс виджета наследоваться дальше,
если да, то вполне возможно, что понадобится объявить некоторые из
его переменных не как private, а как protected.
0 Постарайтесь, по возможности, следовать стилистическим соглашениям,
принятым в Qt.
0 Подумайте и примите решение, какие сигналы будет высылать виджет.
О Также, подумайте и примите решение, какие слоты будут определены в
классе виджета.
О Перезапишите методы sizeHinto и sizePolicyO, для того чтобы новый
виджет без проблем мог использоваться в лейауте.
С Примечание )
Можно обойтись и без перезаписи метода sizePolicyO, вызвав метод
setsizePolicy () и передав ему нужные значения из конструктора
создаваемого класса.
Последний пункт списка нуждается в отдельном пояснении. Как вы уже
знаете (см. гл. 10), классы лейаутов отвечают не только за расположение
виджетов, но и управляют их размерами. Виджеты гарантированно будут
иметь приемлемые размеры, если лейауты используют значения,
возвращаемые методами sizeHinto И sizePolicyO.
Метод sizeHinto возвращает объект класса QSize, который информирует
о том, какие размеры необходимы виджету. Это зависит, прежде всего, от
содержимого виджета. В примере 24.2 создаются два виджета кнопок,
имеющих надписи различной длины. Вызовы метода sizeHint () возвращают для
каждой из созданных кнопок разные значения. Вызов pcmdi->sizeHint о
возвращает значение (80, 24), a pcmd2->sizeHint о — значение (144, 24).
р-—.
[|]ример 24.2. Вызов методов sizeHinto
String str = "Button";
QPushButton* pcmdl = new QPushButton(str, this);
QSize size = pcmdl->sizeHint(); // size = (80, 24)
str = "This is very long button label";
QPushButton* pcmd2 = new QPushButton(str, this);
8ize = pcmd2->sizeHintО; // size = (144, 24)
Метод QWidget::sizePolicyO Возвращает объекты класса QSizePolicy, В KO-
^ьгх содержится информация, влияющая на интерпретацию значения,
298 Часть IV. Графика и а»
возвращаемого методом sizeHinto при изменении размеров окна. Объе|
создается передачей в конструктор класса QSizePoiicy двух флагов — ^
вертикального и горизонтального направлений, указанных в табл. 24.1.
Таблица 24.1. Перечисление sizeType класса QSizePon{
Константа
Fixed
Minimum
Maximum
Preferred
MinimumExpanding
Expanding
Ignored
Описание
Должно учитываться только значение, возвращаемое
методом sizeHintO
Виджет не должен быть меньше значения, возвращаемого
методом sizeHint ()
Виджет не должен быть больше значения, возвращаемого
методом sizeHint ()
Виджет может быть больше или меньше значения,
возвращаемого методом sizeHint ()
Виджет не должен быть меньше, чем sizeHinto. Лейаут
постарается предоставить виджету как можно больше места
Может быть больше или меньше sizeHinto. Лейаут
постарается предоставить виджету как можно больше места
Значение, возвращаемое методом sizeHint О, не берется во
внимание. Лейаут постарается предоставить виджету как
можно больше места
Возьмем, например, виджет класса QScrolview. Определить заранее, какого
размера он должен быть, невозможно. Благодаря полосам прокрутки с этим
виджетом можно работать, даже если его размер будет меньше размера,
возвращаемого методом sizeHint (). Поэтому значение, возвращаемое методом
sizeHinto, должно приниматься во внимание как рекомендуемый размер.
Но чем больше будет размер виджета QScroiiview, тем удобнее будет им
пользоваться. Для обеспечения подобного поведения в конструкторе этого
класса вызывается метод setsizePoiicyO, в который передаются два флага
Expanding для горизонтали и вертикали:
setSizePolicy(QSizePoiicy(QSizePoiicy::Expanding, QSizePoiicy::Expanding)
);
В листинге 24.1 приведена программа, результат которой изображен на
рис. 24.1, на котором продемонстрирован собственный виджет индикатор
прогресса. Само приложение состоит из индикатора прогресса и полось
прокрутки. Значение, отображаемое электронным индикатором, изменяете
в зависимости от указателя текущего положения полосы прокрутки.
глава 24. Разработка собственных элементов управления
299
Рис. 24.1. Демонстрация собственного индикатора прогресса
^Листинг 24.1. Файл main.cpp
^include <qapplication. h>
#include <qvbox.h>
#include <qscrollbar.h>
^include "CustomWidget. h"
n
int main(int argc, char** argv)
I
QApplication app(argc, argv);
QVBox vbx;
CustomWidget* pew = new CustomWidget(&vbx);
QScrollBar* phsb =
new QScrollBar(0, 100, 2, 10, 0, Qt::Horizontal, &vbx);
QObject::connect(phsb, SIGNAL(valueChanged(int)),
pew, SLOT(slotSetProgress(int))
app.setMainWidget (&vbx);
vbx.show();
return app.exec();
}
Создаются виджеты собственного индикатора професса — указатель pew,
а также полосы прокрутки — указатель phsb. После чего сигнал
ValueChanged (int) ПОЛОСЫ прокрутки соединяется СО СЛОТОМ
slotSetProgress (int), служащим для отображения значений целого типа
Индикатора професса, при помощи метода connect ().
^Листинг 24.2. Файл CustomWidget.h
#ifndef _CustomWidget_h_
define _CustomWidget_h_
300 Часть IV. Графика Извун
#include <qframe.h>
class CustomWidget : public QFrame {
Q_OBJECT
protected:
int m_nProgress;
virtual void paintEvent(QPaintEvent*);
public:
CustomWidget(QWidget* pwgt = 0, const char* pszName = 0);
virtual QSize sizeHint() const;
signals:
void progressChanged(int);
public slots:
void slotSetProgress(int n);
};
#endif //_CustoniWidget_h_
В листинге 24.2 определение класса CustomWidget содержит целочисленную
переменную mnProgress, необходимую для хранения текущего значения
индикатора прогресса. Производится перезапись методов paintEvent о и
sizeHint (). Последний информирует лейаут о желаемом размере. В классе
определяется сигнал progresschanged(int), который будет высылаться
каждый раз при изменении значения индикатора прогресса. Слот
slotsetProgress () управляет установкой значения индикатора прогресса.
) Листинг 24.3. Конструктор CustomWidget. Файл CustomWidgetcpp
CustomWidget::CustomWidget( QWidget* pwgt /*= 0*/,
const char* pszName/'' 0*/
)
: QFrame(pwgt, pszName)
, m_nProgress(0)
{
setWFlags(WNoAutoErase);
setBackgroundMode(NoBackground);
setLineWidth(3);
setFrameStyle(Box | Sunken);
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
}
r ява 24. Разработка собственных элементов управления 301
g конструкторе класса, в листинге 24.3, с помощью методов setwnagso и
etBackgroundMode () производятся установки, способствующие подавлению
эффекта мерцания (см. гл. 17). Методы setLineWidth () И setFrameStyle ()
устанавливают толщину и стиль рамки. Первый параметр QSizePoiicy::
afrjmm, передаваемый в метод setsizePoiicyO, говорит лейауту о том, что
ширина виджета не должна быть меньше значения, возвращаемого методом
sizeHint(). Второй параметр qsizePolicy::Fixed сообщает лейауту, что
высота виджета должна быть равна значению, возвращаемому методом
sizeHint ().
Листинг 24.4. Метод обработки события paintEvent ().
файл CustomWidget.cpp
/♦virtual*/ void CustoniWidget::paintEvent (QPaintEvent*)
{
QPixmap pix(size ());
Pix.fill(black);
QPainter painter(&pix);
float fPar = (widthО / 255.Of);
for (int i = 0; i < (int)(width() * (m_nProgress / lOO.Of)); ++i) {
QColor color(0, (int)(i / fPar), 0);
painter.setPen(QPen(color, 1, QPen::SolidLine));
painter.drawLine(i, 0, i, height());
}
painter.setPen(QPen(green, 1, QPen::SolidLine));
QString str = QStringO .setNum(m_nProgress) + "%";
painter.drawText(rect(), AlignCenter, str);
drawFrame(&painter);
::bitBlt(this, 0, 0, &pix);
}
Метод paintEvent о производит отображение индикатора прогресса
(листинг 24.4). Создается объект растрового изображения pix размером, равным
актуальному размеру виджета, который возвращает метод size о. Этот объ-
еКт выступает в качестве промежуточного буфера рисования. Объект
художника — painter, инициализируется контекстом растрового изображения.
" Цикле создается объект класса QColor с различными градациями зеленого
Цвета. Вызов метода setPen () устанавливает перо, а метод drawLine () рисует
^тикальные линии. По окончании работы цикла производится установка
302 Часть IV. Графика HaD
пера зеленого цвета. Строковой переменной str присваивается приведенн0
к строковому типу значение индикатора професса, после чего она отобра,
жается с помощью метода drawTexto, посредством передачи флага
AlignCenter, в центре индикатора. Адрес объекта художника (&painter)
передается В метод QFrame: :drawFrame() ДЛЯ ТОГО, чтобы класс QFrame смог
отобразить свою рамку. В завершение содержимое растрового изображения
копируется в контекст виджета.
Листинг 24.5. Слот-метод elotSetProgreeeO. Файл CustomWidget.cpp
void CustomWidget::slotSetProgress(int n)
{
if (n > 100) {
m_nProgress = 100;
>
else if (n < 0) {
m_nProgress = 0;
>
else {
m_nProgress = n;
>
repaint();
emit progressChanged(m_nProgress);
}
Приведенный в листинге 24.5 слот slot Set Progress о управляет установкой
значения индикатора професса (переменная mnProgress). Также он следит
за тем, чтобы значение индикатора професса не выходило за пределы
диапазона — от 0 до 100. После присвоения значения методом repaint ()
производится перерисовка и высылается сигнал progresschanged(int) с его
актуальным значением.
г .. !
j Листинг 24.6. Метод sizeHint (). Файл Custom Widget.cpp
/♦virtual*/ QSize CustorriWidget::sizeHint() const
{
return QSize(200, 30);
}
В листинге 24.6 метод sizeHinto информирует лейаут о размере, который
желает иметь виджет.
гияяа 24. Разработка собственных элементов управления 303
резюме
Унаследовав класс QWidget или его наследника, можно создавать свои
элементы управления. Быстрота реализации нового виджета во многом зависит
0Г удачно подобранного базового класса. При создании нового класса вид-
#ега важно помнить, какие методы событий необходимо перезаписать и
щцсие сигналы и слоты будет предоставлять виджет.
Классы, базирующиеся на QWidget, предоставляют интерфейс оповещения о
размере. На вершине этого интерфейса находится класс QSizePoiicy. С его
помощью виджет может сообщить, желает ли он подвергаться
уменьшению/увеличению по вертикали или горизонтали. Посредством метода
sizeHinto лейаут узнает о размерах, которые необходимы виджету, и
интерпретирует их в соответствии со значением объекта QSizePoiicy.
ГЛАВА 25
Звук
С земли иль с неба звук донесся?
Вальтер Скотт
По мнению психологов, человек воспринимает посредством слуха около
40% информации. Поэтому не исключайте возможность использования в
своих приложениях информации звукового характера. Профамма, прежде
всего, должна быть привлекательна для пользователя не только с точки
зрения зрительного эффекта, но также и слухового восприятия.
Воспроизведение звука
Вы наверняка замечали, что в критических ситуациях некоторые профам-
мы, для привлечения вашего внимания, используют короткий звуковой
сигнал. Чтобы воспользоваться подобным приемом в своих профаммах,
можно просто вызвать статический метод QAppiication:: beep о. Нужно
учесть, что издаваемый звук неприятно фомок и поэтому им лучше
пользоваться только в случаях крайней необходимости, например, когда
происходит действительно что-то очень важное.
Для серьезного профаммирования вам этого, естественно, будет
недостаточно и наверняка потребуется нечто большее, чем выдача простого
предупреждающего звукового сигнала. Наверняка захочется воспроизвести какой-
нибудь звуковой файл. Для проведения подобного рода операций Qt
предоставляет класс QSound.
Для воспроизведения звука в этом классе имеется статический метод р1ау(Ь
которому нужно передать путь и имя звукового файла в формате WAV
(Microsoft Wave). Это самый простой способ проифывания звукового файла
и вместе с тем самый экономичный, с точки зрения использования
ресурсов памяти.
QSound::play("music/yesterday.wav");
ftiaga 25. Звук 305
fy0i вашей программе необходимо проиграть звуковой файл более одного
раза, то этим статическим методом уже не обойтись, и для этой цели уже
потребуется создать объект класса QSound. Он предоставляет метод
setLoops (), который принимает в качестве параметра то количество раз,
которое звуковой файл должен быть воспроизведен. Передача, в качестве
параметра —1, создаст бесконечный цикл повторений. Этот метод должен
вызваться до запуска метода play ().
QSound sound("music/yesteday.wav");
sound. setLoops (3); // 3 раза
sound, play ();
Если вам нужно узнать количество оставшихся повторений для
проигрывания, ТО НУЖНО просто вызвать метод QSound::loopsRemaining(), который
вернет интересующее вас значение.
Для того чтобы остановить проигрывание звукового файла, нужно вызвать
метод QSound: :stop(). В соответствии с вышеприведенным примером
операция остановки воспроизведения выглядит следующим образом:
sound.stop();
Но этот метод применяется, разумеется, в том случае, когда создан объект
класса QSound и вызван метод play ().
Если вам вдруг потребуется узнать, было ли закончено проигрывание до
КОНЦа ИЛИ ОНО было прервано, ТО МОЖНО вызвать метод QSound:: is Finished (),
который вернет значение true, в случае, если проигрывание было
выполнено до конца, или false, если оно было остановлено.
Проверка возможности воспроизведения
Файлы для проигрывания должны быть записаны в формате WAV. Это
распространенный формат в ОС Windows. Звук, записанный в этом формате,
может быть представлен самым различным образом:
О со сжатием и без него;
О может быть стерео или моно;
О иметь 16- или 8-битовый звуковой канал;
О частота может лежать в пределах от 11 до 48 килогерц,
«се данные в формате WAV корректно распознаются классом QSound. И
если в операционной системе звук настроен правильно, то проблем с его
воспроизведением быть не должно. Но программа должна быть сама способна
Контролировать возможность выдачи звука, чтобы своевременно
проинформировать пользователя о возможных проблемах. Для этого класс QSound СО-
^ЖИТ два метода — QSound::availible () И QSound: : isAvailable ().
306 Часть IV. Графика ищ,^
Вызовом метода QSound: :avaiiibie() можно узнать о готовности системы к
выдаче звука. Этот метод возвращает значение true в том случае, если
система готова, и false, если система по каким-либо причинам не готова.
Для того чтобы определить возможность проигрывания звукового файла
вам потребуется вызвать метод QSound: :isAvaiiabie(), который возвращает
true, если никаких проблем со звуковым файлом нет и его воспроизведение
возможно, в противном случае этот метод вернет значение false.
Пример программы, воспроизводящей звук
Далее мы рассмотрим создание приложения с тремя кнопками, от нажатия
которых зависит ход его дальнейшего действия (листинг 25.2). Кнопка Play
(Воспроизвести) предназначена для воспроизведения звука, а кнопка Stop
(Остановить) его останавливает. Кнопка Loop (Цикл) является кнопкой-
выключателем (toggle button), т. е. она не возвращается в исходное
состояние после ее нажатия, благодаря чему она может находиться в одном из
двух положений — включено (on) или выключено (off). В данном примере
это очень удобно, поскольку позволяет задавать программе два режима
воспроизведения. При выключенном состоянии этой кнопки звук будет
проигрываться только один раз. При включенном — бесконечное количество раз,
пока воспроизведение не будет остановлено нажатием кнопки Stop
(Остановить) (рис. 25.1).
Рис. 25.1. Программа, воспроизводящая звук
| Листинг 25.1. Файл SoundPlayer.pro
xll:REQUIRES = nas
TEMPLATE = app
CONFIG += qt warn_on release
HEADERS = SoundPlayer.h
SOURCES = SoundPlayer.cpp main.cpp
TARGET = SoundPlayer
Обратите внимание на первую строку листинга 25.1. Параметр xll:requires
не имеет никакого значения для ОС Windows, он нужен для платформ,
базирующихся на XII, т. к. для них необходима поддержка NAS (Network
Audio System, сетевая система звука), которую можно загрузить с FTP-cep"
вера ftp.x.org:/contrib/audio/nas.
Глава 25. Звук
307
щйстинг 25.2. Файл main.cpp
linclude <qapplication.h>
linclude "SoundPiayer.h"
„
int main(int argc, char** argv)
{
QApplication app(argc, argv);
SoundPiayer soundPiayer;
app.setMainWidget(&soundPiayer);
soundPiayer.setCaption("Player");
soundPiayer.show();
return app.exec();
}
Р^стинг 25.3. Файл SoundPlayer.h
#ifndef _SoundPlayer_h_
♦define _SoundPlayer_h_
♦include <qhbox.h>
class QSound;
class SoundPiayer : public QHBox {
Q_OBJECT
private:
QSound* mjpsnd;
public:
SoundPiayer() ;
Public slots:
void slotPlayO ;
void slotStop();
void slotLoopO;
};
♦endif //_SoundPlayer_h_
Основной класс SoundPiayer унаследован от класса qhbox, что позволяет
автоматически разместить все кнопки в ряд. Впоследствии каждая из них бу-
308
Часть IV. Графика и звук
дет соединена с одним из трех слотов: siotPiayO, slotstopo и slotLoop о
(листинг 25.3).
{ Листинг 25.4. Файл SoundPlayer.cpp
#include <qmessagebox.h>
#include <qpushbutton.h>
#include <qsound.h>
#include "SoundPlayer.h"
//
SoundPlayer::SoundPlayer() : QHBox()
{
if (!QSound::isAvailable()) {
QMessageBox::warning(this,
"No Sound",
"Sorry, I can't play the sound"
);
}
QPushButton* pbtnPlay = new QPushButton("&Play", this);
QPushButton* pbtnStop = new QPushButton("&Stop", this);
QPushButton* pbtnLoop = new QPushButton("&Loop", this);
pbtnLoop->setToggleButton(true);
connect(pbtnPlay, SIGNAL(clicked()), SLOT (slotPlay()));
connect(pbtnStop, SIGNAL(clicked()), SLOT (slotstop()));
connect(pbtnLoop, SIGNAL(clicked()), SLOT(slotLoop()));
m_psnd = new QSound ("music/yesterday.wav", this);
}
//
void SoundPlayer::slotPlay()
{
m_psnd->play();
>
//
void SoundPlayer::slotStop()
{
m_psnd->stop();
}
Г пава 25. Звук
309
void SoundPlayer::slotLoop()
{
((QPushButton*)sender())->isOn() ? m_psnd->setLoops(-1)
: m_psnd->setLoops(1);
mjpsnd->stop();
>
Обратите внимание на слот siotLoopO, приведенный в листинге 25.4,
который нуждается в отдельном пояснении. Этот слот соединен посредством
указателя pbtnLoop с объектом кнопки Loop (Цикл). Метод sender о
возвращает указатель типа QObject на объект этой кнопки. Для того чтобы узнать
о состоянии самой кнопки, этот указатель нужно привести к типу
QPushButton и вызвать метод isono, который вернет значение true в том
случае, если кнопка находится во включенном состоянии, или false, если
она выключена.
( Примечание )
Можно обойтись и без вызова метода sender (), и без операции приведения
типа. Для того чтобы получить доступ к объекту кнопки Loop (Цикл) из слота,
можно просто сделать ее указатель pbtnLoop переменной класса SoundPlayer.
В первом случае устанавливается бесконечный цикл, а во втором —
воспроизведение выполняется один раз. После установки этого значения требуется
остановить сам проигрыватель, чтобы пользователь мог запустить его уже в
новом режиме.
Резюме
В этой главе мы познакомились с возможностями, предоставляемыми Qt
Для воспроизведения звука. Рассмотрели методы для воспроизведения,
прерывания и установки количества повторений звучания. Узнали, как
определять готовность самой системы для воспроизведения звука.
К сожалению, Qt пока не предоставляет возможности для проигрывания
Формата MIDI (Musical Instrument Digital Interface, электронный интерфейс
Музыкальных инструментов). Отсутствует возможность управления гром-
Костью звука или системным микшером. Но это наверняка временно и эти
Проблемы будут решены в последующих версиях этой библиотеки.
ЧАСТЬ V
Создание приложений
Глава 26. Сохранение настроек приложения
Глава 27. Буфер обмена и перетаскивание
Глава 28. Интернационализация приложения
Глава 29. Создание меню
Глава 30. Диалоговые окна
Глава 31. Предоставление помощи
Глава 32. Панель инструментов и строка состояния
Глава 33. Создание приложений
ГЛАВА 26
Сохранение
настроек приложения
Изменение может стать Вашим союзником...
Изменение станет Вашим врагом, если оно
застанет Вас врасплох.
Бак Роджерс, вице-президент IBM
Возможность изменения и сохранения настроек приложения незаменима
для "приспособления" интерфейса программы под пользователя. На самом
деле, пользователю будет очень приятно, если при запуске приложения
будут восстановлены настройки, примененные при предыдущем сеансе
работы, и приложение будет находиться на том же месте, иметь те же размеры и
выглядеть так, как угодно пользователю. Также возникает необходимость
сохранения названий и путей к последним открытым документам, чтобы
пользователь мог быстро сделать выбор из списка.
Данные настроек приложения представляют собой совокупность ключей и
значений. Ключ (key) представляет собой строковое значение — имя, с
помощью которого можно программным путем запрашивать и устанавливать
адресуемое ключом значение (key value).
Место, где хранятся эти данные, зависит от конкретной платформы
Например, приложение, запущенное в ОС Windows, сохраняет данные в
системном реестре, например, в ветках реестра HKEY_LOCAL_MACHlNE
\Software или HKEY_CURRENT_USER\Software.
( Примечание )
Системный реестр ОС Windows — это центральная база данных для хранения
установок приложения и установок самой системы.
При запуске приложения в Linux для сохранения данных будет
использоваться директория $HOME/.qt или $QTDIR/etc.
В Qt для работы с настройками используется класс QSettings. Для записи
настроек приложения применяется метод writeEntryO. Первым параметром
Глава 26. Сохранение настроек приложения
313
в метод передается ключ, а вторым — значение. Если такого ключа не
существует, то он будет создан. Тип значения, передаваемый вторым
параметром, может быть: bool, double, int, QString или QStringList. Методы, ис-
пользуемые для записи настроек приложения, приведены в примере 26.1.
тример 26.1. Методы записи настроек приложения
settings.writeEntry("/Settings/StringKey", "String Value");
settings.writeEntry("/Settings/IntegerKey", 213);
settings.writeEntry("/Settings/BooleanKey", true);
Есть определенные ограничения, которые необходимо учитывать при
записи настроек. А именно — размер ключа не должен превышать 255 символов,
и ни одно из строковых значений не должно быть более 65 535 символов.
В Примере 26.1 относительные КЛЮЧИ: StringKey, IntegerKey И BooleanKey.
Для получения данных настройки приложения нужно воспользоваться
методом readEntryo, который возвращает значения типа QString. Этим
методом можно ограничиться при преобразовании типов возвращаемых
значений. Но класс QSettings предоставляет методы для чтения разных типов —
readNumEntry (), readDoubleEntry () И readBoolEntry (). Это существенно об-
легчает задачу. В каждый из этих методов можно передавать три параметра.
Первый параметр — это сам ключ, второй — значение ключа, которое будет
возвращаться в случае, если ключ не найден, а в третьем параметре можно
передать адрес переменной булевого типа, в которую вернется результат
выполнения метода. В примере 26.2 показаны вызовы методов чтения.
мер 26.2. Методы чтения настроек приложения
QString str = settings.readEntry("/Settings/StringKey", "");
int n = settings.readEntry("/Settings/IntegerKey", 0);
bool b = settings.readEntry("/Settings/BooleanKey", false);
Для удаления ключей и их значений нужно передать имя ключа в метод
removeEntryO класса QSettings (пример 26.3).
| Пример 26.3. Удаления ключа
settings.removeEntrу ("/Settings/StringKey");
Чтобы не передавать в методы ключ полностью, можно задать его префикс
Методом beginGroup (). После использования ключей, связанных с этим
Префиксом, необходимо вызвать метод endGroupO. Методы префиксов
разрешается вкладывать друг в друга, получая тем самым длинные ключи.
314
Часть V. Создание приложений
Пример 26.4 демонстрирует ВЛОЖение методов beginGroupO И endGroupo
друг в друга.
Пример 26.4. Вложение методов beginGroupO и endGroupO
settings.beginGroup( "/Settings");
settings.beginGroup( "/Colors");
int nRed = settings.readEntry( "/red");
settings.endGroup();
settings.beginGroup( "/Geometry");
int nWidth = settings.readEntry( "/width");
settings.endGroup();
settings.endGroup();
Программа, приведенная в листингах 26.1—26.8, создает окно, показанное
на рис. 26.1. Надпись информирует о количестве запусков приложения.
Виджет выпадающего списка управляет изменением расцветки текстового
поля и предоставляет два режима — Classic (Классический) (черный текст,
белый фон) и Borland (стиль расцветки фирмы "Борланд") (желтый текст,
синий фон). Флаг Disable edit (Отключить редактирование)
включает/выключает режим редактирования текстового поля. Все
вышеперечисленные настройки и размеры окна сохраняются после его закрытия и
восстанавливаются при следующем запуске программы.
[Non Commercial] - MyProgram BOD
This program has been started 35 times
Эта прог • - - сохраняет все свои настройки и текст)
(Borland 3 l~ Dlsableedit
Рис. 26.1. Программа, сохраняющая и восстанавливающая настройки
Листинг 26.1. Файл main.cpp
#include <qapplication.h>
#include "MyProgram.h"
Гпава 26. Сохранение настроек приложения
315
jjjt main(int argc, char** argv)
{
QApplication app(argc# argv);
MyProgram myProgram;
app.setMainWidget(SmyProgram);
myProgram.show();
return app.exec();
}
В листинге 26.1 создается виджет класса MyProgram.
[Листинг26.2. Файл MyProgram.h
lifndef _MyProgram_h_
Idefine _MyProgram_h_
linclude <qwidget.h>
linclude <qsettings.h>
class QComboBox;
class QCheckBox;
class QTextEdit;
class QLabel;
class MyProgram : public QWidget {
QjOBJECT
Private:
QSettings m_settings;
QComboBox* m_pcbo;
QCheckBox* m_pchk;
QTextEdit* m_ptxt;
QLabel* m__plbl;
int m_nCounter;
Public:
MyProgram(QWidget* pwgt = 0, const char* pszName = 0);
virtual -MyProgramO;
void writeSettingsO ;
void readSettings ();
316
Часть V. Создание приложений
public slots:
void slotCheckBoxClicked ( );
void slotComboBoxActivated(int);
};
#endif //_MyProgram_h_
Определение класса MyProgram, приведенное в листинге 26.2, содержит
переменную msettings класса QSettings. Также, класс содержит указатели на
виджет выпадающего списка — m_pcbo, на флаг — m_pchk, на виджет
текстового поля — m_ptxt, на виджет надписи — m_j>ibi, и счетчик количества
запусков программы — m_nCounter. В классе MyProgram определены методы
для записи и чтения данных настроек программы (writesettingso и
readSettingsO). В нем определены два слота: slotCheckedBoxClicked() и
siotcomboBoxActivatedo — для выполнения действий, вызываемых
изменением состояний флага и выпадающего списка.
•■ *.
[ Листинг 26.3. Конструктор MyProgram. Файл MyProgram.cpp
MyProgram::MyProgram(QWidget* pwgt /*=0*/, const char* pszName /*=0*/)
: QWidget(pwgt, pszName)
{
m_plbl = new QLabel(this);
m_ptxt = new QTextEdit(this);
mjpcbo = new QComboBox(this);
mjpchk = new QCheckBox("Disable edit", this);
m_pcbo->insertItem("Classic") ;
m_j>cbo->insertItem( "Borland");
connect(m_pchk, SIGNAL(clicked()), SLOT(slotCheckBoxClicked()));
connect(m_pcbo,
SIGNAL(activated(int)),
SLOT(slotComboBoxActivated(int))
);
QVBoxLayout* pvbxLayout = new QVBoxLayout(this);
QHBoxLayout* phbxLayout = new QHBoxLayout();
pvbxLayout->setMargin(5);
phbxLayout->setSpacing(15);
pvbxLayout->setSpacing(15);
pvbxLayout->addWidget(m_plbl);
pvbxLayout->addWidget(m_ptxt);
Глава 26. Сохранение настроек приложения
317
phbxLayout->addWidget(mjpcbo);
phbxLayout->addWidget(m_pchk);
pvbxLayout->addLayout(phbxLayout);
m_settings.setPath("BHV","MyProgram");
readSettings();
В листинге 26.3, в конструкторе класса, создается виджет надписи (m_pibi),
виджет текстового поля (m_ptxt), виджет выпадающего списка (m_pcbo) и
виджет флажка (m_pchk). Методом insertitemo в виджет выпадающего
списка добавляются опции Classic и Borland, обозначающие текущую расцветку
текстового поля. Сигнал clicked о виджета флажка (указатель m_pchk)
соединяется СО СЛОТОМ siotcheckBoxCiickedO, а сигнал activated (int) ВИДЖета
выпадающего списка — со слотом siotcomboBoxActivatedn. После
размещения элементов при помощи лейаутов (см. гл. 10) производится
инициализация объекта настроек msettings именем фирмы — BHV, и названием
продукта — MyProgram. Затем производится вызов метода readSettings ().
[^Листинг 26.4. Метод readSettings (). Файл MyProgram.cpp
void MyProgram::readSettings()
i
m_settings.beginGroup("/Settings");
QString strText = m_settings.readEntry("/text", "");
int nWidth = m_settings.readNumEntry("/width", widthO);
int nHeight = m_settings.readNumEntry("/height", height());
int nComboItem = m_settings.readNumEntry("/highlight", 0);
bool bEdit = m_settings.readBoolEntry("/edit", true);
m_nCounter = m_settings.readNumEntry("/counter", 1);
QString str = QString().setNum(m_nCounter++);
m_plbl->setText("This program have been started " + str + " times");
m_ptxt->setText(strText);
resize(nWidth, nHeight);
m_pchk->setChecked(bEdit);
siotcheckBoxCiickedO ;
318
Часть V. Создание приложений
m_pcbo->setCurrentItem(nComboItem);
slotComboBoxActivated(nCoitiboItem) ;
m_settings.endGroup();
}
В методе чтения настроек readsettings () объекту класса QSettings
передается префикс ключа с помощью метода beginGroup о (листинг 26.4). Это
делается для того, чтобы не передавать в методы чтения настроек весь ключ
полностью. Строковой переменной strText присваивается значение ключа
/text, прочитанное методом readEntryO. В том случае, если ключ с именем
/text не существует, метод вернет значение, указанное во втором
параметре, т. е. пустую строку. Аналогично производится инициализация
переменных nwidth и nHeight, предназначенных для хранения размеров окна,
nComboitem, хранящей индекс опции виджета выпадающего списка, bEdit,
хранящей значение разрешения редактирования, и mncounter, ведущую
подсчет количества запусков программы. После прочтения производится
установка и изменение виджетов в соответствии с полученными
значениями. Значение счетчика mncounter увеличивается на единицу. В завершение,
для снятия установленного префикса ключа производится вызов метода
endGroup().
Листинг 26.5. Деструктор ~MyProgram(). Файл MyProgram.cpp
/*virtual*/MyProgram::~MyProgram()
{
writeSettings() ;
}
Деструктор, приведенный в листинге 26.5, — это самое удобное место,
в котором можно произвести запись настроек приложения, т. к. он
вызывается при уничтожении виджета. Для этого производится вызов метода
writeSettings().
( Примечание )
Вместо деструктора можно воспользоваться методом обработки события
closeEvent (), который вызывается при закрытии окна виджета.
I Листинг 26.6. Метод writeSettings*). Файл MyProgram.cpp
void MyProgram::writeSettings()
{
m_settings.beginGroup("/Settings");
m_settings.writeEntry("/counter", m_nCounter);
Гпава 26. Сохранение настроек приложения 319
m_settings.writeEntry("/text", m_ptxt->text());
m_settings.writeEntry("/width", width());
m_settings.writeEntry("/height", height());
m_settings.writeEntry("/highlight", m_pcbo->currentItem());
m_settings.writeEntry("/edit", m_pchk->isOn());
m_settings.endGroup();
)
p методе writesettings о, после установки префикса ключа с помощью
метода beginGroupo производится запись данных настроек приложения.
Производится вызов серии методов writeEntry о, в которых первым параметром
указывается имя ключа, а во втором — его значение (листинг 26.6). Метод
writesettings () вносит в реестр ОС Windows изменения, показанные на
рис. 26.2.
I Name
JiSkDePei*)
Л£] counter
UST|ed.t
^height
^highlight
L^ltext
1^] width
Type_
REG_SZ
REGJMORD
REG DWORD
REG DWORD
REG.DWORD
REG SZ
REG_DWORD
Data
(value not set) 1
0x00000023 (35)
0x00000000 (0)
OxOOOOOOal (161)
0x00000001 (1)
Эта программа сохраняет 1
0x0000012c(300) J
Рис. 26.2. Значения ветки в реестре ОС Windows
Листинг 26.7. Метод slotCheckBoxClicked(). Файл My Program.срр
void MyProgram::slotCheckBoxClicked()
{
m_j>txt->setEnabled (!m_pchk->isOn ()) ;
}
Метод siotcheckBoxciickedo устанавливает виджет текстового поля в
активное или неактивное состояние, в зависимости от состояния виджета
Флажка (листинг 26.7).
/Листинг 26.8. Метод slotComboBoxActivatedO. Файл MyProgram.cpp
void MyProgram::slotComboBoxActivated(int n)
m_j>txt->setPaletteForegroundColor(n ? yellow : black);
m_ptxt->setPaletteBackgroundColor(n ? darkBlue : white);
320 Часть V. Создание приложена
Метод siotconiboBoxActivatedo листинга 26.8 производит установку цвета
шрифта и фона виджета текстового поля в зависимости от индекса элемента
выпадающего списка п.
Управление сеансом
Нельзя исключать и такую ситуацию, когда пользователь решил завершить
сеанс работы в операционной системе и забыл, что работал с программой
над одним из документов. После завершения сеанса все несохраненные
данные будут безвозвратно утеряны. К таким данным относятся как
документы, так и настройки самой программы. Для предотвращения подобного
программа должна отслеживать такие случаи, чтобы вовремя напомнить
пользователю о необходимости записи измененных документов.
Для этого необходимо унаследовать класс QAppiication и перезаписать в нем
методы QAppiication::commitData() И QAppiication::saveState(), которые
вызываются при завершении сеанса работы в операционной системе. В
качестве параметра ЭТИ методы Получают Объект класса QSessionManager.
С этим объектом можно работать дальше, например вызвать метод
aiiowinteraction (), чтобы узнать о возможности предоставления управления
процессом пользователю и отобразить диалоговое окно. Методом cancel о
можно прекратить процесс выхода из системы, но это должно происходить
ТОЛЬКО В ИСКЛЮЧИТелЬНЫХ случаях. При ПОМОЩИ Метода setRestartHint ()
можно сделать так, чтобы при следующем запуске операционной системы
программа автоматически запускалась. В этот метод нужно передать одно из
следующих значений:
П Res tart if Running — значение по умолчанию, при котором программа
будет запущена при запуске операционной системы;
П RestartAnyway — программа должна автоматически запускаться при
каждом запуске операционной системы;
П RestartNever — программа не должна автоматически запускаться при
входе в операционную систему.
Методом QAppiication::isRestroredо возвращает значение, с помощью
которого можно проверить, была ли программа запушена автоматически
(true) или ее запустил пользователь (false).
Программа, показанная на рис. 26.3, по завершении сеанса работы в
операционной системой выводит сообщение.
| ЕхДуоы peiation^ itos. О dialog о* |
Рис. 26.3. Программа, контролирующая завершение сеанса работы в ОС
ruaga 26. Сохранение настроек приложения
321
I Листинг 26.9. Файл main.cpp
j^i i
«include <qapplication.h>
«include <qmessagebox.h>
«include <qsessionmanager.h>
«include <qlabel.h>
class MyApplication : public QApplication {
public:
//
MyApplication(int argc, char** argv)
: QApplication(argc, argv)
{
>
//
virtual ~MyApplication()
{
}
//
virtual void commitData(QSessionManagerfi sm) {
QMessageBox::information(0,
"Dialog",
"You are exiting operation system"
);
>
};
//
int main (int argc, char** argv)
I
MyApplication app(argc, argv);
QLabel lbl("Exit you'r operation system to see the dialog box", 0);
app.setMainWidget(&lbl);
lbl.showO;
return app.exec();
ь
Листинге 26.9 класс MyApplication наследуется ОТ класса QApplication.
этом классе перезаписывается метод commitData о, который будет вызван
322 Часть V. Создание прилож^,,г
при завершении работы операционной системы. При его вызове на экран
отобразится окно с сообщением, информирующее о выходе из операциоц.
ной системы. В основной программе вместо создания объекта класса
QApplication создается объект Класса MyApplication.
Резюме
Qt предоставляет возможность хранения информации о конфигурации при*
ложений, необходимых для того, чтобы дать пользователю возможность
настраивать приложение под себя. Данные настроек приложения — это
совокупность ключей и их значений. Ключи — это значения строкового типа
состоящие из подстрок, разделенных знаком /". Значения могут иметь тип
int, double, bool или строковый тип. Все значения можно считывать при
ПОМОЩИ Метода readEntryO И записывать методом writeEntryO.
Механизм управления сеансом работы в операционной системе дает
возможность отслеживания завершения ее работы. Это удобно для
напоминания пользователю о необходимости записи измененных документов до
закрытия операционной системы.
ГЛАВА 27
Буфер обмена
и перетаскивание
Чтобы полностью понять величественную
и красивую мысль, требуется не меньше
времени, чем для того, чтобы ее породить.
Жан Юберт
Буфер обмена
Буфер обмена (Clipboard) обеспечивает возможность обмена данными между
разными приложениями. Это один из самых популярных способов
копирования данных из одного приложения в другое. Он представляет собой
область памяти, к которой могут иметь доступ все запущенные приложения
системы. Любое из этих приложений может взаимодействовать с буфером
обмена, записывать или считывать из него информацию. Программы,
работающие с буфером обмена, должны предоставлять в своем меню
стандартные команды: вырезать (cut), скопировать (сору) и вставить (paste). Все
команды необходимо снабжать комбинациями клавиш: <Ctrl>+<X>,
<Ctrl>+<C> и <Ctrl>+<V>.
Для работы с буфером обмена в Qt используется класс QCiipboard. He стоит
Пытаться создавать объект этого класса, т. к. он создается при запуске
приложения автоматически и может существовать в приложении только в
единственном ЧИСЛе. Объект класса QCiipboard высылает сигнал dataChanged ()
каждый раз, когда одно из приложений помещает в буфер обмена новые
Данные. Если необходимо контролировать данные, размешенные в буфере
°бмена, то тогда этот сигнал соединяют с соответствующим слотом.
Например:
Connect(qApp->clipboard(), SIGNAL(dataChanged()),
pwgt, SLOT(slotDataControl())
);
324 Часть V. Создание приложений
Данные можно помещать в буфер обмена при помощи методов setTexto
setPixmapO, setlmageO ИЛИ setDataO. Например:
QClipboard* pcb = QApplication::clipboard();
pcb->setText("My Text");
( Примечание )
При помощи метода setData () можно помещать в буфер обмена любые
данные. Метод принимает указатель на объект класса, унаследованного от
QMimeSource. QMimeSource — это абстрактный класс, являющийся основой для
типов данных, которые могут быть преобразованы в другие форматы.
С помощью методов text о, image о, pixmapo получают их данные из
буфера обмена. Например:
QClipboard* pcb = QApplication::clipboard();
QString str = pcb->text();
if (Istr.isNullO) {
cout « "Clipboard Text: " « str « endl;
}
Перетаскивание
Перетаскивание (drag&drop) — это новая мощная технология обмена
данными между приложениями. С ее помощью можно предоставлять
пользователю более интуитивный подход при работе с приложением, чем с буфером
обмена. Перетаскивание, в настоящее время, является неотъемлемой частью
практически любого приложения. Процесс перетаскивания выглядит
следующим образом: пользователь нажимает левой кнопкой мыши на объекте
и, не отпуская ее, перетаскивает объект из окна одной программы в окно
другой. Это позволяет обращаться с виртуальными объектами как с
объектами реального мира, перетаскивая их с одного места на другое. Ярким
примером является Recycle Bin (Корзина) на рабочем столе ОС Windows,
в которую сбрасывают ненужные файлы.
Класс QWidget обладает всеми необходимыми методами для поддержки
технологии перетаскивания, а некоторые из классов иерархии виджетов
содержат полную ее реализацию. Поэтому прежде чем приступить к реализации
перетаскивания, необходимо убедиться в том, что оно не
реализовано
в виджете. Например, класс QLineEdit предоставляет возможность
перетаскивания выделенного текста.
Qt предоставляет следующие классы для размещения данных различных ти
пов при перетаскивании:
д! 27- Буфер обмена и перетаскивание 325
n O^olorDrag — служит для перетаскивания цветовых значений;
n QjConDrag — предназначен для перетаскивания иконок. Этот класс ис-
«ОЛЬЗуеТСЯ классом QlconView;
П QjmageDra9 ~" предоставляет возможность перетаскивания растровых
изображений;
л QTextDrag — служит для перетаскивания текстовой информации;
q ooriDrag — предназначен для хранения списка (ссылок) URI (Universal
Resource Identifier, универсальный идентификатор ресурса) и
последующего перемещения. Этот класс часто применяется для перетаскивания
( "Примечание )
URI представляет собой способ для описания различных информационных
ресурсов. Его реализация для Интернета получила название URL (Uniform
Resource Locator, универсальный указатель ресурса), которая обеспечивает
доступ к ресурсам по сетевым протоколам
Вышеперечисленных классов на все случаи, естественно, не хватает, т. к.
может понадобиться перетаскивать и принимать свои собственные типы
данных (например, звуковые данные). Как поступать в подобных
ситуациях? Классы QTextDrag, QlmageDrag, QUriDrag И QColorDrag унаследованы ОТ
класса QDragObject, который в свою очередь унаследован от класса
QMimeSource. А это значит то, что потребуется всего лишь создать свой
собственный класс, унаследованный от класса QDragObject и реализовать в нем
методы format () И encodedData ().
Программирование поддержки перетаскивания можно условно разделить на
Две части. Первая часть включает в себя реализацию кода для
перетаскивания объекта (drag), а вторая часть реализует область приема для
сбрасываемых в нее объектов (drop). Также вторая часть должна распознавать, в
состоянии ли она принять перетаскиваемый объект или нет.
Реализация Drag
Реализация первой части перетаскивания начинается с перезаписи методов
"OUSePressEvent () И mouseMoveEvent (). В Первом ИЗ ЭТИХ меТОДОВ
производится запоминание позиции указателя мыши, в которой была нажата кноп-
**• Эта позиция пригодится в методе mouseMoveEvent () для определения мо-
Мента старта операции перетаскивания.
„Примечание ^)
Для классов, унаследованных от QScrollview, нужно вместо методов
mousePressEvent () и mouseMoveEvent () перезаписать методы
contentsMousePressEvent () и contentsMouseMoveEvent (). Отличие в том, что
они осуществляют перетаскивание объектов, находящихся в окне просмотра.
326
Часть V. Создание приложений
Рис. 27.1 демонстрирует перетаскивание текстовой информации из окна
виджета в окно редактора.
i nl
This is draggable text
р»|н| &Ш ttl i ~taH %\\
— _— _— — _
_^j |10 jrj j Western
1 ■ i ■ 2- ■ 3 '-4 ■ ■5• " -6 < -7 •
f global resource so all applications are -^->
ified of changes See the multiclip Tik
mole in the Ot Designer examples ^m ,. 1
1_Рдг. Нр1п nrpc: F1 J
Рис. 27.1. Перетаскивание текста
Листинг 27.1. Файл main.cpp
#ifndef _Drag_h_
#define _Drag_h_
#include <qlabel.h>
# include <qdragobj ect.h>
#include <qapplication.h>
#include "imgl.xpm"
class Drag : public QLabel {
Q_OBJECT
private:
QPoint mjptDragPos;
void startDragO
{
QTextDrag* pdrag = new QTextDragC'This is draggable text", this);
pdrag->setPixmap(QPixmap(imgl_xpm));
pdrag->drag();
}
protected:
virtual void mousePressEvent(QMouseEvent* pe)
if (pe->button() == LeftButton) {
mjptDragPos = pe->pos () ;
QWidget::mousePressEvent(pe);
Глав3 27. Буфер обмена и перетаскивание 327
virtual void mouseMoveEvent(QMouseEvent* pe)
{
if (pe->state() & LeftButton) {
int distance = (pe->pos() - mjptDragPos) .manhattanLengthO ;
if (distance > QApplication::startDragDistance()) {
startDrag();
}
}
QWidget::mouseMoveEvent(pe);
}
public:
Drag(QWidget* pwgt = 0, const char* pszName = 0)
: QLabel("This is draggable text", pwgt, pszName)
{
}
>;
#endif //_Drag_h_
Определение класса Drag, приведенное в листинге 27.1, содержит
переменную mptDragPos, предназначенную для сохранения положения курсора
мыши в момент нажатия левой кнопки. Инициализация этой переменной
производится в методе mousePressEvent () только в том случае, если была нажата
левая кнопка мыши.
Метод mouseMoveEvent () нужен для распознавания начала перетаскивания.
Это сделано потому, что нажатие левой кнопки мыши и последующее
перемещение указателя не всегда говорит о желании пользователя перетащить
объект, т. к. у пользователя могла просто дрогнуть рука, случайно
переместив указатель мыши. Чтобы быть уверенным, необходимо вычислить
расстояние между текущей позицией и той, в которой была нажата левая
кнопка мыши. Если это расстояние превышает величину, возвращаемую
статическим методом startDragDistance о (обычно 4 пиксела), то тогда
Можно исходить из того, что перемещение указателя мыши не было
случайным и пользователь действительно хочет перетащить выбранный объект.
После установления начала перетаскивания вызывается метод startDragO.
В этом методе создается объект перетаскивания класса QTextDrag. В его
Конструктор передается текстовая строка и указатель на виджет, из которого
осуществляется перетаскивание.
■1?Т , -т.
J;•» Внимание!1, j
Передача указателя на виджет вовсе не означает, что этот виджет будет
предком и будет нести ответственность за уничтожение объекта перетаскивания. На
самом деле ответственность за уничтожение объектов перетаскивания несет
328
Часть V. Создание приложен^
только менеджер перетаскивания. Уничтожение перетаскиваемого объекта
производится в любом случае и не играет роли, отпущен он в принимающей
зоне или нет.
Вызов метола setPixmapo устанавливает небольшое растровое изображение
следующее вместе с указателем мыши при перетаскивании. Метод drag о'
запускает операцию перетаскивания.
Реализация Drop
На рис. 27.2 показан виджет, способный принимать сбрасываемые в него
объекты (в данном случае — файлы). После сбрасывания виджет отображает
путь и имя файлов.
I imij.i.i.ujjwjffTxi,
C:\qt32\souce\qacbon.cpp
CAq souceSqbutton cpp
С \qt3i. .souce\qbutton.h • Drtpped
^m ' »
Address \^i C:\qt32\sourcj
m
~k qaction.q
' » 24 Kl
Рис. 27.2. Виджет, принимающий сбрасываемые объекты
Листинг 27.2. Файл Drop.h
#ifndef _Drop_h_
#define _Drop_h_
# include <qdragobj ect.h>
#include <qlabel.h>
class Drop : public QLabel {
Q OBJECT
protected:
virtual void dragEnterEvent(QDragEnterEvent* pe)
pe->accept(QUriDrag::canDecode(pe));
(пава 27. Буфер обмена и перетаскивание 329
virtual void dropEvent(QDropEvent* pe)
{
QStringList strList;
if (QUriDrag::decodeLocalFiles(pe, strList)) {
setText(QString("%l - Dropped").arg(strList.join("\n")));
}
}
public:
Drop (QWidget* pwgt = 0, const char* pszName «= 0)
: QLabe1("Drop Area", pwgt, ps zName)
{
setAcceptDrops(true);
}
};
#endif //_Drop_h_
Для того чтобы виджет был в состоянии принимать сбрасываемые объекты,
В КОНСТруКТОре Класса Drop ПРОИЗВОДИТСЯ ВЫЗОВ Метода set Accept Drops (), В
который передается true (листинг 27.2). В этом классе для получения
сбрасываемых объектов необходимо Перезаписать методы dragEnterEvent () И
dropEvent ().
( Примечание )
Для классов, унаследованных от QScrollview, нужно вместо методов
dragEnterEvent () и dropEvent () перезаписать методы
contentsDragEnterEvent() и contentsDropEvent(), а затем вызвать
viewport ()->setAcceptDrops (true) из конструктора для того, чтобы окно
просмотра виджета было в состоянии принимать сбрасываемые объекты.
Метод dragEnterEvent () вызывается каждый раз, когда перетаскиваемые
объекты пересекают границу виджета. Этот метод нужен для того, чтобы
виджет был в состоянии сообщать о готовности принимать перетаскиваемые
объекты, при этом указатель мыши из перечеркнутого круга превращается в
стрелку с прямоугольником (возможно со знаком плюс), в противном
случае внешний вид указателя мыши остается без изменений.
Обычно виджет не способен принимать данные всех типов, поэтому
необходимо опрашивать тип данных перетаскиваемых объектов с помошью
статического метода canDecodeO соответствующего класса. Передача в метод
accepto значения true сообщает о готовности виджета принять
перетаскиваемые объекты, a false сообщает об отказе. Вызов метода accepto без
параметров соответствует вызову с параметром true. Вызов метода ignore о
Соответствует ВЫЗОВУ accept (false).
330 Часть V. Создание приложен^
( Примечание )
Класс QDragEnterEvent унаследован от класса QDragMoveEvent (см. рис. 13.1)
Этот класс предоставляет методы accept () и ignore (), в которые можно
передавать и объекты класса QRect. С их помощью можно ограничить размеры
принимающей области в самом виджете. Чтобы разрешить, например,
сбрасывание объектов во всей области виджета, нужно сделать следующий вызов-
accept (recto )•
В нашем случае (см. листинг 27.2), метод canDecodeO вызывается из класса
QUriDrag. Этот класс является реализацией для URI.
Метод dropEvento вызывается при сбрасывании перетаскиваемых объектов
в пределах окна виджета, что происходит в момент отпускания
пользователем правой кнопки мыши. Вызов статического метола decodeLocaimeso
производит запись списка файлов в переменную strList. Вызов метода
join о объединяет список строк в одну строку, разделяя их символом
возврата каретки (/п). После этого полученная строка отображается с помощью
Метода setText().
Резюме
Основной задачей буфера обмена является поддержка простейшей формы
обмена информации для различных приложений. Например, с его помощью
одна программа может записывать данные в буфер обмена, а другая —
читать их из него.
Хорошее приложение характеризует его интуитивность в обращении.
Использование технологии перетаскивания (drag&drop) — это верный шаг к
достижению этой цели. Для того чтобы перетащить объект, нужно лишь
просто нажать на него левой кнопкой мыши и, не отпуская указатель
мыши, переместить его в нужное место, а затем отпустить кнопку.
Qt предоставляет ряд классов для транспортирования информации
посредством drag&drop. Вкратце их применение можно описать следующим
образом: класс QTextDrag используется для перетаскивания текста, класс
OimageDrag — для перетаскивания растрового изображения, класс
QUriDrag — для перетаскивания файлов. Этих классов вполне достаточно,
когда нужно перетаскивать специфические данные. В других случаях
потребуется реализация своего собственного класса, унаследованного от
QDragObject.
Чтобы из виджета можно было перетаскивать объекты, необходимо
перезаписать Методы mousePressEvent () И mouseMoveEvent (). Для получения перб-
ТасКИВаеМЫХ Объектов необходимо перезаписать методы dragEnterEvent () и
dropEvento, а также вызвать из конструктора метод setAcceptDrops(),
передав в него true.
ГЛАВА 28
Интернационализация
приложения
Труднее всего примириться с хорошим
примером.
Марк Твен
Современные коммуникационные технологии до предела сокращают не
только время, но и расстояния, и наш огромный мир плотно опутан ими.
И именно эти современные технологии накладывают дополнительные
требования на реализацию программ. Здесь уже не обойтись без поддержки
интернационализации. Программа должна предоставлять возможность
выбора языка интерфейса, чтобы каждый пользователь мог выбрать свой
родной язык.
Для поддержки интернационализации в ваших приложениях требуется
проделать следующие шаги:
1. Подготовить приложение к интернационализации (если оно еще не готово).
2. Запустить утилиту LUPDATE.
3. Перевести команды созданной программы (например, с помощью
программы Qt Linguist).
4- Запустить утилиту LRELEASE для генерирования двоичных файлов
переводов, которые впоследствии будут загружаться в объект класса
QTranslator.
Далее рассмотрим эти шаги подробнее.
Подготовка приложения
К интернационализации
"УЖно стремиться делать ваши приложения готовыми к
интернационализации. А это значит, что при профаммировании все строки, предназначенные
332 Часть V. Создание приложены*
для вывода на экран, нужно заключать в статический метод tro, опреде.
ленный в классе oobject. Текст, переданный в этот метод, может быть пере.
веден позже. Первым параметром в метод tr() передается строка текста
Второй параметр — комментарий, используемый для предоставления
переводчику дополнительной информации. Он не является обязательным и
может быть проигнорирован. Например:
setText(tr("Yes"));
Интернационализация, на самом деле, это гораздо больше, чем просто
перевод текстов с одного языка на другой, она включает в себя также
адаптацию разнообразных форматов дат и цифр. Поэтому важно помещать в
функцию tro не только текст, предназначенный для отображения на
экране, но и валютные обозначения, клавиши ускорителей и другие
отличительные особенности, присущие конкретной стране.
Утилита LUPDATE
После передачи в метол tr () всех нужных строк текста можно приступать к
созданию файлов перевода. Для этого необходимо воспользоваться
специальной утилитой LUPDATE. В данном случае назначение метода tro
сводится к тому, чтобы утилита могла легко найти в программном коде
нуждающийся в переводе текст. Из строк, переданных в метод tro, будут
созданы отдельные TS-файлы (Translation Source, Источник перевода) —
файлы переводов. Чтобы создать для программы, приведенной в
примере 28.1, файлы русского и немецкого переводов (или, иначе, локализовать
программу), нужно сделать следующий вызов:
lupdate main.cpp -ts main_ru.ts main_de.ts
I Пример 28.1. Файл main.cpp
#include <qapplication.h>
#include <qlabel.h>
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QLabel 1Ы (QObject: :tr("Hello"), 0);
app.setMainWidget(slbl);
lbl.show();
return app.exec();
}
глава 28. Интернационализация приложения 333
0 результате будут созданы файлы переводов в формате XML, содержащие
следующий код:
^JDOCTYPE TSXTS>
<context>
<name>QOb j ect</name>
<message>
<source>Hello</source>
translation type="unfinished"x/translation>
</message>
</context>
</TS>
В этот файл можно внести перевод "от руки". Для этого в теге translation
убирается параметр type вместе с его значением и, в текстовую зону тега,
добавляется перевод. Например, для русского варианта тег translation будет
выглядеть так:
<translation>3flpaBCTByii</translation>
Подготовленные файлы переводов необходимо включить в файл проекта.
Для этого в него необходимо внести следующую строку:
TRANSLATIONS = raain_ru.ts main_de.ts
Программа Qt Linguist
Эта программа входит в пакет Qt и предоставляет более удобный способ для
редактирования файлов переводов. Применение этой программы
целесообразно в больших проектах. Для начала редактирования требуется запустить
программу, передав ей в качестве параметра файл перевода. Следующая
команда запускает Qt Linguist и загружает в него файл русского перевода
(рис. 28.1):
linguist main_ru.ts
Слева показана группа элементов перевода. В верхнем правом окне
показана таблица со следующими колонками: Source text (Исходный текст),
Translation (Перевод) и Done (Состояние). Если текст переведен, в поле
Done (Состояние) будет стоять галочка, в противном случае — знак вопроса.
В среднем окне, в поле Translation (Перевод), можно вводить и изменять
Перевод исходного текста Source text (Исходный текст). Окно Phrases and
Roesses (Фразы и догадки) предлагает варианты перевода Qt Linguist.
После завершения работы с файлом перевода его необходимо преобразовать
в QM-файл (Messages file, файл сообщений) для дальнейшего использования
334 Часть V. Создание приложения
перевода в приложении. Для этого выберите команду меню File | Release
(Файл | Релиз), после чего появится стандартное диалоговое окно для
записи, в котором нужно указать имя файла и нажать кнопку Save (Сохранить).
Q Qt Linguist by Tro Itech - main_ru ts ЯП ЕЗ
File Edit Translation Validation Ehrases View Help
xjljSl d » ** xt Tr la 1
1 1 Done j Context) Items |
<& QObject 1Л
J Hello 3flP--CTByvi
Source text 1
Hello
Translation 1
Здравствуй 1
JSl
Phrases and guesses-
Source phrase | Translahon j Definition | |
Hello Здравствуй Guess (Ctrl+1)
1/1 jMOD
Рис. 28.1. Программа Qt Linguist
Утилита LRELEASE.
Пример программы, использующей перевод
Конвертирование из файлов переводов в QM-файлы лучше проводить при
помощи утилиты LRELEASE. Следующая команда создаст для каждого
файла перевода указанного в файле проекта отдельный QM-файл:
lrelease myproject.pro
Полученные QM-файлы передаются в объект класса QTransiator. Объект
класса QTransiator отвечает за перевод текстов с одного языка на другой-
Этот перевод производится посредством QM-файла, загруженного в него.
Пример 28.2 отображает на экране сообщение на русском языке,
соответствующее английскому сообщению "Hello" (рис. 28.2).
r^aga 28. Интернационализация приложения 335
| Здравствуй I
Рис. 28.2. Переведенное сообщение
.' Пример 28.2. Файл main.cpp
^include <qapplication.h>
^include <qtranslator.h>
#include <qlabel.h>
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QTranslator* ptranslator = new QTranslator(0);
ptranslator->load("main_ru.qm", ". ") ;
app.instaiiTransiator(ptranslator);
QLabel lbl(QObject::tr("Hello"), 0);
app.setMainWidget (&1Ы) ;
lbl.show();
return app.exec();
}
В примере 28.2 создается объект класса QTranslator. Вызовом метода load о
производится загрузка файла перевода main_ru.qm, указанного в первом
параметре этого метода. Второй параметр задает директорию, содержащую
файлы переводов. В нашем случае вторым параметром передается строка,
содержащая точку, что говорит о том, что QM-файлы находятся в той же
Директории, что и само приложение. Вызов метода instaiiTransiator () из
объекта класса QApplication применяет созданный объект переводчика ко
всему приложению.
Хорошим тоном считается предоставление пользователю возможности
изменения языка интерфейса посредством пункта меню или с помощью
диалогового окна настройки приложения. В этих случаях требуется, в процессе
Работы программы, динамически менять язык. Для этого можно создать
Сдельный метод для смены языка, который может выглядеть следующим
°бразом:
void MyProgram::switchLanguage(int n)
{
QTranslator* ptranslator = new QTranslator(0);
336 Часть V. Создание приложены*
switch (n) {
case RUSSIAN : ptranslator->load("myprogram_ru.qm"#".");
break;
case GERMAN : ptranslator->load("myprogram_de.qm",".");
break;
}
qApp->installTranslator(translator);
}
Резюме
Всего 4 шага отделяют обычное приложение от приложения с поддержкой
интернационализации. Статический метод tr() класса QObject выполняет
сразу два действия. Во-первых, он помогает утилите LUPDATE находить в
программе текст, нуждающийся в переводе. Во-вторых, он является
методом для перевода текста. Этот метод очень эффективен в поиске строк
перевода, поэтому не нужно бояться снижения эффективности программ.
Чтобы подготовить перевод, нужно запустить программу LUPDATE,
которая создаст файл перевода с расширением ts (или TS-файл) на основании
кода в С++-файлах. Этот файл необходимо перевести "от руки" или с
помощью программы Qt Linguist. Для использования перевода в программе
нужно переработать файлы перевода в QM-файлы. Это преобразование
производится при помощи утилиты LRELEASE.
Загрузка QM-файлов в объект класса QTranslator осуществляется методом
loado. После этого объект перевода применяется с помощью метода
install/Translator () класса QApplication.
ГЛАВА 29
Создание меню
— А что у нас сегодня в меню? (Menu)
— Филе! (File).
Виктор Свитковский
Меню является важной и неотъемлемой частью практически любого
приложения. Оно находится в верхней части главного окна приложения и
представляет собой секцию для расположения большого количества команд, из
которых пользователь может выбирать нужную. В приложениях
используются меню четырех основных типов:
О меню верхнего уровня;
О всплывающие меню;
О отрывные меню;
О контекстные меню.
В библиотеке Qt реализован класс QMenuData, который является базовым для
классов меню QMenuBar и QPopupMenu. Определения двух последних классов
находятся в заголовочных файлах qmenubar.h и qpopupmenu.h. Основное
назначение базового класса QMenuBar — это размещение команд в меню.
Каждой команде присваивается идентификационный номер, который
создается автоматически или устанавливается в соответствующем методе самим
разработчиком. Все команды меню могут быть соединены со слотами для
Исполнения соответствующего кода при выборе команды пользователем.
Анатомия меню
Пользователю будет гораздо легче привыкнуть к работе с новой
программой, если ее меню будет похоже на меню других программ. На рис. 29.1
показаны составляющие типичного меню.
338
Часть V. Создание приложений
Меню верхнего уровня
Команда с флажком
Команда с пиктограммой
Клавишное сокращение
"Горячие" клавиши
Разделитель
Меню со стрелкой
Подменю
Недоступная команда
Рис. 29.1. Анатомия меню
Основной отправной точкой меню является меню верхнего уровня. Оно
представляет собой меню с постоянно видимым набором команд. Они, в
свою очередь, могут выбираться при помощи указателя мыши или
клавиатуры, используя клавишу <Alt> и клавиши курсора. Команды меню
верхнего уровня предназначены для отображения выпадающих меню, поэтому их
не следует использовать для других целей, т. к. это может изрядно озадачить
пользователя. Старайтесь группировать команды по каким-либо признакам
и объединять их в одно выпадающее меню, которое, в свою очередь, будет
вызываться при выборе соответствующей команды верхнего меню. Класс
омегшваг отвечает за меню верхнего уровня и он определен в заголовочном
файле qmenubar.h.
"Горячие" клавиши информируют пользователя о комбинации клавиш, с
помощью которой выполняется то же действие, что и при выборе
соответствующей команды меню Например, для отображения окна AboutQt в
приведенном выше примере используется комбинация клавиш <CtrI>+<Q>
(рис. 29.1). Старайтесь по возможности использовать для "горячих" клавиш
стандартные комбинации. Некоторые из них указаны в табл. 29.1.
Таблица 29.1. Некоторые стандартные комбинации для "горячих" клавиш
Клавиши
<Esc>
<F1>
<Shift>+<F1>
<Ctrl>+<N>
<Ctrl>+<0>
<Ctrl>+<P>
<Ctrl>+<S>
Описание
Отменяет текущую операцию
Вызов помощи
Вызывает контекстную помощь
Создать
Открыть ____
Печать
Сохранить ^_____
Гпава 29. Создание меню
339
Таблица 29.1 (окончание)
[Клавиши
|<Ctrl>+<Z>
f^Ctrl>+<X>
[<Ctrl>+<C>
[<Ctrl>+<V>
[<Ctrl>+<F4>
|<Ctrl>+<F6>
r^Shift>+<arl>+<F6>
Описание
Отменить предыдущее действие
Вырезать
Копировать
Вставить
Закрыть активный документ MDI-приложения
Активировать окно просмотра следующего документа
MDI-приложения
Активировать окно просмотра предыдущего документа
MDI-приложения
По возможности, для всех пунктов меню должны быть определены клавиши
для быстрого вызова. Это позволит пользователю выбирать команды не
только посредством мыши, но и при помощи клавиатуры, нажав
подчеркнутую букву в названии команды совместно с клавишей <Alt>. Например, для
выбора команды Exit (Выход) нужно нажать <Alt>+<E> (см. рис. 29.1).
Основные отличия подобного рода комбинаций для быстрого вызова от
"горячих" клавиш состоят в следующем:
□ такие комбинации состоят из буквенной клавиши и клавиши <Alt>;
О они встречаются не только в меню, но и в диалоговых окнах;
□ они представляют собой контекстное исполнение команд. Например,
чтобы вызвать диалоговое окно About Qt (О Qt), нужно открыть меню Menu
(Меню) комбинацией клавиш <AIt>+<M>, а затем нажать <AIt>+<A>.
Стрелка у команды SubMenu (Полменю) (см. рис. 29.1) говорит о том, что
при ее выборе появится вложенное подменю, в нашем случае Test (Тест).
Вложенное подменю удобно применять для того, чтобы разгрузить меню,
если оно содержит большое количество команд. Для удобства и понимания
Программы рекомендуется, чтобы степень вложенностей не превышала двух
Уровней.
Разделитель — это черта, которая отделяет одну группу команд от другой.
Команда с флажком используется для предоставления пользователю
информации о режиме работы приложения и сигнализирует об
активированной команде меню.
Пиктограмма команды отображает команду меню в графическом виде.
Иногда встречаются команды, которые нельзя исполнить в определенный
Момент времени. В таких случаях приложение должно делать такие команды
340 Часть V. Создание приложений
меню недоступными, т. е. делать их выбор невозможным. Недоступные
команды меню отображаются другим, как правило, — серым, цветом.
Нужно также помнить, что в тех случаях, когда команда меню вызывает
диалоговое окно, ее название должно содержать три точки в конце. Это
/травило, правда, не распространяется на вызов простых окон сообщений.
В листинге 29.1 реализуется меню, показанное на рис. 29.1.
i Листинг 29.1. Файл main.cpp
#include <qapplication.h>
#include <qwidget.h>
#include <qpopupmenu.h>
#include <qmenubar.h>
#include <qpixmap.h>
#include "img4.xpm"
//
int main(int argc, char** argv)
{
QApplication app(argc# argv);
QWidget wgt;
QMenuBar* pmnuBar = new QMenuBar(&wgt);
QPopupMenu* pmnu = new QPopupMenu(&wgt);
pmnu->setCheckable(true);
pmnu->insertItem("&About Qt",
&app#
SLOT(aboutQtO),
Qt::CTRL + Qt::Key_Q
);
pmnu->insertSeparator();
int nld = pmnu->insertItem("&CheckableItem");
pmnu->setItemChecked(nld, true);
pmnu->insertItem(QPixmap(img4_xpm), "Slconltem");
QPopupMenu* pmnuSubMenu = new QPopupMenu(&wgt);
pmnuSubMenu->insertItem ("&Test");
pmnu->insertltem("SSubMenu", pmnuSubMenu);
nld = pmnu->insertItem(,,&DisabledItem") ;
pmnu->setItemEnabled(nId# false);
pmnu->insertSeparator();
Глава 29. Создание меню
341
pmnu->insertItem("&Exit", &app, SLOT(quit()));
pmnuBar->insertItem(,,&Menu", pmnu) ;
app.setMainWidget(&wgt);
wgt.resize(pmnuBar->sizeHint());
wgt.show();
return app.exec();
}
Чтобы создать полноценное меню, требуется к каждой команде меню
верхнего уровня присоединить соответствующее всплывающее меню. За
всплывающие меню отвечает класс QPopupMenu. Итак, для создания меню
необходимо иметь виджет класса QMenuBar — указатель pmnuBar и, по меньшей
мере, один виджет класса QPopupMenu — указатель pmnu (листинг 29.1).
( Примечание )
Если виджет QPopupMenu не прикреплен ни к одной из команд меню верхнего
уровня, то меню отображено не будет, т. к. оно не имеет смысла.
Для добавления всплывающего меню к меню верхнего уровня нужно
передать в метод insertitemo название команды и указатель на всплывающее
меню. Вызов метода setcheckableo со значением true говорит о том, что
всплывающее меню может иметь команды с флажками.
Метод insertitemo принимает четыре параметра. Первый задает название
команды меню. В названии можно указать букву для быстрого вызова,
поставив перед ней символ &. Не упускайте из виду того, что команды меню
не должны содержать одинаковых букв, используемых для быстрого вызова,
ведь в противном случае одна из команд будет недоступна. Вторым
параметром в этот метод передается указатель на объект, содержащий слот,
который вызывается при выборе пользователем команды меню. Сам слот
передается третьим параметром. Последний параметр задает комбинацию для
"горячей" клавиши. В нашем примере для отображения диалогового окна
AboutQt (О Qt) используется комбинация клавиш <Ctrl>+<Q>. Нажатие
этой комбинации приведет к тому же действию, что и выбор
соответствующей команды меню, а именно — будет вызван слот объекта приложения
aboutQto. Для составления комбинаций "горячих" клавиш можно
воспользоваться табл. 13.1.
Вызов метода insertseparatoro добавляет разделитель в меню.
Метод insertitemo возвращает идентификационный номер команды меню.
Этим номером можно воспользоваться, чтобы получить доступ к команде
Меню. Таким образом, передав в метод setitemcheckedo первым параметром
Идентификационный номер команды, можно установить при помощи вто-
V. Создание приложений
рого параметра значение ее флажка. Этим идентификатором можно
воспользоваться также и для того, чтобы сделать некоторые из команд меню
недоступными — С ПОМОЩЬЮ метода setltemEnabled ().
В метод insertitemo первым параметром можно передавать объекты класса
QPixmap, для установки пиктограммы команды.
Отрывные меню
/
Qt предоставляет возможность реализации отрывных меню (Tear off menu).
Нажатие мышью на прерывистую линию приводит к тому, что
всплывающее меню отделяется от меню верхнего уровня, превращаясь в отдельное
окно, которое свободно перемещается (рис. 29.2). Такое меню очень
удобно, например, лля настройки конфигурации программы.
I \1Щ-
Menu
1 Кепи
Item2
КетЗ
Exit
№1
i
. _ □ х
м
'UJMiU.JUxll
|ч п
Рис. 29.2. Отрывное меню
Листинг 29.2. Файл main.cpp
#include <qapplication.h>
#include <qwidget.h>
#include <qpopupmenu.h>
#include <qmenubar.h>
//
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QWidget wgt;
QMenuBar* pmnuBar = new QMenuBar(&wgt);
QPopupMenu* pmnu = new QPopupMenu(&wgt);
pmnu->insertTearOffHandle();
pinnu->insertltem("ltem&l") ;
pmnu->insertltem("ltem&2");
Гпава 29. Создание меню 343
pmnu->insertltem("ltem&3");
pmnu->insertItem("&Exit", &арр, SLOT(quit()));
pmnuBar->insertItem("&Menu", pmnu);
app.setMainWidget(&wgt);
wgt.resize(pmnuBar->sizeHint());
wgt.show();
return app.exec();
}
Чтобы задать отрывное меню, необходимо сначала вызвать метод
insertTearOffHandle () — это отобразит линию отрыва на верхнем бордюре
всплывающего меню.
Контекстные меню
Визитной карточкой профессионального приложения является наличие
контекстного меню. Контекстное меню — это меню, которое обычно
открывается при нажатии правой кнопки мыши. Для их реализации
используется класс QPopupMenu, как и в случае со всплывающими меню. Отличие
состоит лишь в том, что они не присоединяются к виджету QMenuBar. На
рис. 29.2 показано окно программы и контекстное меню, отображаемое при
нажатии правой кнопки мыши. Из меню пользователь может выбрать одну
из трех команд — Red (Красный), Green (Зеленый) или Blue (Синий),
которые задают окну соответствующий цвет фона.
ptUiaiiniiiW-i^l1
Blue
Рис. 29.3. Контекстное меню
[^Истинг 29.3. Файл main.cpp
♦ifndef ContextMenu h
define ' ContextMenu h
344
Часть V. Создание приложении
#include <qwidget.h>
#include <qpopupmenu.h>
// _■ „„д ______ «=====
class ContextMenu : public QWidget {
Q_OBJECT
private:
QPcpupMenu* mjpmnu;
protected:
virtual void mousePressEvent(QMouseEvent* pe)
{
if (pe->button() = RightButton) {
m_pmnu->exec(pe->globalPos());
}
}
public:
ContextMenu(QWidget* pwgt = 0, const char* pszName = 0)
: QWidget(pwgt, pszName)
{
m_pmnu = new QPopupMenu(this);
m_pmnu->insertItem(,,&Red", 0) ;
m_pmnu->insertItem("&Green", 1);
m_pmnu->insertItem("&Blue", 2);
connect(m_pmnu,
SIGNAL(activated(int)),
SLOT(slotActivated(int))
);
}
public slots:
void slotActivated(int nld)
{
QRgb rgb = OxFFOOOO » (nld * 8);
QColor color(rgb);
setPaletteBackgroundColor(color);
}
};
#endif // ContextMenu h
В конструкторе класса ContextMenu (листинг 29.3) создается виджет
контекстного меню — указатель m_pmnu. С помощью метода insert item о
добавляются команды меню, а идентификационный номер для каждой из команд
Глава 29. Создание меню
345
передается во втором параметре. Метод connect о соединяет сигнал меню
activated (int) со слотом siotActivated(int). Сигнал высылается каждый
раз при выборе пользователем одной из команд меню. Этот слот получает
идентификационный номер команды и на его основании устанавливает в
виджете цвет фона в соответствии с выбранной командой.
Показ контекстного меню должен производиться на месте указателя мыши,
при нажатии правой кнопки. Для этого нужно передать в метод exec о
значение объекта события мыши, возвращаемое методом giobaiPoso (метод
mousePressEventO). Этот метод возвращает объекты класса QPoint,
содержащие координаты указателя мыши относительно верхнего левого угла экрана.
Резюме
Большинство программ поддерживают меню, которое предоставляет
пользователю разнообразные возможности по выбору команд, управляющих
различного рода действиями. Меню можно разделить на четыре основных
типа: меню верхнего уровня, всплывающие, отрывные и контекстные меню.
Для всех пунктов меню должны быть определены символьные клавиши для
быстрого вызова, символы которых обозначаются знаком подчеркивания в
названии команды меню, а для наиболее важных или часто используемых
команд — "горячие" клавиши, представляющие собой комбинации клавиш,
которые интерпретируются программой как команды меню. Если вызов
команды отображает диалоговое окно, то рекомендуется добавлять в конце
имени команды три точки. Недоступные команды меню отображаются, как
правило, серым цветом, сообщая пользователю о том, что данная команда
не может быть выполнена.
ГЛАВА 30
Диалоговые окна
Заговори, чтобы я тебя увидел.
Сократ
Диалоговое окно — это центральный элемент, обеспечивающий
взаимодействие между пользователем и приложением. Этот виджет представляет
собой своего рода электронный бланк, который может содержать ряд опций,
изменение которых в ходе работы влечет за собой изменение в работе самой
программы. Диалоговые окна всегда являются виджетами верхнего уровня и
имеют свой заголовок. Их можно разбить на три основные категории:
□ собственные;
□ стандартные;
□ окна сообщений.
Правила создания диалоговых окон
Диалоговые окна очень важны в любом приложении и их создание — это
рутина, которую приходится выполнять разработчику очень часто. Создание
диалогового окна, на самом деле, включает в себя гораздо больше, чем
просто размещение нужных элементов. Важно обеспечить пользователю
возможность интуитивной работы с диалоговым окном, т. е. чтобы
пользователь не тратил время на изучение, а мог сразу начать работать. Для
обеспечения интуитивной работы необходимо учитывать следующие правила:
□ стремитесь к тому, чтобы диалог не содержал ничего лишнего и был как
можно проще. В диалоговом окне настроек программы желательно,
чтобы диалог содержал только основные кнопки, например: Ok, Cancel
(Отмена) и Apply (Применить);
□ объединяйте виджеты в отдельные логические группы, снабжая их пр^
моугольной рамкой и надписью. Используйте для разделения
горизонтальные и вертикальные линии;
Глава 30. Диалоговые окна
347
0 никогда не делайте содержимое окна диалога прокручивающимся. Если
ваш диалог содержит действительно много элементов, тогда постарайтесь
разбить его на группы и разместить их с помошью закладок;
0 нежелательно, чтобы закладки в диалоговом окне превышали более
одного ряда, это усложняет поиск;
О избегайте создания диалоговых окон с неизменяемыми размерами.
Пользователь всегда должен иметь возможность по своему усмотрению
увеличить или уменьшить их размеры;
О сложные диалоговые окна лучше снабжать дополнительной кнопкой Help
(Помощь), при нажатии на которую должно открываться окно
контекстной помощи;
О команды меню, вызывающие диалоговые окна, должны оканчиваться
многоточием, например Open... (Открыть...). Это делается для того,
чтобы пользователь знал, что нажатие команды меню приведет к открытию
диалогового окна;
О старайтесь не добавлять меню в диалоговые окна. Меню должны
использоваться в окне основной программы;
□ по возможности используйте стандартные виджеты, хорошо знакомые
пользователям. Не забывайте, что для освоения новых элементов
управления может понадобиться дополнительное время;
О для показа настроек избегайте использования цвета. В большинстве
случаев текст — лучшая альтернатива. Ведь один и тот же цвет может иметь,
в разных странах, разные смысловые значения. Также не следует
исключать из круга пользователей, неспособных различать цветовые оттенки;
О не забывайте, что пользователь должен работать с диалоговым окном
посредством не только мыши, но и клавиатуры. Для этого необходимо
снабдить все элементы окна клавишами быстрого вызова, которые
позволят, при совместном нажатии буквы с клавишей <Alt>, установить
фокус в нужном элементе.
Класс QDialog
Класс QDialog является базовым для всех диалоговых окон, представленных
в классовой иерархии Qt (см. рис. 4.1). Хотя диалоговое окно можно созда-
^ть при помощи любого виджета, сделав его виджетом верхнего уровня,
^м не менее, удобнее воспользоваться классом QDialog, который
предоставляет ряд возможностей, необходимых всем диалоговым окнам. Диалоговые
°Кна подразделяются на две группы:
" модальные;
U немодальные.
348 Часть V. Создание приложен^
Модальные диалоговые окна
Такие окна обычно используются для вывода важных сообщений.
Например, есть ошибки, на которые пользователь должен отреагировать, прежде
чем продолжить работать с приложением. Модальные окна прерывают
работу приложения и для продолжения работы окно должно быть закрыто.
В этих случаях модальный диалог — идеальное средство для обращения
внимания пользователя на себя.
Для блокировки приложения запускается цикл события только для
диалогового окна, а события клавиатуры, мыши и других элементов приложения
просто игнорируются. Этот цикл запускается вызовом метода exec о. Он
возвращает значение целого типа, после того как диалоговое окно будет
закрыто. Это значение информирует о нажатой кнопке и может принимать
значение QDialog: :Accepted ИЛИ QDialog::Rejected, КОТОрые соответствуют
кнопкам Ok и Cancel (Отмена). Типичным примером модального окна
является напоминание пользователю, перед закрытием приложения, о
необходимости сохранения документа. В момент отображения этого окна
возможность работы с самим приложением должна быть заблокирована.
Немодальные диалоговые окна
Немодальные диалоговые окна ведут себя как нормальные виджеты, не
прерывая, при своем появлении, работу приложения. На первый взгляд может
показаться, что применение немодальных диалогов имеет больше смысла,
т. к. в этом случае пользователь обладает большей свободой в своих
действиях. Но, на самом деле, большинство приложений нуждается в остановке и
ожидании решения пользователя для возобновления своих дальнейших
действий.
Немодальное окно может быть отображено с помощью методов exec о или
showo. Разница лишь в том, что метод show о не возвращает никаких
значений и не вызывает из глобального объекта приложения метод enterioopO,
останавливающий выполнение всей программы. Обычно для показа
немодального диалогового окна используется метод showo, как и для обычного
виджета. Метод hide о позволяет сделать окно невидимым. Этим свойством
можно воспользоваться, Так, например, в этом случае больше не
потребуется создавать каждый раз объект диалогового окна, а при закрытии удалять
его из памяти. Можно ограничиться вызовом методов show о и hide о, чТ°
даст возможность отображать окно диалога, расположенного на том же
месте, на котором оно было. Немодальные диалоговые окна необходимо
снабжать кнопкой Close (Закрыть) для того, чтобы дать возможность
пользователю закрыть его.
Глава 30.
Создание собственного диалогового окна
Программа, показанная на рис. 30.1, является результатом создания
собственного диалогового окна. При ее запуске на экране отображается кнопка
press Me (Нажми меня), нажатие на которую приводит к запуску
диалогового окна ввода имени First Name (Имя) и фамилии Last Name (Фамилия).
. _ П х 1
Press Me |
1 НШ*Ш»ШЯ1Ых1
First Name JLindsey
Last Name jGoes
1 CJk J Cancel j
Рис. 30.1. Диалоговое окно
[Листинг 30.1. Файл main.cpp
#include <qapplication.h>
iinclude "StartDialog.h"
//
int main(int argc, char** argv)
I
QApplication app(argc, argv);
StartDialog StartDialog;
app.setMainWidget(&startDialog);
StartDialog.show();
return app.exec();
В листинге 30.1 создается виджет класса StartDialog, предназначенный для
запуска диалогового окна.
Йистинг 30.2. Файл StartDialog.h
♦ifndef _StartDialog__h_
♦define _StartDialog_h_
♦include <qvbox.h>
♦include <qpushbutton.h>
♦include <qmessagebox.h>
♦include "InputDialog.h"
350
Часть V. Создание приложены
class StartDialog : public QVBox {
Q_OBJECT
public:
StartDialog(QWidget* pwgt = 0, const char* pszName = 0)
: QVBox(pwgt, pszName)
{
QPushButton* pcmd = new QPushButton("Press Me", this);
connect(pcmd, SIGNAL(clicked()), SLOT(slotButtonClicked()));
}
public slots:
void slotButtonClicked()
{
InputDialog* plnputDialog = new InputDialog;
if (pInputDialog->exec() = QDialog::Accepted) {
QMessageBox::information(0,
"Information",
"First Name: "
+ pInputDialog->firstName ()
+ "\nLast Name: "
+ pInputDialog->lastName()
);
}
delete plnputDialog;
}
};
#endif //_StartDialog_h_
В конструкторе класса StartDialog создается кнопка нажатия —
указатель pcmd, сигнал которой соединяется в методе connect о со слотом
slotButtonClicked о (листинг 30.2). В этом слоте создается объект
диалогового окна InputDialog, который не имеет предка.
( Примечание )
Диалоговое окно, не имеющее предка, будет центрироваться по экрану, иначе
оно будет отцентрировано относительно своего предка.
В операторе if производится запуск диалогового окна. После его закрыти
управление передается основной программе и метод exec () возвращает
значение нажатой пользователем кнопки. В том случае, если пользователеь
была нажата кнопка Ok, произойдет отображение информационного окна
введенными в диалоговом окне данными. По завершении метода диалогово
Глава 30. Диалоговые окна 351
окно нужно удалить самому, т. к. у него нет предка, который позаботится
об этом.
[листинг 30.3. Файл InputDialog.h
#ifndef _InputDialog_h_
#define _InputDialog_h_
#include <qdialog.h>
class QLineEdit;
,, «===========„=„„=== ======= ==============
class InputDialog : public QDialog {
Q_OBJECT
private:
QLineEdit* mjptxtFirstName;
QLineEdit* mjptxtLastName;
public:
InputDialog(QWidget* pwgt = 0, const char* psz = 0);
QString firstNameO const;
QString lastName () const;
};
lendif //_InputDialog_h_
Для создания своего собственного диалогового окна нужно унаследовать
класс QDialog. Класс inputDialog содержит две переменные — указатели
mjptxtFirstName И mjptxtLastName на ВИДЖеты однострочного текстового
ПОЛЯ, и два метода, возвращающие содержимое этих полей — firstNameO и
lastName () (ЛИСТИНГ 30.3).
[Листинг 30.4. Конструктор InputDialog. Файл InputDlalog.cpp
InputDialog::InputDialog(QWidget* pwgt/*= 0*/, const char* psz/*= 0*/)
: QDialog(pwgt, psz, true)
{
mjptxtFirstName = new QLineEdit(this);
mjptxtLastName = new QLineEdit(this);
QLabel* plblFirstName = new QLabel("&First Name", this);
QLabel* plblLastName = new QLabel("&Last Name", this);
plblFirstName->setBuddy(mjptxtFirstName);
plblLastName->setBuddy(mjptxtLastName);
352 Часть V. Создание приложены,}
QPushButton* pcmdOk = new QPushButton("&Ok", this);
QPushButton* pcmdCancel = new QPushButton("&Cancel", this);
connect(pcmdOk, SIGNAL(clicked()), SLOT(accept()));
connect(pcmdCancel, SIGNAL(clicked()), SLOT(reject()));
QGridLayout* ptopLayout = new QGridLayout(this, 3, 2, 5, 5);
ptopLayout->addWidget(plblFirstName, 0, 0);
ptopLayout->addWidget(plblLastName, 1, 0);
ptopLayout->addWidget(mjptxtFirstName, 0, 1);
ptopLayout->addWidget(mjptxtLastName, 1, 1);
ptopLayout->addWidget(pcmdOk, 2,0);
ptopLayout->addWidget(pcmdCancel, 2, 1);
}
В Qt нет строгого разграничения между модальным и немодальным
диалоговыми окнами и класс QDialog используется для обоих типов. Поэтому,
чтобы сделать диалоговое окно модальным, необходимо передать в
конструктор класса QDialog третьим параметром значение true (листинг 30.4).
Модальное диалоговое окно всегда должно содержать кнопку Cancel
(Отмена). Сигналы clicked о кнопок Ok и Cancel (Отмена) соединяются со
слотами accept о и rejected о соответственно. Это делается для того, чтобы
метод exec о возвращал при нажатии кнопки Ok значение QDialog::
Accepted, а при нажатии на кнопку Cancel (Отмена) — значение
QDialog::Rejected.
| Листинг 30.5. Метод firstNameO. Файл InputDialog.cpp
QString InputDialog::firstName() const
{
return mjptxtFirstName->text();
}
Метод firstNameO возвращает введенное пользователем имя (листинг 30.5)-
! Листинг 30.6. Метод lastName (). Файл InputDialog.cpp
QString InputDialog::lastName() const
{
return mjptxtLastName->text();
}
Метод firstNameO возвращает введенную пользователем фамилию
(листинг 30.6).
глава 30. Диалоговые окна
353
Стандартные диалоговые окна
использование стандартных окон значительно ускоряет разработку тех
приложений, в которых необходимо использовать стандартные диалоговые окна
выбора файлов, шрифта, цвета и т. д. Вместо того, чтобы тратить время на
разработку своих собственных классов, можно воспользоваться готовыми
классами библиотеки Qt. К достоинствам стандартных диалоговых окон
можно отнести и целостность пользовательского интерфейса, т. к. вид окон
во всех приложениях, использующих их, будет один и тот же.
Диалоговое окно выбора файлов
Класс QFiieDiaiog предоставляет реализацию диалогового окна выбора
файлов (рис. 30.2). Он отвечает за создание и работоспособность сразу трех
диалоговых окон. Одно из них позволяет осуществлять выбор файла для
открытия, второе предназначено для выбора места и имени файла для его
сохранения, а третье окно предназначено для выбора директории. Класс
QFiieDiaiog унаследован от класса QDiaiog. Его определение находится в
файле qfiledialog.h.
Этот класс предоставляет следующие методы:
D getOpenFiieNameO — создает диалоговое окно выбора одного файла. Этот
метод возвращает значение типа QString, содержащее имя и путь
выбранного файла (рис. 30.2);
ВО
>km: J kernel
^j 4= о су еш-
Name |
Щ qhtmap.h
j| qbrush.h
"Uqclipboard.cpp
@| qclipboard.h
3qclipboard_xll.cpp
£ D
РЗЗЖ1
3ljqcolor_p.cpp
~f|qcolor_xll.cpp
5Г qconnection.cpp
j?[ qconnection.h
JjJjqcursor.cpp
Я" qcursor.h
SJjqcursor xll.cpp
Jlqdialog.cpp
_i .. . .
Size
3 KB
3 KB
7 KB
3 KB
23 KB
22 KB
6KB
27 KB
20 KB
3KB
3 KB
7 KB
5KB
24 KB
16KB
Type
C/C++ Header File
C/C++Header File
C++ Source File
C/C++Header File
C++ Source rte
C++ Source File
C/C++ Header File
C++ Source File
C++Source File
C++ Source File
" C++ Header File
C++ Source File
C/C++Header File
*"++ Source File
C++ Source File
| Date Modified
05.03.2001 10:09
05.03.2001 10:09
05.03.2001 10:09
05.03.2001 10:09
05.03.2001 10:09
05.03.2001 10:09
05.03.2001 10:09
05.03.2001 10:09
05.Oj.2001 10:09
05.0 2001 10:09
05.0 2001 10:09
05.0- 2001 10:09
05.0 2001 1 09
05.04 2001 10:00
05.0 1
M
1
zl
File name jqcolor h
Files or lype |*cpp"h
"3
"3
Рис. 30.2. Диалоговое окно выбора файлов
354 Часть V. Создание приложен.*.
П getOpenFiieNameso — создает диалоговое окно выбора нескольких фал
лов. Возвращает список строк — QStringList, содержащих пути и имена
файлов;
О getsaveFiieName () — создает диалоговое окно сохранения файла
Возвращает имя и путь файла в строковой переменной типа QString;
О getExistingDirectoryO — создает окно выбора директории. Этот метод
возвращает значение типа QString, содержащее имя и путь выбранной
директории.
В первом параметре вышеперечисленным методам передается строка,
представляющая собой рабочую директорию.
Вызов метода getOpenFiieNameO запустит диалоговое окно открытия файла.
Второй параметр, передаваемый в этот метод, представляет собой фильтр
(маску), задающий расширение файлов. Например:
QString str = QFileDialog::getOpenFileName("", "*.cpp *.h");
При ПОМОЩИ метода getExistingDirectoryO МОЖНО Предоставить
пользователю возможность выбора директории (рис. 30.3). Например:
QString str = QFileDialog:igetExistingDirectory("");
Browse For Folder
Ch ose a directo у
Lj) Desktop
+ My Documents
r- <^ My Computer
d US Local Disk (G)
+1 ^ id
*_J !Edy
J) ABA
^A ...UPP
FBEffil
OAlo n
ц
1
i
d
Make New Folder ) | OK | Cancel |
Рис. 30.3. Окно выбора директории
Диалоговое окно настройки принтера
Это окно позволяет пользователю выбрать принтер, изменить его парам^
ры, а также задать диапазон страниц для печати. Диалоговое окно настр°
ки принтера реализовано в классе QPrintDiaiog, но вызывать его в '
; отдеЛЬ'
г пава 30. Диалоговые окна
лости от объекта принтера не имеет никакого смысла, т. к. его главная
задача состоит в подготовке этого объекта к выводу на печать. Отображение
окна производится вызовом метода setup о класса QPrinter (см. гл. 23). На
рис. 30.4 показано диалоговое окно класса QPrintDiaiog.
Pr nt
Genera! |
Select Printer
Add Printer Ь'.ИАШ.Ш HP LaserJet
НОИКГТЯ IUPPostScr.
«1 1
Status: Ready
Location:
Comment
Page Range
<? АУ
Г Г
1
HE3|
k 3
Lexmark 4039 RVSFax
PlusPS 4 1
Г* Prmt to file Preferences j 1
Find Printer | | j
| Number of copies: jl ]ij 1
| Print | Cancel | ]
Рис. 30.4. Диалоговое окно настройки принтера
Диалоговое окно выбора цвета
Класс QCoiorDiaiog реализует диалоговое окно выбора цвета. Для того чтобы
показать это окно, вызывается статический метод getcoioro (рис. 30.5).
Первым параметром в этот метод можно передать цветовое значение для
инициализации. Вторым параметром является указатель на виджет предка.
После закрытия диалогового окна метод возвращает номер цвета. Чтобы
Узнать, какой кнопкой было закрыто окно — Ok или Cancel (Отмена),
необходимо вызвать метод isvaiido из возвращенного этим методом объекта
QCoior. Значение true означает, что была нажата кнопка Ok, в противном
случае — Cancel (Отмена). Например:
QColor color = QCoiorDiaiog:igetColor(blue, this);
if (!color. isValidO ) {
// Cancel
356
Часть V. Создание приложена»
■Select color
ga«c colors
■ ■
"
■
■
Custom colors
Г*
r
I ~ГГ
г
ггг
ггг
ГГГГГГГГ
ГГГГГГГГ
u
1 ok |
1
Cancel J
□1
+ 4\
Hue/ |62 Bed |247
Sat |241 £reen: |255
VeL J255 Bbje. fl4
£dd to Custom Colors | J|
Рис. 30.5. Диалоговое окно выбора цвета
Диалоговое окно выбора шрифта
Это окно предназначено для выбора одного из зарегистрированных в
системе шрифтов, а также для задания его стиля и размера (рис. 30.6). Реализа-
Effects
Г Str'feeout
Г Underline
Scipt
z\
на
I
SBMiHMflF
LithographLigfil
Lucida Console
Lucida Sans Unicode
METAArial
MS Mincho
MS Outlook
MS Sans Serif
MS Serif
MS Shell Dig
Font stile
m
±i
_j
d
Normal
Italic
Bold
Bold Italic
Size
m
1Б
18
20
22
24
2Б
28
36
A
I
J
zJ
AaBbYyZz
OK. I Cancel
Рис. 30.6. Диалоговое окно выбора шрифта
глава 30. Диалоговые окна 357
0Я этого диалогового окна содержится в классе QFontDialog, определенном
в заголовочном файле qfontdialog.h.
ддя того чтобы показать диалоговое окно, в большинстве случаев, можно
обойтись методом QFontDialog:: get Font о. Первый параметр этого метода
является указателем на переменную булевого типа. Метод записывает в эту
переменную значение, равное true, в том случае, если диалоговое окно
было закрыто нажатием на кнопку Ok, в противном случае — false. Во втором
опциональном параметре можно передать объект класса QFont, который
будет использоваться для инициализации диалогового окна. После
завершения выбора шрифта и закрытия окна статический метод getFonto
возвращает выбранный пользователем шрифт. Например:
bool bOk;
QFont fnt = QFontDialog::getFont(&bOk, this);
if (!bOk) {
// Была нажата кнопка Cancel
}
Диалоговое окно ввода
Диалоговое окно ввода данных можно использовать для предоставления
пользователю возможности ввода строки или числа. Это окно реализовано
в классе QinputDialog. Конечно, можно и самому написать нечто подобное,
разместив в диалоговом окне виджет класса QLineEdit, но зачем это делать,
когда есть готовый класс? Для более сложных диалоговых окон, имеющих
более одного поля ввода, этот класс уже не подойдет и придется реализовы-
вать свой собственный.
Для отображения диалогового окна ввода класс QinputDialog предоставляет
четыре статических метода:
О getTexto — для текста или пароля;
О get integer о —для ввода целых чисел;
О getDoubieo — для ввода чисел с плавающей точкой двойной точности;
Q getitemo — для выбора элемента из списка строк.
•» эти методы первым параметром передается строка заголовка диалогового
°кна. Второй параметр — поясняющий текст, который будет отображен ря-
Дом с виджетом однострочного текстового поля. Начиная с третьего,
параметры этих методов отличаются друг от друга.
*» Методы getintegero и getDoubieo третьим параметром передается ини-
^Иализационное значение (по умолчанию равное 0), четвертым —
минимально возможное (по умолчанию равно -2147483647), а пятым —
максимально возможное (по умолчанию равно 2147483647).
358 Часть V. Создание приложен.^
Третий параметр метода getTexto управляет режимом ввода паролей, а чет
вертым параметром передается текст (по умолчанию равен QString: muii).
В третьем параметре метода get item о передается список строк (QStringList\
четвертый — задает одну из переданных строк текущей (по умолчанию ра^
вен 0, т. е. первой строке списка), а пятый параметр управляет режимом
возможности редактирования строк (по умолчанию этот режим включен).
Три последних параметра у методов совпадают. Это указатель на
переменную булевого типа, информирующую о кнопке, нажатой при закрытии
окна — Ok или Cancel (Отмена), указатель на виджет предка и указатель на
строку имени виджета. Пример 30.1 отображает диалоговое окно ввода
текста (рис. 30.7). Изменив третий параметр с QLineEdit::Normal на
QLineEdit: : Password, МЫ тем Самым установим ВИДЖет ОДНОСТРОЧНОГО тек-
стового поля в режим ввода паролей.
I Пример 30.1. Диалоговое окно ввода текста
bool bOk;
QString str = QlnputDialog::getText("Input",
"Name:",
QLineEdit::Normal,
QString::null,
&bOk,
this
);
if (!bOk) {
// Была нажата кнопка Cancel
}
[Non-Commercial] - Input Q Q
Name.
Рис. 30.7. Диалоговое окно ввода текста
Диалоговое окно прогресса
Диалоговое окно прогресса реализуется классом QProgressDialog, унаслеД°'
ванным от класса QDiaiog. Это окно информирует пользователя о начале
продолжительной операции и дает ему возможность визуально оценить вре'
мя работы. Окно может содержать кнопку Cancel (Отмена) для прерывания
Глада 30. Диалоговые окна 359
начатой операции. При нажатии на нее высылается сигнал canceiedo,
который следует соединить со слотом, ответственным за прекращение
проводимой операции.
Диалоговое окно открывается в том случае, если длительность всей
операции будет составлять более трех секунд, гарантируя то, что диалоговое окно
прогресса не будет появляться на короткий промежуток времени и лишь
введет пользователя в заблуждение. Время можно изменить, передав в метод
setMinimumDuration () новое целочисленное значение в миллисекундах.
Задать количество шагов от начала до конца операции можно с помощью
метода setTotaisteps () или при создании в третьем параметре конструктора.
В процессе выполнения операции должен вызываться метод setProgesso.
Пример 30.2 создает диалоговое окно прогресса, показанное на рис. 30.8.
В качестве первого параметра, в конструктор класса QProgressDialog,
передается текст надписи, в примере он не имеет особого значения, т. к. после
создания диалогового окна в качестве надписи, с помошью метола setLabel,
устанавливается виджет класса QLabei о. Второй параметр представляет
собой надпись на кнопке Cancel (Отмена). В третьем параметре передается
количество шагов. Четвертый параметр является указателем на виджет
предка. Пятый параметр задает виджету имя. Шестой параметр управляет
модальным режимом, в нашем случае передается true, т. е. будет создано
модальное окно. В метод setMinimumDuration () передается значение 0, и это
говорит о том, что диалоговое окно будет показано без задержек. Вызов
метода setcaptiono устанавливает надпись в заголовке диалогового окна.
В цикле for вызывается метод setProgress (), обновляющий состояние
прогресса. В операторе if, вызовом метода wascanceled (), проверяется нажатие
кнопки Cancel (Отмена), и если она была нажата, то выполнение цикла
прерывается.
! Пример 30.2. Диалоговое окно прогресса
int п 40000;
QProgressDialog* pprd = new QProgressDialog("",
"&Cancel",
n,
this,
"Progress",
true
);
Pprd->setMinimumDuration(0);
Pprd->setLabel(new QLabei("Progress", this));
Pprd->setCaption("Please Wait");
360
Часть V. Создание приложений
for (int i = 0; i < n; ++i) {
pprd->setProgress(i) ;
if (pprd->wasCancelled()) {
break;
}
}
pprd->setProgress(n);
delete pprd;
o^iiwmii-fl'H
P gress I
■ ■■■■■■■■■III 84*
{■ Cancel |
Рис. 30.8. Диалоговое окно прогресса
По окончании операции можно вызвать слот reset (), для того чтобы
установить индикатор прогресса в начало. Можно также сделать так, чтобы
операция установки в начало проводилась автоматически — для этого в
метод setAutoReset () передается значение true. Для автоматического закрытия
окна по окончании операции вызывается метод setAutociose (), в который
передается true.
Диалоговое окно с закладками
Класс QTabDiaiog предоставляет специальное диалоговое окно, используемое
для окон перегруженных информацией и элементами. С его помощью
можно разбить сложное диалоговое окно на закладки, на которых будут
располагаться его элементы. Закладки можно добавлять в виджет QTabDiaiog при
помоши метола addTab (). В метод addTab () передаются два параметра —
указатель на виджет и строка для подписи закладки. Показ закладок
происходит при нажатии на соответствующий заголовок закладки. Одновременно
может быть показана только одна закладка.
Класс QTabDiaiog предоставляет возможность для размещения пяти кнопок:
Default (По умолчанию), Cancel (Отмена), OK, Help (Помошь) и Apply
(Применить). Эти кнопки устанавливаются с помощью соответствующих
методов: setDef aultButton (), setCancelButton (), setOKButton (), setHelpButtonO
или setAppiyButton(). Диалоговое окно всегда должно содержать
кнопки ОК и Cancel (Отмена). При нажатии на кнопку Apply (Применить)
Посылается сигнал applyButtonPressedO. Сигналы defaultButtonPressed()>
cancelButtonPressed () ИЛИ helpButtonPressed () высылаются При нажатИИ
кнопки Default (По умолчанию), Cancel (Отмена) и Help (Помощь). ПереД
Глава 30. Диалоговые окна
361
показом содержимого закладки высылается сигнал currentchanged (),
передающий в параметре указатель на объект класса QWidger. В примере 30.3
реализуется класс диалогового окна, имеющий три закладки, которые
содержат виджеты надписей (рис. 30.9). Сигнал appiyButtonPressed () соединен
со слотом объекта приложения quit (), а это значит, что нажатие на кнопку
ОК производит выход из программы.
[ пример 30.3. Диалоговое окно с закладками
class TabDialog : public QTabDialog {
Q_OBJECT
public:
TabDialog::TabDialog(QWidget* pwgt = 0, const char* pszName = 0)
: QTabDialog(pwgt, pszName)
{
addTab(new QLabel("Label 1", this), "One");
addTab(new QLabel("Label 2", this), "Two");
addTab(new QLabel("Label 3", this), "Three");
connect(this,
SIGNAL(appiyButtonPressed()),
qApp,
SLOT(quit())
);
}
};
[Non-Commercial] - TabDialog f] E3
One wo^ Three |
Label 2 I
OK |
Рис. 30.9. Диалог с закладками
Диалоговое окно мастера
Такое окно было придумано для сопровождения пользователя через
сложные операции. Оно представляет собой разновидность QTabDialog, но только
362 Часть V. Создание приложений
без закладок. Для навигации по страницам такого диалогового окна
используются две кнопки: Next (Дальше) и Back (Назад). Пользователь не сможет
отобразить интересующую его страницу, не пройдя все предшествующие
что гарантирует выполнение всех пунктов, содержащихся на страницах.
Для создания диалогового окна мастера необходимо унаследовать класс
QWizard и добавить страницы с помощью метода addPageO. В этот метод
нужно передать указатель на виджет и строку заголовка. О кнопках
перемещения по страницам беспокоиться не нужно, т. к. они уже
реализованы. Методы setBackEnabled(), setNextEnabled(), setFinishEnabled() и
setHeipEneabiedo позволяют делать кнопки Back (Назад), Next (Дальше),
Finish (Готово) и Help (Помощь) доступными или недоступными. В
примере 30.4 реализовано диалоговое окно мастера с тремя страницами,
содержащими виджеты надписей (рис. 30.10).
Пример 30.4. Диалоговое окно мастера
class Wizard : public QWizard {
QjOBJECT
public:
Wizard::Wizard(QWidget* pwgt = 0, const char* pszName = 0)
: QWizard(pwgt, pszName)
{
addPage(new QLabel("Label 1", this), "One");
addPage(new QLabel("Label 2", this), "Two");
addPage(new QLabel("Label 3", this), "Three");
>
>;
[Non-Commercial] - Wizard
Two
Label 2
Help | <Back | Г
Next > 1
ВЕЗ
Cancel | 1
Рис. 30.10. Страница диалогового окна мастера
Окна сообщений
Не стоит создавать собственные диалоговые окна для вывода сообщений н*
экран, ведь для этого можно воспользоваться уже готовыми окнами, пре"
пава 30. Диалоговые окна 363
доставляемыми классом QMessageBox. Окно сообщения — это самый простой
элемент пользовательского интерфейса, который отображает текстовое
сообщение и ожидает реакции со стороны пользователя. Его основная цель
состоит в информировании о совершении определенного события. Все
окна, предоставляемые классом QmessageBox, — модальные. Они могут содер-
зкать кнопки, (но не больше трех), заголовок и текст сообщения.
Класс QMessageBox предоставляет целую серию статических методов. С их
помощью можно создавать окна сообщений. Эти методы предоставляют
поддержку для трех уровней: информационного, предупреждающего и
критического, которые выбираются в зависимости от обстоятельств. Окна могут
содержать до трех кнопок.
Это очень удобно, т. к. не нужно писать дополнительного кода для
реализации вывода разного рода сообщения. Также можно применять такие окна
для отладочных целей, ведь с их помощью можно вывести необходимую
информацию и приостановить выполнение программы.
Пример 30.5 отображает окно сообщения, показанного на рис. 30.11. Это
окно создается динамически. В первом параметре конструктора передается
заголовок, во втором параметре — текст сообщения, в котором можно
использовать теги. Третий параметр задает предопределенное растровое
изображение, которое будет отображено слева (см. табл. 30.1). Три последних
параметра задают кнопки, которые будут размещены в окне диалога (см.
табл. 30.2). Обратите внимание на последнюю кнопку — всегда определяйте
кнопку Cancel (Отмена) как клавишу <Escape>, т. к. пользователи часто
нажимают именно ее для отмены действия. Вызов метода exec о объекта
класса QMessageBox приводит к остановке работы всей программы и ожиданию
нажатия на одну из кнопок окна сообщения. После нажатия метод exec о
возвращает идентификатор кнопки, который сохраняется в переменной п.
Оператор delete удаляет из памяти виджет окна сообщения. В операторе if
проверяется значение нажатой кнопки на равенство значению кнопки
Yes (Да).
|ПрИмер 30.5. Окно сообщения
QMessageBox* pmbx =
new QMessageBox("MessageBox",
"<b>A</b> <i>Simple</i> <u>Message</u>",
QMessageBox::Information,
QMessageBox::Yes,
QMessageBox::No,
QMessageBox::Cancel I QMessageBox::Escape
);
^t n = pmbx->exec () ;
delete pmbx;
364
Часть V. Создание приложены
if (n = QMessageBox::Yes)
// Нажата кнопка Yes
MessageBoK
J A 5imt>le Message
|: Yes _3| No
Cancel
X
1
Рис. 30.11. Окно сообщения
Параметры, передаваемые в конструктор, также можно установить с
помощью методов, определенных в классе QMessageBox. Текст сообщения
устанавливается МетОДОМ setText(), а теКСТ КНОПКИ — Методом setButtonText().
Первым параметром в метод setButtonText () необходимо передать один из
целочисленных идентификаторов, приведенных в табл. 30.2, а вторым —
текст. При помощи метода setcaptiono устанавливается надпись заголовка
окна. Метод seticono устанавливает растровое изображение, для этого
нужно передать в него одну из констант, указанных в табл. 30.1. Если
указанных в этой таблице растровых изображений недостаточно, то можно
создать и установить свое собственное, передав объект класса QPixmap в метод
setlconPixmap().
Таблица 30.1. Растровые изображения
Константа
NoIcon
Information
Warning
Значение
0
1
2
Константа
NoButton
Ok
Cancel
Yes
Значение
0
1
2
3
Вид
4>
& I
Таблица
Константа
No
Abort
Retry
Ignore
Константа
Critical
Question
Значение
3
4
Вид
О
V
30.2. Перечисление класса QMessageBox
Значение
4
5
6
7
Константа
YesAll
NoAll
Escape
Default
Значение
8
9
0x200
ОхЮО
Глава 30. Диалоговые окна 365
Окно информационного сообщения
рызов статического метода information () отображает на экране окно
информационного сообщения, показанное на рис. 30.12:
^lessageBox::information(0, "Information", "Operation Complete");
Как только окно будет закрыто, метод вернет значение нажатой кнопки (см.
табл. 30.2).
• \ Operation Complete
Рис. 30.12. Окно информационного сообщения
Окно предупреждающего сообщения
Для отображения окна предупреждающего сообщения (рис. 30.13)
вызывается статический метод warning о класса QMessageBox. В примере 30.6, в
метод warning о первым параметром передается нулевой указатель на виджет
предка. Второй параметр — это строка, задающая надпись заголовка окна.
Третьим параметром передается строка сообщения. Четвертый параметр
задает надпись первой кнопки, пятый параметр — надпись второй кнопки,
шестой — надпись третьей кнопки (в примере 30.6 этот параметр равен
Qstring: mull, а это значит, что на третьей позиции кнопка показана не
будет). Седьмой параметр делает кнопку с переданным индексом кнопкой по
умолчанию (в нашем случае этот параметр равен нулю, что соответствует
кнопке Yes (Да)). Восьмой параметр сопоставляет кнопку, соответствующую
переданному индексу, с клавишей <Escape>. В нашем случае этот параметр
равен единице и это соответствует кнопке No (Нет), т. е. нажатие клавиши
<Escape> будет расцениваться как нажатие на эту кнопку. Метод warning о
возвращает индекс нажатой кнопки, который, в нашем случае, проверяется
в условном операторе if, и равенство этой переменной нулю соответствует
нажатию кнопки Yes (Да) (пример 30.6).
[Пример 30.6. Вывод окна предупреждающего сообщения
int n =
QMessageBox::warning(О,
"Warning",
"The text in the file has changed,"
366
Часть V. Создание приложений
"\n Do you want to save the changes?",
"Yes",
"No",
QString::null,
0,
1
);
if <!n) {
// Saving the changes!
>
■1 Warning
&
r
E3|
The text n the file has changed
Do you want to save the changes?
Yes j
No J j
Рис. 30.13. Окно предупреждающего сообщения
Окно критического сообщения
Это окно следует показывать только в тех случаях, когда произошло что-то
очень серьезное (рис. 30.14). Для его отображения нужно вызвать
статический метод critical (), передав ему в первом параметре указатель на виджет
предка, во втором — строку надписи заголовка окна, а в третьем — само
сообщение. В четвертом, пятом и шестом параметрах передаются
целочисленные идентификаторы кнопок (пример 30.7).
!• . ,.-..■»• -. .■■■—, , .■•-.■■. •■ W; •• V " 1
{ Пример 30.7. Окно критического сообщения
int n =
QMessageBox::critical(0,
"Attention",
"This operation will make your "
"computer unusable, continue?",
QMessageBox::Yes I QMessageBox::Default,
QMessageBox::No,
QMessageBox::Cancel I QMessageBox::Escape
);
if (n = QMessageBox::Yes) {
// Do it!
>
Глава 30. Диалоговые окна
367
Attention ЕЗ
This operation wiH make your computer unusable contnue?
J£> | Cancel |
Рис. 30.14. Окно критического сообщения
Окно сообщения About
Такое окно отображается при вызове статического метода about о класса
QMessageBox для предоставления пользователю общей информации о
программе (рис. 30.15). Например, версия, некоторая контактная информация,
информация об авторских правах и т. д. В этот метод передаются три
параметра. Первый параметр — это указатель на виджет предка, второй —
заголовок окна, третий представляет собой само сообщение.
QMessageBox::about(this, "About", "My Program Ver. 1.0");
• \ My Program Ver 1.0
Рис. 30.15. Информация о программе
Окно сообщения About Qt
Для отображения окна About Qt (О Qt) класс QMessageBox предоставляет
статический метод aboutQto. В этот метод передаются два параметра. Первый
служит для установки предка, а второй — это заголовок окна. За
отображаемое сообщение отвечает сама библиотека Qt. Как видно из рис. 30.16,
в этом окне находится, вместе с информацией о версии, и другая
относящаяся к библиотеке информация. Вызов окна производится следующим
образом:
^fessageBox::aboutQt(this);
Окно сообщения об ошибке
°Кно сообщения об ошибке реализуется классом QErrorMessage, а не
классом QMessageBox, как все остальные окна сообщений. Оно представляет
Часть V. Создание приложен,^
About Qt
About Qt
This program uses the non jmmerdal edition of
Qt в a C++ '•oolkit for moltiplatform GUI & application developmerf-
Qt provides single-source portability across MS Windows, Mac О
Linux, and all major commercial Unix variants
Qt is also available for embedded devices.
Qt is a Trdltech product Seeh /www.trolltech.cott/cii
cor more information
J
Рис. 30.16. Окно сообщения About Qt
собой немодальное диалоговое окно. Для отображения окна сообщения об
ошибке создается объект этого класса и вызывается метод message (),
передав в него текст строки, которую требуется отобразить. Например:
(new QErrorMessage(this))->message("Write Error");
Окно, как видно из рис. 30.17, содержит флажок, который может
активироваться пользователем, для того чтобы не показывать данное сообщение
снова при повторении ошибки. Не следует злоупотреблять окном сообщения об
ошибке и нужно применять его только в тех случаях, когда это не повредит
пользователю или для критических ошибок, т. к. после перезапуска
программы состояние флажка будет сброшено.
[Non-Commercial] - Toobar Ц ЕЭ
Л>
[7 §how this message agam
Рис. 30.17. Окно сообщения об ошибке
Резюме
Диалоговые окна — это виджеты верхнего уровня, открывающиеся поверх
окна основного приложения. Для создания таких окон можно
воспользоваться любым классом виджетов, но удобнее использовать класс QDiaiog.
гпава 30. Диалоговые окна 369
Диалоговые окна являются ключевыми элементами для обмена информаци-
СЙ с пользователем и нуждаются в правильном подходе при их создании,
для этого необходимо учитывать ряд правил, помогающих создавать такие
окна, работу с которыми пользователь мог бы начать сразу, не затрачивая
усилий на изучение.
Диалоговые окна подразделяются на две группы — модальные и
немодальные. Модальные диалоги блокируют работу основной программы и
ожидают действий по изменению своих опций со стороны пользователя.
Разблокировка происходит в момент закрытия окна. Немодальные диалоговые
окна могут быть открыты, не препятствуя параллельной работе пользователя с
основной программой. Решение об использовании модального или
немодельного диалогового окна зависит от назначения. Например, в тех
ситуациях, когда нельзя продолжать работу приложения без решения
пользователя, нужно использовать модальный диалог. Стандартное диалоговое окно
выбора файлов является типичным примером использования модального
диалога. Диалоговое окно для поиска лучше делать немодальным, чтобы
обеспечить пользователю возможность выделения заданного текста, не
закрывая само окно поиска.
Применение стандартных диалоговых окон позволяет сэкономить время на
разработку, т. к. можно воспользоваться уже готовыми окнами для
открытия файлов, выбора цвета, настройки принтера и т. д.
Наиболее распространенные в приложениях диалоговые окна — это окна
сообщений. Они используются для оповещения пользователя о важном
событии или для того, чтобы задать ему вопрос, требующий ответа "Да" или
"Нет". Например, "Вы действительно хотите удалить этот файл?"
ГЛАВА 31
Предоставление помощи
Одно из двух: либо пациент жив —
либо пациент мертв.
Алексей Толстой. "Приключения Буратипо"
Главная задача помощи состоит в обеспечении пользователя всей
необходимой информацией о приложении и его элементах, что делает работу
пользователя более удобной. Различают три типа помощи:
□ воздушная подсказка;
□ подсказка "Что это";
□ система помощи.
Воздушная подсказка. Класс QToolTip
Работая с программами, вы, наверное, заметили, что при задержке
указателя мыши над кнопками панелей инструментов автоматически появляется
небольшое текстовое окошко, поясняющее назначение кнопки. Такое окно
называется воздушной подсказкой (tooltip) и, как правило, содержит только
одну строку текста. Можно использовать подсказки, имеющие больше
одной строки, но такие подсказки нужно делать как можно короче.
Присутствие воздушной подсказки в приложении является необязательным,
но лучше все-таки предоставлять ее, поскольку она помогает пользователям
быстрее сориентироваться среди множества кнопок. Несомненный плюс
этого типа подсказки в том, что, не останавливая своей работы с
приложением, пользователь параллельно получает информацию об элементах
приложения.
Чаще всего такие подсказки применяются для кнопок панелей
инструментов, но их можно с успехом использовать для любых виджетов. При этом не
Гпава 31. Предоставление помощи 371
следует забывать, что воздушные подсказки не нужно применять в случаях,
когда объяснение излишне. Например, будет нелогично, если для кнопки
Cancel (Отмена) повторить ее надпись в воздушной подсказке.
Подсказки реализуются с помощью класса QTooiTip (рис. 31.1). Добавить их
К виджету можно следующим образом:
QTooiTip::add(pcmd, "Button");
[Button И
Рис. 31.1. Воздушная подсказка
В методе add о допускается задавать даже отдельные участки виджета для
появления воздушной подсказки. Например:
QTooiTip:: add (pcmd,
QRect(0, 0, pcmd->width(), pcmd->height() /2),
"Button"
);
Воздушные подсказки можно удалять из виджетов при помощи метода
remove о. В этот метод передается указатель на виджет. Например:
QTooiTip:: remove (pcmd);
Подсказка "Что это". Класс QWhatsThis
Иногда требуется отобразить больше информации о виджете, чем способна
предоставить воздушная подсказка. Подсказка What's this (Что это) является
промежуточной по объему между воздушной подсказкой и системой помощи
(online help). По своей функциональности эта подсказка очень похожа на
воздушную подсказку, только с той разницей, что она не появляется
автоматически при задержке указателя мыши. Для ее отображения нужно войти
в специальный режим, нажав на кнопку ?, находящуюся на панели
инструментов или в области заголовка окна. Также можно воспользоваться
стандартной комбинацией клавиш <Shift>+<Fl>.
Пример, показанный на рис. 31.2, отображает виджет надписи. Нажатие на
кнопку ? изменит указатель мыши на указатель, показанный в левой части
Рисунка. Нажатие этим указателем мыши на элемент надписи выведет
подсказку.
372 Часть V. Создание приложен^
J Please press the ?-ButtorJ
r"TffffS?lfflg i I x jl
rji _ ^ ; Please press the ^-Button
This is the Label
Class Q Label
Рис. 31.2. Подсказка What's this
Листинг 31.1. Файл main.cpp
#include <qapplication.h>
#include <qlabel.h>
#include <qwhatsthis.h>
//
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QLabel 1Ы ("Please press the ?-Button",
0,
0,
Qt::WStyle_Customize I Qt::WStyle_Title
I Qt::WStyle_SysMenu | Qt::WStyle_ContextHelp
QWhatsThis:: add (&1Ы,
"<I>This is the Label</I>"
"<BRXB>Class QLabeK/B>"
);
app. setMainWidget (&1Ы) ;
lbl.show();
return app.exec();
}
В листинге 31.1 создается виджет надписи. В третьем параметре его
конструктора передаются атрибуты внешнего вида окна, а присоединенный
логической операцией | (ИЛИ) флаг Qt: :Wstyie_ContextHeip произведет
отображение в области заголовка окна кнопки ? (см. гл. 4). После создания видже"
та надписи, с помощью метода add о, к нему добавляется контекстная
помощь What's this (Что это). Как видно из листинга 31.1, текст
подсказки, устанавливающийся методом add о, может быть в формате RichTex
(см. приложение 1).
Глава 31. Предоставление помощи
373
Если понадобится создать кнопку ? на панели инструментов, то необходимо
вызвать статический метод QWhatsThis: rwhatsThisButtonO и передать в него
указатель на виджет предка. Например:
QToolBar* ptoolbar = new QToolBar(this);
QToolButton* ptoolbutton = QWhatsThis::whatsThisButton(ptoolbar);
Чтобы убрать подсказку из виджета, нужно вызвать статический метод
QWhatsThis::remove о, передав ему указатель на сам виджет.
Для запуска режима What's this (Что это) можно воспользоваться и
собственной кнопкой, которую необходимо соединить со слотом, в котором
будет вызываться статический метод QWhatsThis::enterWhatsThisMode().
Система помощи (Online Help)
Большие приложения нуждаются в объемной системе помощи, подробно
описывающей все функциональные возможности программы. Самый
простой вариант — это предоставление пользователю специального навигатора,
который будет открываться при нажатии соответствующего пункта меню
Help (Помощь) или при нажатии на клавишу <F1>. Текст помощи может
находиться в формате HTML, в котором помимо текстовой предоставляется
возможность использовать и графическую информацию, ссылки на другие
документы, а также воспользоваться форматированием шрифтов. Большой
плюс состоит в том, что для создания файлов в формате HTML существует
огромное количество редакторов. При острой необходимости и наличии
нужных навыков можно написать или подправить такой файл без
специализированного редактора в простом текстовом, "от руки".
Класс QTextBrowser располагает всем необходимым для реализации
навигатора, который способен показывать текст в формате HTML. Пример,
показанный на рис. 31.3, демонстрирует применение этого класса.
[[Листинг 31.2. Файл main.cpp
♦include <qapplication.h>
♦include <qdir.h>
♦include "HelpBrowser.h"
//
int main (int argc, char** argv)
{
QApplication app(argc, argv);
HelpBrowser helpBrowser(QDir("doc/index.htm").absPath());
374
Часть V. Создание приложений
app.setMainWidget(&helpBrowser);
heipBrowser.resize(360, 260) ;
heipBrowser.show() ;
return app.exec();
}
■1 [Non-Commercial] - HeipBrowser
| Home |
\\o i cS/a 'ca
File menu commands
1 The File menu offers the following commands
J New Creates a new document
J Open Opens an existing document
1 Close Closes an opened document
1 Save As Saves an opened document to a specified file
1 Exit Exits Application
BDOl
»
'ot
name J
Рис. 31.3. Навигатор помощи
В основной программе, показанной в листинге 31.2, создается виджет
навигатора помощи — heipBrowser. Обратите внимание на параметр,
передаваемый в его конструкторе. Вызов метода absPathO преобразовывает
относительный путь doc/index.h, передаваемый в конструктор класса QDir, в
абсолютный.
Листинг 31.3. Файл HelpBrowser.h
#ifndef _HelpBrowser_h_
#define _HelpBrowser_h_
#include <qvbox.h>
# include <qtextbrowser.h>
#include <qpushbutton.h>
class HeipBrowser : public QVBox {
Q_OBJECT
public:
HeipBrowser(const QString& strPath, QWidget* pwgt = 0)
: QVBox(pwgt)
глава 31. Предоставление помощи
375
QHBox* phbx = new QHBox(this);
QPushButton* pcmdBack = new QPushButton ("«", phbx);
QPushButton* pcmdHome = new QPushButton("Home", phbx);
QPushButton* pcmdForward = new QPushButton("»", phbx);
QTextBrowser* ptxtBrowser = new QTextBrowser(this);
connect(pcmdBack, SIGNAL(clicked()),
ptxtBrowser, SLOT(backward())
);
connect(pcmdHome, SIGNAL(clicked()),
ptxtBrowser, SLOT(home())
);
connect(pcmdForward, SIGNAL (clicked()),
ptxtBrowser, SLOT(forward())
);
connect(ptxtBrowser, SIGNAL(backwardAvailable(bool)),
pcmdBack, SLOT(setEnabled(bool))
);
connect(ptxtBrowser, SIGNAL (forwardAvailable(bool)),
pcmdForward, SLOT(setEnabled(bool))
);
ptxtBrowser->setSource(strPath);
}
};
#endif //_HelpBrowser_h_
В конструкторе класса HeipBrowser (листинг 31.3) создаются виджеты
кнопок ДЛЯ дополнительной навигации — указатели pcmdBack, pcmdHome,
pcmdForward, которые размещаются в горизонтальном лейауте (указатель
phbx). После этого с помощью метода connect о сигналы clicked о кнопок
подсоединяются к соответствующим слотам виджета класса QTextBrowser
(backward n, home() и forward о). Это необходимо для перемещения по
документам помощи. Последние два метода connect о соединяют сигналы
backwardAvailable(bool) И forwardAvailable(bool) ВИДЖета класса
QTextBrowser со слотами setEnabled (bool) кнопок нажатия (указатели
PcmdBack и pcmdForward). Это позволяет делать кнопки, в зависимости от
ситуации, активными или неактивными. Метод setsourceo считывает
документ, переданный в конструкторе первым параметром.
Резюме
Использование помощи — верный шаг для повышения интуитивности
работы пользователя с приложением. Помощь необходима для снабжения
376
Часть V. Создание прило.
пользователя интересующей его информацией и подразделяется на тр|
тегории: воздушная подсказка, подсказка "Что это" и система пол
(Online Help).
Воздушная подсказка представляет собой небольшую по объему, част»
раниченную одной строкой, информацию. Она появляется в специа
отведенной области при задерживании указателя мыши на виджете.
Подсказка "Что это" очень похожа на воздушную подсказку, но с той
ницей, что она предоставляет большую по объему информацию и м
быть отображена только после запуска специального режима.
Система помощи снабжает пользователя исчерпывающей информаци
функциях приложения. Чтобы воспользоваться ею, совсем не обязате
запускать само приложение, т. к. информация находится в формате Н'
и ее можно прочитать при помощи любого доступного в системе брау
Для реализации своего собственного навигатора можно воспользовг
классом QTextBrowser.
ГЛАВА 32
Панель инструментов
и строка состояния
Все следует делать настолько простым,
насколько это возможно, но не проще.
Альберт Эйнштейн
Панель инструментов
Основная цель панели инструментов (Tool Bar) — предоставить
пользователю быстрый доступ к командам программы одним нажатием кнопки мыши.
Это делает панель инструментов более удобной по сравнению с меню, в
котором нужно сделать, по меньшей мере, два нажатия. Еще одно
достоинство в том, что панель инструментов всегда видима, и не нужно тратить время
на поиски необходимой команды в меню или вспоминать комбинацию
клавиш ускорителя.
Панель инструментов представляет собой область, в которой расположены
кнопки, дублирующие часто используемые команды меню. Для панелей
инструментов библиотека Qt предоставляет класс QTooiBar, который определен
в заголовочном файле qtoolbar.h. Процесс создания панели инструментов
несложен, и со временем вы сможете формировать панели, имеющие
лояльно сложную структуру и удовлетворяющую любым требованиям
современных приложений.
v точки зрения внутренней реализации, панель инструментов — это не что
иНое, как дочернее окно (dock window), в котором обычно располагается ряд
кнопок-инструментов, за которые отвечает класс QTooiButton, и каждая из
Которых снабжена растровым изображением (пиктограммой). Наряду с
Сопками, в панели инструментов могут быть размешены и любые другие
вНджеты.
378
Часть V. Создание приложение
Кнопка-инструмент
Реализация кнопок-инструментов находится в классе QTooiButton. Класс
унаследован от класса QButton (см. гл. 6), а его определение находится в
заголовочном файле qtoolbutton.h. Этот тип кнопок отличается от остальных
тем, что он содержит вместо надписи небольшое растровое изображение
символизирующее определенную команду меню. Ввиду отсутствия текста на
такой кнопке их рекомендуется снабжать дополнительными небольшими
текстовыми пояснениями (см. гл. 31). Пояснения должны всплывать в тот
момент, когда пользователь задержит указатель мыши в области кнопки на
небольшой промежуток времени.
Программа, окно которой показано на рис. 32.1, демонстрирует применение
панелей инструментов и кнопок-инструментов. Панели инструментов могут
перемещаться с одного места на другое и отрываться от основного окна,
превращаясь при этом в окна со своими собственными заголовками. В
нашем случае был ИСПОЛЬЗОВан класс QMainWindow.
Примечание )
Класс QMainWindow представляет собой класс главного окна приложения,
который содержит в себе строку меню, несколько зон для размещения панелей
инструментов и строку состояния (см. гл. 33).
Рис. 32.1. Панели инструментов
| Листинг 32.1. Файл MainWindow.h
#ifndef _MainWindow_h_
#define MainWindow h
#include <qmainwindow.h>
#include <qtoolbar.h>
#include <qtoolbutton.h>
#include <qmessagebox.h>
fj&ea 32. Панель инструментов и строка состояния
linclude "imgl.xpm"
linclude "img2.xpm"
linclude "img3.xpm"
linclude "img4.xpm"
и ==========================================_==_===
class MainWindow : public -QMainWindow {
qjOBJECT
public:
MainWindow(QWidget* pwgt = 0, const char* pszName = 0)
: QMainWindow(pwgt, pszName)
{
}
addDockWindow(createToolBar(), DockTop);
addDockWindow(createToolBar(), DockBottom);
addDockWindow(createToolBar(), DockLeft);
addDockWindow(createToolBar(), DockRight);
QToolBar* createToolBar()
{
QToolBar* ptb = new QToolBar(this, "Linker ToolBar");
ptb->setLabel("Not implemented");
createToolButton(ptb, imgl_xpm);
createToolButton(ptb, img2_xpm);
ptb->addSeparator();
createToolButton(ptb, img3_xpm);
createToolButton(ptb, img4_xpm);
return ptb;
}
QToolButton* createToolButton(QToolBar* ptb, const char** pimg_xpm)
{
QPixmap pix = QPixmap(pimg_xpm);
return
new QToolButton(pix, "Empty", "", this, SLOT(slotNoImpl()), ptb);
}
Public slots:
void slotNoImpK)
{
QMessageBox::information(0, "Message", "Not implemented");
}
};
*endif // MainWindow_h_
380
Часть V. Создание приложений
Класс Mainwindow, приведенный в листинге 32.1, унаследован от класса
QMainwindow. С помощью метода addDockwindow () в конструкторе класса к
главному окну добавляются четыре панели инструментов, создаваемые
методом createTooiBar () класса Mainwindow. Второй параметр метода
addDockwindows () задает расположение панели инструментов в главном окне
приложения. В самом методе createTooiBar () производится создание
четырех кнопок-инструментов вызовами метода createTooiButton () класса
Mainwindow. В него передаются указатели на виджет предка и на массив
растровых данных, хранящихся в файле формата ХРМ (см. гл. 18). Из
растровых данных создается объект класса QPixmap, который передается в первом
параметре конструктора класса QTooiButton. Вторым параметром в
конструктор передается поясняющая надпись. Третий параметр задает заголовок
группы. При создании каждая из кнопок соединяется со слотом
siotNoimpio, который отображает диалоговое окно при нажатии. Наконец,
последним параметром в конструктор передается указатель на виджет
предка. Разделитель между кнопками вставляется с помощью метода
addSeparator().
Строка состояния
Этот виджет располагается в нижней части главного окна и отображает, как
правило, текстовые сообщения для предоставления информации о
состоянии приложения или выдачи справки о командах меню или кнопках
панелей инструментов. Строку состояния реализует класс QstatusBar,
определенный в заголовочном файле qstatusbar.h. Различают следующие типы
сообщений:
П промежуточный— вызывается методом message о. Для очистки строки
состояния следует вызвать метод clear о. Если во втором параметре
метода message о задан временной интервал, то строка состояния будет
очищаться автоматически по его истечении. Примером промежуточного
отображения является вывод поясняющего текста для команд меню;
П нормальный — используется для отображения часто изменяющейся
информации (например, для отображения позиции указателя мыши). Для
отображения такой информации лучше всего поместить в строку
состояния отдельный виджет. Например, в том случае, если приложение
должно отображать прогресс работы какой-либо операции, то лучше
разместить в строке состояния индикатор прогресса выполнения. Чтобы
разместить виджет в строке состояния, нужно передать его указатель в
метод addwidgeto. Виджеты можно удалять из строки состояния с
помощью метода removeWidget ();
П постоянный — отображает информацию, необходимую для работы с при-
ложением. Например, для отображения состояния клавиатуры в стро*е
Глава 32. Панель инструментов и строка состояния
381
состояния могут быть внесены виджеты надписей, отображающие
состояние клавиш <Caps Lock>, <Num Lock>, <Insert> и т. д.
Пример, показанный на рис. 32.2, помещает в строку состояния два видже-
та надписи для отображения актуальных координат указателя мыши.
■ [Non-Commercial] - Status.- НЮ ЕЗ
Y=87JX=139
Рис. 32.2. Строка состояния, отображающая актуальную позицию указателя мыши
ГЛйстинг 32.2. Файл MainWindow.h
#ifndef _MainWindow_h_
#define _MainWindow_h_
#include <qmainwindow.h>
#include <qstatusbar.h>
#include <qlabel.h>
// _=_=_=_____=_____=__==_=__===================_
class MainWindow : public QMainWindow {
QjOBJECT
Private:
QLabel* m_jDlblX;
QLabel* m_plblY;
Protected:
virtual void mouseMoveEvent(QMouseEvent* pe)
{
mj?lblX->setText("X=" + QString().setNum(pe->x()));
m_j)lblY->setText("Y=" + QString().setNum (pe->y()));
}
Public:
MainWindow(QWidget* pwgt = 0, const char* pszName = 0)
: QMainWindow(pwgt, pszName)
382
Часть V. Создание приложен^
{
setMouseTracking(true);
mjplblX = new QLabel(this);
mjolblY = new QLabel(this);
statusBar()->addWidget(m_plblY);
statusBar()->addWidget(m_plblX);
}
};
#endif //_MainWindow_h_
Класс Mainwindow наследуется от класса QMainWindow. Этот класс содержит
переменные, хранящие указатели на виджеты надписей mjoibix и т_р1Ыу,
которые создаются в конструкторе и помещаются в строку состояния
методом addwidgeto. Для отображения актуальных координат указателя мыши
текст виджетам надписей присваивается в методе обработки события
mouseMoveEvent (). Значения координат указателя мыши возвращаются
методами х() и у о объекта события (указатель ре).
Резюме
Панели инструментов используются для предоставления быстрого доступа
к часто используемым командам меню. Этот виджет представляет собой
дочернее окно, в котором помещены кнопки, позволяющие осуществлять
запуск команд одним нажатием левой кнопки мыши.
Виджет строки состояния располагается в нижней части главного окна
программы и используется для отображения информационных сообщений трех
типов: промежуточного, нормального и постоянного.
ГЛАВА 33
Создание приложений
Приступай к решению любой задачи, имея
в виду решить ее наилучшим образом.
Одна из трех заповеден IBM
Класс главного окна QMainWindow
QMainWindow— это очень важный класс, который реализует главное окно
(рис. 33.1), содержащее в себе типовые виджеты, необходимые большинству
приложений, такие как — меню (QMenuBar) (см. гл. 29), секции для панелей
инструментов (QTooiBar) (см. гл. 32), рабочую область, строки состояния
(QStatusBar) (см. гл. 32). В этом классе внешний вид уже подготовлен и его
виджеты не нуждаются в дополнительном размещении, т. к. они уже
находятся в нужных местах.
Изображенное на рис. 33.1 окно приложения имеет рамку, область
заголовка для отображения имени и три кнопки, управляющие окном. Кроме
этого, окно приложения имеет меню, которое располагается под заголовком
окна. Панель инструментов расположена под меню. Ниже рабочей области
находится строка состояния.
Указатель На ВИДЖеТ Меню МОЖНО ПОЛУЧИТЬ ВЫЗОВОМ метода QMainWindow: :
«fcnuBarO и установить в нем нужные всплывающие меню (пример 33.1).
[Пример 33.1. Меню
QPopupMenu* pmnuFile = new QPopupMenu(this);
BunuFile-^insetltemC'&Save") ;
"fenuBarO^insertltemC'&File", pmnuFile);
384
Часть V. Создание приложен^
Текстовый заголовок
Область заголовка
Иконка приложения
Панель инструментов
Document - Wordkad
tie Edit View Insert Format Help
Кнопка "Свернуть"
Кнопка "Развернуть"
Кнопка "Закрыть"
Кнопка прокрутки
Рабочая область
Полоса прокрутки
Строка состояния J ^^ ^е$5 F ж
Рис. 33.1. Анатомия главного окна приложения
Кнопка изменения
размеров окна
Как правило, устанавливаются следующие всплывающие меню:
П File (Файл) — содержит основные операции для работы с файлами.
Например, New (Создать), Open (Открыть), Save (Сохранить), Print (Печать)
и Quit (Выход);
П Edit (Правка) — содержит команды общего редактирования: Cut
(Вырезать), Сору (Копировать), Paste (Вставить), Undo (Отменить), Redo
(Повторить), Find (Найти), Replace (Заменить) и Delete (Очистить);
П View (Вид) — содержит команды, изменяющие представление данных
окна. Например, команда Zoom (Масштаб) масштабирует отображение
документа. В это меню можно включать и те команды, которые
управляют отображением элементов интерфейса приложения, например,
панели инструментов, строки состояния;
П Help (Справка) — необходима для предоставления помощи
пользователю, при освоении приложения. А также, обычно, включает в себя
информацию об авторских правах приложения. Например, при выборе
команды About (О программе) появится окно, отображающее имя
приложения, версию, информацию об авторских правах и др.
Методы topDockO, bottomDock(), LeftDockO и rightDockO класса
QMainWindow возвращают указатели на виджеты класса QDockArea. Эти видже-
ты представляют собой контейнеры для классов виджетов, унаследованных
ОТ Класса QDockWindow. ОДИН ИЗ КОТОРЫХ — Класс QToolBar.
Чтобы получить указатель на рабочую область, следует вызвать метод
QMainWindow: : centra lWidget О, который вернет указатель на QWidget. Для Ус~
гпава 33. Создание приложений
385
сновки виджета рабочей области потребуется вызвать метод QMainWindows::
setcentraiwidget () и передать в него указатель на этот виджет.
Метод QMainwindow: :statusBar() возвращает указатель на виджет строки
состояния. Кнопка для изменения размеров окна, расположенная в нижнем
правом углу строки состояния (см. рис. 33.1), является всего лишь
подсказкой для пользователя, сообщающей о том, что размеры главного окна
могут быть изменены. Этот виджет реализован в классе QSizeGrip. Получить
указатель на него из класса главного окна (QMainwindow) невозможно, т. к.
0н находится под контролем виджета строки состояния.
Предшествующее окно
Многие приложения при запуске показывают так называемое
предшествующее окно (Splash Screen). Это окно отображается на время, необходимое для
инициализации приложения, и информирует о ходе запуска приложения.
Зачастую такое окно используют для маскировки длительного процесса
старта программы.
В библиотеке Qt это окно реализовано в классе QSpiashScreen. Объект этого
класса создается в функции main () до вызова метода exec () объекта
приложения. Программа, приведенная в листинге 33.1, отображает перед
запуском предшествующее окно, производящее отсчет прогресса инициализации
в процентах (рис. 33.2).
My Application 2004
Loading modules: 74*»
Version 1.0
Рис. 33.2. Предшествующее окно
15
Листинг 33.1. Файл main.cpp
♦include <qapplication.h>
♦include <qlabel.h>
♦include <qsplashscreen.h>
386
Часть V. Создание приложены
п
void loadModules(QSplashScreen* psplash)
{
for (int i = 0; i < 100; ++i) {
psplash->message("Loading modules: " + QStringO.setNum(i) + n%-
Qt::AlignHCenter I Qt::AlignBottom,
Qt::black
);
}
}
//
int main (int argc, char** argv)
{
QApplication app(argc, argv);
QSplashScreen splash(QPixmap("splash.png"));
splash.show();
QLabel lbl ("<HlXCENTER>My Application<BR>"
"Is Ready!</CENTER></Hl>",
0
);
app.setMainWidget(&lbl) ;
loadModules(&splash);
splash.finish(&lbl);
lbl.resize(250, 250);
lbl.show();
return app.exec();
}
В листинге 33.1 объект предшествующего окна создается после объекта
приложения. В конструктор предшествующего окна передается растровое
изображение, которое будет отображаться после вызова метода show (). Вид-
жет QLabel представляет, в данном примере, само приложение, которое
должно быть запущено. Функция loadModules () является эмуляцией загрУ3"
ки модулей программы, в нее передается адрес объекта предшёствуюшег
окна, чтобы функция была в состоянии отображать информацию прогресс
загрузки. Отображение информации производится при помощи метод
message о, в который первым параметром передается текст, вторым— Ра
положение текста (см. табл. 5.1), а третьим — цвет текста (см. табл. 1°- ''
Вызов метода finish о производит закрытие предшествующего окна. ЕсД
Глава 33.
его не закрывать, то оно останется видимым до тех пор, пока пользователь
не шелкнет на нем мышью.
Класс действия QAction
Действие QAction — это очень мощный концепт, ускоряющий разработку
приложений. Раньше вам нужно было создавать команды меню, соединять
их со слотами, затем создавать панели инструментов и соединять их с теми
же слотами и т. д. А это приводит к дублированию программного кода и
вызывает проблемы при синхронизации элементов пользовательского
интерфейса. Представьте себе, что вам нужно сделать одну из команд меню
недоступной, например команду Open (Открыть). Вам нужно сделать ее
недоступной в меню, а потом проделать то же самое и с кнопкой панели
инструментов этой команды.
О&ьекты класса QAction предоставляют решение этой проблемы. Объекты
имеет смысл использовать тогда, когда одна и та же команда применяется в
нескольких местах. Это значительно упрощает программирование.
Например, команда меню File | New (Файл | Создать) дублируется кнопкой на
панели инструментов и для них можно создать один объект действия, и, тем
самым, если вдруг команду потребуется сделать недоступной, мы будем
иметь дело только с одним объектом.
Действие QAction объединяет следующие элементы интерфейса пользователя:
О текст для всплывающего меню;
О текст для всплывающей подсказки;
О текст подсказки "Что это";
О "горячие" клавиши;
О ассоциированные пиктограммы;
О строку состояния.
Для установки каждого из вышеперечисленных элементов в объекте QAction
существует свой метод (пример 33.2):
[Пример 33.2. Установка объекта действия
QAction* pactSave = new QAction(this, "file save action");
PactSave->setText("&Save");
PactSave->setAccel(CTRL + Key_S);
PactSave->setToolTip("Save Document");
PactSave->setStatusTip("Save the file to disk");
PactSave->setWhatsThis("Save the file to disk");
PactSave->setIconSet(QlconSet(QPixmap(save xpm)));
388 Часть V. Создание приложений
connect(pactSave, SIGNAL(activated()), this, SLOT(slotSave());
QPopupMenu* pmnuFile = new QPopupMenu(this);
pactSave->addTo(pmnuFile) ;
Метод QAction::addTo() позволяет внести объект действия в нужный вид-
жет. В нашем примере это виджет всплывающего меню (QPopupMenu).
Установку некоторых из элементов, показанных в примере 33.2, можно
произвести и в конструкторе QAction, как это показано в примере 33.3.
Пример 33.3. Установка элементов интерфейса пользователя
в конструкторе QAction
QAction* pactSave = new QAction("Save",
QPixmap(save_xpm),
"&Save",
CTRL + Key_S,
this,
"file save action"
);
Создание SDI- и MDI-приложений
Существует два типа приложений, базирующихся на документах. Первый
тип — это SDI (Single Document Interface, однодокументный интерфейс),
второй — MD1 (Multiple Document Interface, многодокументный
интерфейс). В SDI-приложениях рабочая область одновременно является окном
приложения, а это значит, что невозможно открыть в одном и том же
приложении сразу два документа. MDI-приложение предоставляет рабочую
область (класса QWorkSpace), способную размещать в себе окна виджетов, что
дает возможность одновременной работы с большим количеством
документов.
SDI-приложение
Типичным примером SDI-приложения является программа Windows
Notepad (Блокнот). Пример, приведенный в листинге 33.2, реализует
упрошенный вариант этой программы, представляющей собой текстовый редактор-
Результат показан на рис. 33.3.
| Листинг 33.2. Файл DocWindow.h
#ifndef _DocWindow_h_
#define DocWindow h
Глава 33. Создание приложений
389
#include <qtextedit.h>
,1 ========================
class Docwindow: puhlic QTextEdit {
q_OBJECT
private:
QString m_strFileName;
public:
Docwindow( QWidget* pwgt = 0,
const char* pszName = 0,
WFlags flags = 0
);
public slots:
void slotLoad ();
void slotSave ();
void slotSaveAs();
};
#endi f //_DocWindow_h_
■ [Noncommercial]-SDIProgram HDE3
Ble Help
J Это простое SDI приложение]
Рис. 33.3. SDI-приложение
Класс Docwindow, унаследованный от класса QTextEdit, представляет собой
окно для редактирования (листинг 33.2). В его определении содержится
переменная mstrFiieName, в которой хранится имя изменяемого файла. Слоты
slotLoad(), slotSave () И slotSaveAs() необходимы ДЛЯ проведения ОПера-
Ций чтения и записи файлов.
[^Тйстинг 33.3. Конструктор класса DodWindow. Файл DocWindow.cpp
Docwindow::DocWindow( QWidget * pwgt /*=0 */,
const char* pszName/*=0*/;
WFlags flags /*=0*/
)
390 Часть V. Создание приложен^
: QTextEdit(pwgt, pszName)
{
setWFlags(flags);
}
В конструктор, приведенный в листинге 33.3, передаются три параметра:
указатель на виджет предка, имя и флаги виджета. Ввиду того, что
конструктор класса QTextEdit не предоставляет возможности передачи флагов
виджета, этот параметр устанавливается при помощи метода setWFiagso.
Флаги необходимы для управления окном виджета (см. гл. 4).
\ Листинг 33.4. Метод slotLoadO. Файл DocWindow.cpp
void DocWindow::slotLoad()
{
QString str = QFileDialog::getOpenFileName();
if (str.isEmptyO) {
return;
}
QFile file(str);
if (file.open(IO_ReadOnly)) {
QTextStream stream(&file);
setText(stream.read());
file.close();
m_strFileName = str;
setCaption(str);
}
}
Метод slotLoadO, приведенный в листинге 33.4, отображает диалоговое окно
ОТКРЫТИЯ файла ВЫЗОВОМ статического метода QFileDialog:: getOpenFileName О,
с помощью которого пользователь выбирает файл для чтения. В том случае,
если пользователь отменит выбор, нажав на кнопку Cancel (Отмена), этот
метод вернет пустую строку. В нашем примере это проверяется с помошью
метода QString: :isEmpty(). ЕСЛИ метод getOpenFileName () ВОЗВратИТ
непустую строку, то будет создан объект класса QFile, который будет проиниииа-
лизирован этой строкой. Передача ioReadOniy в метод QFile:: open о
говорит о том, что файл открывается только для чтения. В случае успешного
открытия файла создается объект потока stream, который в нашем примере
используется для чтения текста из файла. Чтение осуществляется методом
QTextStream:: read (), возвращающим объект строкового типа QString. Текст
устанавливается в виджете методом setText о.
Глава 33. Создание приложений
391
Г/1истинг 33.5. Метод slotSaveAs (). Файл DocWindow.cpp
void DocWindow::slotSaveAs()
i
QString str = QFileDialog::getSaveFileName(m_strFileName);
if (Istr.isEmptyO) {
m_strFileName = str;
slotSaveO ;
}
Метод slotSaveAs () отображает диалоговое окно записи файла, с помощью
статического метода QFileDialog: :getSaveFileName() (ЛИСТИНГ 33.5). Если
это окно не было отменено пользователем (т. е. метод вернул непустую
строку), то в переменную mstrFiieName записывается имя файла, указанное
пользователем в диалоговом окне, и вызывается метод siotsaveo
(листинг 33.6).
[Листинг 33.6. Метод slotsave(). Файл DocWindow.cpp
void DocWindow:: slot Save ()
{
if (m_strFileName.isEmpty()) {
slotSaveAs();
return;
}
QFile file(m_strFileName);
if (file.open(IO_WriteOnly)) {
QTextStream(&file) «textO;
file.close ();
setCaption(m_strFileName);
}
Запись в файл представляет собой более серьезный процесс, чем
считывание, т. к. связана с рядом обстоятельств, которые могут сделать ее
невозможной. Например, на диске не хватит места или он будет недоступен для
записи и т. д. Для записи в файл нужно создать объект класса QFile и
передать в него строку с именем файла. Затем надо вызвать метод open (),
передав в него значение iowriteoniy (флаг, говорящий о том, что будет
выполняться запись в файл), и если файл не существует, то он будет создан, если
существует — будет открыт для записи. В случае успешного открытия
создается промежуточный объект потока и, при помощи оператора «, в него пе-
392 Часть V. Создание приложен,,;.
редается содержимое текста виджета, вызовом метода text о. После этого
файл закрывается методом QFiie::ciose() и в заголовке виджета будет
отображено имя файла. Такой заголовок имеет смысл в том случае, если вид,
жет имеет собственное окно.
| Листинг 33.7. Файл SDIProgram.h
#ifndef _SDIProgram_h_
#define _SDIProgram_h_
#include <qapplication.h>
#include <qmainwindow.h>
# include <qmes sagebox.h>
#include <qmenubar.h>
#include <qstatusbar.h>
#include <qpopupmenu.h>
#include "DocWindow.h"
#include "SDIProgram.h"
// =====„====_ == —_—=
class SDIProgram : public QMainWindow {
Q_OBJECT
public:
SDIProgram(QWidget* pwgt = 0, const char* pszName = 0)
: QMainWindow(pwgt, pszName)
{
QPopupMenu* pmnuFile = new QPopupMenu(this);
QPopupMenu* pmnuHelp = new QPopupMenu(this);
DocWindow* pdoc = new DocWindow(this, "editor");
pmnuFile->insertItem("&Open...", pdoc, SLOT(slotLoad()),
CTRL + Key_0);
pmnuFile->insertItem("&Save"/ pdoc, SLOT(slotSave()),
CTRL + Key_S);
pmnuFile->insertItem("S&ave As...", pdoc, SLOT(slotSaveAs ()));
pmnuFile->insertSeparator();
pmnuFile->insertItem("&Quit", qApp, SLOT (quit()), CTRL + Key_Q);
pmnuHelp->insertItem("&About", this, SLOT(slotAbout()), Key_Fl);
menuBar()->insertItem("&File", pmnuFile);
menuBar()->insertltem("&Help", pmnuHelp);
setCentralWidget(pdoc);
statusBar()->message("Ready", 2000);
Гпава 33. Создание приложений 393
public slots:
void slotAboutO
{
QMessageBox::about(this, "Application", "SDI Example");
}
};
fendif //_SDIProgram_h_
Класс SDiProgram унаследован от класса Qmainwindow (листинг 33.7). В его
конструкторе создаются два виджета — всплывающие меню File (Файл) и
Help (Помощь). Команда меню Open... (Открыть...) соединяется в методе
insertitemo со слотом siotLoadO, команда Save (Сохранить) — со слотом
siotsaveo, а команда SaveAs... (Сохранить как...) — со слотом siotSaveAs ().
Слоты реализованы в классе Docwindow. Команда About (О программе)
соединяется со слотом siotAbouto, предоставляемым классом SDiProgram.
Вызов метода menuBarO возвращает указатель на виджет меню верхнего уровня,
а вызов методов insertitemo из этого виджета добавляет созданные
всплывающие меню File (Файл) и Help (Помощь). Метод message о, вызываемый
из виджета строки состояния, отображает надпись Ready на время,
установленное во втором параметре (в нашем примере оно соответствует двум
секундам).
MDI-приложение
MDI-приложение позволяет пользователю работать с несколькими
открытыми документами. По своей сути оно очень напоминает обычный рабочий
стол, только в виртуальном исполнении. Пользователь может разложить в
его области несколько окон документов, или свернуть их. Окна документов
могут перекрываться друг другом, а также быть развернуты во всю рабочую
область.
Рабочая область, внутри которой размещаются окна документов,
реализуется классом QWorkspace (рис. 33.4). Виджет этого класса производит
"закулисное" управление динамически создаваемыми окнами документов. При
помощи слотов title о и cascade о, определенных в этом классе,
производится упорядочивание окон. Метод QWorkspace: :windowList о возвращает
список всех содержащихся в нем виджетов.
Программа, окно которой показано на рис. 33.5, реализует основные
функции, присущие MDI-приложению. В качестве класса окна документа
применяется класс Docwindow, использованный при реализации SDI-при-
Ложения.
394
Часть V. Создание приложен
QMainWindow
Главное окно приложения
О [Non-Comme rcu» I
File Windows Help
QWorkArea ,
Рабочая область i
Окно документа Окно документа
Рис. 33.4. Структура MDI-приложения
[Non-Commercial] - MDI Program
Fjle Wmu ws Help
, D x
Документ!
Документе
щвзашгш
Рис. 33.5. MDI-приложение
Гпава 33. Создание приложений
Ййстинг 33.8. Файл MDIProgram.h
lifndef _MDIProgram_h_
l^efine _MDIProgram_h_
finclude <qmainwindow.h>
class QToolBar;
class QPopupMenu;
class QWorkspace;
class DocWindow;
/7 ============„==_====_=
class MDIProgram: public QMainWindow {
Q_OBJECT
private:
QWorkspace* mjpws;
QPopupMenu* mjpmnuWindows;
public:
MDIProgram(QWidget* pwgt = 0, c<
private slots:
DocWindow*
void
void
void
void
void
void
slotNewDoc
slotLoad
slotSave
slotSaveAs
slotAbout
slotWindows
slotWindowsActivated
const char* pszName = 0);
int) ;
#endif //_MDIProgram_h_
Определение класса MDiProgram, приведенное в листинге 33.8, содержит
переменные, хранящие указатели на виджет рабочей области (m_pws) и на
всплывающее меню Windows (Окна) (m_pmnuwindows). В этом классе
определены слоты, предназначенные для работы с окнами документов
(slotWindows () и slotWindowsActivated о), записи и считывания информации
из файлов (siotLoado, siotsaveo и siotSaveAso), создания новых документов
(slotNewDoc о), а также предоставления информации о самом приложении.
Листинг 33.9. Конструктор MDiProgram. Файл MDiProgram.срр
*®IProgram::MDiProgram(QWidget* pwgt/*=0*/, const char* pszName/*=0*/)
: QMainWindow(pwgt, pszName)
396
Часть V. Создание приложены
{
QAction* pactNew = new QAction("New File",
QPixmap(filenew),
"&New",
CTRL+Key_N,
this
);
pactNew->setStatusTip("Create a new file");
connect(pactNew, SIGNAL(activated()), SLOT(slotNewDoc()));
QAction* pactOpen = new QAction("Open File",
QPixmap(fileopen),
"&Open...",
CTRL+Key_0,
this
);
pactOpen->setStatusTip("Open an existing file");
connect(pactOpen, SIGNAL(activated()), SLOT(slotLoad()));
QAction* pactSave = new QAction("Save File",
QPixmap(filesave),
"&Save",
CTRL+Key_S,
this
);
pactSave->setStatusTip(tr("Save the file to disk"));
connect(pactSave, SIGNAL(activated()), SLOT(slotSave()));
QToolBar* ptbFile = new QToolBar(this);
addToolBar(ptbFile, "File Operations", DockTop, true);
pactNew->addTo(ptbFile);
pactOpen->addTo(ptbFile);
pactSavei->addTo (ptbFile) ;
QPopupMenu* pmnuFile = new QPopupMenu(this);
menuBar()->insertItem("&File", pmnuFile);
pactNew->addTo(pmnuFile);
pactOpen->addTo(pmnuFile);
pactSave->addTo(pmnuFile);
pmnuFile->insertItem("Save &As...", this, SLOT(slotSaveAs()));
pmnuFile->insertSeparator();
pmnuFile->insertItem("&Quit",
qApp,
SLOT (closeAHWindows ()),
Глава 33. Создание приложений
397
CTRL+Key_Q
);
m_pmnuWindows = new QPopupMenu(this) ;
ra_prnnuWindows->setCheckable(true);
connect(mjpmnuWindows, SIGNAL(aboutToShow()), SLOT(slotWindows()));
menuBar()->insertItem("&Windows", mjpmnuWindows);
menuBar()->insertSeparator();
QPopupMenu* pmnuHelp = new QPopupMenu(this);
menuBar()->insertItem("&Help"/ pmnuHelp);
pmnuHelp->insertItem( "&About", this, SLOT(slotAbout()), Key_Fl);
m_pws = new QWorkspace(this);
m_pws->setScrollBarsEnabled(true);
setCentralWidget(mjpws);
statusBar()->message("Ready", 3000);
}
В конструкторе класса MDiProgram создаются три объекта действий для
команд создания, открытия и записи документов — указатели pactNew, pactopen
и pactsave соответственно (листинг 33.9). Сигнал объектов activated о
СОеДИНЯеТСЯ СО СЛОТаМИ Класса MDI Program. Вызовами Метода QAction: :
addToo объекты действий добавляются к панели инструментов и в меню.
Команда меню File | Quit (Файл | Выход) соединяется со слотом объекта
приложения cioseAiiwindowsO, который производит закрытие всех окон
приложения.
Чтобы выделить окно текущего документа, необходимо, чтобы меню
Windows (Окна) поддерживало флажки. Для этого вызывается метод
setcheckabieo, в который передается значение true.
Для создания рабочей области MDI-приложения необходим виджет
Workspace. Чтобы содержимое рабочей области можно было прокручивать,
вызывается метод setscroiiBarEnabiedO и в него передается true.
Установка рабочей области в главном окне виджета производится методом
setCentralWidget().
[Листинг 33.10. Метод slotNewDoc (). Файл MDI Prog ram. срр
DocWindow* MDiProgram::slotNewDoc()
{
DocWindow* pdoc = new DocWindow(mjpws, 0, WDestructiveClose);
pdoc->setCaption("Unnamed Document");
pdoc->set!con(QPixmap(filenew));
398
Часть V. Создание приложений
pdoc->show();
return pdoc;
}
Слот siotNewDocO, приведенный в листинге 33.10, создает новое окно
документа. Для этого создается виджет класса Docwindow, в конструктор которого
третьим параметром передается значение WDestructiveCiose, говорящее
виджету о том, что он должен быть уничтожен при закрытии его окна.
Метод setcaptiono устанавливает заголовок окна. Небольшое растровое
изображение в области заголовка устанавливается методом seticono. Вызов
метода show о отображает созданное окно документа в рабочей области
MDI-приложения.
! Листинг 33.11. Метод slotLoad (). Файл MDIProgram.cpp
void MDIProgram::slotLoad()
{
slotNewDoc()->slotLoad();
>
Внутри слота slotLoad о вызывается метод slotNewDoc (), который создает
новый виджет документа и возвращает его указатель, после чего
производится делегирование операции считывания далее, к виджету созданного
документа (листинг 33.11).
! Листинг 33.12. Метод slotSave (). Файл MDIProgram.cpp
void MDIProgram::slotSave()
{
Docwindow* pdoc = (DocWindow*)m_pws->activeWindow();
if (pdoc) {
pdoc->slotSave();
}
}
Слот siotsave () получает указатель на текущее окно документа при помоши
виджета рабочей области и производит делегирование операции сохранения
(листинг 33.12).
j Листинг 33.13. Метод slotSaveAs (). Файл MDIProgram.cpp
void MDIProgram::slotSaveAs()
{
DocWindow* pdoc = (DocWindow*)m_j3ws->activeWindow();
Глава 33. Создание приложений 399
if (pdoc) {
pdoc->slotSaveAs();
}
Действия слота siotsaveAsO аналогичны действиям siotsaveo
листинга 33.12, только с делегированием метода siotsaveAsO (листинг 33.13).
[.Листинг33.14. Метод slotAbout(). Файл MDIProgram.cpp
void MDIProgram::slotAbout()
I
QMessageBox::about(this, "Application", "MDI Example");
}
Слот siotAbouto отображает окно сообщения, информирующее
пользователя о самой программе (листинг 33.14).
[Листинг 33.15. Метод slotwindows(). Файл MDIProgram.cpp
void MDIProgram::slotwindows()
{
m_pmnuWindows->clear() ;
bool bEnabled = m_j3WS->windowList().isEmpty();
int nld;
nld = m_pmnuWindows->insertItem("&Cascade", m_pws, SLOT(cascade 0));
ra_pmnuWindows->setItemEnabled(nld, !bEnabled);
nld = m_pmnuWindows->insertItem("&Tile"/ mjpvis, SLOT(tile()));
m_pmnuWindows->setItemEnabled(nld, !bEnabled);
m_pmnuWindows->insertSeparator();
QWidgetList listDoc = m_pws->windowList();
for (unsigned int i = 0; i < listDoc.count(); ++i) {
nld = ra_pmnuWindows->insertItem(listDoc.at(i)->caption(),
this,
SLOT(slotWindowsActivated(int))
);
m_pmnuWindows->setItemParameter(nId, i);
V. Создание приложен,^
bool bChecked = m_pws->activeWindow() = listDoc.at(i);
ra_pmnuWindows->setItemChecked(nId/ bChecked);
}
}
Еще одним отличием MDI- от SDI-приложения является наличие
всплывающего меню Windows (Окна), назначение которого — управление окнами
документов, находящимися в рабочей области. Для отображения актуальной
информации, перед показом, это меню необходимо очистить методом
clear о (листинг 33.15). Переменная bEnabied принимает значение true,
если рабочая область содержит хоть одно окно документа, в противном
случае — false. В соответствии со значением этой переменной команды меню
Cascade (Каскад) и Tile (Мозаика) становятся доступными или
недоступными. В цикле for производится размещение названий окон документов
в меню и соединение со слотом siotwindowsActivated(int). Метод
setitemParametero устанавливает значение, высылаемое сигналом при
активации окна. Переменная булевого типа bChecked получает значение true
в том случае, если найдено активное окно. Вызов метода setitemCheckedo
отмечает его флажком.
; Листинг 33.16. Метод slotwindowsActivated(). Файл MDIProgram.cpp
void MDIProgram::slotWindowsActivated(int id)
{
QWidget* w = m_pws->windowList().at(id);
w->setFocus() ;
}
В слоте siotwindwsActivate(int) устанавливается фокус окна, значение
идентификационного номера которого совпадает со значением, переданным
в этот слот (листинг 33.16).
Резюме
Существует два типа приложений — SDI (Single Document Interface, одно-
документный интерфейс) и MDI (Multiple Document Interface,
многодокументный интерфейс). Главное отличие MDI- от SDI-приложения состоит в
том, что SDI-приложение содержит только одно окно документа, а MDI-при-
ложение способно содержать несколько таких окон, что дает пользователю
возможность параллельно работать сразу над несколькими документами.
Класс QMainwindow предоставляет уже готовый лейаут, размещающий в себе
виджеты, необходимые большинству приложений. В центре размещена
рабочая область, которая может содержать только один виджет. При помоши
Глава 33. Создание приложений 401
класса QWorkspace в этой области можно размещать сразу несколько видже-
^в, что позволяет реализовывать MDI-приложения. Виджеты находятся в
рабочей области в виде отдельных окон, которые можно перемещать,
изменять размеры, сворачивать, разворачивать, упорядочивать их и т. д.
Класс действия QAction предоставляет возможность централизации всех
элементов интерфейса, связанных с конкретной командой в одном объекте.
£го позволяет значительно сократить время разработки программы, а также
уменьшить объем исходного кода.
ЧАСТЬ VI
Особые возможности Qt
Глава 34. Процессы и потоки
Глава 35. Дата, время и таймер
Глава 36. Библиотека контейнеров
Глава 37. Работа с файлами, директориями
и потоками ввода/вывода
Глава 38. Программирование поддержки сети
Глава 39. Работа с XML
Глава 40. Программирование баз данных
Глава 41. Динамические библиотеки и система расширений
Глава 42. Совместное использование Qt
с платформозависимыми API
Глава 43. Qt Designer. Быстрая разработка прототипов
ГЛАВА 34
Процессы и потоки
...каждый крупный результат является
конечным продуктом длинной
последовательности маленьких действий.
Кристофер Алексапдер
Процессы
Когда пользователь или программа производит запуск другой программы,
операционная система всегда создает новый процесс. Процесс — это
экземпляр программы, загруженной в память компьютера для выполнения.
По своей сути, процессы — это независимые друг от друга программы,
обладающие своими собственными данными. Процесс можно
охарактеризовать как общность кода, данных и ресурсов, необходимых для его работы.
Под ресурсами подразумеваются объекты, запрашиваемые и используемые
процессами в период их работы. Любая прикладная программа, запущенная
на вашем компьютере, представляет собой не что иное, как процесс.
Создание процесса может оказаться полезным для использования
функциональных возможностей программ, не имеющих графического интерфейса и
работающих с командной строкой. Очень полезное свойство — довольно
простой запуск других программ из текущей программы.
Процессы можно создавать с помощью класса QProcess, который определен
в заголовочном файле qprocess.h. Считывая из стандартного канала вывода
и ошибок, можно узнать об окончании работы запущенной программы. Для
создания процесса нужно указать программу, которая должна быть
выполнена. Аргументы можно передать и позже, вызовами методов set Arguments О
и addArgument (). Запуск процесса выполняется методом start о. После
этого данные в стандартный поток ввода можно записывать при помощи
метода writeTostdin (). Вызов метода ciosestdin () производит закрытие потока.
Глава 34. Процессы и потоки
405
для чтения данных, выводимых запущенным процессом, следует
воспользоваться методами readstdout () или readstderr (). Эти методы считывают
данные в объекты класса QByteArray. Для считывания только одной
СТРОКИ информации МОЖНО Прибегнуть К методам readLinestdout () ИЛИ
readLineStderr (), которые возвращают строковые объекты класса QString.
Приложение, изображенное на рис. 34.1, иллюстрирует применение
некоторых методов класса QProcess. В текстовом поле Command (Команда) может
быть введена любая команда, соответствующая операционной системе. Если
запушенная команда или программа осуществляют вывод на консоль, то
отображение будет производиться в виджете многострочного текстового поля.
[Non-Commercial]-С'
НПО
Volume in drive С has no label.
Volume Serial Number is 3839-1BFC
Directory of C:\Examples\Process
№092004 11:18
1809.2004 11:18
18.09.2004 13.56
IB 09.2004 1355
18092004 13.55
18.09.2004 13:55
1809.2004 1356
5Ffe(s)
<DIR>
<DIR>
366 main cpp
117 Process pro
1 811 SheDh
3.222 Makefile
45 056 Process exe
50 572 bytes
2Dir(s) 4.030 431232 bytes free
£ommand:[dir
Рис. 34.1. Простейшая командная оболочка
I
Листинг 34.1. Файл shell.h
class Shell : public QVBox {
QJDBJECT
private:
QProcess* m_process;
QLineEdit* m_txtCommand;
QTextEdit* m_txtDisplay;
public:
//
Shell(QWidget* pwgt = 0, const char* pszName = 0)
: QVBox(pwgt, pszName)
{
m_process = new QProcess(this);
m_txtDisplay = new QTextEdit(this);
406 ; Часть VI. Особые возможности сц
QHBox* phbx = new QHBox(this);
QLabel* plbl = new QLabel ("SCoinmand:", phbx);
m_txtCoramand = new QLineEdit("dir", phbx);
plbl->setBuddy (m_txtCommand);
QPushButton* pcmd = new QPushButtonC'&Enter", phbx);
connect (mjprocess,
SIGNAL(readyReadStdout()),
SLOT(slotDataOnStdout())
);
connect(n^txtCommand,
SIGNAL (returnPressed()),
SLOT (slotRetumPressed ())
);
connect (pcmd, SIGNAL (clicked ()), SLOT (slotRetumPressed ()));
}
public slots:
//
void slotDataOnStdout()
{
while (m_process->canReadLineStdout()) {
m_txtDisplay->append(m_process->readLineStdout() + "\n");
}
}
//
void slotRetumPressed ()
{
m_process->clearArguments();
#ifdef Q_WS_WIN
m_process->addArgument("cmd.exe /C " + m_txtCommand->text());
#else
QStringList list = QStringList::split(" ", m_txtCommand->text());
QStringList::iterator it;
for (it = list.begin(); it != list.endO; ++it) {
m_process->addArgumenc(*it);
}
#endif if (!m_process->start()) {
m_txtDisplay->append("Error!");
}
}
Глвва 34. Процессы и потоки
407
В конструкторе класса shell (листинг 34.1) производится создание объекта
Класса QProgress. Его СИГНал readyReadStdOut () соединяется СО СЛОТОМ
slotDataOnStdout (), В котором вызывается метод readLineStdout () ДЛЯ СЧИ-
тьшания строк. Эту операцию нужно проводить в цикле, завершение
которого произойдет в тот момент, когда метод canReadLinestdout () вернет
значение false, сигнализирующее об окончании чтения.
Слот slotReturnPressed () соединен с сигналом кнопки clicked о (указатель
pond) и с сигналом однострочного текстового поля retumPressed ()
(указатель mtxtcorrmand). Сначала производится очистка аргументов объекта
процесса (указатель m_process) с помощью метода clearArguments (). Некоторые
команды ОС Windows, например dir, не являются отдельными
программами, поэтому они должны быть исполнены посредством командного
интерпретатора cmd (для семейства Windows NT) или command (для Windows 9дс).
Следовательно, запуск процесса на платформе Windows требует отдельного
решения (листинг 34.1). Во всех остальных случаях необходимо "разбить"
передаваемую строку на аргументы и передать их в цикле, в метод
addArgument (). Для запуска процесса вызывается метод start о.
Возвращение этим методом значения true говорит об успешном выполнении
операции запуска.
Потоки
Для реализации потоков Qt предоставляет класс QThread, определение
которого содержится в заголовочном файле qthread.h. Но давайте сначала
разберемся, что же собой представляют потоки. Поток — это независимая
задача, которая выполняется внутри процесса и разделяет вместе с ним общее
адресное пространство, код и глобальные данные. Процесс, сам по себе, не
является исполнительной частью программы, поэтому для исполнения
программного кода он должен иметь хотя бы один поток (далее — основной
поток). Конечно, можно создавать и более одного потока. Вновь созданные
потоки начинают сразу же выполняться параллельно с главным потоком,
при этом их количество может изменяться — одни создаются, другие
завершаются. Завершение основного потока приводит к завершению
процесса, независимо от того, существуют другие потоки или нет. Создание
нескольких потоков в процессе получило название многопоточность. Много-
поточность требуется для выполнения действий в фоновом режиме,
параллельно ходу действий основной программы и позволяет разбить
выполнение задач на параллельные потоки, которые могут быть абсолютно
независимы друг от друга. А если приложение выполняется на компьютере
с несколькими процессорами, то разделение на потоки может значительно
Ускорить работу всей программы, т. к. каждый из процессоров получит для
выполнения отдельный поток. К тому же, в последнее время используется
408
Часть VI. Особые возможности Qt
технология представления, в операционной системе, одного физического
процессора как нескольких логических (например, HyperThreading от Intel).
Приложения, имеющие один поток, могут выполнять за один промежуток
времени только одну определенную операцию, а все остальные операции
ждут окончания. Например, такие операции, как вывод на печать,
считывание большого файла, ожидание ответа на посланный запрос или
выполнение сложных математических вычислений могут привести к блокировке или
зависанию всей программы. При помощи многопоточности можно решить
такую проблему, запустив подобные операции в отдельно созданных
потоках. Тем самым, при зависании одного из потоков функционирование
основной программы не будет нарушено.
Среди разработчиков встречается разное отношение к многопоточному
программированию. Некоторые стараются сделать все свои программы
многопоточными, а других многопоточность пугает. Важно учитывать и то
обстоятельство, что использование потоков существенно усложняет процесс
отладки, а также написание самого приложения. Но при правильном и
обоснованном применении многопоточности можно существенно улучшить
скорость приложения. Неправильное ее применение, наоборот, может
привести к снижению скорости приложения. Поэтому использование потоков
должно быть обоснованным. А это значит, что если вы не можете
сформулировать причину, по которой следует сделать приложение многопоточным,
то лучше отказаться от использования многопоточности. Если вы
сомневаетесь, то на начальных стадиях постарайтесь создавать два прототипа для
тестирования — один многопоточный, а другой нет. Тем самым, прежде чем
тратить время на разработку программы, можно определить, является ли
многопоточность решением поставленной задачи или нет. В том случае,
если достижения того же результата можно добиться без использования
многопоточности, то стоит отдать предпочтение последнему варианту.
Приоритеты
У каждого потока есть приоритет, указывающий процессору, как должно
протекать выполнение потока по отношению к другим потокам.
Приоритеты разделяются по группам:
□ в первую входят четыре наиболее часто применяемых приоритета. Их
значимость распределяется по возрастанию— idiePriority, LowestPriority*
LowPriority, NormaiPriority. Они подходят для решения задач, которым
процессор требуется только время от времени, например, для фоновой
печати или для каких-нибудь несрочных действий;
П ВО вторую группу ВХОДЯТ два приоритета — HighPriority, HighestPriority-
Пользуйтесь такими приоритетами с большой осторожностью. Обычно
это потоки большую часть времени ожидают какие-либо события;
Глава 34. Процессы и потоки
409
0 В Третью ВХОДЯТ два приоритета — TimeCriticalPriority, InheritPriority.
Потоки с этими приоритетами нужно создавать в случаях крайней
необходимости. Эти приоритеты нужны для программ, напрямую
общающихся с аппаратурой или выполняющих операции, которые ни в коем случае
не должны прерваться.
На рис. 34.2 отображено окно программы, запускающей три потока с
разными приоритетами. В данном случае, первым из них будет выполнен
поток С приоритетом NormalPriority, ВТОРЫМ — LowPriority И Третьим —
IdlePriority.
■[Non-C
NoimaPno
LowPrioiity:
UePln
- .]-Thre..
■DO
Illllllllllllllllllllllllllllllll
lllllllllllllllllllllllllll
1
83*
r-
Рис. 34.2. Приоритеты потоков
| Листинг 34.2. Файл Threads.pro
TEMPLATE = app
CONFIG += qt warn_on release thread
SOURCES = main.cpp
TARGET = Threads
Для использования многопоточности необходимо добавить в проектный
файл в опцию config значение thread (листинг 34.2).
^Листинг 34.3. Класс MyThread. Файл main.cpp
class MyThread : public QThread {
QProgressBar* m_pprb;
Public:
MyThread(QProgressBar* pprb) : QThread()
, m_pprb(pprb)
{
}
void run()
{
for(int i = 0; i <= 100000000; ++i) {
410
Часть VI. Особые возможности п*
m_pprb->setProgress(i / 1000000);
}
}
};
Класс MyThread представляет собой класс для управления потоком. Он
должен быть унаследован от класса QThread. В нем нужно перезаписать метод
run о, в который должен быть помешен код, выполняющийся в потоке
(листинг 34.3). В конструктор класса MyThread передается указатель на вид-
жет индикатора прогресса, чтобы поток мог отображать проделываемую им
работу.
{ Листинг 34.4. Функция main (). Файл main.cpp
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QGrid grid(2, Qt:-.Horizontal);
(new QLabel("NormalPriority:", Sgrid));
QProgressBar* pprbl = new QProgressBar(Sgrid);
(new QLabel("LowPriority:", Sgrid));
QProgressBar* pprb2 = new QProgressBar(Sgrid);
(new QLabel("IdlePriority:", Sgrid));
QProgressBar* рргЬЗ = new QProgressBar(Sgrid);
grid.resize(400, 80);
grid.show();
MyThread threadl(pprbl);
MyThread thread2(pprb2);
MyThread thread3(рргЬЗ);
threadl.start(QThread::NormalPriority);
thread2.start(QThread::LowPriority);
thread3.start(QThread::IdlePriority);
app.setMainWidget(Sgrid);
return app.exec();
)
В листинге 34.4 после создания виджетов надписей и прогресса
производится создание трех потоков — threadl, thread2 и thread3. Запуск потоков про"
изводится методом start (), в который передается приоритет потока. ОбъеК
Гласа 34. Процессы и потоки
411
потока будет работать до тех пор, пока метод run о не завершится или не
будет вызван метод stopo. Выполнение потока можно приостановить на
определенное время при помощи метода sleep ().
Синхронизация
Если ваши потоки не будут использовать совместные данные, то можно
Прервать чтение этой главы, вышеизложенного материала о потоках будет
вполне достаточно. Основные сложности возникают тогда, когда потокам
нужно совместно использовать одни и те же данные. Так как несколько
потоков могут одновременно обращаться и записывать данные в одну область,
то это может привести к нежелательным последствиям. Представьте себе
такую ситуацию: один поток занимается вычислениями, используя значения
какой-нибудь глобальной переменной, а в это время другой поток вдруг
изменяет значение этой переменной, поток, занимающийся вычислениями,
продолжает свою работу, ничего не подозревая и используя уже измененное
значение. Для предотвращения подобных ситуаций требуется механизм,
позволяющий блокировать данные, когда один из потоков намеривается их
изменить. Этот механизм получил название синхронизация.
Синхронизация позволяет задавать критические секции (critical sections), в
которые имеет доступ только один из потоков. Это гарантирует то, что
временные данные ресурса, контролируемые критической секцией, будут
невидимы другими потоками и они не изменят их. И только после того, как
поток выполнит всю необходимую работу, он освобождает ресурс, и, затем,
доступ к этому ресурсу может получить любой другой поток. Например,
если один поток записывает информацию в файл, то все другие не смогут
использовать этот файл до тех пор, пока поток не освободит его.
Мьютексы
Мьютексы (mutex) обеспечивают взаимоисключающий доступ к ресурсам,
гарантирующий, что критическая секция будет обрабатываться только
одним потоком. Поток, владеющий мьютексом, обладает эксклюзивным
правом на использование ресурса, защищенного мьютексом, и другой поток не
Может завладеть мьютексом, который уже занят. Этот механизм реализован
в классе QMutex. Метод lock () класса QMutex производит блокировку ресурса.
Для обратной операции существует метод uniocko, который открывает
закрытый ресурс для других потоков. Класс QMutex также содержит метод
tryLocko, который немедленно возвращает значение false, если мьютекс
УЖе захвачен другим потоком, не ожидая его освобождения. В случае
успешного захвата мьютекса этот метод вернет true.
Библиотека Qt предоставляет класс QWaitcondition, обеспечивающий
возможность координации потоков. Если поток намеревается дождаться раз-
412 Часть VI. Особые возможности nt
блокировки ресурса, то он вызывает метод QWaitCondition:: wait О и тем
самым входит в режим ожидания. Выводится он из этого режима
в том случае, если поток, который заблокировал ресурс, вызовет метод
QWaitCondition: :wakeOne() ИЛИ QWaitCondition: :wakeAll(). Разница ЭТИХ Двух
методов в том, что первый выводит из состояния ожидания только один
поток, а второй — все сразу. Также, для потока можно установить время
в течение которого он может ожидать разблокировки данных. Для этого
нужно передать в метод wait о целочисленное значение, обозначающее
временной интервал в миллисекундах.
Пример 34.1 демонстрирует совместное применение классов QMutex и
QWaitCondition. Определены два глобальных объекта от этих классов. В
методе run о производится попытка блокировки ресурса методом lock о
объекта мьютекса и в случае неудачи поток ждет освобождения ресурса, для
чего в метод wait () класса QWaitCondition передается адрес объекта
мьютекса. По окончании действий с ресурсом производится его разблокировка и
вызывается метод wakeAiio класса QWaitCondition, для вывода потоков из
состояния ожидания.
| Пример 34.1. Класс MyThread
QMutex g_mutex;
QWaitCondition g_writeIsDone;
class MyThread : public QThread {
public:
void run()
{
g_mutex.lock() ;
g_writeIsDone.wait(&g_mutex);
// Производятся действия с ресурсом
g_mutex.unlock() ;
g_writeIsDone.wakeAll();
}
};
( Примечание )
Qt предоставляет глобальный объект мьютекса, который может быть
заблокирован вызовом QApplication::lock() и разблокирован QApplication::
unlock().
Работая с многопоточностью, нужно помнить о возможном возникновений
тупиковых ситуаций, когда потоки могут заблокировать друг друга. ПреД'
Глава 34. Процессы и потоки
413
ставьте себе такую ситуацию, когда поток заблокировал ресурс А, а после
работы над ним собирается работать с ресурсом В. Другой же поток
заблокировал ресурс В и по окончании намеревается работать с ресурсом А.
Л вот один из потоков, закончив работу, обнаружил, что нужный ему
ресурс заблокирован другим потоком. Он переходит в режим ожидания,
надеясь дождаться разблокировки ресурса, но то же самое делает и другой
поток. В итоге — оба ждут друг друга. Если ни один из этих потоков не
освободит занятый им ресурс, то оба "зависнут" и не смогут продолжать свою
работу дальше.
1
Рис. 34.3. Взаимная блокировка (Deadlock)
Это явление получило название взаимной блокировки (deadlock) (рис. 34.3).
Существует множество решений такой проблемы. Например, можно так
организовать работу потока, чтобы в том случае, если поток не сможет
получить доступ к необходимому ресурсу, он просто произведет освобождение
всех занятых им ресурсов, а позже повторит попытку захватить
необходимые ресурсы.
Семафоры
Семафоры являются обобщением мьютексов. Как и мьютексы, они служат
Для защиты критических секций, чтобы доступ к ним одновременно мог
Иметь определенное число потоков. Все другие потоки обязаны ждать.
Например, предположим, что программа поддерживает пять ресурсов одного и
того же типа, одновременный доступ к которым может быть предоставлен
Только пяти потокам. Как только все пять ресурсов будут заблокированы,
следующий поток, запрашивающий ресурс данного типа, будет приостанов-
414 Часть VI. Особые возможности rv.
лен до освобождения одного из них. Принцип действия семафоров очень
прост. Они начинают действовать с установленного значения счетчика
Каждый раз, когда поток получает право на владение ресурсом, значение
этого счетчика уменьшается на единицу. И наоборот, когда поток уступает
право владения этим ресурсом, счетчик увеличивается на единицу. При
значении счетчика равном нулю семафор становится недоступным.
Механизм семафоров реализует класс QSemaphore. Счетчик устанавливается в
конструкторе при создании объекта этого класса.
Резюме
Процессы представляют собой программы, независимые друг от друга и
загруженные для исполнения. Каждый процесс должен создавать хотя бы
один поток, именуемый основным потоком. Основной поток процесса
создается в момент запуска программы. Однако сам процесс может создавать
несколько потоков одновременно.
Многопоточность позволяет разделять задачи и независимо работать над
каждой из них для того, чтобы максимально эффективно задействовать
процессор. Написание многопоточных приложений требует больше времени
и усложняет процесс отладки, поэтому многопоточность нужно применять
тогда, когда это действительно необходимо. Многопоточность удобно
использовать для того, чтобы блокировка или зависание одного из методов не
стали причиной нарушения функционирования основной программы.
Для использования многопоточности нужно унаследовать класс от QThread
и перезаписать метод runo, в который должен быть помещен код для
исполнения в потоке. Чтобы запустить поток, нужно вызвать метод start о.
Для координации между потоками библиотека Qt содержит классы QMutex,
QWaitContion И QSemaphore.
При работе с потоками нередко требуется синхронизировать
функционирование потоков. Причиной синхронизации является необходимость
обеспечения доступа нескольких потоков к одним и тем же данным, которые
могут использоваться одновременно только одним потоком.
ГЛАВА 35
Дата, время и таймер
Что часто повторяется,
то вскоре становится доказанным.
О. Минье
Дата и время
Приложениям часто требуется информация о дате и времени. Например,
для календаря, для выдачи отчетной информации или для реализации часов.
Qt предоставляет для работы с датой и временем три класса: QDate, QTime и
QDateTime, определенные в одном заголовочном файле qdatetime.h.
Класс даты QDate
Класс QDate представляет собой структуру данных для хранения дат и
проведения с ними разного рола операций. В конструкторе класса QDate
передаются три целочисленных параметра. В первом передается год, во
втором — месяц, а в третьем — день. Например, следующий вызов создаст
объект, который будет содержать дату "25 октября 2004":
QDate date(2004, 10, 25);
Эти значения можно установить и после создания объекта даты с помошью
метода setYMDO. Например:
QDate date;
date.setYMD(2004, 10, 25);
Для получения значений года, месяца и дня, установленных в объекте даты,
следует вызвать следующие методы:
О year о — возвращает целый год в диапазоне от 1752 до 8000;
О month о — возвращает целое значение месяца в диапазоне от 1 до 12
(с января по декабрь);
О day о — возвращает день месяца в диапазоне от 1 до 31.
416 Часть VI. Особые возможности Си
С помощью метода daysinMonth () можно узнать количество дней в месяце, а
с помощью метода daysinYearo — количество дней в году.
Для получения дня недели следует вызвать метод dayofweeko. Для
получения дня года служит метод dayofYear (). Можно также узнать номер недели
ДЛЯ чего нужно Вызвать метод weekNumber ().
Метод tostrmg () позволяет получить текстовое представление даты, а в
качестве параметра можно передать одно из указанных в табл. 35.1 значений.
Таблица 35.1. Перечисления DateFormat класса Qt
Константа
TextDate
ISODate
LocalDate
Значение
0x0000
0x0001
0x0002
Описание
Специальный формат Qt (определен по умолчанию)
ISO 8601 расширенный формат (YYYY-MM-DD)
Формат, зависящий от установленного в операционной
системе языка страны
Если таблица не предоставляет нужный формат, то можно определить свой
собственный, передав в метод tostringo строку, описывающую его.
Например:
QDate date(2004, 10, 25);
QString str;
str = date.toString("d.M.yy"); //str = "3.7.04"
str = date.toString("dd/MM/yy"); //str = "03/07/04"
str = date.toString("yyyy.MMM.ddd"); //str = "2004.икш.Сб"
str = date.toStringC'yyyy.MMMM.dddd");//str = "2004.Июль.суббота"
При помощи метода addDaysO можно получить измененную дату, добавив
или отняв от нее дни. Действия методов addMonthsO и addYearsO
аналогичны, лишь с той разницей, что они оперируют месяцами и годами.
Например:
QDate date(2004, 1, 3);
QDate date2 = date.addDays(-7);
QString str = date2.toString("dd/MM/yy"); //str = "27/12/03"
Класс QDate предоставляет метод f romstring (), позволяющий проводить
обратное преобразование из строкового типа к типу QDate. Для этого в первом
параметре метода нужно передать формат (табл. 35.1).
Одна из самых частых операций — это получение текущей даты. Для ее
получения НУЖНО вызвать метод currentDateO. При ПОМОШИ метода daysToO
можно узнать разницу в днях между двумя датами. Следующий пример
определяет количество дней от текущей даты до Нового года:
Глава 35. Дата, время и таймер 417
QDate dateToday = QDate::currentDate();
QDate dateNewYear(dateToday.year(), 12, 31);
cout « "Осталось "
« dateToday.daysTo(dateNewYear)
« " дней до Нового года";
Объекты дат можно сравнивать друг с другом и для этого в классе QDate
определены операторы =, !=, <, <=, > и >=. Например:
QDate datel(2004, l, 3);
QDate date2(2004, 1, 5) ;
bool b = (datel = date2); //b = false
Класс времени QTime
Контроль над временем — очень важный процесс, с помощью которого
можно вычислять задержки в работе программы, отображать на экране
текущее время, проверять время создания файлов и т. д.
Для работы со временем библиотека Qt предоставляет класс QTime. Как и в
случае с объектами даты, с объектами времени можно проводить операции
сравнения ==, •=, <, <=, > или >=. Объекты времени способны хранить время
с точностью до миллисекунд. В конструктор класса QTime передаются
четыре параметра. Первый параметр задает часы, второй — минуты, третий —
секунды, а четвертый — миллисекунды. Третий и четвертый параметры
можно опустить, по умолчанию они будут равны нулю. Например:
QTime time(20, 4,);
Эти значения можно устанавливать и после создания объекта времени
вызовом метола setHMS (). Например:
QTime time;
time.setHMS (20, 4, 23, 3);
Для получения значений часов, минут, секунд и миллисекунд,
установленных в объекте времени, в классе QTime определены следующие методы:
О hour о — возвращает положительные значения часа в диапазоне от 0
до 23;
О minute о — возвращает целое значение в диапазоне от 0 до 59,
представляющее собой минуты;
О second () — возвращает целое значение в диапазоне от 0 до 59,
представляющее собой секунды;
Q msec о — возвращает целое значение в диапазоне от 0 до 999,
представляющее собой миллисекунды.
418 Часть VI. Особые возможности д*
Класс QTime предоставляет метод tostringo для передачи данных объекта
времени в виде строки. В этот метод в качестве параметра можно передать
одно из значений, указанных в табл. 35.1, или задать свой собственный
параметр. Например:
QTime time (20, 4, 23, 3);
QString str;
str = time.toString("hh:mm:ss.zzz"); //str = "20:04:23.003"
str = time.toString("h:m:s ap"); //str = "8:4:23 pm"
При помощи метода fromstringo можно произвести обратное
преобразование из строкового типа в тип QTime. Для этого в первом параметре метода
нужно передать одно из значений формата, приведенных в табл. 35.1.
Получить измененный объект времени можно, добавив или отняв значения
секунд (или миллисекунд) от существующего объекта. Эти значения
передаются в методы addsecso и addMSecsO. Для получения текущего времени
В классе QTime содержится статический метод currentTime ().
При помощи метода start о можно начать отсчет времени, а для того
чтобы узнать, сколько времени прошло с момента начала отсчета, следует
вызвать метод elapsed о. Например, на базе этих методов можно сделать
небольшой профайлер. Следующий пример вычисляет время работы функции
test О:
QTime time;
time, start () ;
test ();
cout « "Время работы функции test() равно "
« time.elapsed()
« " миллисекунд"
« endl;
Недостаток класса QTime состоит в ограничении 24-часовым интервалом, по
истечении которого отсчет снова будет производиться с нуля. Для*решения
проблемы можно воспользоваться классом QDateTime, но его точность лежит
в секундном интервале.
Класс даты и времени QDateTime
Объекты класса QDateTime содержат в себе дату и время. Вызовом метода
date о можно получить объект даты (QDate), а вызов time о возвращает
объект времени (QTime). Этот класс также содержит методы toStringO W*
представления данных в виде строки. Для этого можно воспользоваться
одним из форматов в табл. 35.1.
Глава 35. Дата, время и таймер
419
Таймер
В программах часто возникает потребность в периодическом повторении
определенных действий через заданные промежутки времени. Конечно, в
некоторых случаях можно воспользоваться и объектом класса QTime и
сделать примерно следующее:
QTime time;
time.start
effort; time, elapsed () < 1000;) {
)
function();
Но такой подход обладает огромным недостатком. Исполнение цикла на
секунду приостанавливает выполнение всей программы. Вследствие этого,
события интерфейса пользователя не будут обрабатываться, и если одно из
окон перекроет окно приложения, то оно все это время перерисовываться
не будет, т. е. приложение на все это время "замрет".
В ПОДОбНЫХ ситуациях Конечно МОЖНО, ВЫЗОВОМ Метода processEventsO
класса приложения QApplication, приостанавливать выполнение цикла, для
того чтобы программа получала возможность обработки поступивших
событий. Например:
QTime time;
time.start();
for(;time.elapsed() < 1000;) {
qApp->processEvents();
>
Ввиду того, что обработка поступивших событий может составить более
одной секунды, подобное решение, к сожалению, не в состоянии
гарантировать требуемой точности. Таймер предоставляет собой решение этой
проблемы. События таймера происходят асинхронно и не прерывают обработку
Других событий, выполняемых в том же потоке. Таймер — это гарант,
обеспечивающий передачу управления программе. Долгая обработка событий
влечет за собой задержки выполнения события таймера, т. е. таймер ждет
своего времени, как и остальные события. Период между событиями
таймера носит название интервал запуска (firing interval). Таймер перехолит в
сигнальное состояние по истечении интервала запуска, который указывается в
Миллисекундах. Точность интервала запуска ограничивается, к сожалению,
точностью системных часов, а это значит, что на таймер нельзя полагаться
Как на секундомер. Следовательно, при написании программы имитации
Часов будет нелишним, после каждого сообщения таймера, проверять
текущее время (см. листинг 35.3). Так как временной интервал, задаваемый в
420 Часть VI. Особые возможности ^
таймере, представляет собой целое число, то самый большой временной
интервал, который можно установить, — 24 дня. Эту проблему можно ре_
шить введением дополнительного счетчика для таймера.
Существует много областей для использования таймера. Например, в
текстовом редакторе его используют для автоматического сохранения файлов
или в качестве альтернативы многопоточности (см. гл. 34), разбив
программу на части, каждая из которых будет выполняться при наступлении
события таймера. Также таймер используется для отображения информации о
состоянии изменяющихся с течением времени данных. Таймер незаменим
для избежания разногласий, связанных с мощностью и возможностями
разных компьютеров, т. е. для исполнения программ в режиме реального
времени.
Событие таймера
Каждый класс, унаследованный от QObject, содержит свои собственные
встроенные таймеры. Вызов метода QObject::startTimer() производит
запуск таймера. В качестве параметра ему передается интервал запуска в
миллисекундах. Метод startTimerO возвращает идентификатор, необходимый
для распознавания таймеров, используемых в объекте. По истечении
установленного интервала запуска генерируется событие QTimerEvent, которое
передается В метод timerEvent (). Вызвав метод QTimerEvent: :timerld()
объекта события QTimerEvent, можно узнать идентификатор таймера,
инициировавшего это событие. Идентификатор можно использовать для
уничтожения таймера, передав его в метод QObject::kiliTimer (). Для уничтожения
всех таймеров объекта следует воспользоваться методом QObject::
kiliTimers (), которым удобно пользоваться также в тех случаях, когда
объект использует только один таймер. Вместо того чтобы получить его
идентификатор и удалить таймер, можно просто вызвать метод QObjecc::
kiliTimers о. Программа, показанная на рис. 35.1, отображает надпись,
которая появляется и исчезает через заданные промежутки времени.
1 «ДЛИ-II ИХ!
Е . ing Label
Рис. 35.1. Мигающая надпись
| Листинг 35.1. Файл main.cpp
#include <qapplication.h>
#include "BlinkLabel.h"
Глава 35. Дата, время и таймер
421
jj!t main (int argc, Char** argv)
{
QApplication app (argc, argv);
BlinkLabel lbl("<FONT COLOR - RED>Blinking LabeK/FONT>") ;
app.setMainWidget(&lbl);
lbl.show();
return app.exec();
}
В функции main о создается виджет класса BlinkLabel, в конструктор
которого первым параметром передается отображаемый текст в формате
RichText (листинг 35Л).
I Листинг 35.2. Файл BlinkLabel.h
fesiL-.: .;
#ifndef _BlinkLabel_h_
♦define _BlinkLabel_h_
♦include <qlabel.h>
// __„„===_ » _=ет==г========================================
class BlinkLabel : public QLabel {
Q_OBJECT
private:
bool m_bBlink;
protected:
virtual void timerEvent(QTimerEvent*)
{
m_bBlink = m_bBlink ? false : true;
if (mJoBlink) {
repaint();
}
else {
erase();
}
}
Public:
BlinkLabel(const QString& strText,
int nlnterval = 200,
422
Часть VI. Особые возможности л«
QWidget* pwgt = 0,
const char* pszName = О
)
: QLabel(strText, pwgt, pszName)
, m_bBlink(true)
{
startTimer(nlnterval);
}
};
#endif //_BlinkLabel_h_
Класс BlinkLabel, приведенный в листинге 35.2, содержит переменную бу-
левого типа mbBiink, управляющую отображением надписи. Также, в
конструктор класса передается интервал мигания nmtervai. По умолчанию он
равен 200 миллисекундам. Вызов метода startTimerO запускает таймер со
значением переданного интервала запуска. По истечении этого интервала
происходит создание события QTimerEvent, которое передается в метод
timerEvent(), в котором происходит смена значения переменной mbBiink.
В соответствии с установленным значением выполняется одно из действий:
□ false — вызывается метод erase о, который очищает всю область надписи;
□ true — вызывается метод rapaint (), который заново перерисовывает
надпись.
Класс QTimer
Применение объекта класса QTimer гораздо проще, чем использование
события таймера, определенного в классе oobject. К недостаткам работы с
событием таймера относится необходимость наследования одного из
классов, наследующих oobject. Затем, в унаследованном классе, нужно
реализовать метод, принимающий объекты события таймера. А если в объекте
создается более одного таймера, то возникает необходимость различать
таймеры, чтобы узнать, который из них явился инициатором события.
Для ликвидации этих неудобств Qt предоставляет класс таймера QTimer,
являющийся непосредственным наследником класса oobject. Чтобы запустить
таймер, сначала нужно создать объект класса QTimer, а затем вызвать метод
start о. В первом параметре метода передается значение интервала запуска
в миллисекундах, а второй параметр управляет одноразовым режимом
отработки таймера (singieshot). Этот параметр по умолчанию имеет значение
false, что задает циклический режим отработки, а для одноразовой
отработки таймера этот параметр должен иметь значение true.
Класс QTimer содержит статический метод singieshot о, с помощью
которого можно запустить одноразовый таймер без создания объекта класса
fnaea 3i>. дата, время и таймер
primer. Первый параметр метода задает интервал запуска, а второй параметр
является указателем на объект, с которым должно осуществиться
соединение. Слот для соединения передается в третьем параметре. Этим можно
воспользоваться, например, для прекращения работы демо-версии
программы через 5 минут после ее запуска (пример 35.1).
[пример35.1. Завершение работы программы после пяти минут работы
К!~— : •'
int main( int argc, char **argv)
{
QApplication app( argc, argv);
QTimer::singleShot(5*60*1000, &app, SLOT(quit()));
MyProgram myProgram;
app.setCentralWidget(SmyProgram);
return a.exec();
}
По истечении интервала запуска таймера высылается сигнал timeout о,
который нужно соединить со слотом для выполнения нужных действий. При
помощи метода changeinterval () можно изменить интервал запуска
таймера. В том случае, если таймер был активен, он будет остановлен и запущен
с новым интервалом. При помощи метода isActiveO можно проверить,
находится таймер в состоянии активности или нет. Вызовом метода stop о
можно остановить таймер.
Программа, окно которой изображено на рис. 35.2, реализует часы,
отображающие дату и время. Отображаемая информация актуализируется в
соответствии с установленным полусекундным интервалом запуска таймера.
1 »»-1п«х1
04.09.2004
14:36:02
Рис. 35.2. Электронные часы
I Листинг 35.3. Файл Clock.h
#ifndef _Clock_h_
#define Clock h_
#include <qlabel.h>
#include <qdatetime.h>
#include <qtimer.h>
424 Часть VI. Особые возможности pf
class Clock : public QLabel {
Q_OBJECT
public:
Clock(QWidget* pwgt = 0, const char* pszName = 0)
: QLabe1(pwgt, ps zName)
{
QTimer* ptimer = new QTimer(this);
connect(ptimer, SIGNAL(timeout()), SLOT(updateDateTime ()));
ptimer->start(500, false);
updateDateTime();
}
public slots:
void updateDateTime()
{
QString str = QDateTime::currentDateTimeO.toString(LocalDate);
setText("<H2><CENTER>" + str + "</CENTER></H2>");
}
);
#endif //_Clock_h_
В конструкторе класса clock (листинг 35.3) создается объект таймера
(указатель ptimer). Его сигнал timeout о соединяется со слотом, ответственным за
обновление отображаемой информации updateDateTime о. Вызов метода
start о запускает таймер. Первым параметром в него передается интервал
запуска и значение false, задающее циклический режим. Слот
updateDateTime () получает актуальную дату и время вызовом метода
currentDateTime (). Затем он преобразовывает ее к строковому типу и
передает для отображения в метод setText ().
Резюме
Классы QDate, QTime и QDateTime предназначены для хранения дат и
времени, а также для проведения с ними разного рода операций. Чаще всего
требуется получение текущей даты и времени. С их помощью можно узнать,
например, время, прошедшее с момента запуска программы. Эти классы
предоставляют методы для преобразования даты и времени в строку
определенного формата. Есть и методы для проведения обратного
преобразования — из строки.
Таймер уведомляет приложение об истечении заданного промежутка
времени. События таймера относятся к разряду внешних прерываний. Внешние
pgga 35- Дата» время и таймер 425
Прерывания — это прерывания, вызываемые асинхронными событиями,
например, устройствами ввода/вывода или самим устройством таймера.
Интервалы запуска таймера устанавливаются в миллисекундах. Недостаток
состоит в том, что если программа занята интенсивными вычислениями, то
события таймера могут быть обработаны по окончании процесса
вычисления. Таймер можно настроить так, чтобы он работал либо циклически, либо
только один раз. В циклическом режиме таймер будет активироваться до тех
пор, пока не завершится программа или пока он не будет остановлен. При
выходе из приложения таймеры автоматически уничтожаются.
ГЛАВА 36
Библиотека контейнеров
Ты Дерево. Животное, или Минерал?
Льюис Кэрролл. "Атса в Зазеркалье"
Одна из самых распространенных проблем в программировании
заключается в организации обработки групп элементов. Реализация и отладка
программ с использованием подобного рода структур отнимает у разработчиков
много времени, т. к. они вынуждены в каждом приложении решать одни и
те же проблемы. Библиотека контейнеров предоставляет для этого набор
повторно используемых классов, на правильную работоспособность которых
можно положиться. Это позволяет разработчику сконцентрироваться на
реализации самого приложения и не вникать в детали реализации
используемых контейнерных классов.
Контейнерные классы — это классы, которые в состоянии хранить в себе
элементы различных типов данных и манипулировать ими. Почти все
контейнерные классы в библиотеке Qt реализованы как шаблонные и, таким
образом, они могут хранить данные любого типа. Основная идея шаблона
состоит в создании родового класса, который определяется при создании
объекта этого класса. Классы контейнеров могут включать целые серии
других объектов, которые, в свою очередь, тоже могут являться контейнерами.
Qt предоставляет следующие разновидности классов контейнеров:
П вектор — это структура данных, очень похожая на обычный массив.
Использование класса вектора предоставляет некоторые преимущества, по
сравнению с обычным массивом. Например, можно получить количество
элементов, содержащихся в нем, или динамически изменить его размер;
П список — структура данных, представляющая собой упорядоченный
набор связанных друг с другом элементов. Преимущество списков перед
векторами и очередями состоит в том, что вставка и удаление элементов
в любой позиции происходит эффективнее, но приходится жертвовать
скоростью доступа к произвольным элементам — она снижается;
Глава 36. Библиотека контейнеров
427
0 словари похожи на обычные, используемые в повседневной жизни. Они
хранят элементы одного и того же типа, индексируемые ключевыми
значениями. Главное его достоинство в том, что он позволяет быстро
получать значение, ассоциированное с заданным ключом. Ключевые
значения должны быть уникальными;
О стек реализует структуру данных, работающую по принципу LIFO (Last
In First Out — последним пришел — первым ушел), т. е. из стека
удаляется первым элемент, который был вставлен позже всех остальных;
О очередь реализует структуру данных, работающую по принципу FIFO
(First In First Out — первым пришел — первым ушел), т. е. из очереди
удаляется не последний вставленный элемент, а, наоборот, тот элемент,
который был вставлен в очередь раньше всех остальных.
Классы контейнеров Qt можно разбить на две группы: контейнеры данных
и контейнеры указателей. Это разделение обоснованно тем, что главным
требованием к классам, объекты которых должны храниться в контейнере
данных, является наличие доступного оператора присваивания и
конструктора копирования. Но классы, унаследованные от QObject, не имеют этой
возможности, т. к. конструктор копирования и оператор присваивания
находятся в секции private. Поэтому их объекты не могут храниться в
контейнерах данных. Единственное, что можно сделать, — это сохранить в
контейнерах данных указатели. Для удобства Qt предоставляет отдельные
контейнерные классы, специально созданные для хранения указателей.
Контейнеры данных
Итераторы на данный тип контейнеров по своему синтаксису сильно
приближены к STL (Standard Template Library, стандартная библиотека
шаблонов). И хотя эти классы и имеют много совпадающих методов, но. по
непонятным причинам, не имеют общего класса, от которого они были бы
унаследованы. Таким образом, во всех контейнерах этой группы определены
операторы присваивания, сравнения, индексации, а также методы size о,
eraptyO и clear().
Каждый контейнерный класс имеет два итератора: обычный и константный.
Разница в том, что константный итератор не разрешает изменять данные.
Вызов метода begin о из объекта контейнера возвращает итератор,
указывающий на первый его элемент, а вызов метода end о возвращает итератор,
Указывающий на конец контейнера. Операторы ++ и — объекта итератора
Производят перемещения на следующий или предыдущий элемент
соответственно. Значение элемента, на который указывает итератор, можно
получить при помощи оператора *.
428
Часть VI. Особые возможности сц
Векторы
Qt предоставляет для реализации векторов два основных класса-
QValueVector И QMemArray.
Класс QValueVector
Этот класс определен в заголовочном файле qvaluevector.h. Представляет
собой совместимую с STL структуру, размер которой можно задать в
конструкторе при создании. По умолчанию, только что созданный вектор будет
иметь размер, равный 0, т. к. не содержит ни одного элемента. Изменить
его размер можно либо добавив к нему элементы, либо вызвав метод
resize о. Для добавления элементов в конец вектора нужно воспользоваться
методом pushjoack (). К элементам вектора можно обратиться как
посредством оператора operator[], так и при помощи итератора. Например:
QValueVector<int> vec;
vec.push_back(10) ;
vec.push_back(20);
vec.push_back(30) ;
for(int i = 0; i < vec.sizeO; ++i) {
cout « vec[i] « endl;
}
На консоли будет отображено следующее:
ю
20
30
Класс QMemArray
Класс QMemArray определен в заголовочном файле qmemarray.h. Этот класс
очень ПОХОЖ на QValueVector. Изображенные на рис. 36.1 QByteArray И
QPoinArray не являются отдельными классами, а представляют собой
определения ТИпа, параметризирующие шаблонный класс QMemArray. QByteArray
параметрИЗИрует класс QMemArray ТИПОМ char, a QPointArray — ТИПОМ QPoint.
Размер объекта QMemArray можно указать при его создании. Для изменения
размера следует вызвать метод resize о. К элементам можно обратиться как
при помощи оператора operator[], так и посредством итератора. Класс
QMemArray содержит следующие методы:
П find о — предназначен для поиска элементов и возвращает индекс
найденного элемента. Если поиск прошел неудачно, то метод вернет — К
П сору о — копирует контейнер;
Глава 36. Библиотека контейнеров
429
I QGArray j|
hmmmaammC mm, mm* ь ii Л
4 QMemArray jj
П QPointArray \
4~~QByteArray \j
1
QBltArray
QCString
Рис. 36.1. Иерархия классов массивов
О fill 0 — применяется для заполнения контейнера указанным
элементом, который передается в первом параметре. Второй параметр не
обязателен, с его помощью можно задать новый размер массива.
Класс QByteArray
QByteArray не является отдельным классом, а представляет собой тип
QMemArray<char>. Его определение находится в заголовочном файле qstring.h.
Объекты типа QByteArray можно использовать везде, где требуется
промежуточное хранение данных.
Класс QBitArray
Этот класс управляет битовым (или булевым) массивом. Каждое из
сохраняемых значений занимает только один бит, не расходуя лишней памяти.
Значения упаКОВЫВаЮТСЯ В баЙТЫ С ПОМОЩЬЮ QByteArray.
Очень часто этот тип используется для хранения большого количества
переменных типа bool, которые, во многих компиляторах, определяются
типом int, который равен, как правило, 32 битам.
Для операций с битами этот класс предоставляет метод для считывания
testBito и записи setBito. Наряду с этими методами, существует также
operator[], с помощью которого можно обращаться к каждому биту в
отдельности, но пользоваться им не рекомендуется из-за того, что он гораздо
медленнее вышеприведенных методов.
Класс QPointArray
QPointArray также не является отдельным классом, а представляет собой тип
QMemArray<QPoint>. Его объекты хранят координаты точек. Например, они
используются некоторыми методами класса QPainter (см. гл. 17).
Списки
Список — это последовательность элементов, связанных между собой с
помощью указателей. Сильная сторона этого контейнера заключается в воз-
430 Часть VI. Особые возможности nf
можности удаления и вставки элементов, которые неестественны и неудоби
ны для векторов, поскольку требуют перемещения целого ряда элементов
В списках же эти операции сводятся к переопределению четырех
указателей, независимо от позиции удаляемого или вставляемого элемента. Но есть
и недостаток — списки плохо приспособлены для поиска определенного
элемента по индексу, для этой цели лучше использовать вектор.
Списки реализует класс QVaiueList, определенный в заголовочном файле
qvaluelist.h. От него унаследованы следующие классы (рис. 36.2):
□ QStringList — СПИСОК СТрОК;
П QVaiueList — контейнерный класс стековой структуры данных;
П QCanvasitemList — список элементов изображения холста (см. гл. 20).
| QVaiueList Ь
Н~ QStringList Ь
Н QValueStack fe
Ч QCanvasitemList ||
Рис. 36.2. Иерархия класса списка QVaiueList
Доступ к элементам объекта QVaiueList производится при помощи
итератора QValueListConstlterator ИЛИ QValueListlterator. Так, например, методы
remove о и insert о ожидают, в качестве параметра, объекты итераторов.
Табл. 36.1 содержит некоторые из методов класса QVaiueList
Таблица 36.1. Некоторые методы контейнера QVaiueList
Метод
count()
clear()
append()
prepend()
insert()
remove()
find()
contains()
at()
Описание
Возвращает количество записанных
элементов
Удаляет все элементы
Добавляет один элемент в конец списка
Добавляет один элемент в начало списка
Добавляет элемент на указанное место
Удаляет элемент на указанном месте
Производит поиск элемента
Подсчитывает количество двойников
элемента
Возвращает указанный элемент списка
Время исполнения
Быстрое (0(1))
Медленное (0(п))
Быстрое (0(1))
Быстрое (0(D)
Быстрое (0(D)
Быстрое (0(D)
Медленное (0(п))
Медленное (0(п))
Медленное (0(п))
Слава 36. Библиотека контейнеров
431
Одна из самых распространенных операций — это обход списка, для
последовательного получения значений каждого элемента списка. Например:
QValueList<int>list;
list.append(10);
list.append(20);
list.append(30);
QValueList<int>::iterator it = list.begin();
while (it != list.endO) {
cout « *it « endl;
++it;
}
На консоли будет отображено следующее:
ю.
20
30
Словари
Этот контейнер был сделан для быстрого поиска элементов. Поэтому в этот
контейнер заносятся элементы вместе с ключом, по которому их можно
найти. В качестве ключей могут выступать значения любого типа.
Необходимо следить за тем, чтобы не было занесено двух разных элементов с
одинаковым ключом, ведь в этом случае один из этих элементов невозможно
будет отыскать. Каждый ключ должен быть уникален.
При создании объекта класса омар нужно передать его размер в
конструктор. Этот размер не является, как это принято в других контейнерных
классах, размером, ограничивающим максимальное количество элементов, а
представляет собой количество позиций. Количество позиций должно быть
больше количества элементов, ожидаемых для хранения. Иначе поиск
элементов в словаре будет проводиться недостаточно эффективно. Желательно,
чтобы это значение относилось к разряду простых чисел, т. к. в этом случае
размещение элементов будет более удобным (см. приложение 2). Табл. 36.2
содержит некоторые из методов класса омар.
Таблица 36.2. Некоторые методы контейнера QMap
Метод
count()
insert()
Описание
Возвращает количество записанных
элементов
Добавляет элемент на указанное место
Время исполнения
Быстрое (0(1))
Нормальное (0(log п))
432 Часть VI. Особые возможности п*
Таблица 36.2 (окончание)
Метод
remove()
replace()
findO
operator[]()
contains()
clear()
Описание
Удаляет элемент на указанном месте
Замещает элемент на указанном месте
Производит поиск элемента
Операция индексации
Подсчитывает количество двойников
элемента
Удаляет все элементы
Время исполнения
Нормальное (0(1од п))
Нормальное (0(1од п))
Нормальное (0(1од п))
Нормальное (0(1од п))
Нормальное (0(1од п))
Медленное (О(п))
Один из самых частых способов обращения к элементам словаря — это
использование ключа в operator[]. Но можно обойтись и без него, т. к. ключ
и значение можно получить с помощью методов итератора key о и data о.
Например:
QMap<QString, QString> mapPhonebook;
mapPhonebook["Piggy"] = "+49 631322187",-
mapPhonebook["Kermit"] = "+49 631322181";
mapPhonebook["Gonzo"] = "+49 631322186";
QMap<QString, QString>::iterator it = mapPhonebook.begin();
while(it != mapPhonebook.end()) {
cout « "Name:" « it.key()
« " Phone:" « it.data()
« endl;
++it;
}
На консоли будет отображено следующее:
Name:Gonzo Phone:+49 631322186
NameiKermit Phone:+49 631322181
Name:Piggy Phone:+49 631322187
Особое внимание следует обратить на использование operator!], который
может применяться как для вставки, так и для получения значения
элемента. Но надо быть осторожным, т. к. задание ключа, для которого элемент не
существует, приведет к тому, что элемент будет создан. Чтобы избежать
этого, нужно использовать метод find о, с помощью которого следует
проверять существование элемента, привязанного к ключу. Этот метод
возвращает итератор, указывающий на конец контейнера, если ключ не найден-
Глава 36. Библиотека контейнеров
433
Проверку можно осуществить при помоши метода containtso класса омар.
Например:
if(mapPhonebook.contains("Kermit")) {
cout « "Phone:" « mapPhonebook[] « endl;
}
Стек
Класс QVaiuestack представляет собой реализацию стековой структуры. Этот
класс унаследован от класса QVaiueList. Процесс помещения элементов в
стек обычно называется проталкиванием (pushing), а извлечение из него
верхнего объекта — выталкиванием (poping). Каждая операция
проталкивания увеличивает размер стека на 1, а каждая операция выталкивания
уменьшает его размер на 1. Для этих операций в классе QVaiuestack
определены методы push о и роро. Метод top о возвращает ссылку на элемент
вершины стека. Следующий пример демонстрирует использование класса
стека.
QValueStack<int> stk;
stk.push(10);
stk.push(20);
stk.push(30);
while (!stk.empty()) {
cout « stk.popO « endl;
}
На консоли будет отображено следующее:
30
20
10
Очередь
Qt не предоставляет для контейнеров данных отдельной реализации класса
очереди. В качестве альтернативы может быть использован класс Qvaiuevector.
Контейнеры указателей
Классы этой контейнерной группы хранят только адреса элементов. Одна из
интересных особенностей контейнеров, хранящих указатели, — это
автоматическое уничтожение. При ее активизации, вместе с удалением самого
контейнера, производится удаление управляемых им элементов.
434
Часть VI. Особые возможности
Of
Класс QPtrCoiiection является базовым классом для всех классов этой
группы, за исключением класса QPtrQueue (рис. 36.3). Класс QPtrCoiiection
предоставляет методы для управления настройками контейнера, такими как
например, автоматическое уничтожение — метод setAutoDeiete (). Он
содержит определения виртуальных методов, служащих для получения
количества элементов (метод count о), а также методы для создания (метод
newItemO) И уничтожения элементов (методы deleteltemO И clear()).
| QGList
QPtrCoiiection
QPtrQueue
Н QPtrVector
1 j,jiuin,mMtjumvKnm
■\ QAsciiDict
A QCache
1 juwjj^uhuhijw, цитат
j QDIct
■\ Qjgoig
.j
Ji
з
ZJt
QPtrList
QObjectList
Ц QStrList U
4 QStriLlst h
Чатлч—1Н1цц|ин1»11И111щ,1 nil
Рис. 36.3. Иерархия классов контейнеров указателей
Ниже следует описание самых важных, из приведенных на рис. 36.3, классов:
□ класс QPtrVector — реализует класс вектора указателей. Элементы можно
помещать при помощи метода insert о, а удалять с помощью remove о;
П класс QPtrList — реализует класс списка указателей. При помощи
методов next о, prevo, first о и lasto можно перемещаться по списку.
Метод current о возвращает ссылку на актуальный элемент;
□ класс QPtrDict — реализует словарь указателей. В противоположность
классу омар, его элементы можно заносить только методом insert о, а не
с помощью operator[]. Доступ к элементам можно получить при помощи
итератора;
П класс QPtrstack — реализует стек, в который помещаются указатели. Для
стеков нельзя использовать итераторы, а также нельзя получить доступ
к произвольному элементу без изменения размера стека методами push О
и pop о;
□ класс QPtrQueue — реализует структуру данных очереди для размещения
указателей. Вызовом метода enqueue о можно поставить элемент в коней
очереди, вызовом dequeue о — удалить первый элемент в очереди, вернув
его значение. Вызов метода head о возвращает первый элемент очереди,
не удаляя его.
Для получения доступа к элементам контейнеров используются классы
итераторов. Имена этих классов состоят из имени класса-контейнера с присое-
Глава 36. Библиотека контейнеров
435
динением к нему слова iterator. Например, классу QPtrList соответствует
итератор QPtrListiterator. Контейнеры этой группы не предоставляют
методов begin о и endo, возвращающих итератор начала и итератор конца.
В ней применяется свой, особый способ создания итераторов, который
выглядит следующим образом: сначала создается объект контейнера, затем
созданный контейнер передается в конструктор класса итератора.
Созданный таким образом итератор будет указывать на первый элемент
контейнера. Например:
QPtrList<QWidget> list;
QPtrListIterator<QWidget> it(list);
QWidget* w;
for (; w = it.current(); ++it) {
// Что-то происходит
}
Строки
Практически все приложения оперируют текстовыми данными. В Qt
реализован класс QString, объекты которого могут хранить строки, находящиеся в
формате Unicode, где каждый символ занимает два байта. Класс QString
предоставляет целую серию методов и операторов, позволяющих проводить
со строками разного рода операции. Например, соединять строки,
осуществлять поиск подстрок, преобразовывать их в верхний или нижний реестр и
многое другое.
Строки можно сравнивать друг с другом при помоши операторов сравнения
=, !=, <, >, <= и >=. Например:
QString str = "Lo";
bool Ы = (str = "Lo"); // bl = true
bool b2 = (str != "Lo"); // b2 = false
При помощи метода isEmptyO можно узнать, не пуста ли строка. Того же
результата можно добиться, проверив длину строки методом lengtho.
В классе QString имеется различие между пустыми и нулевыми строками,
таким образом, следующие строки не равны друг с другом:
QString strl = "";
QString str2 = QString::null;
bool b = (strl == str2); //b = false
Объединение строк является одной из самых распространенных операций.
Провести его можно разными способами. Например, при помощи
операторов += и + или вызовом метода append о. Например:
436
Часть VI. Особые возможности о»
QString strl = "Lo";
QString str2 = "stris";
QString str3 = strl + str2; // str3 = "Lostris"
strl.append(str2); //strl = "Lostris"
Для замены определенной части строки другой класс QString предоставляет
метод replace о. Например:
QString str = "Lostris";
str.replace("stris", "gik"); // str = "Logik"
Для преобразования данных строки в верхний или нижний регистр
используются метод lower о или upper о. Например:
QString strl = "LoStRiS";
QString str2 = strl.lower О; // str2 = "lostris"
QString str3 = strl.upper(); // str3 = "LOSTRIS"
При помощи метода setNumO можно конвертировать числовые значения в
строковые. Того же результата можно добиться вызовом статического
метода number (). Например:
QString str = QString::number(35.123);
Аналогичного результата можно добиться также и при помощи метода
sptintf о, похожего на одноименную функцию стандартной библиотеки С
stdio.h. Первым параметром задается формат, а за ним следуют аргументы,
количество которых не ограничено. Например:
QString str = QString().sprintf("%d", 35.123);
Преобразование из строкового в числовое значение производится методами,
содержащими в своем имени название типа. В данных методах вторым
параметром можно передавать ссылку на переменную булевого типа для
получения информации о том, успешно ли была проведена операция.
Например:
bool ok;
QString str = "234";
double d = str.toDouble(fiok);
int n = tolnt(fiok);
Строка может быть разбита на список строк при помощи статического
метода split о класса QstringList. Следующий пример создаст список из двух
строк Ringo и Star:
QString str = "Ringo Star";
QstringList list = QStringList::split(" ", str);
Глава 36. Библиотека контейнеров
437
Операция объединения списка строк в одну строку производится при
помощи метода joino. Например, объединить список из двух элементов
(Ringo и Start) в одну строку, разделив их знаком пробела, можно
следующим образом.
str = list.join(" ");
Регулярные выражения
Для работы с регулярными выражениями библиотека Qt предоставляет
класс QRegExp. Регулярные выражения — это мощное средство анализа и
обработки строк. Они содержат в себе шаблон, предназначенный для поиска в
строке. Это позволяет быстро и гибко извлекать совпавший с шаблоном
текст. Но следует заметить, что работа с регулярными выражениями
производится медленнее методов, определенных в классе QString, и поэтому их
применение должно быть обоснованным. Табл. 36.3 содержит основные
шаблонные символы, поддерживаемые классом QRegExp.
Таблица 36.3. Некоторые символы последовательностей
Символ
1 $
[]
-
L л
*
+
?
{п}
[ (,п>
|__ (п,}
1 {n,m}
|_ 1
[ /Ь
[ /в
Описание
Любой символ
Должен быть конец строки
Ищет символ из заданного набора символов
Определяет диапазон символов [ ]
Ищет любой символ, не вошедший в набор
Символ должен встретиться в строке определенное
число раз
Символ должен встретиться в строке минимум 1 раз
Символ должен встретиться в строке 1 раз
или не встретиться вообще
Символ должен встретиться в строке указанное число
раз
Допускается минимум п совпадений
Допускается до п совпадений
Допускается min n, max m
Ищет один из двух символов
Определяет границы слова
Противоположная граница слова
Примеры
а.Ь
АЬс$
[abc]
[0-9A-Za-z]
rdef]
a*b
a+b
a?b
a{3}b
a{,3}b
a{3,}b
a{2,3}b
aclbc
a/b
a/Bd
438
Часть VI. Особые возможности Qt
Таблица 36.3 (окончание)
Символ
Г (*
\d
\D
\S
\S
\w
\w
\A
\b
\B'
\Z
\z
Описание
Ищет и сохраняет в памяти группу найденных символов
Любое число
Все, кроме числа
Любой тип пробелов
Все, кроме пробелов
Любая буква, включая знак подчеркивания
Все, кроме букв
Начало строки
Целое слово
Не слово
Конец строки (совпадает с символом конца строки
или перед символом перевода каретки)
Конец строки (совпадает только с концом строки)
Примеры
(abiac)ad
Для того чтобы найти символы, нужно поместить эти символы в
квадратные скобки. Например [ab] будет совпадать с а и ь. Чтобы не писать все
символы подряд, можно указать диапазон, например [a-z] будет совпадать с
любой буквой в верхнем регистре, a [a-z] совпадает с любой буквой в
нижнем регистре, а [0-9] будет совпадать с любой цифрой. Например, [a-z7]
будет совпадать с любой буквой в нижнем регистре и с цифрой 7.
Также можно исключать символы, поставив перед ними знак л. Например
[ло-9] будет соответствовать всем буквам, кроме цифр.
Указанные в табл. 36.3 величины в фигурных скобках называются
пределами. Пределы позволяют точно задать количество раз, которое символ
должен повторяться в тексте. Например, а{4, 5} будет совпадать с текстом,
если встретится в нем не менее 4 раз, но не более 5.
Шаблоны можно комбинировать при помощи символа |, задавая ветвления
в регулярном выражении. Регулярное выражение с двумя ветвями совпадает
с подстроковым, если совпадает одна из ветвей. Например:
QRegExp rxp( "(.com|.ru)");
int nl = rxp.search("www.bhv.ru"); // nl = 7 (найдено совпадение
на 7-й позиции)
int n2 = rxp.search("www.bhv.de"); // n2 = -1 (совпадений не найдено)
Глава 36. Библиотека контейнеров 439
Указанные в табл. 36.3 символы с обратной косой (обратным слэшем)
позволяют значительно упростить регулярные выражения. Например,
регулярное выражение [a-zA-zo-9] будет аналогично выражению \w.
Произвольный тип
Объекты класса QVariant могут содержать данные разного типа, включая
КОНТеЙнерЫ. К ЭТИМ типам ОТНОСЯТСЯ: int, unsigned int, double, bool,
QString, QStringList, QFont, QBrush, QString, QColor И Др. Важно
учитывать то обстоятельство, что частое использование этого типа может
отрицательно отразиться на скорости программы и эффективном использовании
памяти, а также может заметно снизить читабельность самой программы.
Поэтому объекты класса QVariant не следует использовать без особой
необходимости.
Для создания объекта класса QVariant необходимо передать в его
конструктор переменную нужного типа. Например:
QVariant vl(34);
QVariant v2(true);
QVariant v2("Lostris");
Метод type о позволяет узнать тип записанных в объекте QVariant данных.
Этот метод возвращает целочисленный идентификатор типа. Чтобы
преобразовать его в строку, следует передать его в статический метод
typeToName (). Того же результата можно добиться и вызовом метода
typeNameO, который возвращает информацию о типе в виде строки.
Чтобы получить данные из объекта QVariant нужного типа, существуют
специальные методы asTO и toTO, где т — это имя типа. Метод toTO создает
новый объект типа т и копирует данные из объекта QVariant в нужный
объект. Метод asTO не создает нового, а просто возвращает ссылку на
требуемый объект. Например:
QVariant v2(23);
int a = v2.tolnt();
Модель общего использования данных
Из соображений эффективности, во многих классах Qt избегается
копирование данных и, вместе* этого, используется ссылка на эти данные
(рис. 36.4). Этот принцип получил название общее использование+данных
(shared data). Различают неявные и явные общие данные.
440
Часть VI. Особые возможности г^
| Объект 1 |
Д] I
Счетчик
ссылок|
Объект 2
Объект 3
ТУТ
0111001011010000101011001011
Данные
Рис. 36.4. Три объекта с общими данными и счетчиком ссылок
Модель неявных общих данных
В этой модели вызов конструктора копирования или оператора
присваивания не приведет к копированию данных, а только увеличит счетчик ссылок
на эти данные, прибавив к нему 1. Соответственно при удалении элемента
счетчик ссылок уменьшится на 1. Если значение счетчика ссылок
становится равным 0, то данные уничтожаются. Копирование данных происходит
только при изменениях. Например:
QString strl;
QString str2;
strl = "Новая строка" // Счетчик ссылок = 1
str2 = strl; // strl и str2 указывают на одни и те же данные
// Счетчик ссылок = 2
strl += " добавление"; // Производится копирование данных для strl
Следующие классы Qt используют неявную модель общих данных: QString,
QPalette, QPen, QBrush И QPixmap.
Модель явных общих данных
При работе с объектами классов, использующими модель явных общих
данных, необходимо учитывать то обстоятельство, что изменение данных
одного объекта может повлечь за собой изменение данных и других
объектов. Например:
QMemArray<int> al;
QMemArray<int> a2;
al.fill(2, 10);
al = a2;
al[3] = 34;
al.detachO;
al[4] = 34;
В вышеприведенном примере объекты ai и а2 являются массивами,
содержащими значения целого типа. Изменение значений в объекте al повлечет
Глава 36. Библиотека контейнеров 441
за собой изменение значений объекта а2, т. к. они указывают на одни и те
лее данные. Для проведения*операции копирования вызывается метод
detach о. Следует учесть, что этот метод производит копирование только в
тех случаях, когда счетчик ссылок больше 1, иначе, из целей
эффективности, копирование производиться не будет. Этот метод следует использовать
перед тем, как внести изменения и только в случаях, если эти данные
применяются другими объектами.
Следующие КЛаССЫ Qt ИСПОЛЬЗУЮТ МОДеЛЬ ЯВНЫХ Общих даННЫХ QMemArray,
QByteArray, QBitArray, QPointArray И Qlmage.
Резюме
Контейнер — это объект, предназначенный для хранения и управления
содержащимися в нем элементами. Он заботится о выделении и
освобождении памяти, а также отвечает за помещение и удаление элементов.
В Qt реализованы классы векторов, списков, словарей, стеков и очередей.
Контейнерные классы Ql подразделяются на классы для хранения данных и
классы для хранения указателей.
Класс QString представляет собой реализацию класса строк и содержит
целый ряд методов, для проведения различного рода операций с ними.
Регулярные выражения представляют собой мошный механизм для
проверки строк на совпадение с указанным шаблоном.
ГЛАВА 37
Работа с файлами, директориями
и потоками ввода/вывода
Цивилизация движется вперед путем
увеличения чисел операций, которые мы можем
осуществлять, не раздумывая над ними.
Апх}>ред Порт Уайтхед
Редко встречается приложение, которое не обращается к файлам, чтобы
записывать или считывать из них информацию. Работа с директориями и
файлами — это та область, в которой не все операции являются платфор-
монезависимыми, поэтому Qt предоставляет свою собственную поддержку
этих операций, состоящую из следующих классов:
П QDir — для работы с директориями;
П QFile — для работы с файлами;
П QFiieinfo — для получения файловой информации;
П QioDevice — абстрактный класс для ввода/вывода;
П QBuffer — для эмуляции файлов в памяти компьютера.
Ввод/вывод. Класс QIODevice
Это абстрактный класс, обобщающий устройство ввода/вывода, который
содержит виртуальные методы для открытия и закрытия устройства
ввода/вывода, а также для чтения и записи блоков данных или отдельных
символов.
Реализация конкретного устройства происходит в унаследованных классах.
В Qt есть четыре класса, наследующие класс QioDevice (рис. 37.1):
П QFile — для операций с файлами;
П QBuffer — для организации буфера;
П QSocket и QSocketDevice — для сокетных соединений (см. гл. 38).
Глава 37. Работа с файлами, директориями и потоками ввода/вывода 443
1 QIODevica
гС
Й
К
т
QBuffer
QFile
QSocket
QSocketDevice
3
J
3
П.
Рис. 37.1. Иерархия классов ввода/вывода
Для создания класса устройства ввода/вывода, которому Qt не
предоставляет поддержки, необходимо унаследовать класс QioDevice и реализовать
методы: open(), close(), flush(), size(), readBlock(), writeBlock(), putch(),
getcho и ungetcho. В большинстве случаев требуется перезаписать в
унаследованном классе виртуальный метод atEndO и установить атрибуты
устройства вызовом метода из конструктора setFiags().
Благодаря интерфейсу класса QioDevice можно работать со всеми
устройствами одинаково, при этом не имеет значения, является ли устройство
файлом, буфером или другим устройством. Пример 37.1 выводит на консоль
данные из любого устройства.
[Пример 37.1. Вывод данных устройства
void print(QIODevice* pdev)
{
pdev->open(IO_ReadOnly)
for (; !pdev->atEnd();) {
cout « pdev->getch() « endl;
}
pdev->close();
}
Класс QioDevice предоставляет ряд методов, с помощью которых можно
получить информацию об устройстве ввода/вывода. Например, некоторые
устройства могут только записывать информацию, некоторые — только
считывать, а другие способны делать и то, и другое. Чтобы узнать об этом,
следует воспользоваться методами isReadableO, isWriteableO и isReadWriteO .
Работа с файлами. Класс QFile
Класс QFile унаследован от класса QioDevice (см. рис. 37.1). В нем
содержатся методы для открытия, закрытия, чтения и записи файлов. Создать
объект можно, передав в конструкторе класса QFile строку, содержащую
444
Часть VI. Особые возможности Ot
имя файла. Можно ничего не передавать в конструкторе, а сделать это
после создания объекта, вызовом метода setNameo. Например:
QFile file;
file.setName("myfile.dat") ;
Для работы с файлом его необходимо открыть в одном из режимов,
определенных в заголовочном файле класса QiODevice:
П iOReadOniy — открытие только для чтения;
□ iowriteonly — открытие только для записи;
□ IO_ReadWrite — открытие ДЛЯ чтения И записи (lO_ReadOnly I IO_WriteOnly);
□ ioAppend — открытие для добавления;
□ io_Raw — открытие для небуферизованного доступа;
П iOTransiate — применяется для перевода знаков новой строки в
зависимости от платформы. Для ОС Windows, например, \г\п, а для
MacOS — /г.
В процессе работы с файлами иногда необходимо узнать, открыт файл или
нет. Для этого вызывается метод isOpen (), который вернет true, в том
случае, если файл открыт, и false — если он закрыт. Чтобы закрыть файл,
нужно вызвать метод close о. С закрытием произведется запись всех
данных буфера. Если требуется произвести запись данных буфера в файл без
его закрытия, то вызывается метод flush о.
Проверить, существует ли нужный вам файл, можно статическим методом
exists о. Этот метод принимает строку, содержащую полный или
относительный путь к файлу. Если файл найден, то метод возвратит true, в
противном случае — false. Для проведения этой операции существует и
нестатический метод exists(). Методы readBlockO И writeBlockO ПОЗВОЛЯЮТ
считывать и записывать файлы блоками. Пример 37.2 демонстрирует
применение некоторых методов работы с файлами.
I Пример 37.2. Файловые операции
QFile filel("myfilel.dat");
QFile file2("myfile2.dat");
if (file2.exists()) {
// Файл уже существует. Перезаписать?
}
if (!filel.open(IO_ReadOnly)) {
cerr « "Ошибка открытия для чтения" « endl;
}
Глава 37. Работа с файлами, директориями и потоками ввода/вывода 445
if (!file2.open(IO_WriteOnly)) {
cerr « "Ошибка открытия для записи" « endl;
}
char а [1024];
while(!filel.atEnd()) {
int nBlocksize;
nBlocksize = filel.readBlock(a, 1024);
file2.writeBlock(a, nBlocksize);
}
filel.close();
f ile2.close();
Если требуется считать или записать данные за один раз, то используют
методы writeBiockO и readAiK). Все данные можно считать в объект класса
OByteArray, а потом записать из него в другой файл (пример. 37.3).
Пример 37.3. Запись файла за один раз
QFile filel("myfilel.dat");
QFile file2("myfile2.dat");
if (file2.exists()) {
// Файл уже существует. Перезаписать?
}
if (!filel.open(IO_ReadOnly)) {
cerr « "Ошибка открытия для чтения" « endl;
}
if (!file2.open(IO_WriteOnly)) {
cerr « "Ошибка открытия для записи" « endl;
}
QByteArray a = filel.readAll();
file2.writeBlock(a);
f ilel.close();
file2.close();
С Примечание )
Операция считывания сразу всех данных из файла занимает много
оперативной памяти, а значит, к этому следует прибегать только в случаях острой
необходимости или в том случае, когда файлы занимают мало места. Расход
памяти при считывании сразу всего файла можно значительно сократить при том
условии, что файл содержит избыточную информацию. Тогда можно
воспользоваться функциями сжатия qCompress () и qUncompress (), которые определе-
446
Часть VI. Особые возможности Qt
ны в заголовочном файле qcstring.h. Эти функции получают, в качестве
аргумента, объект класса QByteArray и возвращают, в качестве результата, новый
объект класса QByteArray.
Для проведения операций удаления файла класс QFile содержит
статический метод remove о. В этот метод необходимо передать строку,
содержащую полный или относительный путь удаляемого файла.
Класс QBuffer
Класс QBuffer унаследован от QioDevice (см. рис. 37.1). Он представляет
собой эмуляцию файлов в памяти компьютера (in-memory files). Это
позволяет записывать информацию в оперативную память и использовать объекты
как обычные файлы (открывать при помощи метода open о и закрывать
методом close()). При ПОМОЩИ методов writeBlockO И readBlockO МОЖНО
считывать и записывать блоки данных. Сами данные внутри объекта
хранятся в объекте типа QByteArray. При помощи метода buffer () можно
получить ДОСТУП К Объекту QByteArray, а При ПОМОЩИ метода setBuf fer () МОЖНО
записать его в объект QBuffer.
Класс QBuffer полезен для проведения операций кэширования. Например,
можно считывать файлы растровых изображений в объекты класса QBuffer,
а затем получать из них данные по мере необходимости.
Работа с директориями. Класс QDir
Разные платформы имеют разные способы представления путей. ОС
Windows содержит буквы дисков, например c:\Windows\System. UNIX
использует root, например /usr/bin. Обратите внимание, что для разделения
имен директорий в обоих представлениях используются разные знаки. Для
представления директорий в платформонезависимой форме. Qt
предоставляет КЛаСС QDir.
Для этих целей класс предоставляет целый ряд статических методов:
П QDir:: current о — возвращает путь директории приложения;
П QDir::currentDirPath() — возвращает абсолютный путь директории
приложения;
П QDir:: root о — возвращает root-директорию;
П QDir::drives о — возвращает указатель на список объектов класса
QFile Info",
П QDir::home о — возвращает персональную директорию пользователя.
Существование директории можно проверить с помощью метода exists О ■
Чтобы перемещаться по директориям, можно использовать методы cd() и
Глава 37. Работа с файлами, директориями и потоками ввода/вывода 447
cdUpO. Метод cd() принимает в качестве аргумента абсолютный путь
директории. Вызов cd("..") эквивалентен вызову метода cdup(). Для получения
абСОЛЮТНОГО ПУТИ ДИреКТОрИИ МОЖНО ВЫЗВаТЬ МеТОД convertToAbs ().
Для создания директории нужно вызвать метод mkdirt), в который
передается абсолютный путь. В случае успешного проведения этой операции
метод вернет значение true или, в случае неудачи, false.
Если требуется переименовать директорию, то вызывается метод rename о.
В этот метод первым параметром нужно передать старый путь, а вторым —
новый. Если операция будет проведена успешно, то метод вернет true,
иначе — false.
Удаление директорий производится методом rmdiro, который, в качестве
аргумента, получает путь и, в случае успеха, возвращает true, а в случае
неудачи — false.
Просмотр директории
При помощи класса QDir можно получить содержимое указанной
директории. При этом допускается применять различные фильтры, чтобы
исключить из списка не интересующие вас файлы. Программа, показанная на
рис. 37.2, осуществляет рекурсивный поиск файлов в директории,
указанной в текстовом поле Directory (Директория). Нажатие кнопки с растровым
изображением каталога откроет диалоговое окно для выбора нужной
директории. В текстовом поле Mask (Маска) задается фильтр для отображаемых
файлов. Например, для отображения исходных файлов на языке C++ нужно
задать в поле Mask (Маска) *.срр и *.п. После нажатия кнопки Find (Поиск)
производится отображение файлов, в соответствии с заданными
параметрами. Результаты отображаются в виджете многострочного текстового поля.
I [Non-Commercial] - FdeF nder НО □ jl
I Directory |C7qt3 source q£ I II
Maw. |" cpp * h Find j
I C7qt3 ource/q> uttongrouph ~Tj
I I C7qL,2/source/qvgroupbox cpp II
I I C7ql"" rce/qvgroupbox h II
I I C7ql ource/qwhat us cpp II
I I C7ql tource/qwhatsthis h II
I C7ql ж Nidgetinterface_p h II
1 C7ql jrce/ Jgetpkigin cpp II
j С /ql "<tourc»''q(*»dgetplugin h
I I Г /ql «idgetresizehandler cpp
I I shandler_ph II
I IL 'ql jrce/qwdgeUtackcpp I
j C7qt3<. ource/qwidgetstack h 3 1
Рис. 37.2. Программа отображения файлов директории по маске
448
Часть VI. Особые возможности Qt
\ Листинг 37.1. Конструктор класса FileFinder. Файл FileFinder.cpp
FileFinder::FileFinder(QWidget* pwgt/*= 0*/, const char* pszName/*= 0*/)
: QWidget(pwgt, pszName)
{
m_ptxtDir = new QLineEdit (QDir: : current () .absPathO , this);
m_ptxtMask = new QLineEdit("*.cpp *.h", this);
m_ptxtResult = new QTextEdit(this);
QLabel* plblDir = new QLabel("&Directory", this);
QLabel* plblMask = new QLabel("&Mask", this);
QPushButton* pcmdDir = new QPushButton(QPixmap(fileopen), "", this);
QPushButton* pcmdFind = new QPushButton("&Find", this);
connect(pcmdDir, SIGNAL(clicked()), SLOT(slotBrowse()));
connect(pcmdFind, SIGNAL(clicked()), SLOT(slotFind()));
plblDir->setBuddy(m_ptxtDir);
plblMask->setBuddy (m_ptxtMask) ;
QGridLayout* ptopLayout = new QGridLayout(this. 3, 3, 5, 5);
ptopLayout->addWidget(plblDir, 0, 0);
ptopLayout->addWidget(plblMask, 1, 0);
ptopLayout->addWidget(m_ptxtDir, 0, 1);
ptopLayout->addWidget(m_ptxtMask, 1, 1);
ptopLayout->addWidget(pcmdDir, 0, 2);
ptopLayout->addWidget(pcmdFind, 1, 2);
ptopLayout->addMultiCellWidget(m__ptxtResult, 2, 2, 0, 2) ;
В конструкторе класса FileFinder (листинг 37.1) создаются виджеты
однострочного и многострочного текстовых полей (указатели mptxtDir,
mptxtMask и mptxtResuit). Первый виджет инициализируется абсолютным
путем, возвращаемым методом absPathO, который, в свою очередь,
инициализируется значением текущей директории, возвращаемой методом
current о. Второй виджет инициализируется строкой, предназначенной для
фильтрации найденных файлов. Для старта операции поиска и для запуска
диалогового окна выбора директории создаются две кнопки нажатия
(указатели pcmdFind И pcmdDir), КОТОрые соединяются СО слотами slotFind О И
slotBrowse. В конструкторе создаются два виджета надписей, а вызов
методов setBuddyO ассоциирует их с виджетами однострочных текстовых полей.
Созданные виджеты размещаются в виде таблицы при помощи объекта
класса QGridLayout (см. гл. 10).
Глава 37. Работа с файлами, директориями и потоками ввода/вывода 449
{ Листинг 37.2. Метод slotBrowse () .Файл FileFinder.cpp
void FileFinder::slotBrowse()
{
QString str = QFileDialog::getExistingDirectory(m_ptxtDir->text());
m_ptxtDir->setText(str);
}
Метод siotBrowseO открывает диалог для выбора поисковой директории
(листинг 37.2). После закрытия диалогового окна директория записывается
в текстовое поле Directory (Директория) с помощью метода setText ().
Листинг 37.3. Метод slotFind (). Файл FileFinder.cpp
void FileFinder::slotFind()
{
start(QDir(m_ptxtDir->text())) ;
}
Метод slotFind о запускает операцию поиска и передает в метод start о
выбранную пользователем директорию (листинг 37.3).
I Листинг 37.4. Метод start (). Файл FileFinder.cpp
void FileFinder::start(QDir dir)
{
QStringList::Iterator it;
QStringList listFiies =
dir.entryList(m_ptxtMask->text(), QDir::Files);
for (it = listFiles.begin(); it != listFiies.end(); ++it) {
m_ptxtResult->append(dir.absFilePath(*it));
}
QStringList listDir = dir.entryList(QDir::Dirs);
for (it = listDir.begin(); it != listDir.end(); ++it) {
if (*it == "." II *it = "..") {
continue;
}
start(QDir(dir.absFilePath(*it))) ;
}
}
В методе start о переменная listFiies получает список файлов текущей
директории (листинг 37.4). Для этого в методе установлены два фильтра,
450 Часть VI. Особые возможности ^
первый из которых распространяется на имена и расширения, а второй
(QDir:: Files) говорит о том, что список должен содержать только файлы
Элементы полученного списка добавляются в виджет многострочного
текстового поля с помошью метода append о. По завершении работы цикла
добавления в метод entryListo передается параметр QDir::Dirs для
получения списка директорий. Для каждого из элементов списка, кроме "." и "."
снова вызывается метод start ().
Информация о файлах. Класс QFilelnfo
Задача этого класса состоит в предоставлении информации о свойствах
файлов, например: имя, размер, время последнего изменения, права доступа
и т. д. Объект класса QFilelnfo создается передачей в его конструктор
строки с именем файла, но можно передавать и объекты класса QFiie.
Файл или каталог?
Иногда нужно убедиться, что исследуемый объект является каталогом, а не
файлом, и наоборот. Для этой цели существуют методы isFileO и isDiro.
В том случае, если объект является файлом, метод isFileO возвращает
значение булевого типа true, иначе этот метод вернет false. Если объект
является директорией, то метод isDiro возвращает true, иначе — false. Кроме
этих методов, класс QFilelnfo содержит метод isSymLinkO, возвращающий
true, если объект является символьной ссылкой (symbolic link).
( Примечание )
Символьные ссылки применяются в UNIX для обеспечения связи с файлами
или каталогами. Создаются они при помощи команды in с ключом -s.
Путь и имя файла
Чтобы ПОЛУЧИТЬ ПУТЬ К файлу, НУЖНО Воспользоваться МетОДОМ absFilePathO-
Для получения относительного пути к файлу следует использовать метод
filePathO. Для получения имени файла нужно вызвать метод fiieNameO,
который возвращает имя файла вместе с его расширением. Если нужно
только имя файла, то следует вызвать метод baseNameO. Для получения
расширения используется метод extensionO.
Информация о дате и времени
Иногда нужно узнать время создания файла или время его последнего
изменения либо чтения. Для этого класс QFilelnfo предоставляет методы
createdO, lastModifiedO И lastReadO. Эти методы возвращают объекты
fnaea 37. Работа с файлами, директориями и потоками ввода/вывода 451
класса QDateTime (см. гл. 35), которые можно преобразовать в строку
методом tostring (). Например:
// Дата и время создания файла
filelnfо.created().toString();
// Дата и время последнего изменения файла
filelnfо.lastModified().toString();
// Дата и время последнего чтения файла
filelnfо.lastRead().toString();
Получение атрибутов файла
Атрибуты файла дают информацию о том, какие операции можно
проводить с файлом. Для их получения в классе QFiieinfo реализованы
следующие методы:
О isReadabieO — возвращает true, если из указанного файла можно читать
информацию;
О iswriteabie () — возвращает true, если в указанный файл можно
записывать информацию;
О isHiddenO — возвращает true, если указанный файл является скрытым;
□ isExecutableO — возвращает true, если указанный файл можно
исполнять. Это определяется не на основании расширения файла, как
привыкли считать программисты в DOS и ОС Windows.
Определение размера файла
Метод size о класса QFiieinfo возвращает размер файла в байтах. Размер
файлов редко отображается в байтах, чаще используются специальные
буквенные обозначения, сообщающие об его размере. Например, для
килобайта — это буква К, для мегабайта — М, для гигабайта — G, а для
терабайта — Т. Следующая функция позволяет сопровождать буквенными
обозначениями размеры, лежащие даже в терабайтном диапазоне (вполне
возможно, что лет через десять это будет обычный размер файла):
QString fileSize(Q_UINT64 nSize)
{
for (int i = 0; nSize > 1024; nSize /= 1024, ++i) {
}
return QString().setNum(nSize) + "BKMGT"[i];
}
452
Часть VI. Особые возможности Gt
Потоки ввода/вывода
Объекты файлов сами по себе обладают только элементарными методами
для чтения и записи информации. Использование потоков делает запись и
считывание файлов более простым и гибким. Для файлов, содержащих
текстовую информацию, следует использовать класс QTextstream, а для
двоичных файлов — класс QDataStream.
Применение классов QTextstream и QDatasteam такое же, как и для
стандартного потока в языке C++ (iostream), с той лишь разницей, что они
работают с объектами класса QiODevice. Благодаря этому, потоки можно
использовать И ДЛЯ СВОИХ Собственных КЛасСОВ, унаследованных ОТ QiODevice. Для
записи данных в поток используется оператор «, а для чтения данных из
потока — ».
Класс QTextstream
Класс QTextstream предназначен для чтения текстовых данных. В качестве
текстовых данных могут выступать не только объекты, произведенные
классами, унаследованными ОТ QiODevice, НО И Переменные ТИПОВ char, QChar,
char*, QCString, QString, short, int, long, float И double. Числовые данные,
передаваемые в поток, автоматически преобразуются в текст. Можно
управлять форматом ИХ Преобразования, например, метод QTextstream:: precision О
задает количество знаков после запятой. Особенно хорошо использовать
этот класс для считывания и записи текстовых данных, находящихся в
формате Unicode. Но для этого нужно передать в метод setEncoding ()
значение Qt::Unicode.
Чтобы считать текстовый файл, необходимо создать объект типа QFile и
считать данные методом QTextstream: :readLine(). Например:
QFile file("myfile.txt");
if (file.open(IO_ReadOnly)) {
QTextstream stream(&file);
QString str;
while (1stream.atEnd()) {
str = stream.readLine();
cout « stream, data () « endl;
}
if (file.statusO !- IO_OK) {
cerr « "Ошибка чтения файла" « endl;
}
file.close();
Глава 37. Работа с файлами, директориями и потоками ввода/вывода 453
Методом QTextstream: :read() можно считать сразу весь текстовый файл
в одну строку. Например:
QFile file("myfile.txt");
QTextstream stream(&file);
QString str = stream.read();
Чтобы записать в файл текстовую информацию, необходимо создать объект
класса QFile и воспользоваться оператором «. Перед записью можно
провести необходимые преобразования со строкой. Например:
QFile file("myfile.txt");
QString str = "This is the test";
if (file.open(IO_WriteOnly)) {
QTextstream t(&file) « str.upperO; // Запишет-THIS IS THE TEST
file.close ();
}
if (file.status() != I0_0K) {
cerr « "Ошибка записи" « endl;
}
Класс QTextstream создавался для записи и чтения только текстовых
данных, поэтому при записи двоичных данных они будут искажены. Для
чтения и записи двоичных данных без искажений следует пользоваться
классом QDataStream.
Класс QDataStream
Класс QDateStream является гарантом того, что формат, в котором будут
записаны данные, останется платформонезависимым и его можно будет
считать и на других платформах. Это делает класс незаменимым для обмена
данными между клиентом и сервером с использованием сокетных
соединений (см. гл. 38). Класс поддерживает очень большое количество типов
данных, К которым ОТНОСЯТСЯ QByteArray, QFont, Qlmage, QMap, QPixmap, QString,
QVaiueList и variant. Следующий пример записывает в файл объект цвета
(QCoior), объект растрового изображения (Qimage) и числовое значение:
QFile file("myfile.bin");
if(file.open(IO_WriteOnly)) {
QDataStream stream(&file);
stream « QColor::red « Qlmage ("myimg.jpg") « 10;
}
if (file.status() != IO_OK) {
cerr « "Ошибка записи" « endl;
}
454
Часть VI. Особые возможности Qt
Для считывания этих данных из файла нужно сделать следующее:
int n;
Qlmage img;
QColor color;
QFile file("myfile.bin");
if(a.open(IO_ReadOnly)) {
QDataStream stream(&file);
stream » color » img » color;
}
Резюме
Qt имеет абстрактный класс QiODevice, который представляет собой
устройство ввода/вывода. Классы, унаследованные от QiODevice, предоставляют
возможность записывать и считывать данные в файл байтами или блоками.
Классы QFile и QBuffer наследуют этот класс.
Класс QFile содержит методы, необходимые для работы с файлами, такие
как открытие, закрытие, чтение и запись.
Класс QBuffer предназначен для эмуляции файлов в оперативной памяти.
Такие файлы хранятся не на диске, а в памяти компьютера, что
существенно ускоряет процесс обращения к ним.
Qt предоставляет классы потоков, которые делают работу с данными файлов
более удобной. Это два класса: Qdatastream — для двоичных данных, и
QTextstream — для текстовых. Использование этих потоков очень похоже на
использование обычного потока в языке C++. Как и в стандартных
потоках, для чтения и записи перегружены операторы « и ». Так как потоки
принимают объекты унаследованных от QiODevice классов, то вместо
объекта класса QFile данные можно перенаправить в объект класса QBuffer.
Класс QDir предоставляет платформонезависимый подход для работы с
директориями. Этот класс содержит методы для создания и уничтожения
директорий, а также для получения главной, текущей и персональной
директории пользователя.
При помощи класса QFiieinfo можно получить всю информацию о файлах
и директориях.
ГЛАВА 38
Программирование
поддержки сети
Информация есть информация,
а не энергия и не материя.
Н. Винер "Кибернетика"
Сокетное соединение
Сокет — это устройство пересылки данных с одного конца связи на другой.
Другой конец может принадлежать процессу, работающему на локальном
компьютере, а может располагаться и на удаленном компьютере,
подключенному к Интернету и расположенному в другом полушарии земли.
Сокетное соединение — это соединение типа точка-точка (point to point),
которое производится между двумя процессами.
Сокеты разделяют на дейтагралшные (datagram) и поточные. Дейтаграммные
сокеты осуществляют обмен пакетами данных. Поточные сокеты
устанавливают связь и производят потоковый обмен данными через установленную
ими связь. На практике, поточные сокеты используются гораздо чаще, чем
дейтаграммные из-за того, что они предоставляют дополнительные
механизмы, направленные против искажения и потери данных. Поточные сокеты
работают в обоих направлениях, т. е. то, что один из процессов записывает
в поток, может быть считано процессом на другом конце связи, и наоборот.
Для сокетов Qt предоставляет класс QSocket, определенный в заголовочном
файле qsocket.h, являющийся реализацией класса QSocketDevice,
определенного в заголовочном файле qsocketdevice.h, который, в свою очередь,
представляет собой набор методов для конкретных платформозависимых сетевых
API. Класс QSocketDevice унаследован от класса QioDevice, а это значит, что
к его объектам для чтения и записи можно применять все методы этого
Класса И классы ПОТОКОВ QDataStream И QTextStream (см. гл. 37).
456
Часть VI. Особые возможности о
Модель клиент-сервер
Сценарий модели клиент-сервер выглядит очень просто: сервер предлагает
услуги, а клиент ими пользуется. Программа, использующая сокеты, может
выполнять либо роль сервера, либо роль клиента. Для того чтобы клиент
мог взаимодействовать с сервером, ему нужно знать его IP-адрес и номер
порта, через который клиент, желающий воспользоваться этими услугами
сервера, должен сообщить о себе. Когда клиент устанавливает соединение с
сервером, система назначает данному соединению отдельный сокет,
которому соответствует случайно выбранный, свободный порт. После этого
устанавливается связь между двумя этими сокетами, по которой высылаются
данные запроса к серверу. А сервер высылает клиенту по этому соединению
готовые, обработанные результаты согласно его запросам. Сервер не
ограничен связью только с одним клиентом, на самом деле он может
обслуживать многих клиентов.
Рис. 38.1. Модель клиент-сервер
Каждому сокету соответствует уникальный номер порта. Некоторые номера
зарезервированы для так называемых стандартных служб (табл. 38.1).
Таблица 38.1. Зарезервированные номера портов
Порт
11
21
22
23
25
80
110
139
Сервис
systat
FTP
SSH
Telnet
SMTP
HTTP
POP3
Netbios-SSN
Описание
Показ зарегистрированных в системе пользователей
Доступ к файлам по сети
Зашифрованное соединение к удаленному компьютеру
Удаленное соединение с компьютером
Отсылка электронной почты
Web-сервер (иногда применяются порты 8080 или 8000)
Получение электронной почты
Разделение сетевых ресурсов
Глава 38. Программирование поддержки сети
457
Таблица 38.1 (окончание)
Порт
Г-143
194
Г 442
Сервис
IMAP
IRC
HTTPS
Описание
Пересылка электронной почты
Internet Relay Chat
Зашифрованный HTTP
Реализация сервера
Для реализации сервера Qt предоставляет удобный класс QServersocket. Это
абстрактный класс и для его использования нужно унаследовать и
перезаписать виртуальный метод newConnection (). Этот метод вызывается при
соединении с клиентом.
Рис. 38.2. Реализация сервера
| Листинг 38.1. Файл main.cpp
#include <qapplication.h>
#include <qtextview.h>
#include <qvbox.h>
#include <qlabel.h>
#include "MyServer.h"
//
int main(int argc, char** argv)
{
QApplication appfargc, argv);
MyServer server(2323);
QVBox vbx;
new QLabel("<Hl>Server</Hl>", &vbx);
458
Часть VI. Особые возможности Gt
QTextEdit* ptxt = new QTextView(&vbx);
QObject::connect(&server, SIGNAL(message(const QString&)),
ptxt, SLOT(append(const QString&))
);
app.setMainWidget(&vbx);
vbx.show();
return app.exec();
}
В листинге 38.1 создается объект сервера. Чтобы запустить сервер, нужно
создать объект класса MyServer, передав в конструктор номер порта, по
которому должен осуществляться нужный сервис. В нашем случае передается
номер порта, равный 2323. Слот append о созданного виджета
многострочного текстового поля (указатель ptxt) соединяется для отображения
информации с сигналом объекта сервера message о.
| Листинг 38.2. Файл MyServer.h
#ifndef _MyServer_h_
#define _MyServer_h_
#include <qsocket.h>
#include <qserversocket.h>
#include <qtextstream.h>
class MyServer : public QServerSocket {
Q_OBJECT
public:
//
MyServer(int nPort, int nBacklog = 0, QObject* pobj = 0)
: QServerSocket (nPort, nBacklog, pobj)
{
if (!ok()) {
exit(l);
}
}
//
virtual void newConnection(int nSocketld)
{
QSocket* socket = new QSocket(this);
socket->setSocket(nSocketld);
Глава 38. Программирование поддержки сети
459
connect(socket,
SIGNAL(delayedCloseFinished()),
SLOT(slotConnectionClosed())
);
connect(socket, SIGNAL(readyRead()), SLOT(slotReadClient()));
}
signals:
void message(const QString&);
public slots:
//
void slotReadClient()
{
QSocket* socket = (QSocket*)sender();
if (socket->canReadLine()) {
QString str = socket->readLine();
emit message("Client has sended - " + str);
QTextStream (socket) « "Server has recived - " « str;
}
}
//
void slotConnectionClosed()
{
QSocket* socket = (QSocket*)sender();
delete socket;
}
};
#endif //_MyServer_h_
Как видно из листинга 38.2, конструктор класса MyServer производит вызов
конструктора QServerSocket, после чего начинается ожидание соединения с
клиентами. При помощи метода ok о в конструкторе производится проверка
правильности инициализации сокета. Каждое новое соединение приводит к
вызову метода newconnection (). В этом методе сервер создает новый сокет и
соединяет его сигналы readyToRead () И delayedCloseFinished () СО слотами
slotReadClient() и slotConnectionClosedО . В слоте slotReadClient()
ПРОИЗВОДИТСЯ преобразование указателя к типу QSocket, возвращаемого методом
sender о. Возвращение методом canReado объекта класса QSocket значения
true означает, что из сокета можно получить информацию. Информация
считывается из сокета методом readLineo и записывается в
переменную строкового типа str. После этого происходит высылка сигнала
460 Часть VI. Особые возможности д*
message (const QString&), в котором передается считанная информация. Это
нужно для того, чтобы ее мог отобразить присоединенный к этому сигналу
виджет многострочного текстового поля. В заключение, сервер записывает в
сокет подтверждение о получении информации. Слот siotconnectionciosedo
вызывается при закрытии соединения. В этом методе производится
уничтожение объекта сокета.
Реализация клиента
Для реализации клиента нужно создать объект от класса QSocket, а затем
вызвать метод connectToHost (), передав в него первым параметром имя
компьютера или его IP-адрес, а вторым — номер порта сервера. Объект
класса QSocket сам попытается произвести установку связи с сервером и, в
случае успеха, вышлет сигнал connected о. В противном случае будет выслан
сигнал error(int) с кодом ошибки, определенной в перечислении QSocket::
Error. Это может произойти, например, в том случае, если на указанном
компьютере не установлен сервер или не соответствует номер порта.
Соединение с сервером не останавливает выполнение самой программы. Это
особенно важно, т. к. при использовании этого класса в программах с
графическим интерфейсом пользователя он не будет заблокирован и профамма
продолжит реагировать на действия пользователя. После установления
соединения объект класса QSocket может высылать или считывать данные
сервера. Рис. 38.3 иллюстрирует взаимодействие клиента с сервером. Окно про-
фаммы, отображенное в левой части рисунка, является реализацией
клиента. Высылка на сервер информации, введенной в однострочном текстовом
поле окна клиента, производится после нажатия на кнопку Send (Послать).
rajtiii'iiHtiHIOrPFJI I : ^5ГГ1
Client Server
J Connected to server I I Client has sended - Testl I
J Server has recived - Testl I I Client has sended - T 12
j Server has recived - Test2 I I Client has sended - Tps о
II Server has recived - TesG I II
|Test4|
I Send |
Рис. 38.3. Взаимодействие клиента с сервером
I Листинг 38.3. Файл MyClient.cpp
#ifndef _MyClient_h_
#define _MyClient_h_
Глава 38. Программирование поддержки сети 467
#include <qsocket.h>
finclude <qvbox.h>
$include <qtextview.h>
finclude <qlineedit.h>
finclude <qlabel.h>
finclude <qpushbutton.h>
#include <qtextstream.h>
// ===__=_===_======================================================
class MyClient : public QVBox {
Q__OBJECT
private:
QSocket* mjpsocket;
QTextView* mjptxtInfo;
QLineEdit* mjptxtInput;
public:
//
MyClient(const QString& strHost, int nPort)
{
new QLabel("<Hl>Client</Hl>", this);
mjptxt Info = new QTextView(this);
mjptxtInput = new QLineEdit(this);
QPushButton* pcmd = new QPushButton("&Send", this);
connect(pcmd, SIGNAL (clicked()), SLOT(slotSendToServer()));
mjpsocket = new QSocket(this);
connect(m_psocket, SIGNAL (connected()), SLOT(slotConnected()));
connect(m_psocket, SIGNAL(error(int)), SLOT(slotError(int)));
connect (mjpsocket, SIGNAL(readyRead()), SLOT(slotReadyRead()));
m_psocket->connectToHost(strHost, nPort);
>
private slots:
//
void slotSendToServer()
{
QTextStream (mjpsocket) « mj?txtlnput->text() « "\n";
mjptxtInput->setText("");
}
//
void slotReadyRead()
462
Часть VI. Особые возможности! qt
{
while (m_psocket->canReadLine()) {
mj?txtInfo->append(m_psocket->readLine());
}
}
//
void slotConnected()
{
mjptxtlnfo->append("Connected to server");
}
//
void slotClosedO
{
m_ptxtInfo->append("Connection closed");
}
//
void slotError(int e)
{
mj?txtInfo->append("Can not find server");
}
};
#endif //_MyClient_h_
В листинге 38.3, в конструкторе, создается пользовательский интерфейс
программы, состоящий из надписи, кнопки нажатия, однострочного и
многострочного текстовых полей. Сигнал clicked о виджета кнопки нажатия
соединяется со слотом siotsendToServer () класса Myciient, ответственным
за отправку данных на сервер. Также, в конструкторе происходит создание
объекта сокета (указатель m_psocket) и соединение сигналов connectedO,
error () И readyReadO СО слотами slotConnected (), slotErrorO И
siotReadyRead () соответственно. После этого из объекта сокета вызывается
метод connectToHost (), осуществляющий связь с сервером. Первым
параметром в этот метод передается имя компьютера, а вторым — номер порта.
Если запуск сервера и клиента производится на одном и том же
компьютере, то в качестве имени компьютера можно передать localhost. Номер
порта, в нашем случае, должен соответствовать значению 2323. После нажатия
кнопки Send (Послать) производится вызов слота siotsendToServerO,
который записывает в сокет текстовую информацию, находящуюся в виджете
однострочного текстового поля (указатель m_ptxtinputo). Слот
siotReadyToRead () вызывается при поступлении данных от сервера.
Полученная информация заносится в виджет многострочного текстового поля
(указатель m_ptxtInfo) С ПОМОЩЬЮ метода append(). СЛОТ slotConnected()
Глава 38. Программирование поддержки сети
463
вызывается в случае установления связи с сервером. Слот siotsiosedo
вызывается при закрытии установленной связи. Слот siotErrorO вызывается
при ошибке и невозможности установления связи.
Высокоуровневые классы
Для облегчения работы Qt предоставляет специализированные классы,
базирующиеся на классе QSocket. Работа этих классов асинхронна, так же как
у класса QSocket, поэтому не нужно бояться того, что приложение будет
заблокировано на время отправки сообщений или получения данных.
Класс QFtp
Передача файлов является часто выполняемой операцией практически во
всех сетях. FTP (File Transfer Protocol, протокол передачи файлов) —
протокол, наиболее известный из старых протоколов, являющийся одной из
первых сетевых служб. Целью создания этого протокола было предоставление
пользователям доступа к файлам на удаленном компьютере. Кроме
предоставляемого сервиса, протокол имеет целый ряд команд, с помощью которых
можно управлять удаленным компьютером для передачи данных. Например:
О get — скопировать файл с удаленного сервера;
О put — копировать файл на удаленный сервер;
□ rmdir — удалить каталог на удаленном сервере;
О mkdir — создать каталог на удаленном сервере;
□ cd — открыть каталог на удаленном сервере;
О rename — переименовать файл или каталог на удаленном сервере;
О close — закрыть соединение с удаленным сервером.
Класс QFtp реализует сторону клиента FTP-протокола и содержит в себе
методы для наиболее часто используемых операций с FTP. Название этих
методов соответствует названию FTP-команд, например: get о соответствует
команде get, a put () — команде put и т. д. Также класс QFtp предоставляет
возможность исполнения любых FTP-команд. Для этого в метод
rawcommando нужно передать строку, содержащую необходимую команду.
Например:
ftp.rawCommand("MKDIR MyDir");
Каждый из методов команд возвращает идентификационный номер,
который используется в сигналах класса QFtp. Этим можно воспользоваться для
оповещения пользователя о проводимых операциях. Например, в
начале исполнения одной из команд объект класса QFtp высылает сигнал
464
Часть VI. Особые возможности Qt
commandstarted(int), а по завершении команды высылается сигнал
commandFinished(int, bool). При этом идентификационный номер
проводимой операции содержится в параметре типа int.
Класс QFtp содержит методы для осуществления соединения с FTP-cep-
вером: connectToHost(), iogin(). Пример 38.1 демонстрирует считывание
файла file.txt, размешенного на удаленном компьютере, посредством команд
FTP, и запись его в текущей директории под именем MyFile.txt. Для этого,
сначала открывается файл для записи, а затем запускается серия из пяти
FTP-команд. Обратите внимание на второй аргумент метода get о, с
помощью которого задается устройство ввода/вывода.
| Пример 38.1. Считывание файла с FTP-сервера
CFile file("MyFile.txt") ;
QFtp ftp;
if (file.open(IO_WriteOnly)) {
ftp.connectToHost("ftp://ftp.bhv.ru");
ftp.login();
ftp.cd("pub");
ftp.getCfile.txt ", &file) ;
ftp.close ()
file.close ();
}
Класс QHttp
HTTP (HyperText Transfer Protocol, протокол передачи гипертекста) является
стандартным и самым известным протоколом для обмена данными в сетях.
Qt предоставляет класс QHttp для реализации стороны клиента
HTTP-протокола. Использование этого класса очень похоже на использование класса
QFtp. Пример 38.2 демонстрирует запись страницы www.bhv.ru/index.html
в текущую директорию под именем Page.html.
| Пример 38.2. Считывание файла с HTTP-сервера
QFile file("Page.html");
QHttp http;
if (file.open(IO_WriteOnly)) {
http.setHost("www.bhv.ru");
http.get("index.html", & file);
http.closeConnection()
file.close();
}
Глава 38. Программирование поддержки сети
465
Класс QUrl
Любой адрес к ресурсу можно задать при помощи URL (Uniform Resource
Locator, универсальный указатель ресурса). Его формат выглядит
следующим образом:
метод://компьютер[:порт] [путь][?запрос],
где:
П метод — принимает различные обозначения. Например, для
использования гипертекста используется обозначение http, для доступа к файлу —
file, для электронной почты — maiito;
О компьютер — это имя компьютера или его IP-адрес;
О порт — номер порта сервера для соединения. Это могут быть
зарезервированные порты (см. табл. 38.1);
П путь — это виртуальный иерархический путь к ресурсу сервера.
"Виртуальный" означает, что он физически не существует и его перевод
принимает на себя сторона сервера. "Иерархический" означает, что он
состоит из подпутей, разделенных знаком /;
П запрос имеет следующий формат: имя1=значение&имя2=значение2&. . ., И
предназначен для передачи серверу некоторой дополнительной
информации.
Класс QUrl — это реализация универсального указателя ресурса. Класс
предоставляет высокоуровневый интерфейс для получения информации,
например, имя файла, путь, протокол и порт.
Qt предоставляет класс QUrioperator, который содержит большое количество
методов, обеспечивающих прозрачный доступ по сети к данным для
проведения с ними различного рода операций, таких как копирование,
переименование, удаление и т. д. В качестве параметра в эти методы требуется
передавать строку в URL-формате. Например, копирование локального файла
на удаленный сервер посредством FTP можно провести следующим
образом:
QUrioperator qurl;
qurl.copy("ftp://ftp.trolltech.com/qt/pdf/whitepaper.pdf",
"file:c:/whitepaper.pdf");
При удачном проведении операций объект класса QUrioperator высылает
сигнал finished (QNetworkOperation*). Для визуального отображения прово-
ДИМЫХ операций МОЖНО соединить сигнал dataTransferProgress(int, int,
QNetworkOperation*) С соответствующим СЛОТОМ
466
Часть VI. Особые возможности о*
Резюме
Сокетные соединения — это стандартный механизм обмена данными через
сеть в обоих направлениях. Каждому сокету соответствует пара значений:
сетевой адрес и номер порта.
Сценарий клиент-сервер выглядит следующим образом: сервер занимает
определенный порт, по которому он предоставляет свои услуги, после этого
он начинает ожидать поступления запросов от клиентов через этот порт.
Чтобы подключиться к серверу, клиент должен знать его адрес и номер
порта. Для соединения с сервером клиент должен создать сокет.
Для более простой работы с сетью Qt предоставляет специализированные
классы QFtp, QHttp и QUrioperator, представляющие собой реализации
стороны клиента.
ГЛАВА 39
Работа с XML
Все воистину мудрые мысли были обдуманы
уже тысячи раз, но чтобы они стали
по-настоящему вашими, нужно честно обдумывать
их еще и еще, пока они не укоренятся в
вашем мозгу.
Гете
В настоящее время XML — одна из самых активно используемых
технологий. Зайдите в книжный магазин, и вы, наверное, поразитесь количеству
книг, посвященных XML. С распространением Интернета обмен данными
между разными платформами стал необходимостью для всех программ,
работающих с данными. Это и послужило поводом для создания XML.
Разработка XML (Extensible Markup Language, расширяемый язык разметки)
началась в 1996 году, а в феврале была стандартизирована организацией
W3C (World Wide Web Consortium). На самом деле XML не представляет
собой ничего нового и является упрощенной версией языка SGML,
разработанного в начале 80-х. Разработчики XML переняли из него все самое
лучшее с учетом опыта языка HTML и создали нечто, по своей мощности
не уступающее SGML, но вместе с тем гораздо удобнее и проще как для
понимания, так и для использования.
XML не имеет лицензии, что позволяет использовать этот формат в своих
программах бесплатно. Поддержка XML во всем мире исключает
зависимость от отдельной фирмы или платформы, оказывающей поддержку для
XML. Так как XML-документ представляет собой текст в формате ASCII
или Unicode, то он является читабельным и для человека. XML-документ
может быть изменен при помощи простых текстовых редакторов, например,
программой Notepad (Блокнот) из стандартного набора ОС Windows.
468 Часть VI. Особые возможность, qt
Основные понятия
и структура XML-документа
XML — это средство хранения структурных данных в текстовом файле
Примером структурированных данных являются: адресная книга,
генеалогическое дерево, информация о продуктах и т. д. Язык XML очень похож на
HTML. Язык XML описывает структуру самого документа без детализации
его отображения. Также следует заметить, что XML гораздо строже, чем
HTML, например, спецификация XML при ошибке запрещает
приложению, работающему с XML-документом, предпринимать попытку
корректировки. В подобных случаях приложение должно прекратить считывание
дефектного файла и сообщить об ошибке.
В примере 39.1 приведен упрощенный вариант XML-документа. Этот
пример представляет собой структуру адресной книги, предназначенную для
хранения имен, телефонов и адресов электронной почты.
{ Пример 39.1. XML-документ. Файл aderessbook.xml
<?xml version = "1.0"?>
<!— My Address Book —>
<addressbook>
<contact number = "l*^
<name> Piggy</name>
<phone>+49 631322187</phone>
<email>piggy@mega.de</email>
</contact>
<contact number = "2">
<name>Kermit</name>
<phone>+49 631322181</phone>
<email>kermit@mega. de</email>
</contact>
</addressbook>
Любой XML-документ начинается с заголовка, говорящего о
принадлежности к этому формату и указывающего номер версии. Сам XML-документ
состоит из множества элементов (elements). Имена элементов заключаются
между символами < и >, образуя теги. Теги нужны для обозначения грании
элемента. Например, начало элемента обозначается открывающимся тегом
<name>, а его конец — закрывающимся </name>. Между этими тегами может
находиться содержание, например, <name>Piggy</name>, но это
необязательно. Иногда встречаются элементы, не имеющие содержания, например —
<emptyx/empty>. Для подобных случаев спецификация XML предусматрива-
Глава 39. Работа с XML
469
ет сокращенную форму, предыдущую надпись можно заменить на <empty/>.
Может показаться, что подобные теги не могут содержать информацию, но
это не так — информацию можно сохранить при помощи атрибутов,
следующих сразу за именем элемента. Например, <empty number = "i"x/empty>
или <empty number = "i"/>. Комментарии представляют собой одну или
несколько строк текста, ограниченных тегами <! — и —>.
Основные отличия тегов HTML от тегов XML заключаются в следующем:
О теги используются только для сохранения информации, а интерпретация
этих данных целиком лежит на приложении, которое их считывает.
Например, в XML тег <р> необязательно является тегом параграфа, как
у HTML, а может обозначать, например, properties (свойства);
О для описания документов можно использовать теги с любыми
подходящими названиями. Следует учитывать, что строчные и прописные буквы
В Именах различаются, так, например, <TagX/Tag> И <tagx/tag>
ЯВЛЯЮТСЯ разными тегами.
Рис. 39.1 представляет отрывок графического эквивалента структуры,
приведенной в примере 39.1. Обратите внимание, что элементы
XML-документа задают иерархическую структуру в виде дерева
^Ш!
Р [COMMENT] -
$$ aaressbook
$fc contact
Я $$ name
Ш [TEXT]
4Ь Phone
Ш [TEXT]
$$ email
5E2 [TEXT]
► version = "1.0"
► My Address Book
► (number="1")
► Piggy
► +49 631322187
► pigg1 gmega de
Рис. 39.1. XML-документ
XML и Qt
Библиотека Qt активно использует формат XML. Например, программа
Qt Designer (см. гл. 43) сохраняет файлы пользовательского интерфейса
именно в этом формате. Он также используется утилитами Qt,
предназначенными для интернационализации приложений (см. гл. 28):
Для использования XML в своих программах Qt предоставляет две
возможности. Первая именуется DOM, вторая — SAX. Нельзя однозначно сказать,
что одна из них лучше другой. Каждая имеет свои преимущества.
470
Часть VI. Особые возможности п*
Работа с DOM
DOM (Document Object Model, объектная модель документа) — это
стандартное API для анализа XML-документов, разработанное W3C. Самое
большое преимущество состоит в возможности представления XML-
документа, в памяти компьютера, в виде древовидной структуры. Цена
этого удобства очевидна — большой расход памяти. Но если на том
компьютере, где запускается ваша программа, нет недостатка в оперативной памяти, то
использование DOM будет наиболее подходящим решением. Рис. 39.2
отображает иерархию классов QDomNode, предоставляемого Qt для работы
с DOM. Все классы иерархии определены в одном и том же
заголовочном файле qdom.h. Самые используемые из этих классов — QDomNode,
QDomElement, QDomAttr И QDomText.
1 QDomNode"
H QDomProcessinglnstruction \к
H QDomNotation |f
QDomEntityReference
QDomEntity
|-| QDomElement"
\-\ QDomDocumentType [3
{ QDomDocumentFragment~~kj
] QDomDocument ~T|
QDomAttr
QDomCharacterData к
wmmmMim'mmmmmtim»u.imi!.vjmB№
f-j QDomComment"
4
QDomText
QDomCDATASection
Э
Рис. 39.2. Иерархия классов для работы с DOM
Чтение XML-документа
Класс QDomElement создан для представления элементов. Иерархия DOM
содержит узлы различного типа. Например, узел элемента соответствует
открытому и закрытому тегу. Данные, находящиеся между этими тегами,
представляют собой узлы потомков, также типа "элемент". Все узлы
иерархии DOM являются объектами класса QDomNode. Эти объекты способны со-
Глава 39. Работа с XML
471
держать в себе любые типы узлов. Для проведения операций над узлом, его,
прежде всего, необходимо преобразовать к нужному типу. Для
преобразования Объектов QDomNode В QDomElement следует ВОСПОЛЬЗОВаТЬСЯ меТОДОМ
QDomNode::toEiement(). Нужно всегда проверять возвращаемое этим методом
значение, в случае ошибки будет возвращено нулевое значение, которое
можно проверить методом isNuiK).
Программа, приведенная в листингах 39.1—39.3, осуществляет чтение XML-
документа, приведенного в примере 39.1, и выводит его данные на экран
следующим образом:
Attr:l
TagName:name Text:Piggy
TagNameiphone Text:+49 631322187
TagName:email Text:piggy@mega.de
Attr:2
TagName: name Text: Kermi t
• TagNamerphone Text:+49 631322181
TagName: email Text: kermit@mega.de
! Листинг 39.1. XmlDOMRead.pro
TEMPLATE = app
CONFIG += qt warn_on release
Win32: CONFIG += console
SOURCES = main.cpp
TARGET = XmlDOMRead
Обратите внимание на файл проекта — в опции config добавилось значение
console. Это нужно для того, чтобы приложение было в состоянии
осуществлять вывод на консоль (листинг 39.1).
Листинг 39.2. Функция main (). Файл main.cpp
int main()
{
QDomDocument domDoc;
QFile fileC'addressbook.xml") ;
if(file.open(IO_ReadOnly)) {
if(domDoc.setContent(&file) ) {
QDomElement domElement = domDoc.documentElement();
traverseNode(domElement);
}
472
Часть VI. Особые возможности Ot
file.close() ;
}
return 0;
}
В функции maino, показанной в листинге 39.2, создаются объекты классов
QDomDocument И QFile. Объект Класса QDomDocument представляет собой XML-
документ. Для считывания XML-документа в метод setcontexto объекта
класса QDomDocument необходимо передать объект, произведенный от класса,
унаследованного от QiODevice. В данном случае — это класс QFile. Далее,
для получения объекта класса QDomEiement производится вызов метода
documentEiemento объекта класса QDomDocument. Полученный объект
передается в функцию traverseNode (), которая обеспечивает прохождение по всем
элементам XML-документа.
| Листинг 39.3. Функция traversNode (). Файл main.cpp
void traverseNode(const QDomNodeS node)
{
QDomNode domNode = node.firstChildO ;
while(!domNode.isNull()) {
if(domNode.isElement()) {
QDomEiement domElement = domNode.toElement();
if(!domElement.isNull()) {
if (domElement. tagNameO == "contact") {
cout « domElement.attribute("number", "")
« endl;
}
else {
cout « "TagName: " « domElement.tagName()
« "\tText: " « domElement.text()
« endl;
}
}
}
traverseNode(domNode);
domNode = domNode.nextSiblingO ;
}
}
Для прохождения по иерархии DOM удобно применить рекурсию. Из
рекурсивной фуНКЦИИ traverseNode О вызываются методы firstChildO и
nextsibiingo объекта класса QDomNode. Ели метод nextsibiingo возврашает
Глава 39. Работа с XML 473
нулевое значение, то это значит, что узлов больше нет. Все получаемые
узлы проверяются, в цикле, методом isEiement (). В том случае, если
полученный узел является элементом, то производится его преобразование к типу
QDomEiement с помощью метода toEiement () с последующим присвоением
результата объекту класса QDomEiement. При встрече элемента с именем
contact производится, с помощью метода attribute*), отображение
значения еГО атрибута number. В ОСТалЬНЫХ Случаях ВЫЗЫВаюТСЯ МеТОДЫ tagName ()
и text () для отображения имени и данных элемента.
Создание и запись XML-документа
При создании XML-документа необходимо иметь в своем распоряжении
механизм создания элементов. Для этого класс QDomDocument содержит серию
Методов (например, createElement(), createTextNode(), createAttribute ()).
Каждый из этих методов возвращает объект узла. Программа, показанная
в листинге 39.4, демонстрирует процесс создания XML-документа,
имеющего структуру, приведенную в примере 39.1.
| Листинг 39.4. Функция main(). Файл main.cpp
int main()
{
QDomDocument doc("addressbook");
QDomEiement domElement = doc.createElement("adressbook");
doc.appendChild(domElement);
QDomEiement contactl =
contact(doc, "Piggy", "+49 631322187", "piggy@mega.de");
QDomEiement contact2 =
contact(doc, "Kermit", "+49 631322181", "kermit@mega.de");
QDomEiement contact3 =
contact(doc, "Gonzo", "+49 631322186", "gonzo@mega.de");
domElement.appendChild(contactl) ;
domElement.appendChild(contact2);
domElement.appendChild(contact3) ;
QFile file("adressbook.xml");
if(file.open(IOJWriteOnly)) {
QTextStream(&file) « doc.toStringO ;
file.closeO ;
\
return 0;
474
Часть VI. Особые возможности Qt
После создания объекта класса QDomDocument необходимо создать начальный
элемент, который представляет собой начальный узел создаваемой иерархии
(листинг 39.4). В нашем случае этот элемент имеет имя addressbook.
Вызов метода appendChiido добавляет созданный элемент. Вызов функции
contact о создает элемент, содержащий контактную информацию
(листинг 39.5). Эти элементы добавляются методом appendChiido в начальный
элемент документа domEiement. Для записи XML-документа в файл
необходимо вызвать метод tostring (), который возвратит текстовое представление
XML-документа, и после этого произвести запись в файл.
| Листинг 39.5. Функция contact (). Файл main.cpp
QDomElement contact( QDomDocument& domDoc,
const QStrings strName,
const QStrings strPhone,
const QStrings strEmail
)
{
static int nNumber = 1;
QDomElement domEiement = makeElement(domDoc,
"contact",
QStringO .setNum(nNumber)
);
domEiement.appendChild(makeElement(domDoc, "name", "", strName));
domEiement.appendChild(makeElement(domDoc, "phone", "", strPhone));
domEiement.appendChild(makeElement(domDoc, "email", "", strEmail));
nNumber++;
return domEiement;
}
В листинге 39.5 приведена функция contact о. Она содержит статическую
переменную, увеличивающую свое значение после каждого вызова
функции. Это необходимо для присвоения контактным адресам определенного
Номера. Из ЭТОЙ ФУНКЦИИ ПРОИЗВОДИТСЯ ВЫЗОВ ФУНКЦИИ makeElement () ДЛЯ
создания элементов, представляющих собой составные части контактного
адреса.
{ Листинг 39.6. Функция makeElement (). Файл main.cpp
QDomElement makeElement( QDomDocument& domDoc,
const QString& strName,
Глава 39. Работа с XML
475
const QStringS strAttr = QString::null,
const QStrings strText = QString:mull
)
(
QDomElement domElement = doraDoc.createElement(strName);
if (IstrAttr.isEmptyO) {
QDomAttr domAttr = domDoc.createAttribute("number");
domAttr.setValue(strAttr);
domElement.setAttributeNode(domAttr);
}
if ("strText-isEmptyO ) {
QDomText domText = domDoc.createTextNode(strText);
domElement.appendChild(domText);
}
return domElement;
}
В функции makeEiement () (листинг 39.7) производится создание элемента с
ПОМОЩЬЮ метода createElement () объекта класса QDomDocument. В ТОМ случае,
если третьим параметром было передано значение атрибута, к элементу
будет добавлен атрибут number. Его создание производится вызовом метода
createAttribute () объекта класса QDomDocument. ВЫЗОВ метода setValue О
присвоит этому атрибуту значение, переданное в метод. Если в
четвертом параметре функции была передана строка текста, то методом
createTextNode () объекта класса QDomNode будет создан текстовый узел.
Вызов метода appendChild () внесет текстовую информацию в объект элемента.
Работа с SAX
Ввиду большого расхода памяти, работа с моделью DOM не всегда
желательна или возможна. Существует принципиально другой способ анализа
XML-документов — это SAX (Simple API for XML, простой API для XML),
который является стандартом JavaAPI для считывания XML-документов.
Чтение XML-документа
Определение всех классов поддержки SAX находится в одном и том же
заголовочном файле — qxml.h. Класс QxmisimpieReader представляет собой
XML-анализатор, базирующийся на SAX. Он читает XML-документ блока-
Ми и сообщает о том, что было найдено, вызовом соответствующих методов.
В этом и состоит его основное преимущество, т. к. в память помещаются
только фрагменты, а не весь XML-документ. Но это также и недостаток,
т. к. информация не считывается вся сразу и невозможно получить иерар-
476 Часть VI. Особые возможности д*
хию XML-документа Иерархия классов для работы с SAX имеет вид,
показанный на рис. 39.3. Класс QxmicontentHandier должен использоваться для
соединения с объектом класса QXmisimpieReader. Другие классы, такие как
QXmlEntityResolver, QXmlDTDHandler, QXmiErrorHandier, QXmlDeclHandler и
QxmiLexicaiHandier, просто содержат определения виртуальных методов,
соответствующих различным событиям анализа XML-документа.
| QXmiErrorHandier
I QXmlContentHandlerJ
QXmlDeclHandler |
pQXm
QXmiDefauitHandier
rOXmlDTDHandier
1 QXmlEntityResolver ||]
foXmlLexicalHandler |
Рис. 39.3. Классы для работы с SAX
В большинстве случаев для считывания XML-документа можно прекрасно
обоЙТИСЬ ТОЛЬКО ДВУМЯ классами: QXmlContentHandler И QXmiErrorHandier.
Интерфейс класса QContentHandier содержит методы, связанные с
отслеживанием структуры документа, их вызов происходит в следующем порядке:
1. Метод startDocument о вызывается при начале чтения XML-документа.
2. Метод startEiement () вызывается при начале чтения элемента.
3. Метод characters () вызывается при чтении данных элемента.
4. Метод endEiement о вызывается при завершении обработки элемента.
5. Метод endDocument о вызывается при завершении обработки документа.
Для удобства существует класс QXmiDefauitHandier, который унаследован от
всех шести классов (см. рис. 39.3). Он содержит пустые реализации
виртуальных методов этих классов. Для чтения XML-документа нужно
унаследовать его И перезаписать следующие методы: startDocument (), startEiement (),
characters (), endEiement (), endDocument () И fatalError (). Определение
последнего метода унаследовано от класса QXmiErrorHandier. Если эти методы
Возвращают значение true, TO ЭТО ГОВОРИТ Объекту класса QXmisimpieReader О
том, что он должен продолжить анализ файла. Значение false говорит об
ошибке, а чтобы отобразить соответствующее сообщение о ней, необходимо
перезаписать метод errorstringo.
Программа, приведенная в листингах 39.7 и 39.8, производит чтение XML-
документа. Результаты, выводимые на консоль, аналогичны выводу
программы, приведенной в листингах 39.1—39.3.
Глава 39. Работа с XML
477
| Листинг 39.7. Функция main (). Файл main.cpp
int main()
{
AddressBookParser handler;
QFile file("addressbook.xml") ;
QXml InputSource source(file);
QXmisimpieReader reader;
reader.setContentHandler(&handler);
reader.parse(source);
return 0;
}
В основной программе, приведенной в листинге 39.7, создаются объекты
Классов AddressBookParser, QFile (ДЛЯ чтения файла) И QXmisimpieReader (для
анализа файла). Чтобы поместить XML-документ в SAX-анализатор, нужно
создать объект класса QXmiinputsource, передав ему указатель на QiODevice.
Созданный объект нужно передать в метод parse о объекта класса
QXmisimpieReader. Этот метод запустит процесс анализа XML-документа. До
его ВЫЗОВа необходимо установить В методе setContenHandler () Объект
класса AddressBookParser, который производит отображение XML-локумента.
[Листинг 39.8. Класс AddressBookParser. Файл main.cpp
class AddressBookParser : public QXmlDefaultHandler {
private:
QString m_strText;
public:
//
bool startElement(const QString&,
const QStrings,
const QStrings,
const QXmlAttributess attrs
)
{
for(int i = 0; i < attrs.count(); i++) {
if(attrs.localName(i) == "number") {
cout « "Attr:" « attrs.value(i) « endl;
}
}
return true;
}
478
Часть VI. Особые возможности Qt
,/
bool characters(const QStrings strText)
{
m_strText = strText;
return true;
}
//
bool endElement(const QString&, const QString&, const QStrings str)
{
if (str != "contact" && str != "addressbook") {
cout « "TagName:" « str
« "\tText:" « m_strText
« endl;
}
return true;
}
//
bool fatalError(const QXmlParseExceptions exception)
{
cerr « "Line:" « exception.lineNumber()
« ", Column:" « exception.columnNuniber()
« ", Message:" « exception.messageO .ascii()
« endl;
return false;
}
};
В листинге 39.8 класс AddressBookParser реализует виртуальные методы,
которые вызываются при прохождении по XML-документу. При нахождении
тегов вызываются соответствующие методы startElement () И endElement (),
которые должны быть перезаписаны для того, чтобы реагировать надлежащим
образом. Метод startElement () вызывается тогда, когда при считывании
встречается открытие тега. Третий параметр, передаваемый в этот метод, —
это имя тега. Четвертый — это список атрибутов. В нашем случае, если
название атрибута совпадает со строкой number, то производится вывод его
значения. Метод characters о производит запись содержимого текущего
элемента в переменную mstrText, это необходимо для того, чтобы можно
было получить доступ к этому содержимому из любого метода класса
AddressBookParser. Метод endElement () вызывается всегда, когда при чтении
встречается закрытие тега. Третий параметр, передаваемый в этот метод, —
имя тега. В случае несовпадения его со строками contact и addressbook,
Глава 39. Работа с XML
479
производится отображение данных элемента. Метод fataiError ()
вызывается, если не удается проанализировать XML-документ. В этом случае, метод
производит отображение предупреждающего сообщения с указанием номера
строки и столбца XML-документа, в котором произошел сбой и текст
ошибки анализатора.
Резюме
В этой главе мы познакомились с очень популярным форматом хранения и
обмена данными — XML. В настоящее время распространение XML идет
большими темпами, привлекая на свою сторону все больше и больше
разработчиков прикладного программного обеспечения.
Qt предоставляет два способа работы с XML-документами — DOM и SAX.
Первый представляет данные XML-документа в виде иерархии
(древовидной структуры), что очень удобно для работы. Второй способ считывает
данные из XML-документа блоками и сообщает о результатах в
определенные виртуальные методы.
При выборе следует учитывать, что SAX — это низкоуровневый способ и
поэтому более быстрый. Его лучше всего применять в тех случаях, когда не
требуется проведение очень сложных операций. Также следует применять
его для считывания больших XML-документов, т. к. он более экономно
расходует ресурсы памяти.
ГЛАВА 40
Программирование
баз данных
Распознавание проблемы, которая
может быть решена и достойна решения,
есть... тоже своего рода открытие.
Макс Поданы!
База данных представляет собой систему хранения записей, организованных
в виде таблиц. База данных, как правило, включает в себя одну или большее
количество таблиц, которые могут быть связаны между собой. Таблица
состоит из набора строк и столбцов (рис. 40.1). Столбцы таблицы имеют
имена и за каждым из них закреплен тип и/или область значения. Строки
таблицы баз данных называются записями, а ячейки, на которые делится
первичный
ключ
QfiumbejJI
ячейка
(поле)
строка .
(запись)
столбец
(атрибут)
1р'99У
адрес
электронной
почты
+49 631322181
phone
piggy@mega.de
kermit@mega de
gonzo@mega.de
email
количество столбцов
(степень)
Рис. 40.1. Таблица (отношение)
количество строк
(кардинальное число)
Глава 40. Программирование баз данных
481
запись, — полями. Первичный ключ — это уникальный идентификатор. Он
может представлять собой не только один столбец, но и их комбинацию.
Пользователь может выполнять с таблицами множество операций,
например: добавлять, изменять и удалять записи, вести поиск и т. д. Для
выполнения этих операций был разработан язык SQL (Structured Query Language,
язык структурированных запросов), позволяющий писать их в виде
запросов. SQL позволяет не только выполнять запросы и изменять данные, но и
создавать новые базы данных.
Основные положения SQL
Основными действиями, производимыми с базой данных, являются
создание новой таблицы, чтение (выборка и проекция), вставка, изменение и
удаление данных. Сразу следует сказать, что язык SQL нечувствителен к
регистру, а это значит, что буква, набранная в верхнем или нижнем
регистре, считается одной и той же. Например, select = select = select и т. д.
В дальнейшем для выделения ключевых слов SQL будем использовать
верхний регистр.
Создание таблицы
Для создания таблицы, показанной на рис. 40.1, используется оператор
create table, в котором указываются имена столбцов таблицы, их тип, а
также задается первичный ключ:
CREATE TABLE addressbook (
number INTEGER PRIMARY KEY NOT NULL,
name VARCHAR(15),
phone VARCHAR(12),
email VARCHAR(15)
);
Операция вставки
После создания таблицы можно добавлять и сами данные. Для этого SQL
предоставляет оператор вставки insert into. Сразу после названия таблицы
в скобках нужно указать имена столбцов, в которые будут заноситься
данные Сами данные указываются после ключевого слова values.
INSERT INTO addressbook (number, name, phone, email)
VALUES(lf 'Piggy*, '+49 631322187', 'piggy@mega.de');
INSERT INTO addressbook (number, name, phone, email)
VALUES(2, 'Hermit', '+49 631322181', 'kermit@mega.de');
482
Часть VI. Особые возможности о*
Чтение данных
Составной оператор select ... from ... where осуществляет операции
выборки и проекции. Выборка соответствует выбору строк, а проекция —
выбору столбцов. Этот оператор возвращает таблицу, созданную согласно
заданным критериям.
□ Ключевое слово select является оператором для проведения проекции
т. е., в нем указываются столбцы, которые должны стать ответом на
запрос. Если указать после select знак *, то результирующая таблица будет
содержать все столбцы таблицы, к которой был адресован запрос.
Указание конкретных имен столбцов устраняет в ответе все остальные.
□ Ключевое слово from задает таблицу, к которой адресован запрос.
□ Ключевое слово where является оператором выборки. Выборка
осуществляется согласно условиям, указанным сразу после оператора.
Например, для получения адреса электронной почты мисс Piggy нужно
сделать следующее:
SELECT email
FROM addressbook
WHERE name = 'Piggy*;
Изменение данных
Для изменения данных таблицы используется составной оператор
update ... set. После названия таблицы в операторе set указывается
название столбца (или столбцов), в который будет заноситься нужное значение.
Изменение данных производится в строках, удовлетворяющих условию,
поставленному в ключевом слове where. В показанном ниже примере
производится замена адреса электронной почты мисс Piggy с piggy@mega.de на
piggy@supermega.de:
UPDATE addressbook
SET email = 'piggy@supermega.de'
WHERE name = 'Piggy';
Удаление
Удаление строк из таблицы производится при помощи оператора delete .. •
from. После ключевого слова where следует критерий, согласно которому
производится удаление строк. Например, удалить адрес мисс Piggy из
таблицы можно следующим образом:
DELETE FROM addressbook
WHERE name = * Piggy';
Глава 40. Программирование баз данных
483
Использование SQL в библиотеке Qt
Одно из наиболее значимых расширений библиотеки Qt — это SQL. Его
поддержка была внесена, начиная с третьей версии. В настоящее время
существует несколько десятков СУБД (система управления базами данных),
из которых Qt предоставляет поддержку приведенным в табл. 40.1.
Таблица 40.1. Идентификаторы менеджеров баз данных
Идентификатор
[ QOCI8
QODBC3
QMYSQL3
QTDS7
QPSQL7
QDB2
Описание
Доступ к базам данных Oracle 8 и Oracle 7 через Oracle 8 Call
Interface (OCI8)
(ODBC) стандартный ODBC-драйвер для IBM DB2, Sybase SQL,
iODBC и других баз данных
В настоящее время MySQL— самая популярная бесплатная
СУБД. Всю необходимую информацию можно получить на
странице www.mysql.com
Sybase Adaptive Server
PosgreSQL базы данных с поддержкой SQL92/SQL3
DB/2 — платформонезависимая база данных, разработанная IBM
Если в табл. 40.1 нет поддержки нужной вам базы данных, то можно самому
написать для нее драйвер (см. гл. 41).
Соединение с базой данных
Для соединения с базой данных нужно, прежде всего, активизировать
драйвер. Для ЭТОГО вызывается статический метод QSqlDatabase: : addDatabase ()
(листинг 40.1). В него нужно передать строку, обозначающую
идентификатор драйвера СУБД (см. табл. 40.1). При успешном выполнении этот метод
возвращает указатель на объект класса QSqlDatabase, а при неудачном —
нулевой указатель.
Листинг 40.1. Функция соединения с базой данных. Файл main.cpp
bool createConnection()
{
QSqlDatabase* pdb = QSqlDatabase::addDatabase("QMYSQL3");
if (!pdb) {
cerr « "Could not load the SQL driver" « endl;
}
484
Часть VI. Особые возможности Qt
pdb->setDatabaseName ("addressbook");
pdb->setUserName("elton");
pdb->setHostName("lostris") ;
pdb->setPassword("password");
if (!pdb->open()) {
cerr « pdb->lastError().databaseText() « endl;
return false;
}
return true;
}
Для того чтобы подключиться к базе данных, нужны четыре следующих
параметра:
□ ИМЯ базы данных — передается В метод QSqlDatabase: :setDatabaseName();
О имя пользователя, желающего к ней подключиться — передается в метод
QSqlDatabase::setUserName();
0 имя компьютера, на котором размещена база данных — передается в
метод QSqlDatabase::setHostName ();
□ пароль — передается В метод QSqlDatabase: :setPassword().
Методы должны вызываться из объекта, созданного вызовом статического
метода QSqlDatabase: :addDatabase () (ЛИСТИНГ 40.1).
Само соединение осуществляется вызовом метода QSqlDatabase:: open о.
Значение, возвращаемое этим методом, рекомендуется проверять. В случае
возникновения ошибки, информацию о ней можно получить с помощью
метода QSqlDatabase: : lastError (), КОТОрЫЙ возвращает объект класса
QSqiError. Чтобы отобразить ошибку на экране, необходимо преобразовать
ЭТОТ объект методом databaseText ().
Исполнение команд SQL
Для исполнения команд SQL, после установления соединения, можно
использовать класс QSqiQuery, который определен в заголовочном файле
qsqlquery.h. Запросы (команды) оформляются в виде обычной строки,
которая передается в конструктор или в метод QSqiQuery: :exec(). В случае
передачи строки в конструктор, запуск команды будет производиться
автоматически при создании объекта. Программа, приведенная в листинге 40.2,
демонстрирует исполнение команд SQL. В результате на консоль выводятся
следующие данные:
1 Piggy; +49 631322187; piggy@mega.de
2 Kermit; +49 631322181; kermit@mega.de
Глава 40. Программирование баз данных
485
! Листинг 40.2. Исполнение команд SQL. Файл main.cpp
int main(int argc, char** argv)
{
QApplication app(argc, argv, false);
if (!createConnection()) {
return -1;
}
QSqlQuery sqlQuery;
QString str = "CREATE TABLE addressbook ( "
"number INTEGER PRIMARY KEY NOT NOLL, "
"name VARCHAR(15), "
"phone VARCHAR(12), "
"email VARCHAR(15) "
");";
if (IsqlQuery.exec(str)) {
cout « "Unable to create a table" « endl;
}
QString strF =
"INSERT INTO addressbook (number, name, phone, email) "
"VALUES (%s, '%s\ '%s\ ,%s,);n;
str.sprintf(strF, "1", "Piggy", "+49 631322187", "piggy@mega.de");
if (IsqlQuery.exec(str)) {
cout « "Unable to do insert" « endl;
}
str.sprintf(strF, "2", "Kermit", "+49 631322181", "kermit@mega.de");
if (IsqlQuery.exec(str)) {
cout « "Unable to do insert" « endl;
}
if (IsqlQuery.exec("SELECT * FROM addressbook;")) {
cerr « "Unable to execute query - exiting" « endl;
return 1;
}
while (sqlQuery.next()) {
int nNumber = sqlQuery.value(0).tolnt();
QString strName = sqlQuery.value(1).toString();
QString strPhone = sqlQuery.value(2).toString();
QString strEmail = sqlQuery.value(3).toString();
486
Часть VI. Особые возможности Qt
cout « nNumber « " " « strName « ";\t"
« strPhone « ";\t" « strEmail « endl;
}
return 0;
}
В случае удачного соединения с базой данных createconnection () создается
строка, содержащая команду SQL для создания таблицы. Эта строка
передается в метод exec о объекта класса QSqlQuery. Если создать таблицу не
удается, то на консоли будет отображено предупреждающее сообщение. Ввиду
того, что в таблицу будет внесена не одна строка, в строковой переменной
strF при помощи символов спецификации определяется шаблон для
команды insert. Вызов метода sprinf о класса Qstring создает команды и,
используя шаблон, подставляет нужные значения в символах спецификации.
После внесения строк в таблицу производится вызов команды select,
в нашем случае, помещающей все строки и столбцы таблицы в объект
sqiQuery. Вывод значений таблицы на консоль производится в цикле. При
первом вызове метода next () этот объект будет указывать на самую первую
строку таблицы. Последующие вызовы приведут к перемещению указателя
на следующие строки. В том случае, если записей больше нет, метод next о
вернет false, что приведет к выходу из цикла. Для получения результата
запроса следует вызвать метод QSqlQuery: rvalue о, в котором необходимо
передать номер столбца. Этот метод возвращает значения типа QVariant.
QVariant — это специальный класс, объекты которого могут содержать в
себе значения разных типов (см. гл. 36). Поэтому полученное значение
нужно преобразовывать к конкретному типу.
Обращение к данным при помощи курсора
Есть способ более простого обращения к данным, чем использование
объекта класса QSqlQuery. Класс QSqicursor унаследован от QSqlQuery и
расширяет его методами, позволяющими избежать написания команд запросов.
Определение этого класса находится в заголовочном файле qsqlcursor.h. Для
проведения операций с данными таблицы необходимо указать имя таблицы
в конструкторе, при создании объекта класса QSqicursor. Курсор
обеспечивает поочередный доступ к строкам таблицы и с его помощью можно не
только считывать и обрабатывать значения строк, но также изменять или
удалять их. Рис. 40.2 схематически иллюстрирует процесс использования
курсора.
Чтение данных
Метод select о представляет собой эквивалент запроса select в SQL.
Условие для проведения выборки передается в этот метод в качестве параметра.
Глава 40. Программирование баз данных
487
ПРОГРАММА
L
numb&
1
2
3
^
БАЗА ДАННЫХ
name
Рщ~~^.
Kermit
Gonzo
phone
-^
email,
+49 6313221В7ЫсгЧу^п
+49631322181
+49631322186
kermit@ri
gonzo@r
~--\
)
"^ -_ —-—"^
Рис. 40.2. Доступ к данным при помощи курсора
Например, чтобы отобразить строки, имеющие первичный ключ,
меньший 3, нужно сделать следующее:
QSqlCursor sqlCursor("addressbook");
sqlCursor.select("number < 3 ");
if (sqlCursor.isActive()) {
while (sqlCursor.next()) {
cout « sqlCursor. value ("name") .toStringO « endl;
}
}
Операция вставки
Для добавления новых записей в таблицу базы данных, прежде всего,
требуется вызвать метод primeinsert о. Этот метод вернет указатель на новую
строку (QSqiRecord). Затем, методом setvaiueo можно заносить в нее
данные. Например:
QSqlCursor sqlCursor ("addressbook");
QSqiRecord* psqlRecord = sqlCursor.primeinsert();
psqlRecord->setValue("number", "3");
psqlRecord->setValue("name", "Gonzo");
psqlRecord->setValue("phone", "+49 631322186");
psqlRecord->setValue("email", "gonzo@mega.de");
sqlCursor.insert();
Изменение данных
Для изменения данных класс QSqlCursor предоставляет метод primeUpdate ().
Он возвращает указатель копии строки (QSqiRecord). Сначала нужно, при
помощи метода select о, выбрать нужную строку, а затем для изменения
488
Часть VI. Особые возможности Qt
нужных полей вызвать метод setvaiueo. И, в завершение, для записи
изменений в базу данных, необходимо вызвать метод update о. Например:
QSqlCursor sqlCursor("addressbook");
sqlCursor.select("number = 1");
if (sqlCursor.next()) {
QSqlRecord* psqlRecord = sqlCursor.primeUpdate();
psqlRecord->setValue("email", "piggy@supermega.de")
sqlCursor.update();
}
Удаление
Чтобы удалить строку, ее сначала выбирают при помощи метода select (), а
затем вызывают метод primeDeiete () для размножения курсора с первичным
ключом строки. И в завершение производят операцию удаления вызовом
метода del (). Например:
QSqlCursor sqlCursor("addressbook");
sqlCursor.select("number = 1");
if (sqlCursor.next()) {
sqlCursor.primeDeiete();
sqlCursor.del() ;
}
Осведомленные элементы управления
Использование так называемых осведомленных элементов управления — это
самый простой способ отобразить данные таблицы. Здесь не потребуется
цикла для прохождения по строкам таблицы, проведение этой операции
виджет класса QDataTabie возьмет на себя. Программа, окно которой
отображено на рис. 40.3, демонстрирует такую возможность.
■[Non-Commercial]-SQLCursor -ПЕН
II Number [Name | Phone [Email Ы
1 n ~ Piggy +49 631322187 piggy@megade J
|2 J2 Kermit +49631322181 kermit@megade ^J
Рис. 40.3. Использование для отображения данных виджета класса QDataTabie
| Листинг 40.3. Файл main.cpp
int main(int argc, char** argv)
{
QApplication app(argc, argv);
Глава 40. Программирование баз данных
489
if (!createConnection()) {
return -1;
}
QSqlCursor sqlCursorC'addressbook") ;
QDataTable tbl(SsqlCursor);
tbl.addColumn("number", "Number");
tbl.addColumn("name", "Name");
tbl.addColumn("phone", "Phone");
tbl.addColumn("email", "Email");
tbl.refreshO;
tbl. show ();
app.setMainWidget(&tbl);
return app.execO;
>
В листинге 40.3, после соединения с базой данных, проводимого с
помощью функции createConnection (), создается объект курсора sqlCursor.
Для создания виджета класса QDataTable в конструктор необходимо передать
адрес созданного курсора, чтобы виджет мог сам обращаться к базе данных.
В метод addColumn о первым параметром передается надпись столбца
таблицы, а вторым — соответствующее имя столбца таблицы базы данных. Метод
refresh о используется для отображения данных в таблице.
Резюме
Базы данных — одна из самых объемных тем информатики. Поэтому
описание всех возможностей работы с базой данных требует отдельной книги.
Эта глава описывает только лишь малую их часть с применением Qt.
В этой главе мы узнали, что база данных — это хранилище, организованное
в виде таблиц, в каждой ячейке которых содержатся данные определенного
типа: текст, числа и т. д.
Язык запросов SQL — это стандарт, который используется в большом
количестве операционных систем, что обеспечивает его платформонезависи-
мость. Запросы (команды) — это своего рода вопросы, задаваемые базе
данных, на которые она должна давать ответы.
Для работы с базой данных нужно сначала активизировать драйвер, затем
установить связь с базой данных. После этого посредством запросов SQL
можно получать, вставлять, изменять и удалять данные.
Курсоры представляют собой более гибкий инструмент проведения
операций с базой данных. С их помощью можно избежать написания команд
запросов и, соответственно, уменьшить время, затрачиваемое на разработку.
ГЛАВА 41
Динамические библиотеки
и система расширений
Что едино, разъедини.
Сунь-Цзы
Динамические библиотеки
На практике очень часто возникают случаи, когда требуется совместное
использование той или иной функции сразу в нескольких программах,
работающих на одном компьютере. Не совсем экономично, если каждая из этих
программ будет содержать одинаковые функции, а значит, необходим
механизм для вынесения повторяющихся функций в отдельные файлы
(библиотеки), который позволял бы воспользоваться этими функциями. Такие
файлы должны подгружаться динамически в процессе работы самих программ,
по мере необходимости и в зависимости от потребностей. Использование
динамических библиотек дает следующие преимущества:
□ программа занимает меньше места в оперативной памяти и на диске;
О разные программы могут использовать одну и ту же библиотеку;
□ после обновления динамической библиотеки использующие ее
программы совсем не обязательно компилировать заново.
Создание динамической библиотеки
Следующий пример, приведенный в листингах 41.1—41.3, демонстрирует
создание динамической библиотеки, содержащей только одну функцию.
| Листинг 41.1. Файл dynlib.pro
TEMPLATE = lib
CONFIG += plugin
Глава 41. Динамические библиотеки и система расширений
491
SOURCES = dynlib.cpp
HEADERS = dynlib.h
TARGET = dynlib
Обратите внимание на файл проекта, показанный в листинге 41.1. Для
создания динамической библиотеки нужно установить в опции config значение
plugin, а В ОПЦИИ TEMPLATE — Значение lib.
{ Листинг 41.2. Файл dynlib.h
#include <qt.h>
Q_EXTERN_C Q_EXPORT char* sayHelloO;
В динамическую библиотеку должны быть экспортированы прототипы
функций для их дальнейшего использования, и эти функции необходимо
пометить макросом qexport (листинг41.2). Действие макроса qexternc
аналогично действию extern "С" {...}. Он нужен для того, чтобы
компилятор C++ не прикреплял информацию о типе к символьной сигнатуре
функции.
J Листинг 41.3. Файл dynlib.cpp
#include "dynlib.h"
char* sayHelloO
{
return "Hello!";
}
В листинге 41.3 показана реализация экспортируемой функции. Эта
функция возвращает указатель на строку, содержащую текст "Hello!".
Загрузка динамических библиотек
Для загрузки динамической библиотеки совсем не обязательно, чтобы она
была создана с помощью Qt. Для загрузки с динамическими библиотеками
Qt предоставляет класс Qiibrary, определенный в заголовочном файле
qlibrary.h. Этот класс заботится также и о том, чтобы загруженная
библиотека оставалась в памяти на протяжении всего времени использования. В
программе, показанной в листинге 41.4, реализована загрузка динамической
библиотеки и исполнение функции, экспортируемой этой библиотекой.
Экспортируемая функция возвращает текст "Hello!" (листинг 41.3), который
отображает виджет надписи (рис. 41.1).
492
Часть VI. Особые возможности Qt
\\ ]\т-\щ*м
| Hello' J
Рис. 41.1. Исполнение функции динамической библиотеки
Листинг 41.4. Файл main.cpp
#include <qapplication.h>
#include <qlabel.h>
#include <qlibrary.h>
//
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QLabel lbl("", 0);
QLibrary lib("dynlib");
if (Jlib.isLoadedO) {
lib.load();
}
typedef char* (*Fct) ();
Fct fct = (Fct)(lib.resolve("sayHello"));
if (fct) {
lbl.setText(fctO);
}
app.setMainWidget (&1Ы) ;
1Ы. show.() ;
return app.exec();
}
Чтобы использовать в программе динамическую биС.иотеку, нужно создать
объект класса QLibrary и передать в его конструктор имя файла
динамической библиотеки, но заметьте — только имя, без расширения (листинг 41.4).
После этого необходимо убедиться, что библиотека была успешно
загружена. Для этого следует вызвать метод isLoadedo. В том случае, если она еще
не загружена, нужно вызвать метод load ().
Получение указателей на экспортируемые функции производится вызовом
метода resolve о (листинг 41.4). В этот метод передается символьная
сигнатура, по которой будет производиться поиск нужной функции. Этот метод
возвращает указатель на тип void, который представляет собой адрес
найденной функции. Для вызова функции этот указатель необходимо привести
Глава 41. Динамические библиотеки и система расширений 493
к нужному типу. Возвращение методом resolve о нулевого указателя
означает, что функция не найдена. В случае успешной проверки указателя
производится вызов самой функции.
Расширения (plug-ins)
Qt предоставляет систему расширений, похожую, по своей структуре, на
COM (Component Object Model, компонентная модель объектов) компании
Microsoft. В Qt имеются пять классов, с помощью которых можно создавать
следующие расширения:
□ QSqiDriverPiugin — для драйверов баз данных;
□ QlmageFormatPlugin — ДЛЯ поддержки рЭЗЛИЧНЫХ форматов растровых
изображений (см. гл. 18);
□ QTextcodecPiugin — для поддержки кодировок текста;
□ QStyiePiugin — для стилей элементов управления (см. гл. 12);
П QWidgetPiugin — для расширения программы Qt Designer новыми видже-
тами (см. гл. 43).
| QObject '
Ч
QWidgetPiugin |f
|Н QTextCodecPlugin "|
QGPIugln b
H QStyiePiugin j
H QSqiDriverPiugin fe
QlmageFormatPlugin |
Рис. 41.2. Иерархия классов расширений
Все вышеуказанные классы наследуют класс QGPiugin, определенный в
заголовочном файле qgplugin.h (рис. 41.2), который является базовым для всей
системы расширений Qt. Класс приведен в листинге 41.5. Созданные от
этих классов компоненты могут быть динамически подгружены в процессе
работы программы.
| ЛИСТИНГ 41.5. Класс QGPiugin
struct QUnknownlnterface;
class Q_EXPORT QGPiugin : public QObject {
Q_OBJECT
494
Часть VI. Особые возможности Qt
public:
QGPlugin(QUnknownlnterface *i) : _iface( i) {};
-QGPlugin() {};
QUnknovmlnterface* iface()
{
Q_ASSERT( _iface);
QUnknowninterface *i;
_iface->queryInterface(IID_QUnknown, &i);
return i;
}
void setlface( QUnknowninterface *iface)
{
_iface = iface;
}
private:
QGPlugin() : _iface(0);
QUnknowninterface* _iface;
};
QUnknowninterface представляет собой базовый интерфейс. Все интерфейсы
Qt должны наследоваться только от него. Таким образом, клиент, имея
указатель на ounknowinterface, сможет запросить через него и другие
интерфейсы. Интерфейс — это, по сути, таблица указателей на методы. Метод iface о
возвращает указатель на компоненту только в том случае, если интерфейс,
переданный в конструктор или в метод setinterfасе (), поддерживается.
Поддержка интерфейса проверяется вызовом метода queryinterfасе ().
После получения компоненты с ней можно работать посредством интерфейса,
т. е. вызывать методы, описываемые в нем.
Например, реализация расширения для созданного в гл. 12 стиля customstyie
могла бы выглядеть так, как это показано в листинге 41.6.
{ Листинг 41.6. Класс CustomStylePlugin
class CustomStylePlugin : public QStylePlugin {
public:
QStringList keys() const
{
return QStringList() « "CustomStyle";
}
QStyle* create(const QString& strKey)
{
if (strKey == "CustomStyle") {
Глава 41. Динамические библиотеки и система расширений
495
return new CustomStyle;
}
return 0;
}
};
Q_EXPORT_PLUGIN(CustomStylePlugin)
В классе, унаследованном от QStyiePiugin, необходимо реализовать
виртуальные методы keys о и create о. Метод keys() возвращает список строк,
реализованных в классе расширений, в нашем примере оно одно —
Customstyie. Метод create о создает запрашиваемый объект и возвращает
указатель на него. В том случае, если запрашиваемый объект не найден,
метод вернет нулевое значение. Экспортирование класса расширений
производится при помощи макроса qexportplugin. Созданный класс
расширений должен быть откомпилирован в динамическую библиотеку. Его
загрузку можно произвести при помоши класса QStyieFactory. Например:
void main(int argc, char** argv)
{
QApplication::setStyle(QStyieFactory::create("CustomStyle"));
}
Резюме
Динамическая библиотека содержит код, который может использоваться
сразу несколькими приложениями. В отличие от статических библиотек,
код, содержащийся в динамической библиотеке, не включается в основной
код приложения, а находится в отдельном файле. Если при исполнении
программы производится вызов функции из динамической библиотеки, то в
память компьютера загружаются только нужные функции. Это позволяет
сэкономить объем дискового пространства и оперативной памяти,
поскольку общедоступный код находится в отдельном файле, совместно
используемом программами.
Qt предоставляет все необходимые средства для создания и загрузки
динамических библиотек. При помощи функции resolve о можно получить
адрес нужной функции. После приведения адреса к нужному типу можно
производить его вызов.
Qt располагает классами расширений для реализации драйверов баз данных,
создания стилей, расширения возможностей программы Qt Designer и др.
Чтобы создать свое собственное расширение, нужно унаследовать один из
этих классов и произвести перезапись всех необходимых виртуальных
методов.
ГЛАВА 42
Совместное использование Qt
с платформозависимыми API
Алиса рассмеялась.
— Это не поможет! — сказала она. — Нельзя
поверить в невозможное!
— Просто у тебя мало опыта, — заметила
королева. — В твоем возрасте я уделяла
этому полчаса каждый день. В иные дни я
успевала поверить в десяток
невозможностей до завтрака."
Льюис Кэрролл. "Алиса в Зазеркалье"
Несмотря на то, что Qt предоставляет практически весь инструментарий для
реализации любых программ, иногда возникает необходимость в
использовании технологий, связанных с конкретной платформой, или реализации
кода низкого уровня. Прибегнуть к написанию платформозависимого кода
нужно только в случаях острой необходимости. Помните, что если
программа рассчитана на несколько платформ, то функции придется реализо-
вывать для каждой поддерживаемой платформы. По возможности,
рекомендуется весь платформозависимый код объединять в динамических
библиотеках (см. гл. 41), для отделения его от платформонезависимого. Но можно
производить и смешивание. Чтобы на каждой из систем была задействована
только нужная часть кода, следует воспользоваться директивами
препроцессора, определенными специально для каждой из них. Участок программы,
поддерживающей несколько платформ, может выглядеть следующим
образом:
#if defined(Q_WS_WIN)
// Реализация для Windows
#elif defined(Q_WS_X11)
// Реализация для UNIX
#elif defined(Q_WS_MAC)
Глава 42. Совместное использование Qt с платформозависимыми API 497
// Реализация для MacOS
#else
//Не поддерживается
#endif
Совместное использование
с Windows API
Программа, окно которой показано на рис. 42.1, демонстрирует
возможность использования для графического вывода в ОС Windows функций GDI
(Graphical Device Interface, интерфейс графического устройства). При
нажатии правой кнопки мыши в области окна приложения производится вызов
окна сообщения Windows API.
[Noncommercial] - WinAPI RQ Q
GDI Drawin i
Рис. 42.1. Графический вывод с использованием функций GDI
Листинг 42.1. Конструктор класса WinAPI. Файл WinAPl.cpp
WinAPI::WinAPI(QWidget* pwgt/*= 0*/, const char* pszName/*= 0*/)
: QWidget(pwgt, pszName)
{
setBackgroundMode(NoBackground);
}
В листинге 42.1 вызовом метода setBackgroundMode () в конструкторе
производится отключение режима заполнения фоном, для подавления эффекта
мерцания.
498
Часть VI. Особые возможности Qt
! Листинг 42.2. Метод обработки событий ОС Windows. Файл WinAPl.cpp
/♦virtual*/ bool WinAPI::winEvent(MSG* pmsg)
{
QString strl = "Windows Version = "
+ QString().setNum(QApplication::winVersion());
QString str2 = "Windows Message";
switch(pmsg->message) {
case WM_RBUTTONDOWN:
::MessageBox(winld(),
strl.ucs2(),
str2.ucs2(),
MB_OK | MB_ICONEXCLAMATION
);
break;
}
return QWidget::winEvent(pmsg);
}
В листинге 42.2 приведен специальный метод обработки событий ОС
Windows — winEvent о. Если не требуется дальнейшей обработки события с
помощью Qt, то следует возвратить из этого метода значение true. Реализация
этого метода, по своей сути, очень похожа на реализацию так называемой
оконной функции ОС Windows. В нашем случае отслеживается событие
нажатия правой кнопки мыши в ОС Windows. В случае нажатия вызывается
функция MessageBoxo Windows API, отображающая окно сообщения. В
качестве родительского окна первым параметром в эту функцию передается
значение, возвращаемое методом winido. Метод поддерживается для всех
платформ и в случае ОС Windows возвращает идентификационный номер
окна, соответствующий типу hwnd (указатель на окно).
j Листинг 42.3. Метод события перерисовки. Файл WinAPl.cpp
/♦virtual*/ void WinAPI::paintEvent(QPaintEvent*)
{
QPixmap pix(width(), height());
HBRUSH hBrush = ::CreateSolidBrush(RGB(255, 0, 255));
HPEN hPen = ::CreatePen(PS_DASH, 3, RGB(255, 255, 255));
TEXTMETRIC tm;
QString str = "GDI Drawing";
::GetTextMetries(pix.handle(), & tm) ;
::SelectObject(pix.handle(), hBrush);
Глава 42. Совместное использование Qt с платформозависимыми API
499
::SelectObject(pix.handle(), hPen);
::Ellipse(pix.handle(), 0, 0, width(), height());
::Textout(pix.handle(),
width() / 2 - (tm.tniAveCharWidth * str. length()) / 2,
(height() - tm.tmHeight) / 2,
str.ucs2(),
str.length()
);
::DeleteObject(hBrush);
::DeleteObject(hPen);
::bitBlt(this, 0, 0, &pix);
}
В методе события перерисовки paint о производится отображение надписи
и эллипса при помощи функций GDI. Обратите внимание на вызов метода
handle о объекта класса QPixmap (листинг 42.3). Он возвращает значение
типа hdc (указатель на Device Context, контекст устройства), который нужен
для функций gdi, чтобы они могли проводить рисование.
Совместное использование с Linux и KDE
Как и в случае с ОС Windows, в UNIX Qt также предоставляет возможность
доступа к событиям на низком уровне. Класс owidget содержит метод
xiiEvento, который необходим для получения событий оконной системы
X Windows. Чтобы получать события, этот метод нужно просто
перезаписать. Если необходимо, чтобы событие после обработки не подвергалось
дальнейшим обработкам методами событий Qt, то из этого метода нужно
возвратить true.
Класс QPaintDevice содержит ряд методов, возвращающих значения,
использование которых может оказаться полезным для программирования в
системе X Windows. Например, метод xiiDispiayO возвращает указатель
поверхности отображения (тип Display). Метод xiiscreeno возвращает
номер экрана.
Реализация программы в KDE
KDE базируется на Qt. Практически все классы пользовательского
интерфейса наследуются от аналогичных классов библиотеки Qt. Поэтому
разработчик, имеющий опыт программирования с Qt, будет чувствовать себя в
KDE как "рыба в воде". Правда, следует учитывать и то обстоятельство, что
программы в KDE не являются платформонезависимыми, ввиду своей
привязки к ОС Linux. Но, несмотря на это, некоторые из классов KDE, после
500
Часть VI. Особые возможности Qt
небольших изменений, можно компилировать и на других платформах. На
рис. 42.2 отображено окно KDE-приложения, приведенного в листинге 42.4.
-* KDE-''о atton Foix
Hello KDE
Рис. 42.2. Окно KDE-приложения
! Листинг 42.4. Файл main.cpp
#include <kapp.h>
#include <qlabel.h>
//
int main(int argc, char** argv)
{
KApp1ication app(argc, argv, "kdeapp");
QLabel 1Ы ("<Hl><CENTER>Hello KDE</CENTERX/H1>", 0) ;
lbl.setCaption("KDE-Application");
lbl.show();
app.setMainWidget(&lbl) ;
return app.exec();
}
Если вы хотите написать программу, которая будет запускаться в KDE и
использовать все его возможности, то для этого необходимо создать объект
класса KAppiication вместо объекта класса QAppiication. Исторически
сложилось так, что заголовочный файл класса KAppiication называется kapp.h.
а не kapplication.h (листинг 42.4). Класс KAppiication унаследован от
QAppiication и, следовательно, он подчиняется тем же правилам, что и
класс QAppiication. В конструкторе KAppiication кроме двух первых
параметров для создания объекта необходим еще и третий. Этот параметр
должен совпадать с именем самой программы. Он служит для привязки,
благодаря которой все необходимые данные программы будут правильно
найдены, даже в том случае, если сама программа будет переименована.
( Примечание ^
После создания объекта класса KAppiication можно использовать глобальный
указатель qApp. Но с его помощью можно вызывать только методы,
определенные в классе QAppiication.
Глава 42. Совместное использование Qt с платформозависимыми API 501
Чтобы откомпилировать программу, нужно дать следующую команду:
д++ -о kdeapp kdeapp.cpp -I$QTDIR/include -I$KDEDIR/include -Ikdecore
-lkdeui -lqt
Резюме
Qt допускает возможность использования в своих программах платформоза-
висимого кода. Это может быть очень полезно для реализации программ,
использующих возможности, не предоставляемые библиотекой Qt.
ГЛАВА 43
Qt Designer.
Быстрая разработка прототипов
Наш век беспокойства в значительной мере
является результатом попыток выполнить
сегодняшнюю работу вчерашними
средствами...
Маршал Мак-Люгаи
Программа Qt Designer — это средство быстрой разработки приложений
(Rapid Application Development, RAD). Прежде всего, этот инструмент
предназначен для дизайнеров и принцип его работы отвечает принципу
WYS1GWYG (What You See Is What You Get, "Что видишь, то и получишь").
Он предоставляет возможность очень быстро создавать прототипы
приложений, которые базируются на диалоговых окнах, а также могут иметь
главное окно, меню, строку состояния и панель инструментов. Созданные в
Qt Designer файлы описания интерфейса можно конвертировать в исходный
код на языке C++. Кроме виджетов, уже содержащихся в библиотеке Qt,
Qt Designer может дополняться виджетами, созданными самим
разработчиком (см. г/7. 41).
Создание нового проекта в Qt Designer
Окно программы Qt Designer содержит меню и панели инструментов.
Справа расположены три окна (рис. 43.1):
П Project Overview (Обзор проекта) — показывает файлы, принадлежащие
проекту. Чтобы открыть один из файлов проекта, нужно щелкнуть
мышью на его имени, находящемся в списке;
П Property Editor (Редактор свойств) — предоставляет ряд свойств
выбранного в данный момент виджета. Это могут быть цвет фона, шрифт,
максимальный/минимальный размер виджета и т. д.;
Глава 43. Qt Designer. Быстрая разработка прототипов
503
П Object Explorer (Объекты) — отображает список используемых виджетов.
В этом окне их можно выбирать для последующего изменения,
например, при помощи Property Editor (Редактор свойств) изменять их
свойства.
После запуска программы в диалоговом окне New/Open (Создать/Открыть)
будет предложен на выбор один из шаблонов (рис. 43.1). Если при
последующих запусках отображение этого окна нежелательно, го следует
отметить флажок Don't show this dialog in the future (He показывать это окно в
будущем). Впоследствии, для вызова этого диалогового окна нужно выбрать
в меню команду File | New (Файл | Создать).
L3 ^ ш h m is % *?
зэ зз аэ
Widget Мил Window uonriguMon Dung
an S3 с
Dg h Button* ™»hRi«ont T n.*log
IB m)
о а
se the hefe menu to regeter with TroKech
Рис. 43.1. Qt Designer
Чтобы создать приложение, лучше всего прибегнуть к файлу проекта. Это
даст возможность автоматического связывания всех создаваемых шаблонов
с проектом. Для этого выберите в диалоговом окне New/Open
(Создать/Открыть) опцию C++ Project (C++ проект). В появившемся окне
Project Settings (Установки проекта), отображенном на рис. 43.2, задайте
путь для сохранения проекта. Чтобы не задавать его вручную, можно
воспользоваться кнопкой ..., расположенной справа от текстового поля и
вызывающей диалоговое окно выбора директории.
504
Часть VI. Особые возможности Qt
Settings | О- |
Language | О-
database File. |
Ие1р | | ПК |
HI
J
d
J
Cancel
a]
Рис. 43.2. Окно настроек проекта
Затем вызовите диалоговое окно New/Open (Создать/Открыть) и выберите
опцию Dialog (Диалоговое окно). Произойдет создание нового диалогового
окна (рис. 43.3) и добавление его в проект.
l« PT£J«Ct iP<
ф RattoButton
ChecfcBox
IMI 3k *) i* % tf
HDD
on of Qt - For comrmaal evaluations, us* the hefe me
Рис. 43.3. Новое диалоговое окно
При помощи окна Property Editor (Редактор свойств), расположенного
справа, в поле caption (заголовок) можно изменить заголовок диалогового
окна Forml на MyDialog (рис. 43.4). А также необходимо задать имя в поле
name (имя), которое будет использовано для имени класса.
Глава 43. Qt Designer. Быстрая разработка прототипов
505
—
Properties j Signaj Handlers j
j Property
1 palette
1 background] rigm
[Ш font
Value
- *J
Ы
J ""~ "1 1
Widge rigm
5 Shell D
| ct (Arrow
|Ш caption
j icon
1Ш iconT
1 mouseTracking iFalse
, 1
4 1
jdl
Рис. 43.4. Окно свойств. Изменение заголовка диалогового окна
Для сохранения созданного диалогового окна выберите команду меню File j
Save As... (Файл | Сохранить как) или нажмите комбинацию "горячих"
клавиш <Ctrl>+<S>.
Добавление виджетов
Чтобы добавить в диалоговое окно новые виджеты, нужно воспользоваться
инструментами, расположенными слева (см. рис. 43.3). Выберите в списке
нужный вам виджет, щелкните по нему левой кнопкой мыши и разместите
его в области диалогового окна в необходимом месте.
Выберите, на закладке Buttons (Кнопки) две кнопки (PushButton), на
закладке Input (Ввод) — ползунок (Slider), на закладке Display (Отображение) —
электронный индикатор (LCDNumber) и разместите их в окне. Выбранные
вами виджеты отобразятся и в окне Object Explorer (Объекты) (рис. 43.5).
Objects j Members j
1 Name | Class
|C3 F0"^1 IQDialog
j l°*]pushButt. QPushButton
»<]pushButt.. |f Button
.Q-shderl hder
J ^TJlCDNiimb .JQLCDNumbei
2£l
H
|LiL
Рис. 43.5. Окно проводника объектов
Выберите, щелкнув левой кнопкой мыши, в окне Object Explorer (Объекты)
или на самом диалоговом окне одну из созданных кнопок. Введите в поле
text (текст) окна Property Editor (Редактор свойств) текст &Reset, а в поле
name (имя) — Reset. Повторите ту же операцию и со второй кнопкой,
только в поле name (имя) введите Quit, а в поле text (текст) — &Quit. Измене-
506
Часть VI. Особые возможности Qt
ния, вносимые в поле name (имя), необходимы для дальнейшего различения
виджетов, это их имена, которыми вы будете пользоваться.
Размещение (лейауты)
После того как все нужные виджеты добавлены к диалоговому окну, можно
заняться их размещением. Для этого поместите указатель мыши в область
диалогового окна, нажмите левую кнопку и, не отпуская, переместите
указатель. Вы увидите рамку. Охватите этой рамкой виджет ползунка и виджет
электронного индикатора. Отпустите левую кнопку мыши, в результате чего
эти виджеты будут выделены. Выберите команду меню Layout | Lay Out
Vertically (Размещение | Разместить по вертикали) для присвоения
вертикального размещения или просто нажмите комбинацию "горячих" клавиш
<Ctrl>+<L>.
В места, которые должны оставаться свободными, следует поместить
заполнитель пространства (Spacer). Для этого выберите команду меню Layout |
Add Spacer (Размещение | Добавить заполнитель). При его размещении в
диалоговом окне появится контекстное меню, в котором нужно выбрать
команду Vertical (Вертикально). Поместите заполнитель под кнопками
нажатия и выделите их при помощи мыши в отдельную группу. Выберите
команду меню Layout | Lay Out Vertically (Размещение | Разместить по
вертикали) для вертикального размещения группы. У вас должно получиться
так же, как показано на рис. 43.6.
MyDlalog
1
1 |
U
ЯПЕЗ
R et "1
Qui I J
- - 1
Рис. 43.6. Вертикальное размещение виджетов
Последний, завершающий штрих — указание диалоговому окну размещать
свои элементы в горизонтальном порядке. Для этого щелкните на одну из
пустых областей диалогового окна указателем мыши или выберите MyDialog
в окне Object Explorer (Объекты). Затем выполните команду меню Layout |
Lay Out Horizontally (Размещение | Разместить по горизонтали) или нажмите
комбинацию "горячих" клавиш <Ctrl>+<H>. Окно изменится и примет вид,
показанный на рис. 43.7. Теперь при изменении размеров окна размеры и
позиции виджетов будут автоматически изменяться, заполняя всю рабочую
площадь.
Глава 43. Qt Designer. Быстрая разработка прототипов
507
MyDialog
и ____ '
НПЕЗ
Reset j
Qurt | .;
1 g
Рис. 43.7. Горизонтальное размещение вертикальных групп виджетов
Порядок следования табулятора
Порядок следования табулятора нужен для навигации по диалоговому окну
при помощи клавиатуры. Нажатие на клавишу <ТаЬ> производит
перемещение фокуса от одного виджета к другому. Для определения порядка
следования табулятора выберите команду меню Tools | Tab Order
(Инструменты | Порядок следования) или нажмите клавишу <F4>. Окно примет вид,
показанный на рис. 43.8. Нажимая указателем мыши на виджеты,
обозначенные синими кружками, можно изменять порядок следования табулятора.
Рис. 43.8. Порядок следования табулятора
Сигналы и слоты
Для соединения сигналов одного виджета со слотами другого нужно
выбрать команду меню Edit | Connections (Правка | Соединения) и нажать в
появившемся диалоговом окне кнопку New (Создать). После этого
необходимо выбрать, для создаваемого соединения, виджет (в нашем случае —
sliderl), ПОСЫЛаЮШИЙ сигнал, И ВИДЖет, принимающий его (LCDNumberl).
В диалоговом окне View and Edit Connections (Просмотр и редактирование
соединений) соедините сигнал valueChanged(int) CO СЛОТОМ display(int)
(рис. 43.9). Выберите команду меню Preview | Preview Form (Просмотр |
Просмотр формы). Теперь изменения положения ползунка приведут к из-
508
Часть VI. Особые возможности Qt
менению отображаемой информации виджетом электронного индикатора.
Можете сразу проверить и установленный порядок следования табулятора.
View and Edit Correct • s ? □
Connections
Sender -s. ..-- . ~r
V sliderl vaJueChanged ICDN . r1 display(int)
I oSenderT
MyDialog
1 Quit
I Reset
shd il
Рис. 43.9. Окно просмотра и редактирования соединений
Можно также добавить к диалоговому окну свои собственные методы и
слоты. Выберите в меню команду Edit | Slots (Правка | Слоты), нажмите кнопку
New Function (Создать функцию) и задайте в поле Function (Функция) слот
siotQuito. Повторите эту операцию для слота siotReseto (рис. 43.10).
Закройте окно нажатием кнопки ОК. Вызовите диалоговое окно View and Edit
Connections (Просмотр и редактирование соединений). Выберите, для
создаваемого соединения, виджет кнопки нажатия (Reset) посылающий сигнал и
виджет, принимающий его (MyDialog). Соедините сигнал clicked о со
слотом siotReseto. Такую же операцию проделайте и с кнопкой Quit (Выход),
соединив ее сигнал clicked() со слотом siotQuito диалогового окна
(MyDialog).
Edit Functions HE3
Function 1 Return Type
§=> slotQuit() void
$So slotReset() void
J Specifier
non virtual
non virtual
I Access
public
public
|Type
slot
slot
J In Use |
No
No
W Only display slots iiew Function £elete Function
Fun on Properties
Eunction JHIillMTHIl Fjeturntype. Jvod
Specifier {nonvirtual "\ Access: | public j»j 1УР°: 1 slot ZJ
Help | j OK ] Cancel |
Рис. 43.10. Окно определения слотов и методов
Delete |
I
Cancel |
Глава 43. Qt Designer. Быстрая разработка прототипов
509
После соединений необходимо снабдить созданные слоты программным
кодом. Сделайте двойной щелчок мышью в диалоговом окне, появится окно
сообщения, которое предложит вам создать файл MyDialog.ui.h. Этот файл
содержит реализацию созданных слотов. Его расширение ui.h говорит о том,
что он предназначен для чтения и записи не только со стороны
разработчика, но и самого Qt Designer. После положительного ответа откроется окно
редактора, в котором отобразится текст программы, содержащей
определения созданных слотов (рис. 43.11).
шдадд
ш k *з 5 % ¥?
С «
■тиЕ*
' ПМиРяЬд uPioacfe/MuDitlog
JJ С /MrPro|ec»i/M»Oielog 1л
3
fosW
Рис. 43.11. Окно редактора
Допишите в редакторе код, показанный в листинге 43.1.
Листинг 43.1. Реализация слотов
#include <qapplication.h>
void MyDialog::slotReset()
{
sliderl->setValue(sliderl->minValue());
510
Часть VI. Особые возможности Qt
void MyDialog::slotQuit()
{
qApp->quit();
}
Компиляция
Последнее, что осталось сделать, чтобы откомпилировать проект, — это
написать основную программу, которая создаст и отобразит диалоговое окно.
Для этого воспользуйтесь командой меню File | New (Файл | Создать) и
выберите в диалоговом окне New File (Создать файл) опцию C++ Main File
(Основной файл C++). Появится промежуточное окно Configure Main File
(Конфигурация основного файла), в поле Main Form (Основное окно) будут
указаны созданные окна (в нашем случае только одно MyDialog)— нажмите
ОК. Откроется окно редактора, которое будет содержать автоматически
созданный код основной программы.
Теперь запишите все сделанные в файлах изменения. Для этого выберите в
меню команду File | Save All (Файл | Сохранить все) и покиньте программу,
выбрав команду меню File | Exit (Файл | Выход).
После этого откройте консольное окно и зайдите в директорию,
содержащую проектный файл и в командной строке дайте следующую команду для
создания make-файла:
qmake -о Makefile MyApplication.pro
Затем запустите утилиту make. В процессе компиляции из ui-файлов будут
автоматически сгенерированны срр- и h-файлы. После компилирования
будет создана исполняемая программа, отображающая после своего запуска
окно, показанное на рис. 43.12.
V; \ Внимание' J^ ".j
Вносите изменения в диалоговое окно только при помощи Qt Designer, и ни в
коем случае не изменяйте сгенерированный С++-код, т. к. после модификации
ui-файла и после запуска make все изменения, внесенные в С++-файлы, будут
утеряны.
MNon-Commerctal] - MyDialog В ЕЗ
Рис. 43.12. Готовое приложение
Глава 43. Qt Designer. Быстрая разработка прототипов
511
Резюме
Реализация пользовательского интерфейса отнимает много времени.
Программа Qt Designer была создана для ускорения этого процесса. В данной
главе мы создали, при помощи этой программы, приложение,
базирующееся на диалоговом окне. Создав диалоговое окно, к нему можно добавлять
любые виджеты и изменять их свойства. При помощи лейаутов можно
задавать разные способы размещения. В тех местах окна, которые должны быть
свободными, нужно помещать специальный заполнитель. Поддержка
сигналов и слотов позволяет соединять виджеты друг с другом, а также создавать
свои собственные слоты. Qt Designer предоставляет возможность быстрого
просмотра созданного диалогового окна. Эта программа позволяет создавать
и загружать проектные файлы, из которых впоследствии, при помощи
утилиты qmake, создаются make-файлы, интерпретация которых
осуществляется запуском утилиты make.
ПРИЛОЖЕНИЯ
Приложение 1. Таблица описания формата RichText
Приложение 2. Таблица простых чисел
Приложение 3. Глоссарий
Приложение 4. Описание компакт-диска
ПРИЛОЖЕНИЕ 1
Таблица описания
формата RichText
Пример
<Hl>Demo</Hl>
<H2>Demo</H2>
<H3>Demo</H3>
<Р>
<B>Demo </B>
<I>Demo </I>
<U>Demo</U>
</P>
<CENTER>Demo<CENTER>
<UL><LI>Demo</LI></UL>
<OL><LI>Demo</LI></OL>
<A href="demo.jpg">Demo</A>
<IMG src="demo.gif">
<HR>
Demo<BR>Demo
Действие
Demo
Demo
Demo
Demo Demo Demo
Demo
• Demo
• Demo
1. Demo
2. Demo j
Demo J
V *
Demo
Demo J
Таблица описания формата RichText
515
(окончание)
Пример
<TABLE>
<TRXTD>Demo</TDX/TR>
</TABLE>
<author>Demo</author >
Действие
Demo! i
J i
Demo
ПРИЛОЖЕНИЕ 2
Таблица простых чисел
1 2
1 47
| 113
193
271
359
443
541
I 619
719
821
911
1013
1097
1213
1301
1429
1511
1609
1721
1831
1949
3
53
127
197
277
367
449
547
631
727
823
919
1019
1103
1217
1303
1433
1523
1613
1723
1847
1951
5
59
131
199
281
373
457
557
641
733
827
929
1021
1109
1223
1307
1439
1531
1619
1733
1861
1973
7
61
137
211
283
379
461
563
643
739
829
937
1031
1117
1229
1319
1447
1543
1621
1741
1867
1979
11
71
139
223
293
383
463
569
647
743
839
941
1033
1123
1231
1321
1451
1549
1627
1747
1871
1987
13
73
149
227
307
389
467
571
653
751
853
947
1039
1129
1237
1327
1453
1553
1637
1753
1873
1993
17
79
151
229
311
397
479
577
659
757
857
953
1049
1151
1249
1361
1459
1559
1657
1759
1877
1997
19
83
157
233
313
401
487
587
661
761
859
967
1051
1153
1259
1367
1471
1567
1663
1777
1879
1999
23
89
163
239
317
409
491
593
673
769
863
971
1061
1163
1277
1373
1481
1571
1667
1783
1889
2003
29
97
167
241
331
419
499
599
677
773
877
977
1063
1171
1279
1381
1483
1579
1669
1787
1901
2011
31
101
173
251
337
421
503
601
683
787
881
983
1069
1181
1283
1399
1487
1583
1693
1789
1907
2017
37
103
179
257
347
431
509
607
691
797
883
991
1087
1187
1289
1409
1489
1597
1697
1801
1913
2027
41
107
181
263
349
433
521
613
701
809
887
997
1091
1193
1291
1423
1493
1601
1699
1811
1931
2029
43
109
191
269
353
439
523
617
709
811
907
1009
1093
1201
1297
1427
1499
1607
1709
1823
1933
2039
Таблица простых чисел
517
(продолжение)
2053
2143
2273
2377
2473
2617
2707
2801
2917
3041
3181
3301
3391
3527
3617
3727
3851
3947
4079
4211
4297
4447
4583
4691
4813
4951
5051
5179
5309
5437
5531
5659
2063
2153
2281
2381
2477
2621
2711
2803
2927
3049
3187
3307
3407
3529
3623
3733
3853
3967
4091
4217
4327
4451
4591
4703
4817
4957
5059
5189
5323
5441
5557
5669
2069
2161
2287
2383
2503
2633
2713
2819
2939
3061
3191
3313
3413
3533
3631
3739
3863
3989
4093
4219
4337
4457
4597
4721
4831
4967
5077
5197
5333
5443
5563
5683
2081
2179
2293
2389
2521
2647
2719
2833
2953
3067
3203
3319
3433
3539
3637
3761
3877
4001
4099
4229
4339
4463
4603
4723
4861
4969
5081
5209
5347
5449
5569
5689
2083
2203
2297
2393
2531
2657
2729
2837
2957
3079
3209
3323
3449
3541
3643
3767
3881
4003
4111
4231
4349
4481
4621
4729
4871
4973
5087
5227
5351
5471
5573
5693
2087
2207
2309
2399
2539
2659
2731
2843
2963
3083
3217
3329
3457
3547
3659
3769
3889
4007
4127
4241
4357
4483
4637
4733
4877
4987
5099
5231
5381
5477
5581
5701
2089
2213
2311
2411
2543
2663
2741
2851
2969
3089
3221
3331
3461
3557
3671
3779
3907
4013
4129
4243
4363
4493
4639
4751
4889
4993
5101
5233
5387
5479
5591
5711
2099
2221
2333
2417
2549
2671
2749
2857
2971
3109
3229
3343
3463
3559
3673
3793
3911
4019
4133
4253
4373
4507
4643
4759
4903
4999
5107
5237
5393
5483
5623
5717
2111
2237
2339
2423
2551
2677
2753
2861
2999
3119
3251
3347
3467
3571
3677
3797
3917
4021
4139
4259
4391
4517
4649
4783
4909
5003
5113
5261
5399
5501
5639
5737
2113
2239
2341
2437
2557
2683
2767
2879
3001
3121
3253
3359
3469
3581
3691
3803
3919
4027
4153
4261
4397
4523
4651
4787
4919
5009
5119
5273
5407
5503
5641
5741
2129
2243
2347
2441
2579
2687
2777
2887
3011
3137
3257
3361
3491
3583
3697
3821
3923
4049
4157
4271
4409
4547
4657
4789
4931
5011
5147
5279
5413
5507
5647
5743
2131
2251
2351
2447
2591
2689
2789
2897
3019
3163
3259
3371
3499
3593
3701
3823
3929
4051
4159
4273
4421
4549
4663
4793
4933
5021
5153
5281
5417
5519
5651
5749
2137
2267
2357
2459
2593
2693
2791
2903
3023
3167
3271
3373
3511
3607
3709
3833
3931
4057
4177
4283
4423
4561
4673
4799
4937
5023
5167
5297
5419
5521
5653
5779
2141
2269
2371
2467
2609
2699
2797
2909
3037
3169
3299
3389
3517
3613
3719
3847
3943
4073
4201
4289
4441
4567
4679
4801
4943
5039
5171
5303
5431
5527
5657
5783
ПРИЛОЖЕНИЕ 3
Глоссарий
Абстрактный класс (abstract class). Класс, от которого нельзя создавать
объектов. Применяется для объединения общих свойств различных классов в
одном, либо для определения полиморфного интерфейса.
Альфа-канал (alpha channel). Четвертая цветовая компонента, используемая для
задания уровня прозрачности. Содержит 8 дополнительных битов
информации о прозрачности для каждого пиксела.
Анимация (animation). Повторяющиеся изображения, создающие иллюзию
движения.
Антиалиасинг (antialiasing). Устранение ступенчатого эффекта на границах
однородных цветовых областей изображения путем расцветки пикселов,
непосредственно прилегающих к границе, в цвета, промежуточные между
цветами граничащих областей.
Булев тип (boolean type). Тип, который может принимать только два значения:
истина (true) или ложь (false).
Венгерская нотация. Соглашение об именовании переменных, функций,
классов, с включением в их имя информации, описывающей тип и назначение.
Выражение (expression). Комбинация переменных, констант и операторов,
объединенных в одной записи.
Глобальная система координат (world coordinate). Базовая система координат
трехмерного кибернетического пространства, не зависящая от положения
наблюдателя.
Глубина цвета (color depth). Задает максимальное количество цветовых
оттенков, которые можно хранить в изображении. Например, изображение с
глубиной цвета 8 бит содержит до 256 оттенков цвета.
Двойная буферизация (double-buflering). Для осуществления гладкой анимации
изображение выводится только в промежуточный буфер (который не
отображается), после чего осуществляется обмен с основным буфером.
520
Приложение 3
Итератор (interator). Объект, который обеспечивает обход последовательности
элементов, принадлежащих объекту контейнера.
Интернационализация (internationalization). Представляет собой процесс
создания программы, поддерживающей несколько языков и/или стандартов. Этот
процесс может заключаться в изменении форматов даты и символа денежной
единицы, а может требовать также перевода всей текстовой информации,
включая кнопки и меню. Также называют И8п.
Заголовочный файл (header file). Файл, в основном содержащий объявления
классов, переменных и функций, которые используются более чем в одной
единице компиляции. Такие файлы обычно имеют расширения hpp, h, H, hh.
hxx и иногда называются включаемыми файлами.
Интерфейс (interface). Именованное множество операций, характеризующее
поведение отдельного элемента модели.
Интерфейс графического устройства (Graphic Device Interface, GDI). Подсистема
операционной системы Windows, предназначенная для вывода графики.
Класс (class). Класс определяет множество характеристик создаваемых объектов.
Классы объявляются с использованием ключевого слова class или struct.
Компонент (component). Соответствует физической реализации набора
интерфейсов и обеспечивает их реализацию. Представляет собой независимую и
заменяемую часть программы, выполняющую определенные действия.
Контейнер (container). Класс, используемый для управления группой объектов.
Контекст (context). Является поверхностью вывода графики.
Масштабируемый шрифт (TrueType font). Использует описание символов в
векторном формате, благодаря чему может гибко изменять размеры и форму
текста.
Метафайл (metafile). Описание изображения в файле, который хранит
последовательность операторов графического вывода.
Метод (method). Реализация конкретного алгоритма или процедуры,
ассоциированной с соответствующей операцией.
Многоугольник (polygon). Замкнутая двумерная геометрическая форма с тремя
или более сторонами.
Модальное диалоговое окно (Modal dialog). Диалоговое окно, не позволяющее
работать с элементами интерфейса приложения вызвавшего это окно, до
своего завершения.
Модуль расширения (plug-in). Дополнительный программный модуль,
способный функционировать как составная часть программы. Модули расширений
широко применяются для предоставления новых возможностей без
реализации новых версий программ.
Немодальное диалоговое окно (Non modal dialog). Диалоговое окно,
допускающее одновременную работу с элементами интерфейса приложения
вызвавшего его.
Глоссарий
521
Объект (object). Экземпляр класса.
Объектная иерархия (object hierarchy). Связанные друг с другом объекты, в одну
цепочку.
Объект-потомок (child object). Объект иерархической цепочки, связанный с
другим объектом, расположенным ближе к вершине дерева иерархии (объектом-
предком).
Объект-предок (parent object). Объект иерархической цепочки,
располагающийся ближе к вершине дерева иерархии, чем связанный с ним объект,
называемый объектом-потомком (child object).
Отсечение (clipping). Исключение части геометрического примитива,
находящейся вне заданного пространства.
Палитра (palette). Набор цветов, доступных для использования в составе
изображения.
Перечисление (enumeration). Список именованных значений, применяемых в
качестве области значений атрибута отдельного типа.
Пиксел (pixel). Наименьшая область, которая может быть отображена на экране.
Положение каждого пиксела определяется уникальным набором двух чисел,
именуемых координатами.
Примитив (primitive). Простая геометрическая фигура, например точка, линия
или многоугольник.
Прототип (prototype). Версия программы, не обязательно подчиняющаяся
управлению и отвечающая полной спецификации.
Процесс (process). Самостоятельная единица исполнения в операционной
системе.
Растровое изображение (image, bitmap). Изображение, созданное множеством
пикселов разного цвета.
Система координат (coordinate system). Пары или тройки чисел, используемые
для задания положения точки в пространстве или на плоскости.
Сигнал (signal). Метод, предназначенный для осуществления высылки
сообщения.
Слот (slot). Метод, реализующий ответное действие на полученный сигнал.
Событие (event). Условие, обычно получаемое в результате выполнения
некоторой операции пользователем.
Строка (string). Последовательность символов текста.
Текстура (texture). Одно- или двумерное изображение, применяемое для
изменения ивета фрагментов, произведенных растеризацией.
Формат файла (file format). Способ организации данных в файле. Например,
к числу типовых форматов файлов растровых изображений относятся BMP,
GIF, JPG.
522
Приложение 3
Шаблон (template). Конструкция, представляющая семейство классов или функций.
Шрифт (font). Группа представлений графических символов, обычно
использующихся для отображения строк текста.
Буфер обмена (clipboard). Предоставляет возможность обмена данными между
программами.
GNU (GNU's Not UNIX). Рекурсивный акроним, который означает— GNU не
UNIX. Является названием организации, борющейся за открытый исходный
код программ. Одним из ее достижений является общедоступная лицензия
на программное обеспечение (GPL).
GPL (GNU Public License). Общедоступная лицензия GNU.
HTML (Hypertext Markup Language). Гипертекстовый язык разметки документа.
Формат файлов для документов, в которых присутствует текст, графика и
другие элементы. Широко используется в Интернете.
HSV (Hue, Saturation, Value — Цветовой тон, Насыщенность, Интенсивность).
Цветовая модель, предоставляющая возможность задавать цвет по его тону
(чистому цвету), насыщенности (глубине) и интенсивности (яркости).
il8n. Является сокращением английского слова internationalization. Число 18 в
этой аббревиатуре указывает, что между первой буквой (i) и последней (п)
находится еще 18 букв.
LZW (Lempel-Ziv-Welch). Метод сжатия информации, который используется,
например, в файлах формата GIF.
MDI (Multiple Document Interface). Интерфейс для одновременной работы с
несколькими документами.
MIME (Multipurpose Internet Mail Extensions). Является методом, с помощью
которого данные могут быть закодированы в форму, легко передаваемую
через Интернет или другое соединение, и затем преобразованы получателем к
исходному виду. Был разработан для поддержки подключений к электронной
почте, но применяется также и в других целях, например, для drag&drop.
OpenGL (Open Graphic Library). Библиотека графических функций,
разработанная компанией Silicon Graphics.
RGB (Red, Green, Blue). Цветовая модель, согласно которой цвет задается тремя
компонентами: красной, зеленой и синей.
RGBA (Red, Green, Blue, Alpha). Цветовая модель, включающая, помимо
красного, зеленого и синего цветов, также и альфа-компоненту.
ХРМ (XPixMap). Формат графических файлов в форме исходного кода на
языке С. Файл в этом формате можно напрямую включить в программу или
использовать как независимый графический файл.
ПРИЛОЖЕНИЕ 4
Описание компакт-диска
На прилагаемом к данной книге CD помещена библиотека Qt и исходные
тексты описанных, в книге примеров. Каждый из примеров хранится в
отдельном каталоге. Для их компиляции следует скопировать директорию,
содержащую примеры (Examples), на жесткий диск своего компьютера,
создать make-файл, при помощи профаммы qmake, и запустить утилиту make.
В следующей таблице приведено описание содержимого CD.
Папка
/Library
/Examples/AppStyle
/Examples/Background
/Examples/BlinkLabel
/Examples/ButtonGroup
/Examples/ButtonPopup
/Examples/Buttons
Описание
Библиотека Qt для ОС Windows и ОС Linux
(X Windows). (Примечание: для ОС Windows
на CD размещена устаревшая версия Qt2.3,
поэтому для успешной компиляции всех
приведенных в книге примеров рекомендуется
воспользоваться актуальной Trial-версией,
которую можно сгрузить со страницы
http://www.trolltech.com/download/qt
/evaluate.html)
Приложение демонстрирует использование
различных стилей
Приложение демонстрирует установку фона
виджета
Приложение демонстрирует работу таймера
(QTimer)
Приложение демонстрирует возможность
группировки кнопок
Приложение демонстрирует кнопку со
всплывающим меню
Приложение демонстрирует различные кнопки
нажатия (QPushButton)
Глава
12
4
35
6
6
6
524
Приложение 4
(продолжение)
Папка
/Examples/Calculator
/Examples/Canvas
/Examples/CanvasView
/Examples/CheckBox
/Examples/Clock
/Examples/ComboBox
/Examples/ContextMenu
/Examples/Counter
/Examples/CustomStyle
/Examples/CustomWidget
/Examples/DataTable
/Examples/DateTimeEdit
/Examples/Dial
/Examples/Drag
/Examples/Drop
/Examples/DynLib
/Examples/EventChange
/Examples/EventFilter
/Examples/EventSimulation
Описание
Приложение калькулятора, демонстрирующее
табличное размещение (QGridLayout)
Приложение отображает элемент холста
Приложение демонстрирует работу с холстом
Приложение демонстрирует флажки (QCheckBox)
Приложение электронных часов,
демонстрирующее работу таймера и классов даты и времени
(QDateTime)
Приложение демонстрирует виджет
выпадающего списка (QCoinboBox)
Приложение демонстрирует применение
контекстного меню
Приложение демонстрирует механизм сигналов
и слотов
Приложение иллюстрирует создание и
использование своих собственных стилей
Приложение демонстрирует создание и
использование собственных виджетов
Приложение использует осведомленный элемент
для отображения таблицы базы данных
(QDataTable)
Приложение демонстрирует виджеты
отображения даты и времени (QDateTimeEdit)
Приложение демонстрирует виджет установщика
(QDial)
Приложение реализует сторону источника для
перетаскивания
Приложение реализует принимающую сторону
для перетаскивания
Приложение демонстрирует создание и загрузку
динамических библиотек
Приложение демонстрирует искусственное
создание событий
Приложение демонстрирует механизм
фильтрации событий
Приложение демонстрирует искусственное
создание событий
Глава
10
20
20
6
35
8
29
2
12
24
40
9
7
27
27
41
15
14
15
Описание компакт-диска
525\
(продолжение)
Папка
/Examples/FileFinder
/Examples/Grid
/Examples/HBox
/Examples/Hello
/Examples/HelpBrowser
/Examples/lconView
/Examples/lnputDialog
/Examples/KDEApp
/Examples/Label
/Examples/LabelBuddy
/Examples/LabelPixmap
/Examples/Layout
/Examples/LCD
/Examples/LineEdit
/Examples/ListBox
/Examples/ListView
/Examples/MDI
/Examples/Menu
/Examples/MousePaint
/Examples/Movie
/Examples/MultiLineEdit
Описание
Поиск файлов с применением класса QDir
Приложение демонстрирует использование
виджета табличного размещения (QGrid)
Приложение демонстрирует использование
виджета горизонтального размещения (qhbox)
Пример первой программы на Qt, отображающей
надпись "Hello, World"
Приложение предоставляет систему помощи
Приложение демонстрирует виджет показа
иконок(QlconView)
Приложение демонстрирует реализацию
собственного диалогового окна
KDE-приложение для ОС Linux
Приложение демонстрирует виджет надписи
(QLabel)
Приложение демонстрирует возможность
ассоциации виджета надписи с другими виджетами
Приложение демонстрирует использование
в виджете надписи, растровых изображений
Приложение демонстрирует использование
горизонтального и вертикального лейаутов
Приложение демонстрирует виджет
электронного индикатора (QLCDNumber)
Приложение демонстрирует виджет
однострочного текстового поля (QLineEdit)
Приложение демонстрирует виджет простого
списка(QListBox)
Приложение демонстрирует виджет
иерархического списка (QListview)
MDI-приложение
Приложение демонстрирует использование меню
Приложение для рисования, демонстрирующее
обработку событий мыши
Приложение демонстрирует использование
анимации(QMovie)
Приложение демонстрирует виджет
многострочного текстового поля (QMultiLineEdit)
Глава
37 J
10
10
1
31 |
8
30
42 |
5
5
5
10
5
9
8
8
зз I
29 |
13
21
9
526
Приложение 4
(продолжение)
Папка
Г/Examples/MyApplication
/Examples/Network
Г/Examples/OG LDraw
/Examples/OG LPyramid
1 /Examples/OGLQuad
/Examples/Printer
/Examples/Process
/Examples/Progress
/Examples/RadioButton
/Examples/Resize
/Examples/ScrollBar
/Examples/ScrollView
/Examples/SDI
1/Examples/Session
/Examples/Settings
/Examples/Slider
/Examples/SoundPlayer
/Examples/SpinBox
/Examples/SplashScreen
/Examples/Splitter
Описание
Приложение, созданное при помощи Qt Designer
Сервер и клиент приложения, иллюстрирующие
возможности класса QSocket
Приложение демонстрирует вывод графических
примитивов OpenGL
Приложение вращения пирамиды,
демонстрирующее трехмерную графику OpenGL
Приложение демонстрирует эффект
сглаживания цветов вершин четырехугольника
Приложение вывода на печать (QPrinter)
Приложение командной оболочки,
демонстрирующее создание процессов (QProcess)
Приложение демонстрирует работу индикатора
прогресса (QProgressBar)
Приложение демонстрирует переключатель
(QRadioButton)
Приложение демонстрирует обработку события
изменения размеров
Приложение демонстрирует виджет полосы
прокрутки (QScrollBar)
Приложение демонстрирует виджет прокрутки
(QScrollView)
SDI-приложение
Приложение управления сеансом (QSession)
Приложение, сохраняющее свои настройки
(QSettings)
Приложение демонстрирует виджет ползунка
(QSlider)
Приложение проигрывателя, демонстрирующее
воспроизведение звука (QSound)
Приложение демонстрирует виджет счетчика
(QSpinBox)
Приложение отображает предшествующее окно
(QSplashScreen)
Приложение демонстрирует виджет разделителя
(QSplitter)
Глава
43
38
22
22
22
23
34
5
6
13
7
4
33
26 |
26
7
25
9
33
10
Описание компакт-диска 527
(окончание) '
Папка
1 /Examples/SQL
/Examples/StatusBar
/Examples/Styles
/Examples/TabDialog
/Examples/Table
/Examples/TabWidget
/Examples/TeaЮffMenu
/Examples/TextEdit
/Examples/Threads
/Examples/ToolBar
/Examples/ToolBox
/Examples/Validator
/Examples/WhatsThis
/Examples/WidgetPalette
/Examples/Window
/Examples/Wizard
/Examples/XmlDOMRead
/Examples/XmlDOMWrite
/Examples/XmlSAXRead
/Examples/WinAPI
Описание
Приложение осуществляет чтение и запись
в базу данных
Приложение со строкой состояния
Приложение демонстрирует стили,
интегрированные в Qt
Приложение отображает окно диалога с
закладками (QTabDialog)
Приложение демонстрирует виджет таблицы
(QTable)
Приложение демонстрирует виджет закладок
(QTabWidget)
Приложение демонстрирует отрывное меню
Приложение демонстрирует виджет
многострочного текстового поля
Приложение демонстрирует создание потоков,
имеющих разные приоритеты (QThread)
Приложение демонстрирует использование
панелей инструментов (QToolBar)
Приложение демонстрирует виджет
инструментов (QToolBox)
Приложение демонстрирует проверку
пользовательского ввода(QValidator)
Приложение демонстрирует помощь What's This
Приложение демонстрирует изменение палитры
виджета
Приложение демонстрирует применение
прозрачности к виджету верхнего уровня
Приложение отображает окно диалога мастера
(QWizard)
Приложение демонстрирует чтение
XML-документа при помощи DOM
Приложение демонстрирует запись
XML-документа, создание его при помощи DOM
Приложение демонстрирует чтение
XML-документа при помощи SAX
Приложение демонстрирует использование
платформозависимых функций ОС Windows
Глава
40
32 J
.
30 If
8
и
8
29 ||
9 f
34 ||
32
8
9
31 ||
11
18
30
39
39
39 I
42 I
Предметный указатель
А
ACDSee 267
active 158
alpha channel 252
Anti-Aliasing 270
В
bitBltO 187, 192, 246
Bitmap 236
BMP 236
Borland C++ 55
С
Callback functions 36
Clipboard 323
configure 56
critical sections 411
D
Data Compression 445
datagram 455
deadlock 413
debug() 63
desktop 33
disabled 73, 158
drag&drop 129, 187, 324, 325
DOM 470
Drag 325
Drop 325
E
emit 41, 44
enabled 73
F
fatal() 63
Flat Button 95
FTP 456, 463
G
GDI 497
GIF 236, 237, 267
GL_ALPHA28I
GL_BITMAP28I
GL.BLUE 281
GL.BYTE 281
GL_COLOR_BUFFER_BIT 276
GL_COMPILE 288
GL_DEPTH_BUFFER_BIT 276
GL.FLAT 276, 285
GL_FLOAT28l,282
GL_GREEN 281
GLJNT 281
GL_LINE_LOOP 280
GL_LINE_STRIP 280
GLJJNES 280
GL_POINTS 280
GL.POLYGON 280
GL_QUADS 276. 280, 288
GL_RED 281
GL_RGB 281
GL.RGBA 281
GL_SMOOTH 276
GL_TRIANGLE_FAN 288
GL_TRIANGLE_STRIP 280
GL.TRIANGLES 280
GL_UNSIGNED_BYTE 281
glBegin() 276
GLbyte 271
GLdouble 271
GLenum 271
GLfloat 271
Предметный указатель
529
GLint 271
GLshort 271
GLubyte 271
GLuint 271
GLushort 271
giCallList() 287
glClear() 276
glClearColor() 275
glColor() 276
gIDrawPixelsO 281
glEnd() 276
glEndList() 288
glFrustumO 286
glGenLists() 288
glLoadldentityO 275
glMatrixMode() 275. 286
glNewList() 288
glOrthoO 275
gIPointSizeO 281
glRasterPos() 281
glShadeMode() 285
gIShadeModelO 276
glTexlmage2D() 282
glVertex() 276
glViewPort() 275. 286
gmakc 57
Graphics Interchange Format (GIF) 237
H
HSV2H.2I2, 215
HTTP 464
I
inactive 158
J
Joint Photographic Experts Group (JPEG)
237
К
KDE 499
Key 312
Key Value 312
L
link 62
Linux 499
Look&Feel 163
LRELEASE331.334
LUPDATE331.332
LZW237
M
makefile 57, 65
make-файл 58
MDI 388,400
MFC 37
Microsoft Visual C++ 65, 55
Microsoft Visual Studio 54, 63
MIDI 309
MNG 236, 267
MOC 38, 40, 61
Modelview matrix 275
Motif 36
Multiple Document Window Interface 388, 400
N
nmake 54
О
OpenGL 270
P
PBM 236
PGM 236
PNG 236, 237
PNM 236
Portable Network Graphics 237
PPM 236
pro 58
Projection matrix 275
Q
Q Quit 193
Q_ASSERT() 63
Q_CHECK_PTR() 63
Q_EXPORT49l
Q_EXPORT_PLUGIN 495
Q_EXTERN_C491
QJNT8 64
QJNTI6 64
QJNT32 64
QJNT64 64
Q_OBJECT39. 61
Q_UINT8 64
Q_UINTI6 64
Q_UINT32 64
Q.U1NT64 64
QABS() 64
QAction 387
0 activatedO 397
0 addTo() 388, 397
530
qAlphaQ 215
QAppIication 27. 33. 64, 199, 200. 304
0 bcep()304
0 cIipboard() 324
0 closeAllWindowsO 397
0 commitDataO 320, 321
0 exec() 28, 192
0 installGlobalFilter() 199
0 installTranslator() 335
0 isRestrored() 320
0 lastWindowClosedO 27, 46
0 lock()4l2
0 postEventO 200
0 processEvents() 419
0 qApp 105
0 quit() 105, 361
0 restoreOverrideCursorO 76
0 saveStateO 320
0 sendEvent() 189, 200
0 setDoublcClickInterval() 183
0 setFont() 255
0 setOverrideCursor() 76
0 setPaletteO 161
0 setStyleO 165, 169
0 startDragDistanceO 327
0 unlock()412
QBitArray 429
0 operatorll 429
0 setBit()429
0 testBit()429
QBitmap 32, 248
qBlue() 215
QBoxLayout 147
0 addLayout() 147, 148
0 addStretch() 147
0 addWidgetO 147, 149
0 setMaiginQ 149
0 setSpacing() 147
QBrush 28, 32, 221
QBufler442, 446
О bufler()446
0 setBufler() 446
QButton 29, 94
0 clickedO 46, 94, 99, 152
0 groupO 94
0 pixmapO 94
0 pressed() 99
0 released() 99
0 setPixmap() 94
0 setText()94
0 text()94
0 clickedO 97
QButtonGroup 31. 102
0 find() 105
0 id() 104
QByteArray 429
Предметный указатель
QCanvas 32, 259
0 updateQ 261
QCanvasEllipse 260
QCanvasItem 259
QCanvasLine 260
QCanvasPolygon 260
QCanvasPolygonalltem 260
0 areaPoints() 260
0 drawShapeO 260
QCanvasRectangle 260
0 setBiushO 261
0 setPen() 261
0 setSize()26I
0 setX()26l
0 setY()26I
0 show() 261
QCanvasSpline 260
QCanvasSprinte 260
QCanvasText 260
QCanvasView 259, 261
0 canvasQ 265
0 collisions() 265
0 contentsMouseMoveEventO 264
0 contentsMousePressEvent() 264
0 contentsMouseReleaseEventO 264
QCDEStyle 164
QCheckBox 29, 98
0 isChecked() 99
0 setChecked() 99
0 setNoChangeO 99
0 setTristateO 99
QCheckTableltem 124
QChild Event 188
QCIipboard 323
0 dataChangedO 323
0 image() 324
0 pixmap() 324
0 set Data () 324
0 setlmage() 324
0 set PixmapO 324
0 setText() 324
0 text()324
QCIoseEvent 189
0 accept() 189
0 ignore() 189
QColor32, 214, 301
0 black 216
0 blue 216
0 blue()214
0 coloK) 77, 216, 248
0 colorl 77, 216, 248
0 cyan 216
0 dark()2l6
0 darkBluc2l6
0 darkCyan 216
0 darkGray2l6
0 darkGreen 216
0 darkMagenta 216
0 darkRcd2!6
Предметный указатель
531
О dark Yellow 2I6
О getHsv()2l5
О gray 2I6
О green 2I6
О green() 214
О isValid() 355
О light()216
О ItghtCray 216
О magenta 216
О red 216
О red() 214
О igb()2l5
О setHsv()2l5
О setRgb()21S
О white 216
О yellow 216
QColorDialog 32, 355
О getColor() 355
QColorDrag 325
QColorGroup 158, 171
0 Background 159
0 Base 159
0 BrightText 159
0 Button 159
0 ButtonText 159
0 Dark 159
0 Foreground 159
0 Highlight 159
0 HighlightedText 159
0 Light 159
0 Mid 159
0 Midlight 159
0 Shadow 159
0 Text 159
QComboBox 29, 120
0 activatedO 121
0 currentltemO 120
0 insertltemO 120
0 insertStringListO 120
0 setDuplicatesEnabledO 120
0 setEditableO 120, 121
0 setValidator() 139
0 textChanged() 120
QComboTableltem 124
QCommonStyle 164
qCompressO 445
QContentHandler 476
0 characters() 476
0 endDocumentO 476
0 endElement() 476
0 startDocumentO 476
0 startElementO 476
QCursor 76
QCustom Event 188
QDataTable 488
0 addColumn() 489
0 refresh () 489
QDate 415
0 addDays()4l6
0 currentDateO 416
0 day()4l5
0 dayOfWeek()416
0 dayOfYear()4l6
0 dayslnMonth() 416
0 dayslnYearQ 416
0 daysTo()416 *
0 fromString()4l6
0 month()415
0 setYMD()415
0 weekNumber() 416
0 year()415
QDateEdit 138
0 valueChanged() 138
QDateStream 453
QDateTime 418
0 currentDateTimeO 139
QDateTime Edit 138
0 valueChangedO 138
QDB2 483
qDebug() 63
QDial 112
0 dialMovedO 112
0 valueChangedO 113
QDialog 32, 347
0 accept()352
0 Accepted 348, 352
0 exec() 348, 350, 352
0 hide()348
0 Rejected 348, 352
0 rejectedO 352
0 show()348
QDir442,446
0 absPath()374
0 cd()446
0 cdUp()447
0 current() 446
0 currentDirPathO 446
0 drives()446
0 exists()446
0 home()446
0 mkdir()447
0 renameO 447
0 rmdir()447
0 root()446
QDomAttr 470
0 setValue() 475
QDomDocument:
0 appendChild() 474
0 createTextNodeO 475
0 documentElementO 472
0 setContext() 472
0 toString()474
532
Предметный указатель
QDomElemem 470
0 attributeO 473
О tagName() 473
О text()473
QDomNode 470
О firstChildO 472
О nextSibling() 472
О toElementO 471
О traverseNode() 472
QDomText 470
QDoubleValidator 139
QDragEnterEvent 188
0 accept()329
QDragLeaveEvent 188
QDragMoveEvent 188
0 acceptf) 330
0 ignore() 330
QDragObject 325
QDropEvent 188
QErrorMessage 367
0 message() 368
QEvent 28, 178, 193, 199, 201
0 Accel 194
0 AccelAvailable 194
0 AccelOverride 194
0 Accessibility 194
0 ActivateControl 194
0 ApplicationFontChange 194
0 Application PaletteChange 194
0 CaptionChange 194
0 Childlnserted 194
0 ChildRemoved 194
0 Clipboard 194
0 Close 193
0 ContextMenu 194
0 DeactivateControl 194
0 DeferredDelete 194
0 Destroy 193
0 DragEnter 194
0 Drag Leave 194
0 DragMove 194
0 DragResponse 194
0 Drop 194
0 Enter 193
0 Focusln 193
0 FocusOut 193
0 Hide 193
0 HideToParent 194
0 IconChange 194
0 IMCompose 194
0 IMEnd 194
0 IMStart 194
0 KeyPress 193, 201,203
0 KeyRelease 193
0 LanguageChange 194
0 LayoutDirectionChange 194
0 LayoutHint 194
0 Leave 193
0 LocaleChange 194
0 MouseButtonDblClick 193
0 MouseButtonPress 199
0 MouseButtonPress 193
0 MouseButtonRelease 193
0 MouseMove 193
0 Move 193
0 None 193
0 Paint 193
0 PaletteChange 194
0 Parent FontChange 194
0 Parent PaletteChange 194
0 Reparent 193
0 Resize 193
0 Show Fullscreen 194
0 ShowMaximized 194
0 ShowMinimized 193
0 ShowNormal 193
0 ShowToParent 194
0 ShowWindowRequest 194
0 SockAct 194
0 Speech 194
0 Style 194
0 Tablet Move 194
0 TabletPress 194
0 TabletRelease 194
0 Timer 193
0 type() 178, 193
0 Wheel 194
0 WindowActivate 194
0 WindowDeactivate 194
qFatalO 63
QFile 442, 443
0 close()392
0 exists() 444
0 open() 390
0 setName() 444
QFile Dialog 32, 353
0 get Exist ingDirectory() 354
0 getOpenFileNameQ 353
0 getOpenFileNamesO 354
0 getSaveFileNameO 354
QFilelnfo 442, 450
0 absFilePathO 450
0 baseName() 450
0 created()450
0 extension() 450
0 fileNameO 450
0 filePath() 450
0 isDir()450
0 isExecutable() 451
0 isFile()450
0 isHidden() 451
0 isReadable() 451
0 isSymLink() 450
0 isWriteable() 451
Предметный указатель
О lastModifiedO 450
0 lastRead() 450
QFocusEvem 181
QFont 32, 254
QFontDatabase 255
0 families*) 255
QFontDialog 32, 357
0 getFont()357
QFontlnfo 255
0 bold()255
0 family()255
0 italic(> 255
QFontMetrics 255
0 ascem() 256
0 boundRectO 256
0 charWidth() 256
0 descent()256
0 height()256
0 leftBearingO 255
0 lineSpacing() 256
0 rightBearingO 255
0 width()256
QFrame 29, 77, 143
0 Box 77
0 drawFrameO 302
0 HLine77
0 NoFrame 77
0 Panel 77
0 Plain 77
0 Raised 77
0 setFrameO 131
0 setFrameStyleO 77, 301
0 setLineWidth 131
0 setLineWidth() 78, 301
0 setMaigin() 78, 143
0 setMidLineWidth() 78
0 Sunken 77
0 VLine77
0 WinPanel77
QFtp 463
0 commandFinished() 464
0 commandStarted() 464
0 connectToHost() 464
0 get()463
0 login()464
0 put()463
0 rawCommandQ 463
QGL 272
QGLColormap 272
QGLContex 272
QGLFormat 272
0 setFormatO 272
QGLWidget 32, 272
0 contextO 272
0 formatO 272
0 initializeGL() 272
0 paintGLO 273
0 qglClearColor() 275
0 rcsizeGLO 272
0 setContextO 272
QGPIugin 493
0 iface()494
0 setlnterface() 494
qGreen() 215
QGrid31, 145
0 setSpacing() 146
QGridLayout 31, 147, 149
0 addColSpacingO И9
0 addMultiCellWidgetO 149, 152
0 addRowSpacing() 149
0 addWidget() 149, 152
0 setColStretchO 149
0 setRowStretchO 149
QGridView 125
0 paintCellO 125
QGroupBox31,96, 101
QHBox 31
0 setSpacingO 143
0 setStretchfactor() 144
QHBoxLayout31, 147
QHButtonGroup 31, 102
QHeader29, 125
0 setLabelO 125
QHideEvent 189
QHttp 464
QIconDrag 325
Q Icon View:
0 doubleClicked() 122
0 itemRenamedO 122
0 rightButtonPressedO 122
0 setArrangement() 122
0 setSortingO 122
0 TopToBottom 122
QlconViewItem 122, 123
0 setRenameEnabled() 122
Qlmage 32, 239
0 bits() 281
0 convertToGLFormatO 281
0 height()28l
0 invertPixels() 242
0 load()240
0 mirrorO 244
0 pixel() 241
0 save() 240
0 scale()243
0 Scale Free 243
0 ScaleMin 243
0 scanLineQ 239
0 setAlphaBufferO 252
0 setPixel() 241
0 width()28l
QlmageFormatPlugin 493
QlmagelO:
0 defmelOHandlerO 245
0 ioDevice() 245
534
Предметный указатель
QlnputDialog 357
О getDouble() 357
О getlntegerQ 357
О getltem()357
О getText()357
qInstallMsgHandler() 63
QlntValidator 139
QIODevice 442
0 atEnd()443
0 closc()443
0 getch()443
0 IO_Append 444
0 IO_Raw444
0 IO_ReadOnly 444
0 IO_ReadWrite 444
0 IO_Translate 444
0 IO_WriteOnly 444
0 isReadable() 443
0 isReadWrite() 443
0 isWriteableO 443
0 open()443
0 putch()443
0 readBlock() 443
0 setFlags() 443
0 size()443
0 ungetch() 443
0 write Block() 443
QKeyEvent 178, 203
0 accept() 180
0 ascii() 180
0 ignore() 180
0 key() 179
QLabel 29, 83, 133
0 setAligment() 83
0 setBuddy() 87, 131, 141
0 setMovie() 83, 268
0 setNumO 109
0 setPixmapO 83, 87
0 setText() 83, 85, 330
QLayout 31, 146
QLayoutltem 146
QLCDNumbcr 30, 91
0 Bin 91
0 Dec 91
0 display()93
0 Filled 91
0 Flat 91
0 Hex 91
0 Oct 91
0 Outline 91
0 overflow() 91
0 setBinMode() 91
0 setDecMode() 91
0 setHexMode() 91
0 setMode()91
0 setNumDigitsO 91
0 setOctMode() 91
0 setSegmentStyle()91, 152
0 setSmallDecimalPointO 91
0 valueChanged() 93
QLibnwy 492
0 isLoaded() 492
0 load()492
0 rcsolve()492
QLineEdit 30, 129
0 copy() 131
0 cut() 131
0 isRedoAvailableQ 131
0 isUndoAvailable() 131
0 maxLengthO 131
0 Password 129
0 paste() 131
0 returnPressedO 129
0 setEchoMode() 129
0 setMaxLength() 131
0 setReadOnlyO 129
0 setText() 129
0 setValidatoit) 139
0 text() 129
0 textChangedO 129
0 undo() 131
QListBox30, 118
0 clearO N8
0 currentltem() 119
0 doubleClicked() 119
0 insert Item() 118, 120
0 insertStringListO 118
0 returnPressedO 119
0 selected() 119
0 selected ltem() 119
0 setMultiSelection() 119
0 sort() 118
QListBoxItem 118
QListBoxText 118
QListView30, 115
0 addColumnO 116
0 doubleClickedO 117
0 firstChildO 117
0 itemRenamed() 118
0 rightButtonClicked() 117
0 selectionChangedO 117
0 setMultiSelection() 117
0 setSortingO 117
QListViewItem 115
0 addColumnO 115
0 itemBelowO 117
0 setDragEnabledO 118
0 setDropEnabledO 118
0 setOpenO 116
0 setPixmapO 117
0 setRenameEnabled() 118
0 setSelectable() 117
0 setSelectedO N7
0 setText() 117
Предметный указатель
QMainWindow 383
О addDockWindow() 380
0 bottomDockO 384
0 centralWidgeK) 384
О UflDock()384
О menuBarO 383
О right Dock() 384
0 setCentralWidgetO 385, 397
0 statusBarO 385
0 topDock()384
qmake 58, 60, 62, 65
Q Map 431
0 clear()432
0 contains() 432
0 count()431
0 fmd()432
0 insert()431
0 operatorO 432
0 remove () 432
0 replace() 432
QMAX() 64
QMemArray 428
0 copy()428
О ПП() 429
0 find()428
QMenuBar 337
QMenuData 337
QMessageBox 32, 363
0 Abort 364
0 About Qt() 367
0 Cancel 364
0 Critical 364
0 criticalO 366
0 Default 364
0 Escape 364
0 exec() 363
0 Ignore 364
0 Information 364
0 informationO 365
0 No 364
0 NoAH364
0 NoButton 364
0 Nolcon 364
0 Ok 364
0 Question 364
0 Retry 364
0 setButtonTextQ 364
0 setCaptionO 364
0 setkon() 364
0 setlconPixmapO 364
0 setText() 364
0 Warning 364
0 warningO 365
0 Yes 364
0 YesAII 364
QMimeSource 324
0 encodedData() 325
0 format() 325
QMINO 64
QMotifPlusStyle 164
QMotifStyle 164
QMouseEvent 176, 199
0 button() 183, 199
0 globalPosO 183, 345
0 globalX() 183
0 globalY() 183
0 pos() 183
0 state() 183
0 x() 183, 382
0 y() 183, 382
QMoveEvent 192
0 oldPos() 192
0 pos() 192
QMovie 32, 267
0 finished*) 268
0 pause()268
0 paused()268
0 restart()268
0 runningO 268
0 setSpeed() 268
0 step() 268
0 unpause() 268
QMultiLineEdit 30, 135
0 append() 136
0 setAlignment() 136
QMutex28, 411
0 lock()411
0 tryLock()411
0 unlock()4ll
QMYSQL3 483
QM-файл 334
QObject 28, 35, 43, 48, 49, 196
0 blockSignals() 45
0 child()51, 188
0 childEventO 188
0 children()51
0 classNameO 52
0 connect() 44, 49, 113, 350, 375
0 disconnect) 49
0 dumpObjectlnfoO 52, 64
0 dumpObjectTreeO 52
0 event() 189, 193
0 eventFilter() 196, 198, 203
0 inherits() 52
0 installEventFillerO 198, 202
0 isA()52
0 killTimerO 420
0 killTimersO 420
0 name()50, 64
0 parent() 51
0 qiieryList() 51
0 sender() 43, 309
0 setName()50
0 startTimerO 420
Продолжение рубрики см. на с. 536
536
Предметный указатель
QObject (проб.):
О timerEventO 187, 420
0 timerld()420
О tr()332
QObjectList 51
QOCI8 483
QODBC3 483
QPaintDevice 32, 218, 219, 289
О handle()499
О xllDisplay()499
О xllScreen()499
QPaintDevice Metrics 218, 294
QPainter 28, 32, 219, 260
0 begin()219
0 drawEllipse() 294
0 drawImageO 241
0 drawLineO 294, 301
0 drawLineSegment() 225
0 drawPictureO 230
0 drawPolyLineO 224
0 drawRectO 257, 294
0 drawTextO 256, 257, 294, 302
0 end()219
О ПИ Rect() 192
0 flush()220
0 restoreO 231
0 rotate()230
0 save()231
0 scale()230
0 setBrush() 221
0 setClippRect() 232
0 setClipRegionO 232. 233
0 setFonK) 256, 295
0 setPen() 220. 221, 294
0 shearQ 230
0 translate() 230
QPaintEvent 181
0 region() 182
QPalette 158
0 setActiveO 161
0 setBnishO 161
0 setColor() 161
0 setDisabled() 161
0 setlnactive() 161
QPen 29, 33
0 setCapStyle() 221
0 setColor() 220
0 seUoinStyle() 221
0 setWidth() 220
QPicture 229
QPixmap 29. 33, 246
0 convertFromlmage() 252
0 createHeuristicMaskO 251
0 defaultDepthO 246
0 drawPixmapO 247
О Г.ПО 186
0 load()86, 247
0 mask()250
0 save() 247
0 setMask() 248
QPixmapCache 248
0 fmd()248
0 insert()248
QPIatinumStyle 164
Q Point 74, 206
0 isNull()207
0 manhattanLength() 207
0 rx()207
0 setX()207
0 setY()207
0 x()207
0 y()207
QPointArray 208, 429
QPopupMenu 30, 337
0 activatedO 345
0 clear()400
0 exec() 345
0 insertltemO 98, 341
0 insertSeparatorO 341
0 insertTearOfTHandleO 343
0 setCheckable()34l,397
0 setltemCheckedO 341
0 setltemParameteit) 400
QPrintDialog 32, 289, 354
QPrinter 33. 289
О АО 290
0 Al 290
О А2 290
0 A3 290
О А4 290
О А5 290
О А6 290
О А7 290
О А8 290
О А9 290
0 abort() 290
0 BO 290
0 Bl 290
О В2 290
О В3 290
О В4 290
О В5 290
О В6 290
О В7 290
О В8 290
О В9 290
0 BIO 290
О С5Е290
0 Color 290
0 Comml0E290
0 DLE290
0 Executive 290
Предметный указатель
О Folio 290
О fromPageO 294
О Grayscale 290
О Landscape 289
О Ledger 290
О Legal 290
О Utter 290
О newPageQ 294
О Portrait 289
О setColorMode() 290
О setDocNameO 290
О setFromToO 289
О setMinMax() 293
О setNumCopiesO 289.
О setOrientationO 289
О setOutputFileNameO 290
О setPageSize() 290
О setup()294
О Tabloid 290
О toPage() 294
QProcess 404
О addArgument() 404
О clearArguments() 407
О readStderrO 405
О readStdoutO 405
О readyReadStdOutO 407
О setArgumentsO 404
О start() 404, 407
QProgessBar 88
QProgressBar 30
0 setProgressO N3
QProgressDialog 32, 358
0 canceled() 359
0 reset()360
0 setAutoClose() 360
0 setAutoReset() 360
0 setCaption() 359
0 setLabelO 359
0 setMinimumDurationO 359
0 setProgessO 359
0 setTotalStepsO 359
0 wasCanceled() 359
QPSQL7 483
QPtrCollection 434
0 clear()434
0 count()434
0 deleteltem() 434
0 newltem() 434
0 setAiitoDelete() 434
QPtrDict 434
QPtrList 434
QPtrQueue 434
0 dequeueO 434
0 enqueueO 434
0 head()434
QPtrStack 434
QPtrVector 434
QPushButton 30, 95
0 clickedO 352, 375
0 isDown() 173
0 isOn()309
0 setFlat()97
0 setPopupO 98
QRadioButton 30
0 isCheckedO 101
0 setChecked() 101
QRangeControl 107
0 rangeChange() 107
0 setRange() 107
0 setSteps() 107
0 setValueO 107
0 stepChange() 107
0 value() 107
0 valueChangeO 107
QRect74, 171,209
0 bottomRight() 210
0 buttom()209
0 height(> 211
0 lefl() 209
0 rBottom() 210
0 right() 209
0 rLefl()210
0 rRight()210
0 rTop()210
0 setBottomO 210
0 setBottomRight() 210
0 setHeight()211
0 setLeft()210
0 setRight() 210
0 setSize()2N
0 setTop()210
0 setTopLeflO 210
0 setWidth()211
0 setX()210
0 setY()2IO
0 size() 211
0 top()209
0 topLeft()210
0 width()211
0 x()2IO
0 y()210
qRed() 215
QRegExp 140, 437
QRegion:
0 intersectO 233
0 subtract() 233
0 unite()233
QResizeEvent 189
0 oldSizeO 189
0 size() 189
QRgb 214
qRgb() 214
qRgba() 214, 252
qRoundO 64
538
Предметный указатель
QScrollBar 30, 110
0 nextLineO ПО, 111
О nextPagc() 111
О prevLine() 110
О prevPage() 111
О setTrackingO 110
О sliderMoved() ПО
О valueChanged() 110, 299
QScrollView 79
О addChild()79
О contentsDragEnterEventO 329
О contentsDropEvent() 329
О contentsHeightO 79
О contentsMouseMoveEvent() 325
О contentsMousePressEventO 325
О contentsMovingO 81
О contentsWidth() 79
О cornerWidgetO 79
О horizontalScrollBarO 79
О horizontalSIiderPressedO 81
О horizontalSliderReleasedO 81
О moveChildO 79
О removeChild() 79
О verticalScrollBarO 79
О verticalSliderPressedO 81
О verticalSliderReleasedO 81
QSemaphore 29, 414
QServerSocket 457
0 newConnectionO 457
QSessionManager 320
0 allowInteraction() 320
0 cancel()320
0 setRestartHintO 320
QSettings:
0 beginGroupO 313, 319
0 endGroup() 313, 318
0 readBoolEntry()313
0 readDoubleEntryO 313
0 readEntryO 313, 318
0 readNumEntryO 313
0 readSettingsO 316
0 removeEntryO 313
0 writeEntryO 312, 319
0 writeSettingsO 316
QSGIStyle 164
QShowEvent 189
QSignal 48
0 activated() 48
0 connect()48
QSize 208
0 height()208
0 isNull()208
0 rheight()208
0 rwidth() 208
0 scale() 208
0 ScaleFree208
0 ScaleMax 208
0 ScaleMin208
0 setHeightO 208
0 setWidth() 208
0 width()208
QSizeGrip 385
QSizePolicy:
0 Expanding 298
0 Fixed 298
0 Ignored 298
0 Maximum 298
0 Minimum 298
0 MinimumExpanding 298
0 Preferred 298
QSlider 30, 107
0 Above 108
0 Below 108
0 Both 108
0 NoMarks 108
0 setTickmarksO 108, 109
0 setTrackingO 108
0 sliderMovedO 108
0 sliderPressedO 108
0 sliderReleased() 108
0 valueChanged() 108
QSocket 455
0 canRead()459
0 connected() 462
0 connectToHost () 462
0 error()462
0 readLine() 459
0 readyRead() 462
0 readyToRead() 459
QSocket Device 455
QSound 304, 305, 306
0 availible() 305, 306
0 isAvailableO 305, 306
0 isFinishedO 305
0 loopsRemainingO 305
0 play() 304, 305
0 setLoops() 305
0 stop() 305
QSpinBox 30, 136
0 setButtonSymbolsO 137
0 setPrefixO 137
0 setRange() 136
0 setSpecialValueText() 137
0 setSuffixO 137
0 setValidatorO 139
0 setValueO 136
0 setWrappingO 136
0 stepDown() 137
0 stepUp() 137
0 value() 136
0 valueChangedO 137
QSplashScreen 385
0 TmishO 386
0 messageO 386
0 show() 386
Предметный указатель
53^
QSplitter 31, 1SS
QSqlCursor 486
0 del()488
0 primeDelete() 488
0 primelnsertO 487
0 primeUpdate() 487
О select()486
QSqlDatabase:
О add Database() 484
О addDatabase() 483
О lastErrorQ 484
О ореп()484
О setDatabaseNameO 484
О setHostNameO 484
О setUserName() 484
QSqlDriverPlugin 493
QSqIQuery 484
О ехес()484
QSql Record:
О setValue() 487
QStatusBar 380
0 addWidget() 380, 382
0 clear() 380
0 message() 380, 393
0 removeWidgetO 380
QString 435
0 append()435
0 containts() 140
0 isEmpty() 390, 435
0 length()435
0 lower()436
0 numberQ 436
0 replace() 436
0 setNum()436
0 upperQ 436
QStringList:
0 join() 330, 437
0 split() 436
QStyle 164
0 CE_PushButton 172
0 CE_PushButtonLabel 172
0 drawComplexControlO 169
0 drawComplexControlMaskO 169
0 drawControK) 169, 173
0 drawControlMaskO 169
0 drawItemO 169
0 drawPrimitiveO 169
0 StyleFlags 171
QStyleFactory 495
QStylePlugin 493, 495
0 createO 495
0 keys()495
QSyntaxHighlighter 133
Qt28
0 AlignAuto 84
0 Align Bottom 84
0 AlignCenter 84
0 AlignHCenter 84
0 AlignJustify 84
0 AlignLeft84
0 AlignRight 84
0 AlignTop84
0 AlignVCenter 84
0 Alt Button 184
0 ArrowCursor 76
0 BDiagPattern 221
0 BlanckCursor 76
0 Control Button 184
0 CrossCursor 76
0 CrossPattern 221
0 CustomPattern 221
0 DashDotDotLine 220
0 Dash Dot Line 220
0 DashLine 220
0 Dense (Pattern 221
0 Dense2Pattern 221
0 Dense3Pattern 221
0 Dense4Pattern 221
0 Dense5Pattern 221
0 Dense6Pattern 221
0 Dense7Pattern 221
0 DiagCrossPattern 221
0 DontClip257
0 DotLine220
0 ExpandTabs 257
0 FDiagPattern 221
0 ForbiddeCursor 76
0 Horizontal 102, 108, 110
0 HorPattern 221
0 IbeamCursor 76
0 ISODate416
0 Key Button Mask 184
0 Keypad 184
0 LeflButton 184
0 Local Date 416
0 Mid Button 184
0 MouseButtonMask 184
0 NoBrush221
0 No Button 184
0 NoPen220
0 PlanText 132
0 PointingHandCursor 76
0 RichText 132
0 RightButton 184
0 ShiftButton 184
0 ShowPrefix 257
0 SingleLine 257
0 SizeAllCursor 76
0 SizeBDiagCursor 76
0 SizeFDiagCursor 76
0 SizeHorCursor 76
0 SizeVerCursor 76
0 Solid Line 220
Продолжение рубрики см. на с. 540
540
Предметный указатель
Qt (npod.):
О Solid Pattern 221
О SplitHCursor 76
О SplitVCursor 76
О Style_NoBorder 250
О TextDate416
О UpArrowCursor 76
О VerPattern 221
О Vertical 28, 102, 108
О WaitCursor76
О WDestructiveClose 398
О WhatsThisCursor 76
О WNoAutoErase 234
О WordBreak 257
О WStyle_Customize 72, 250
О WStyle_StaysOnTop 72
Qt Designer 502
Qt Linguist 331, 333
qt_cast() 105
QTabBar 126
QTabDialog 360
0 addTab() 360
0 applyButtonPressedO 360
0 cancelButtonPressedO 360
0 defaultButtonPressedO 360
0 helpButtonPressed() 360
0 setApplyButton() 360
0 setDefaultButtonO 360
0 setDefaultButtonO 360
0 setHelpButtonO 360
QTable 123
0 createEditorQ 124
0 setCellWidget() 124
0 selContentsFromEditorO 124
0 setPixmapO 123
0 setText() 123, 125
QTabWidget 30, 126
0 addTab() 127
0 setCurrentTabO 126
0 setTabEnabled() 126
QtDebugMsg 63
QTDIR 54
QTDS7 483
QTextBrowser 373
0 backward() 375
0 backwardAvailable() 375
0 forwardAvailable() 375
0 home()375
0 setSourceO 375
QTextCodecPlugin 493
QTextDrag 325, 327
0 drag()328
0 setPixmapO 328
QTextEdit 131
0 append() 133
0 clear() 132
0 copy() 132
0 cut() 132
0 deselect() 132
0 find() 133
0 insert() 133
0 lines() 133
0 paste() 132
0 redo() 132
0 redoAvailableO 132
0 returnPressedO 132
0 selectAllO 132
0 setBold() 133
0 setCo|or() 133
0 setCurrentFontO 133
0 setEchoModeO 132
0 setltalicO 133
0 setLinkUnderlineO 133
0 setReadOnly() 132
0 setTextO 132
0 setTextO 132, 133, 135, 156
0 setTextFormatO 132, 134
0 setUnderline() 133
0 setUndoDepthO 132
0 setValidatorO 132
0 text() 132
0 textChanged() 132
0 undo() 132
0 undoAvailableO 132
0 zoomlnO 133
0 zoomOutO 133
QTextStream 452
0 precisionO 452
0 read() 390, 453
0 readLine() 452
0 setEncoding() 452
QTextView 133
QtFatalMsg 63
QThread 29, 410
0 sleep()41l
0 start()410
0 stop()411
QTime417
0 addMSecs() 418
0 addSecs()418
0 currentTime0 418
0 elapsed()418
0 fromString()418
0 hour()4l7
0 minute()417
0 msec()417
0 second()417
0 setHMS()417
0 start()4l8
0 toString()4l8
QTimeEdit 138
0 valueChangedO 138
QTimer 422
0 change IntervalO 423
0 isActive() 423
0 singleShotO 422
Предметный указатель
541
О stop()423
О timeoutO 423
QTimerEvent 187
QtMsgType 63
QToolBar 377
0 addSeparator() 380
QToolBox 127
0 addltemO 127
0 count() 127
0 ciirrentChanged() 127
0 currentlndex() 127
0 insertltemO 127
0 item() 127
0 removeltemO 127
QToolButton 377
QToolTip 29, 371
0 add()371
0 remove() 371
QTranslator331, 334
0 load()335
QtWarningMsg 63
qUncompress() 445
QUnknowInterface 494
QUriDrag 325
0 canDecodc() 329
0 decodeLocalFiles() 330
QUrl 465
QUrlOperator 465
0 dataTransferProgressO 465
0 finished()465
QValidator 139
0 Acceptable 139
0 Invalid 139
0 Valid 139
0 validate*) 139
QValueList 430
0 append()430
0 at()430
0 clear()430
0 contains() 430
0 count() 430
0 find()430
0 insert()430
0 prepend()430
0 remove() 430
QValueStack 433
0 pop()433
0 push()433
0 top()433
QValueVector 428
0 push_back() 428
0 resize()428
QVariant 439
0 type()439
0 typeName() 439
0 lypeToName() 439
QVBox31, 144
QVBoxLayout31, 147
QVButtonBox 102
QVButtonGroup 31
QWaitCondition 29,411
0 wait()412
0 wakeA1I() 412
0 wakeOne()412
qWarningO 63
QWhatsThis 29, 373
0 enterWhatsThisMode() 373
0 removeO 373
0 whatsThisButton() 373
QWheelEvent 187
0 delta() 187
0 globalPosO 187
0 pos() 187
0 state() 187
QWidget 30, 50, 70, 178
0 close() 189
0 closeEvent() 189, 318
0 dragEnterEvent() 188, 329
0 dragLeaveEvent() 188
0 dragMoveEvent() 188
0 dropEvent() 188, 329, 330
0 enterEvent() 192
0 event() 178
0 focusInEvent() 181
0 focusOutEvent() 181
0 geometiy() 74
0 height() 73, 74
0 hide() 72, 79, 189
0 hideEventO 189
0 isEnabled()73
0 keyPressEvent() 178, 193
0 keyReleaseEvent() 178
0 leaveEvent() 192
0 mouseDoiibleClickEvent() 73, 183
0 mouseMoveEventO 73, 183, 325, 327, 382
0 mousePressEventO 73, 177, 183, 187, 325, 327
0 mouseReleaseEventO 73, 183
0 move() 74, 79
0 moveEventO 192
0 paintEventO 73, 182, 191, 301
0 paletteO 158
0 pos()74
0 repaint() 182, 302
0 resize() 74, 97, 150, 186
0 resizeEvent() 189, 191
0 setAcceptDrops() 329
0 setBackgroundColor() 75
0 setBackgroundModeO 234, 269, 301
0 set Background PixmapO 74, 250
0 setCaptionO 73, 398
0 setCursor() 77
0 setEnabled() 375
0 setFixedSizeO 90
0 setFontO 255
Поодолжение омбпшеи ru un г 44?
542
Предметный указатель
QWidget (npod.y.
О setGeomertyO 74
О setGeometryO 72
О set!con()398
О setKeyCompression() 178
О setMask()249
О setMinimumSize() 144
О setMouseTracking() 183
О setPalette() 161
О setPaletteBackground() 293
О setPaletteBackgroundColorO 74
О setStyle() 165, 166
О setWFlags() 234, 301, 390
О show() 72, 79, 150, 182, 189, 277
О showEvent() 189
О showFullScreenO 277
О size() 73
О sizeHint() 297
О sizePolicy() 297
О update() 182
О wheelEvent() 187
О width() 73, 74
О winld()498
О х()74
О xllEvent()499
О У()74
QWidgetPlugin 493
QWidgetStack 78, 126
О addWidgetO 78
О id() 79
О raiseWidgetO 79
QWindowsStyle 164, 171
QWizard 362
0 addPage()362
0 setBackEnabled() 362
0 set Finish EnabledO 362
0 setHelpEneabledO 362
0 setNextEnabled() 362
QWorkArea:
0 setScrollBarEnabledO 397
QWorkspace 393
0 cascade() 393
0 title() 393
0 windowList() 393
QXml Default Handler 476
QXmlSimpleReader 475
0 setContenHandlerO 477
QCheckListltem 117
QColorGroup 161
Rendering context 270
RGB 211, 214
SDI 388
setTabOrderO 154
shared data 439
Single Document Window Interface 388, 400
singleshot 422
SlowView 267
Spacer 506
Splash Screen 385
SQL 481
STL 427
systat 456
Tear off menu 342
tmake 58
Toggle button 306
Toggle Button 95
Tool Tip 370
top-level widget 72
TRANSLATIONS 333
Tristate Button 99
TrueColor213
TS-файл 332. 333
и
URI 330
URL 465
w
warningO 63
WAV 305
What's this 372
Windows API 36, 498
Windows Notepad 388
WYSIGWYG 502
RAD 502
Radio Button 100
XBM 236
XML 467
XPixmap 237
XPM 236, 237
Предметный указатель
Активное состояние 158
Альфа-канал 252
Анимация 267
Б
Буфер обмена 323
В
Векторы 426
Взаимная блокировка 413
Вид и поведение 163
Виджет 70
0 верхнего уровня 72
Воздушная подсказка 370
Всплывающее меню 97, 337
Выпадающий список 120
Диалоговое окно 346
м
MDI 393
Масштабирование 232
Матрица:
0 моделирования 275
0 проектирования 275
Меню 337
0 верхнего уровня 337
Метаобъектный компилятор 61
Механизм неявных общих данных 158
Многодокументный оконный интерфейс 388,400
400
Многопоточность 407
Многострочное текстовое поле 135
Модальный диалог 348
Мьютекс411
н
Надписи 83
Настройки приложения 312
Неактивное состояние 158
Недоступное состояние 158
Немодальный диалог 348
3
Закладки 125
И
Иерархический список 115
Индикатор прогресса 88
Интернационализация 331
К
Кисть 221
Клиент-сервер 456
Ключ 312
Кнопка:
0 выключателя 96
0 нажатия 95
0 флажок 98
Контекст:
0 воспроизведения 270
0 рисования 218
Контекстное меню 337, 343
Критические секции 411
Л
Общее использование данных 439
Однодокументный оконный интерфейс 388,400
Однострочное текстовое поле 129
Окно сообщения 363
Отрывное меню 337, 342
Очередь 427
П
Палитра 213
Панель инструментов 377
Переключатель 100
Перемещение 231
Перетаскивание 324
Перо 220
Плоская кнопка 95
Поворот 232
Ползунок 107
Полоса прокрутки 109
Предшествующее окно 385
Прозрачность 248
Процесс 404
Прямоугольник 209
Лейаут 142
Рабочий стол 33
Разделитель 155
544
Предметный указатель
Размер 208
Регулярное выражение 140
Реестр Windows 319
С
Сглаживание 270
Семафор 413
Сжатие данных 445
Сигнал 36, 39, 40, 176
Синхронизация 411
Система помощи 370
Системный реестр 312
Скос 232
Словари 427
Слот 36, 39, 42, 176
Событие 176
Сокет 455
Списки 426
Стек 427
Счетчик 136
Т
У
Установщик 112
Ф
Файл проекта 58, 62
Фильтры событий 36, 196
Функции обратного вызова 36
Ш
Шрифт 254
э
Электронный индикатор 91
Таблица 123
Таймер 36, 420
Точка 206