З кожним роком стає все більше операційних систем, тому розробникам все важче задовольняти потреби користувачів. Три найпопулярніші комп'ютерні платформи – Windows, Linux та Mac OS, а також три мобільні – Android, iOS та Windows Mobile – продовжують активно боротися між собою. А це означає, що якісний додаток має працювати на всіх основних платформах.

Впоратися з цією проблемою допомагає кроссплатформенна розробка. Про одну з найпопулярніших кросплатформових середовищ розробки - Qt Creator - і йтиметься у цій статті. Ми розглянемо як виконується встановлення та налаштування Qt Creator, а також як працювати в Qt Creator.

Що таке Qt Creator

Qt Creator (що нещодавно мав назву Greenhouse) - це одна з найпоширеніших кросплатформових IDE. Її плюси - зручність, швидкість роботи, а також - вільність, оскільки це програмне забезпечення з відкритим вихідним кодом. Підтримуються такі мови, як C, C++, QML.

Програма була написана компанією під назвою Trolltech, яка повною мірою виконала мету створення середовища – роботу з графічним фреймворком Qt. Зручний графічний інтерфейс з підтримкою Qt Widgets і QML, а також велика кількість компіляторів, що підтримуються, дозволяють швидко і зручно створити свій кросплатформовий додаток.

Головне завдання цієї IDE – забезпечити найбільш швидку кроссплатформенну розробку, використовуючи власний фреймворк. Завдяки цьому розробники отримують прекрасну можливість не писати додатки нативно (тобто окремо під кожну платформу), а створити загальний код, і, можливо, підігнати його під особливості ОС.

Qt Creator також включає утиліту Qt Designer, що дозволяє обробити зовнішній вигляд вікна програми, додаючи і перетягуючи елементи (аналогічно Windows Forms в Visual Studio). Як системи збирання використовуються qmake, cmake і autotools.

Установка Qt Creator

Отже, настав час розглянути як встановити Qt Creator. Якщо для Windows розробники подбали і зробили офлайн-установник, то Linux 32-bit цієї можливості не передбачено. Тому під час встановлення вам може знадобитися стабільне інтернет-з'єднання (~20-30 хвилин). Для початку завантажуємо установник:

  • Завантажити Qt Creator для Linux 32-bit (натискаємо " View other options").
  • Завантажити Qt Creator для Linux 64-bit.

Після закінчення завантаження переходимо в папку з файлом, натискаємо правою кнопкою миші та вибираємо пункт "Властивості".

Тепер перейдемо на вкладку "Права"і поставимо галочку "Дозволити запуск цього файлу як програму".

Запускаємо програму.

Тепер натискаємо "Next".

Тут необхідно вибрати існуючий обліковий запис або створити його. Ця дія необхідна для перевірки ліцензії (комерційної чи некомерційної).

Натискаємо "Next".

Вибираємо директорію, в якій перебуватиме Qt. Важливо, щоб у дорозі не було кирилиці та прогалин!

У цьому меню є вибір компонентів. Наприклад, можна вибрати установку інструментів для розробки на Android, або вихідних компонентів (це потрібно для статичного складання, якщо комусь це потрібно - напишіть у коментарях, і я напишу окрему статтю). Якщо Ви не впевнені, чи потрібні Вам ці компоненти чи ні, залиште їх поки що так - навіть після встановлення Qt буде можливим видалення та додавання елементів.

У цьому вікні приймаємо ліцензію. Тиснемо "Next".

Якщо ви готові, починайте встановлення. У Вас запитають пароль суперкористувача (sudo), після чого почнеться скачування та вилучення файлів. Альтернативний спосіб – встановлення через термінал. Для початку потрібно оновити список пакетів.

Завантажуємо та встановлюємо Qt:

sudo apt install qt5-default

Тепер встановлення Qt Creator:

sudo apt install qtcreator

І, якщо потрібне, вихідники.

sudo apt install qtbase5-examples qtdeclarative5-examples

Налаштування Qt Creator

Після завершення інсталяції перезавантажте комп'ютер і запустіть Qt Creator. Перейдіть до меню "Інструменти" -> "Параметри".

Тут слід розглянути кілька вкладок.

1. Середа- це налаштування зовнішнього вигляду самої IDE, а також зміна поєднань клавіш та керування зовнішніми утилітами.

2. Текстовий редактор- тут йде налаштування зовнішнього вигляду, шрифтів та забарвлення редактора.

3. C++- підсвічування синтаксису, робота з розширеннями файлів та UI (тобто формами).

4. Android- тут зібрані шляхи до необхідних інструментів, а також у цьому меню налаштовуються віртуальні пристрої, що підключаються.

Встановлення компонентів Qt Creator

Якщо раптом так сталося, що Ви забули встановити якийсь компонент, або, навпаки, хочете видалити його, то на допомогу прийде Qt Maintenance Tool. Це інструмент, який дозволяє керувати всіма компонентами Qt Creator.

Щоб запустити його, перейдіть до меню програм, виберіть пункт "Розробка" -> "Qt Maintenance Tool".

Виберіть потрібний пункт (Видалити/додати компоненти, оновити компоненти або видалити Qt). Після цього виконайте необхідні операції і закрийте вікно.

Робота з Qt Creator – перший проект

Ну що ж, час пробив! Налаштування Qt Creator завершено. Час зробити свій перший кросплатформовий додаток на Linux, а потім скомпілювати його на Windows. Нехай це буде... програма, яка виводить іконку Qt, кнопку та напис, на яку після натискання кнопки буде виводитися випадкова фраза. Проект нескладний, і, звичайно ж, кроссплатформенний!

Спочатку відкриємо середовище розробки. Натиснемо "Файл" -> "Створити файл або проект...". Виберемо додаток Qt Widgets - його швидко та зручно зробити. А назва йому - "Cross-Platphorm". Ось як!

Комплект – за замовчуванням. Головне вікно також залишаємо без змін. Створюємо проект.

Для початку необхідно налаштувати форму – головне вікно програми. За замовчуванням воно пусте, але це не залишиться надовго.

Перейдемо до папки "Форми" -> "mainwindow.ui". Відкриється вікно Qt Designer:

Видаляємо панель меню та панель інструментів на форму, натиснувши правою кнопкою миші та вибравши відповідний пункт. Тепер перетягуємо елементи Graphics View, Push Button та Label таким чином:

Щоб змінити текст, двічі натисніть на елемент. У властивостях Label (праворуч) вибираємо розташування тексту по вертикалі та по горизонталі – вертикальне.

Тепер настав час розібратися з виведенням іконки. Перейдемо в редактор, зліва клікнемо по будь-якій папці правою кнопкою миші та оберемо "Додати новий...". Тепер натискаємо "Qt" -> "Qt Resource File". Ім'я – res. У вікні натискаємо "Додати" -> "Додати префікс", а після додавання - "Додати файли". Вибираємо файл, а у вікні, що з'явилося "Неправильне розміщення файлу"кликаємо "Копіювати".

Вийшло! Зберігаємо все. Знову відкриваємо форму. Клацаємо правою кнопкою миші по Graphics View, вибираємо "styleSheet..." -> "Додати ресурс" -> "background-image". У лівій частині вікна вибираємо prefix1, а в правій - нашу картинку. Натискаємо "ОК". Налаштовуємо довжину та ширину.

Всі! Тепер можна приступати до коду. Клік правою кнопкою миші по кнопці відкриває контекстне меню, тепер потрібно натиснути "Перейти до слота..." -> "clicked()". У вікні набираємо наступний код:

Або ви можете завантажити повний проект на GitHub. Робота з Qt Creator завершена, натискаємо на значок зеленої стрілки зліва, і чекаємо на запуск програми (якщо стрілка сіра, спочатку натисніть на значок молотка). Запустилось! Ура!

Після доби життя посту я почав помічати витік карми, так що заздалегідь перепрошую за можливо неприпустимий стиль викладу у статті та суб'єктивізм

Привіт, Хабрахабре!

Останнім часом я не міг не звернути увагу на популярність теми Qt на хабрику, проте в коментах продовжують зустрічатися люди, які говорять відверто брехливі та незрозумілі речі. Цим постом я хотів розвіяти трохи помилок про Qt і розповісти, чому ж ти маєш пересісти зі своїх Java/Obj-C/.NET на м'який та пухнастий Qt.

Під катом буде багато вражень, суб'єктивностей та моїх скромних думок на рахунок найчудовішогофреймворку для розробки додатків. Втім, я постараюся додати цікавостей, щоб моя стаття набула хоч якогось техніческолезного сенсу. Сподіваюся, вийде цікаве чтиво і вам сподобається.

Ну що, поїхали?

Вешч №1. З++ API

Ні для нікого не секрет, що Qt дуже зручне API, а конкретніше кажучи, модуль qtbase містить достатню кількість класів для більшості повсякденних завдань ( Qt – це більше, ніж GUI фреймворк, лол). Я вже говорив про обгортки STL-них контейнерів у своїй статті трирічної давності - Тицк. Класи для роботи з рядками, налагоджувальний висновок, і багато-багато чого, так само входять.

QString fruits = "apple, banana, orange, banana"; QStringList fruitsList = fruits.split(", "); qDebug()<< fruitsList; // выведет в консоль [ "apple", "banana", "orange", "banana" ] fruitsList.removeDuplicates(); fruits = fruitsList.join(", "); qDebug() << fruits; // выведет в консоль "apple, banana, orange"
Варто сказати, що Qt також є модулі для зручної роботи з XML, базами даних ( з інтеграцією смачно-смачною к'ютішною системою MVC), OpenGL, аудіо/відео-роботи (Phonon), мережевого програмування, WebKit2. Для лікарні завдань, які стоять перед середньостатистичним проектом - цієї кухні вистачає у 90% випадків, а з модулями рідко трапляються з*еби.

З урахуванням моєї любові до C++, я дуже і дуже задоволений тим, яку підтримку різних нетривіальних речей Qt представляє на крос-платформному рівні. Кілька разів доводилося розрулювати особливо незрозумілі моменти, але це таке.

Вешч №2. Qt Quick

Qt Quick - це мегасмачний підхід до створення графічного інтерфейсу користувача. Використовуючи декларативну мову QML (вгадайте, де її вигадали, лол), схожий на JavaScript, можна досягти високої продуктивності при прототипуванні інтерфейсу в додатках будь-якийскладності. А найцікавіше, що при такому ході справ, із прототипуванням інтерфейсу може впоратися навіть дизайнер, який знає синтаксис JavaScript. Це все були б порожні слова, якби я не показав приклад функціонального коду (більше можна знайти на Qt Project - тамц).

Import QtQuick 2.0 Rectangle ( id: page width: 320; height: 480 color: "lightgray" Text ( id: helloText text: "Hello world!" y: 30 anchors.horizontalCenter: page.horizontalCenter font.pointSize: 24; font. bold: true ) Grid ( id: colorPicker x: 4; anchors.bottom: page.bottom; anchors.bottomMargin: 4 rows: 2; columns: 3; spacing: 3 Cell ( cellColor: "red"; onClicked: helloText.color = cellColor ) Cell ( cellColor: "green"; onClicked: helloText.color = cellColor ) Cell ( cellColor: "blue"; onClicked: helloText.color = cellColor ) Cell ( cellColor: "yellow"; onClicked: hello ) Cell ( cellColor: "steelblue"; onClicked: helloText.color = cellColor ) Cell ( cellColor: "black"; onClicked: helloText.color = cellColor ) ) )

Імплементація об'єкта Cell вкрай тривіальна та визначена таким чином

import QtQuick 2.0 Item ( id: container property або cellColor: rectangle.color signal clicked(color cellColor) width: 40; height: 25 Rectangle ( id: rectangle border.color: "white" anchors.fill: parent ) fill: parent onClicked: container.clicked(container.cellColor) ) )

У цьому коді немає рядка С++ і він нормально працює. Добре, чи не так? Я себе навіть чарівником від цього відчув - простіше в магазин за хлібом сходити, ніж додаток отак склепати. Тим не менш, у складних додатках не вистачає одного QML і ми поєднуємо його з С++. Про це йшлося у багатьох статтях хаба Qt Software – наприклад, тамц.

Вешч №3. Спільнота

Ну, ось ми й дійшли до приємного моменту. Якщо говорити про мене, то я працюю з Qt відносно мало – лише 5 років. Qt проводить щорічні заходи - Qt Developer Days та Qt Contributors" Summit. Я був на кожному з них по одному разу, торік, і мені дуже сподобалося - рівень підготовки високий, а враження доставляють. Мені також доводилося спілкуватися з «ветеранами» Qt – людьми, які відвідували саміт протягом 10 років, уявляю, наскільки круто на своїх очах бачити зростання такого проекту та бути в епіцентрі всієї розробки – просто смачненько.

До новачків ці люди дуже терпимі і ставляться добре, мені було дуже легко і приємно навести контакти з такими чудовими людьми. На Qt Project є форуми, де кожен охочий може отримати відповідь на питання, що хвилює його. Кумедно, але дівне справді дуже живийі там реальновідповідають питання, що виникають, у процесі пізнання Qt.

Вешч №4. Відкритий вихідний код та code review

Сорец кьюта відкрито розробляється в основному компаніями Digia (комм. підтримка +), KDAB, ICS та ентузіастами-розробниками. Хоститься вся ця справа на Gitorious-Тадамці. Щоб зробити свій внесок у розвиток проекту, потрібно пройти строгуперевірку коду - автоматизовану (дотримання стилю коду, про який я вже писав раніше - птссс) і людську - твій код дивитимуться бородаті дядьки, які не довіряють тобі і шукатимуть у твоєму коді бекдори. Все це досить складний процес (заморочки з Git/ревізії на Review Board) і я напевно напишу про це статтю днями.

У мене, до речі, є кілька коммітів у дереві qtbase, так що можете питати у лс – спробую відповісти на запитання.

Вешч №5. Динаміка розвитку проекту

Qt розробляється багато років, з кінця 90-х. За цей час його комерційною версією вже встигли грати такі компанії, як Trolltech і Nokia, а зараз цим займається Digia. Але одне можна точно сказати, проект живе та процвітає. Ще кілька років дизайн все писали на віджетах (С++ класи, всі до одного засновані на QWidget), а сьогодні його може зробити і маленька дитина. Думаю, не варто говорити, що паралельно з ним активно розвивається наймотніша віщ - Qt Creator, який сьогодні радує не тільки Qt програмістів!

^ класненький Qt Creator, в якому можна творити чудеса і тобі за це нічого не буде.

На жаль, у мене немає строгих чисел, але кажуть, що код щодня оптимізується, а codebase ретельно нарощується - додаються нові фічі та виправляються старі баги (у цьому я встиг переконатися вже багато разів). Все це дуже придатно і не може не тішити.

До того ж, зараз триває активний розвиток платформ iOS, Android, Windows Phone, вже зараз можна збирати під них програми!

Підсумок

Я думаю, ви розумієте, що Qt - це правда круто і після прочитання статті ви полюбили його так само, як і я.
Дякую за увагу!

Початківець програміст постійно сумнівається, які технології почати освоювати цю чи якусь іншу. Чи потрібно програмісту-початківцю вивчати Qt? Однозначно потрібно! Наприклад, почитайте цей пост чи пошукайте що-небудь в інтернеті. Все ще не впевнені, чи потрібен Вам Qt? Якщо ви пишете на С++, Qt ви повинні знати, у вас просто немає іншої альтернативи.

Отже, поїхали...

Давайте, наприклад, напишемо простенький калькулятор - додавання двох чисел.

Створюємо новий проект. Як бачимо, існує кілька типів додатків. Ми як початківці, вибираємо "Додаток Qt Widgets":

Вказуємо ім'я проекту та папку для розміщення файлів, у мене: C:\projects\qt

Qt автоматично генерує такі файли:

  • lesson1.pro - файл проекту
  • main.cpp - головний файл з функцією main()
  • mainwindow.cpp - вихідний код головного вікна
  • mainwindow.h - заголовний файл головного вікна
  • mainwindow.ui - файл форми головного вікна

Натискаємо кнопку "Завершити" – відкривається редактор для розробки програм на Qt.

Поки має бути все зрозуміло для Qt-розробника-початківця.

Відкриваємо головну форму (для цього переходимо до mainwindow.ui).

Зліва розташовуються компоненти до створення екранної форми, справа сама форма, вона порожня. Накидаємо потрібні компоненти, незабудемо поля введення QLineEdit назвати так: edtA, edtB і edtC відповідно.

Ми бачимо на екрані поля введення, підписи до них ліворуч та кнопку "A + B =". При натисканні цієї кнопки ми повинні скласти A і B і помістити результат в C.

Як бути? Будь-який початківець Qt-розробник припустить, що потрібно навісити на кнопку обробник натискання цієї кнопки і він має рацію! У будь-яких інших фреймворках є таке поняття як подія. Але в Qt вирішили випендритися і вигадали сигнали/слоти. Хоча, по суті, це практично теж саме.

Клацаємо правою кнопкою миші на кнопку "A + B =", відкривається спливаюче меню:

тиснемо на "Перейти до слота"

Вибираємо сигнал clicked() , і в редакторі коду, що відкрився, пишемо невеликий програмний код на Qt:

Void MainWindow::on_btnOK_clicked() ( int a = ui->edtA->text().toInt(); // Беремо текст edtA і перетворюємо його в число a int b = ui->edtB->text().toInt (); // Беремо текст edtB і перетворюємо його в число b int c = a + b; // Складаємо числа QString s = QString::number(c); // Перетворюємо результат у рядковий вигляд (s); // Вивести результат в edtC)

Функція обробки сигналу clicked() називається слотом on_btnOK_clicked().

Не була б тим, чим вона є без того, що називається proof-of-concept. І в даному випадку роль цього доказу грає Qt Creator - середовище розробки під Qt з відкритим вихідним кодом, написане повністю на ній.

Qt Creator може показати чудово аскетичної IDE, особливо після роботи та програмування в чомусь іншому. Тим часом середовище містить все необхідне для роботи і є досить продуманим. Qt Creator поставляється в пакеті разом із бібліотекою Qt. . Вибирайте спосіб (онлайн, офлайн), платформу та вперед ( Примітка: описаний спосіб може змінитися в майбутньому, але в будь-якому випадку – завантажуйте дистрибутив бібліотеки тільки з офіційного сайту). Установка досить проста і особливих сюрпризів не несе.

До речі, під Linux і Mac OS X існує також варіант установки через репозитарій вашого дистрибутива (через brew у Mac OS X або apt в Ubuntu), але в цьому випадку ви ризикуєте отримати не найсвіжішу версію бібліотеки.

Після завантаження та встановлення залишається запустити Qt Creator і почати розробляти.

Середовище має три теми (тема Flat з'явилася в 4.0) інтерфейсу та кілька варіантів забарвлення синтаксису коду. За бажанням кольору синтаксису можна змінити будь-які інші. Як і генеральний колір IDE.

Плюсом для початківців знайомство із Qt стане вбудована колекція прикладів на будь-яку тему. Вибираємо пункт початок, клацаємо по кнопці Приклади, і вибираємо цікавий. Qt Creator запропонує відразу ж підготувати приклад для подальшого збирання та запуску. Це дуже зручно і дозволяє наживо подивитися на можливості Qt.

Приклади дуже добре допоможуть після деякого часу ознайомлення із середовищем та Qt. Основна фішка Qt Creator — вбудована довідка, що дозволяє не виходячи з середовища отримувати інформацію про будь-який клас і його методи та властивості. Достатньо стати на ім'я класу або екземплярі та натиснути F1. Збоку з'явиться вікно довідки, аналогічне документації https://doc.qt.io/ - головного довідкового порталу Qt.

Якщо немає бажання ділити робочий простір із довідковим вікном і хочеться вдумливого читання, можна натиснути на кнопку Довідкау лівому сайдбарі. Вкладка Проектидозволяє налаштувати деякі етапи складання та запуску проекту, Налагодженняперемикає середовище у відповідний режим, кнопка Дизайнстає активним при редагуванні.ui файлів інтерфейсу. У вікні Редакторвідбувається головне дійство - написання коду. З усім цим ми розберемося пізніше.

У нижній лівій частині екрану розташовані манячі натиснути кнопки створення білда, налагоджувального запуску та запуску програми. А також режим складання - Налагодження, Реліз та Профільування.

Награвшись вдосталь із середовищем, можна переходити до створення свого власного проекту.

Програмування з Qt

Частина 1. Введення. Інструменти розробника та об'єктна модель

Серія контенту:

1. Введення

Існують версії Qt для unix-подібних операційних систем з X Window System (наприклад, X.Org (EN), Mac OS X та Windows). Також Qt Software портує свій продукт на мобільні платформи: Embedded Linux (EN), S60 (EN) та Windows CE. Qt надає великі можливості крос-платформної розробки різних програм, не обов'язково з графічним інтерфейсом. На ньому, зокрема, засновано популярне середовище робочого столу KDE (EN).

Інструментарій розбитий на модулі, кожен із яких розміщується в окремій бібліотеці. Базові класи знаходяться в QtCore, компоненти графічних інтерфейсів – QtGui, класи для роботи з мережею – QtNetwork і т.д. Таким чином, можна збирати програми навіть для платформ, де немає X11 чи іншої сумісної графічної підсистеми.

2. Встановлення Qt

Нам потрібно буде встановити середовище розробки Qt. Програмне забезпечення поширюється на умовах вільної ліцензії GPL 3.0 або LGPL 2.1. Його можна отримати за адресою http://www.qtsoftware.com/downloads (EN).

2.1. Базові бібліотеки та інструменти

У репозиторіях популярних дистрибутивів GNU/Linux вже є готові пакети з середовищем розробки Qt (наприклад, Debian, Fedora, Gentoo, Mandriva, Ubuntu). Тим не менш, користувач може зібрати та встановити інструментарій із вихідних текстів.

Для систем, що використовують X11, необхідно завантажити файл qt-x11-opensource-src-4.x.y.tar.gz , де 4.x.y – остання доступна версія зі стабільних. Ми будемо встановлювати версію 4.5.0.

У директорії з файлом qt-x11-opensource-src-4.5.0.tar.gz виконайте такі команди:

tar xvfz qt-x11-opensource-src-4.5.0.tar.gz cd qt-x11-opensource-src-4.5.0

Перш ніж збирати Qt, запустіть configure скрипт . Повний набір його опцій видається за командою./configure -help , але можна використовувати типові налаштування.

Параметр -prefix задає каталог для встановлення (за замовчуванням використовується /usr/local/Trolltech/Qt-4.5.0). Також є ключі для інсталяції різних компонентів (виконуваних файлів, бібліотек, документації тощо) в різні директорії.

При запуску скрипт вимагає підтвердити згоду користувача з умовами ліцензії GPL/LGPL. Після виконання

./configure

можна запустити складання та встановлення за допомогою команд:

make & make install

Майте на увазі, що компіляція займає багато часу, а для встановлення Qt можуть знадобитися права суперкористувача (файли записуються в /usr/local/).

Якщо в подальшому вам знадобиться в тій же директорії заново конфігурувати і перезабрати Qt, видаліть всі сліди попередньої конфігурації за допомогою make confclean , перш ніж запускати./configure .

Шлях до виконуваних файлів Qt необхідно додати змінну оточення PATH. В оболонках bash, ksh, zsh і sh це можна зробити, дописавши файл ~/.profile наступні рядки:

PATH=/usr/local/Trolltech/Qt-4.5.0/bin:$PATH export PATH

У csh і tcsh потрібно дописати в ~/.login рядок:

setenv PATH /usr/local/Trolltech/Qt-4.5.0/bin:$PATH

Якщо ви використовуєте іншу оболонку, зверніться до відповідних розділів документації.

Крім того, необхідно додати рядок /usr/local/Trolltech/Qt-4.5.0/lib до змінної LD_LIBRARY_PATH , якщо компілятор не підтримує RPATH. Ми використовуємо GNU/Linux та GCC (EN), тому пропускаємо цей крок.

Потім за допомогою утиліти qtdemo запустіть демонстраційні програми для перевірки працездатності встановленого інструментарію.

2.2. SDK

Нещодавно з'явилося крос-платформне середовище розробки Qt Creator. На сайті Qt Software можна знайти повний SDK, що включає IDE (крім бібліотек та основних засобів розробника). Завантажте бінарний файл qt-sdk-linux-x86-opensource-xxx.bin і запустіть майстер установки:

chmod +x ./qt-sdk-linux-x86-opensource-2009.01.bin ./qt-sdk-linux-x86-opensource-2009.01.bin

Якщо не збираєтеся встановлювати SDK в домашню директорію, запускайте інсталятор з правами суперкористувача.


3. Інструменти розробника

До складу Qt включені інструменти розробника з графічним чи консольним інтерфейсом. В тому числі:

  • assistant – графічний засіб для перегляду гіпертекстової документації з інструментарію та бібліотек Qt.
  • designer – графічний засіб для створення та складання інтерфейсів на основі компонентів Qt.
  • qmake - Крос-платформний генератор Makefile.
  • moc – компілятор метаоб'єктів (обробник розширень Qt для C++).
  • uic – компілятор інтерфейсів користувача з файлів.ui, створених у Qt Designer.
  • rcc – компілятор ресурсів із файлів.qrc.
  • qtconfig – графічний засіб встановлення налаштувань користувача для програм Qt.
  • qtdemo – запуск прикладів та демонстраційних програм.
  • qt3to4 – засіб перенесення проектів із Qt 3 на Qt 4.
  • linguist – засіб для локалізації програм.
  • pixeltool – екранна лупа.


3.1. qmake

qmake використовується для автоматичного генерування Makefile на різних платформах.

У цілому нині qmake орієнтується на Qt. Якщо вас цікавлять крос-платформні системи збирання ширшого призначення, то можете звернутися до CMake, яка також підтримує Qt.

Початківцям варто зупинитися на qmake.

Повну документацію по цій утиліті ви можете знайти у Qt Assistant. Також з Qt постачаються сторінки посібника, у тому числі qmake(1) (наберіть у командному рядку man qmake). Тут ми наведемо основні вказівки, які допоможуть збирати код прикладів статті, а також свої прості проекти.

Як приклад створимо директорію myproject і додамо туди файли hello.h, hello.cpp та main.cpp. У hello.h опишемо прототип функції hello():

Лістинг 1.1. Оголошення функцій програми «Hello, World!»
// hello.h void hello();

Реалізацію hello() помістимо в hello.cpp:

Лістинг 1.2. Реалізації функцій програми Hello, World!
// hello.cpp #include #include "hello.h" void hello() ( qDebug()<< "Hello, World!"; }

Тут qDebug() використовується для виведення налагоджувальної інформації. Її можна забрати, оголосивши при компіляції символ QT_NO_DEBUG_OUTPUT . Також є функція qWarning() , що видає попередження, і qFatal() , що завершує роботу програми після виведення повідомлення про критичну помилку в STDERR (те саме, але без завершення роботи, робить qCritical()).

У заголовному файлі містяться оголошення, що додають для qDebug(), qWarning() і qCritical() зручніший синтаксис оператора<< . При этом между аргументами (как в случае qDebug() << a << b << c;) автоматически расставляются пробелы, поддерживается вывод многих типов C++ и Qt, а в конце автоматически добавляется перевод строки.

Код основної програми (тут ми дотримуємося угоди, за якою main() міститься у файл main.cpp):

Лістинг 1.3. Функція main() програми Hello, World!
// main.cpp #include "hello.h" int main() ( hello(); return 0; )

Щоб створити файл проекту, запустіть

Після цього повинен з'явитися файл myproject.pro такого змісту:

#################################### # Automatically generated by qmake ######### ########################### TEMPLATE = app TARGET = DEPENDPATH += . INCLUDEPATH +=. # Input HEADERS += hello.h SOURCES += hello.cpp main.cpp

Оператор = використовується для присвоєння значень змінним, += додає нову опцію змінної, -= видаляє зазначену опцію.

TEMPLATE = app означає, що ми збираємо програму; для бібліотеки використовується TEMPLATE=lib.

TARGET – ім'я цільового файлу (вкажіть TARGET = foobar , щоб отримати файл foobar, що виконується).

DEPENDPATH – директорії для пошуку під час вирішення залежностей.

INCLUDEPATH – директорії із заголовними файлами.

Після запуску

на основі myproject.pro в GNU/Linux буде створено звичайний Makefile:

####### Compile hello.o: hello.cpp hello.h $(CXX) -c $(CXXFLAGS) $ (INCPATH) -o hello.o hello.cpp main.o: main.cpp hello.h $(CXX) -c $(CXXFLAGS) $(INCPATH) -o main.o main.cpp ####### Install install: FORCE uninstall: FORCE FORCE:

Опції qmake впливають на вміст Makefile. Наприклад, qmake -Wall додасть до прапорів компілятора -Wall - виведення всіх попереджень.

За командою make ми отримаємо файл myproject , який виводить на екран рядок «Hello, World!».

Ця схема може здатися надто складною, але в реальних проектах qmake бере на себе велику частину зборки (наприклад, запускає компілятор метаоб'єктів).

3.2. Qt Creator

Описаних вище інструментів достатньо розробки додатків. Ви можете використовувати улюблений текстовий редактор, наприклад, GNU Emacs або Vim. З Qt працюють також традиційні IDE, такі як KDevelop.

Однак нещодавно Qt Software випустила свою крос-платформну IDE Qt Creator. У неї вбудовані всі інструменти розробника, є редактор з підсвічуванням та доповненням коду, відладчик (графічний інтерфейс для gdb), а також реалізована підтримка Perforce, SVN та Git.

Під час роботи в Qt Creator використовується кілька режимів, яким відповідають вкладки на панелі зліва. Для швидкого перемикання між режимами можна використовувати комбінації клавіш Ctrl+1, Ctrl+2 і т.д. Основному режиму редагуваннявідповідає Ctrl+2.


Для навігації в редакторі використовується комбінація клавіш Ctrl+K. Після її натискання потрібно вказати один із префіксів:

Таблиця 1. Префікси для навігації у Qt Creator

Після префіксу натисніть пробіл і введіть відповідну інформацію. Наприклад, для переходу на рядок 93 поточного файлу потрібно надрукувати "l 93" (те ж саме можна зробити за допомогою Ctrl + L), для переходу до документації на тему qobject_cast - "? qobject_cast" і т.д.

У нижній частині вікна відображається поле з автоматичним доповненням.

Малюнок 5. Поле для навігації у Qt Creator

Таблиця 2. Комбінації клавіш для редактора Qt Creator

Ctrl+[На початок Блоку
Ctrl+]Перейти до кінця блоку
Ctrl+UВиділити блок
Ctrl+Shift+UЗняти виділення блоку
Ctrl+IВирівняти блок
Ctrl+< Згорнути блок
Ctrl+>Розгорнути блок
Ctrl+/Закоментувати блок
Ctrl+Shift+Перемістити рядок вгору
Ctrl+Shift+↓Перемістити рядок вниз
hift+DelSВидалити рядок

У вбудованому редакторі реалізовано «розумне» доповнення коду, яке викликається комбінацією клавіш Ctrl+<Пробел>. База символів складається на основі заголовних файлів проекту з INCLUDEPATH.

Для читання документації в IDE передбачено окремий режим довідки. Щоб отримати контекстну допомогу за класом або методом, просто перемістіть текстовий курсор до імені та натисніть F1 . Також корисна клавіша F2 , що переміщає до визначення заголовних файлах.

Щоб переключитися з режиму довідки або налагодження в основний режим редагування, натисніть Esc . У режимі редагування Esc переводить фокус із додаткових вікон (наприклад, виведення компіляції або контекстної довідки) на редактор. Якщо натиснути Esc ще раз, додаткові вікна закриваються.

Як і qmake, Qt Creator використовує файли у форматі.pro, тому в IDE легко імпортуються старі проекти, створені вручну. Також доступний майстер, за допомогою якого можна створити заготівлю нового проекту.

Зараз Qt Creator активно розробляється, але якщо вам потрібна класична IDE для Qt, що працює на різних платформах, це найкращий варіант.

4. Стиль Qt

У Qt використовується CamelCasing: імена класів виглядають як MyClassName, а імена методів – як myMethodName.

При цьому імена всіх класів Qt починаються з Q, наприклад QObject, QList або QFont.

Більшості класів відповідають заголовні файли з тим самим ім'ям (без розширення.h), тобто. потрібно використовувати:

#include #include #include

Тому надалі ми окремо не обговорюватимемо, де оголошено той чи інший клас.

Методи для отримання та встановлення властивостей (getterі setter) іменуються так: властивість fooBar можна отримати за допомогою методу fooBar() і встановити за допомогою setFooBar() .

T fooBar() const; void setFooBar (T val);

При розробці власних програм на Qt варто дотримуватися цього стилю.

5. Об'єктна модель

Для ефективної роботи з класами на стадії виконання Qt використовується спеціальна об'єктна модель, що розширює модель C++. Зокрема, додаються такі можливості:

  • деревоподібні ієрархії об'єктів;
  • аналог dynamic_cast для бібліотеки, що не використовує RTTI;
  • взаємодія об'єктів через сигналиі слоти;
  • властивості об'єктів.

Багато об'єктів визначаються значенням відразу кількох властивостей, внутрішніми станами та зв'язками з іншими об'єктами. Вони є індивідуальні сутності, і їм немає сенсу операція буквального копіювання, і навіть поділ даних у пам'яті. У Qt ці об'єкти успадковують властивості QObject.

У тих випадках, коли об'єкт потрібно розглядати не як сутність, а як значення (наприклад, при зберіганні в контейнері) - використовуються покажчики. Іноді покажчик на об'єкт, успадкований від QObject називають просто об'єктом.

Інструментарій спроектований так, що для QObject та всіх його нащадків конструктор копіювання та оператор присвоєння недоступні – вони оголошені в розділі private через макрос Q_DISABLE_COPY() :

class FooBar: public QObject (private: Q_DISABLE_COPY(FooBar));

Будьте уважні та не використовуйте конструкцію

Foo bar = Foo (baz);

Інші об'єкти (наприклад, контейнери і рядки) повністю визначаються даними, тому у відповідних класах є операція присвоєння та конструктор копіювання. Крім того, об'єкти, які мають однакові дані, можуть прозоро для програміста розділяти їх у пам'яті.


5.1. Система метаоб'єктів

Частина розширень реалізована стандартними методами C++, проте Qt використовує більш складні синтаксичні розширення, тому він використовує автоматичну генерацію коду.

Для цього в C++ реалізований механізм шаблонів, але він не надає всіх необхідних Qt можливостей, погано сумісний з динамічною моделлю об'єктної і повною мірою не підтримується всіма версіями компіляторів.

У складних ситуаціях Qt використовує свій компілятор метаоб'єктів moc, що перетворює код із розширеннями в стандартний код C++. Для позначення того, що клас використовує метаоб'єктні можливості (і, відповідно, має оброблятися moc), у розділі private необхідно вказати макрос Q_OBJECT .

Якщо ви зустрічаєте дивні помилки компіляції, що повідомляють, що у класу не визначений конструктор, або він не має таблиці віртуальних функцій (vtbl), швидше за все ви забули код, що генерується moc. Зазвичай це відбувається, якщо не вказано макрос Q_OBJECT.

Щоб уникнути помилок Q_OBJECT краще використовувати у всіх класах, успадкованих від QObject (непрямо чи напряму).

Використання динамічного підходу пов'язане з певними втратами у продуктивності порівняно зі статичним, проте цими накладками можна знехтувати, якщо взяти до уваги отримані переваги.

Серед інших, метаоб'єктний код додає метод

virtual const QMetaObject* QObject::metaObject() const;

який повертає покажчик на метаоб'єкт.

На системі метаоб'єктів засновані сигнали, слоти та властивості.

При успадкування від QObject пам'ятайте про обмеження, що накладаються moc:

  1. При множинному наслідуванні нащадком QObject повинен бути перший і тільки перший клас, що успадковується: class MyClass: public QObject, public Foo, public Bar ( // ...);
  2. Віртуальне успадкування з QObject не підтримується.

5.2. qobject_cast

Для динамічного наведення QObject використовується функція

T qobject_cast (QObject * object);

Вона працює як стандартна операція dynamic_cast C++, але не вимагає підтримки з боку системи динамічної ідентифікації типів (RTTI).

Нехай у нас є клас MyClass1, що успадковує від QObject і MyClass2, успадковує від MyClass1:

#include class MyClass1: public QObject (Q_OBJECT public: MyClass1(); // ...); class MyClass2: public MyClass1 ( Q_OBJECT public: MyClass2(); // ... );

Динамічне приведення ілюструє наступний код:

QObject *a = new MyClass2; MyClass1 *b = qobject_cast (a); MyClass2 *c = qobject_cast (b);

Ці операції спрацюють коректно на стадії виконання.

Як і у випадку з dynamic_cast, результат приведення можна перевірити:

if (b = qobject_cast (a)) ( // ... )

Система метаоб'єктів дозволяє також перевірити, чи успадковує a клас MyClass1:

if (a->inherits("MyClass1")) ( b = static_cast (a); // ...)

Однак переважний попередній варіант з qobject_cast .

5.3. Дерева об'єктів

Об'єкти класів, що успадковують від QObject можуть бути організовані в деревоподібну структуру. При видаленні об'єкта Qt видаляє його дочірні об'єктиякі у свою чергу видаляють свої дочірні об'єкти і т.д. Іншими словами, видалення об'єкта призводить до видалення всього піддерева, коренем якого він є.

Нехай у нас є класи ClassA та ClassB:

Лістинг 2.1. Оголошення MyClass для програми, що демонструє порядок створення та видалення об'єктів
// myclass.h #include class MyClass: public QObject (public: MyClass (char id, QObject *parent = 0); ~ MyClass(); private: char id_; );
Лістинг 2.2. Визначення методів MyClass для програми, що демонструє порядок створення та видалення об'єктів
// myclass.cpp #include #include #include "myclass.h" MyClass::MyClass (char id, QObject *parent) : QObject(parent), id_(id) ( qDebug()<< "+" >> id_; ) MyClass::~MyClass() ( qDebug()<< "-" >> id_; )

Тут батьківський об'єкт встановлюється у конструкторі QObject:

QObject::QObject (QObject *parent = 0);

Його можна встановити в подальшому за допомогою методу setParent() і отримати за допомогою parent():

void QObject::setParent (QObject *parent);
QObject* QObject::parent() const;

Якщо створити в стеку по одному з об'єктів A і B, спочатку буде створено A, потім B. Відповідно до стандарту C++, видалення відбувається в зворотному порядку - спочатку B, потім A:

Лістинг 2.3. Створення екземплярів MyClass у стеку
// main.cpp #include "myclass.h" int main() ( MyClass a ("A"); MyClass b ("B"); return 0; )

Якщо створити B у купі та призначити його дочірнім об'єктом для A, то разом з A автоматично вилучиться B:

Лістинг 2.4. Створення екземпляра A класу MyClass у стеку, а екземпляра B – у купі, як дочірнього для A
// main.cpp #include "myclass.h" int main() ( MyClass a ("A"); MyClass *b = new MyClass ("B", &a); return 0; )

Аналогічно для складніших дерев:

Рисунок 8. Приклад багаторівневого дерева об'єктів
Лістинг 2.5. Багаторівневе дерево об'єктів з коренем в стеку
// main.cpp #include "myclass.h" int main() ( MyClass a ("A"); MyClass *b = новий MyClass ("B", &a); MyClass *c = новий MyClass ("C", &a);MyClass *d = new MyClass ("D", c);

Після видалення A все дерево видаляється.

Таким чином, програміст повинен створювати об'єкти в купі та ставити відповідні ієрархії, а турботу про управління пам'яттю Qt бере на себе.

Якщо об'єкт і його дочірні об'єкти створені в стеку, такий порядок видалення може призвести до помилок.

int main() ( MyClass b ("B"); MyClass a ("A"); b.setParent(&a); // ... return 0; )

Тут при виході з області дії спочатку буде видалено об'єкт A, оскільки він був створений останнім. При цьому Qt видалить і його дочірній об'єкт B. Але потім буде спроба видалення B, що призведе до помилки.

В іншому випадку проблем не буде, тому що при виклику деструктора QObject об'єкт видаляє себе зі списку дочірніх батьківських об'єктів:

int main() ( MyClass a ("A"); MyClass b ("B", &a); // ... return 0; )

Взагалі кажучи, дочірні об'єкти повинні розміщуватись у купі.

У кожного QObject є властивість objectName, для доступу до якого використовуються методи

QString objectName() const; void setObjectName (const QString&name);

За промовчанням objectName – порожній рядок. Через цю властивість об'єктам у дереві можна присвоїти імена для подальшого пошуку.

const QList & children() const;

- Повертає список дочірніх об'єктів.

T findChild (const QString&name = QString()) const;

- Повертає дочірній об'єкт з ім'ям name, який можна призвести до типу T, або 0, якщо об'єкт не знайдено. Без аргументу nameповертає усі дочірні об'єкти. Пошук здійснюється рекурсивно.

QList QObject::findChildren (const QString&name = QString()) const;

- Повертає всі дочірні об'єкти з ім'ям name, які можуть призвести до типу Tабо порожній список, якщо таких об'єктів не знайдено. Без аргументу nameповертає усі дочірні об'єкти. Пошук здійснюється рекурсивно.

QList QObject::findChildren (const QRegExp®Exp) const;

– аналогічно, але з пошуком за регулярним виразом regExp.

void dumpObjectTree();

– виводить налагоджувальну інформацію про дерево об'єктів із цим коренем.

5.4. Сигнали та слоти

Під час створення графічних інтерфейсів взаємодія об'єктів часто здійснюється через зворотні виклики, тобто. передачу коду для подальшого виконання (у вигляді покажчиків на функції, функторів тощо). Також популярна концепція подійі обробників, в якій обробник діє як перехоплювач події певного об'єкта.

Qt вводиться концепція сигналів і слотів.

Сигналвідправляється під час виклику відповідного йому методу. Програмісту при цьому потрібно лише вказати прототип методу у розділі signals.

Слотє методом, що виконується при отриманні сигналу. Слоти можуть оголошуватися в розділі pulic slots, protected slots або private slots. При цьому рівень захисту впливає лише на можливість виклику слотів як звичайні методи, але не на можливість підключення сигналів до слотів.

Модель сигналів та слотів відрізняється від моделі подій та обробників тим, що слот може підключатися до будь-якого числа сигналів, а сигнал може підключатися до будь-якого числа слотів. При надсиланні сигналу будуть викликані всі підключені до нього слоти (порядок викликів не визначено).

5.4.1. Оголошення сигналів та слотів, відправка сигналів

Як типовий приклад слота розглянемо метод отримання властивості ( getter). Методу встановлення властивості ( setter) при цьому відповідатиме сигнал.

Лістинг 3.1. Клас MyClass зі слотом void setValue (int x) та сигналом void valueChanged (int x)
// myclass.h #include class MyClass: public QObject ( Q_OBJECT public: MyClass(int x, QObject *parent = 0); int value() const; public slots: void setValue (int x); signals: void valueChanged (int x); ;);

Зверніть увагу на макрос Q_OBJECT , що сигналізує Qt про те, що використовуються можливості системи метаоб'єктів.

Лістинг 3.2. Реалізація методів класу MyClass зі слотом void setValue (int x) та сигналом void valueChanged (int x)
// myclass.cpp #include #include "myclass.h" MyClass::MyClass (int x, QObject *parent) : QObject(parent) ( setValue (x); ) int MyClass::value() const ( return x_; ) void MyClass::setValue ( int x) ( if (x_ == x) return; x_ = x; emit valueChanged (x); )

Ключове слово emit відповідає за надсилання сигналу.

Для сигналу задається лише прототип, причому сигнал неспроможна повертати значення (тобто, вказується void). За реалізацію відповідає компілятор метаоб'єктів, він перетворює розширений синтаксис з ключовими словами signals, slots, emit в стандартний код C++.

Насправді ключові слова можна замінити на макроси Q_SIGNALS, Q_SLOTS і Q_EMIT . Це корисно, якщо ви використовуєте сторонні бібліотеки, де вже використовуються слова signals, slots або emit .

Обробка ключових слів вимикається прапором no_keywords. У файл проекту qmake (.pro) додайте

CONFIG += no_keywords

Ви можете переглянути результат роботи компілятора метаоб'єктів у файлі moc_slots.cpp, який генерується на основі slots.h і компілюється разом з рештою.cpp.

5.4.2. Підключення сигналу до слота

Лістинг 3.3. Підключення сигналу void MyClass::valueChanged (int x) до слота void MyClass::setValue (int x)
// main.cpp #include #include #include "myclass.h" int main() ( MyClass a(1); MyClass b(2); QObject::connect (&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int))); a.setValue (3); qDebug()<< "a:" << a.value(); // 3 qDebug() << "b:" << b.value(); // 3 return 0; }

Тут за допомогою QObject::connect сигнал об'єкта a з'єднується зі слотом об'єкта b (передаються покажчики QObject). Макроси SIGNAL та SLOT формують рядкові сигнатури методів. Їхні аргументи мають містити прототипи без зазначення імен змінних, тобто. SIGNAL(valueChanged(int x)) – неприпустимий варіант.

Сигнатури використовуються для звіряння типів: сигнатура сигналу повинна відповідати сигнатурі слота. При цьому слот сигнатури може бути коротшим, якщо додаткові аргументи ігноруються.

Інший варіант виклику QObject::connect:

b.connect (&a, SIGNAL(valueChanged(int)), SLOT(setValue(int)));

Таким чином, тут виклик MyClass :: setValue для a задіє MyClass :: setValue для b .

Зверніть увагу на рядок if (x_ == x) return; . Вона потрібна, щоб уникнути проблем під час циклічних сполук. Наприклад, наступний код спрацює:

Лістинг 3.4. Циклічне з'єднання сигналів void MyClass::valueChanged (int x) зі слотами void MyClass::setValue (int x)
// main.cpp #include #include #include "slots.h" int main() ( MyClass a(0); MyClass b(1); MyClass c(2); QObject::connect (&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue) (int)));QObject::connect (&b, SIGNAL(valueChanged(int)), &c, SLOT(setValue(int))); (setValue(int))), a.setValue (3); qDebug()<< "a:" << a.value(); // 3 qDebug() << "b:" << b.value(); // 3 qDebug() << "c:" << c.value(); // 3 return 0; }

QObject::connect повертає true , якщо з'єднання успішно встановлено, і false інакше – наприклад, коли сигнал чи слот не виявлено, або їх сигнатури несумісні.

Якщо додати за допомогою QObject::connect однакові з'єднання, то слот викликатиметься кілька разів.

5.4.3. Вимкнення

Для відключення сигналу від слота використовується QObject::disconnect:

QObject::disconnect (&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int)));

При успішному вимкненні повертається true.

Якщо замість сигналу (SIGNAL(...)) вказати 0, то одержувач сигналу (b) і слот відключаються від будь-якого сигналу:

QObject::disconnect (&a, 0, &b, SLOT(setValue(int)));

Якщо 0 вказати замість одержувача сигналу (b) і слота (SLOT(...)) , відключено буде все, що підключено до цього сигналу:

QObject::disconnect (&a, SIGNAL(valueChanged(int)), 0, 0);

Якщо 0 вказати замість слота (SLOT(...)) , то буде вимкнено все, що підключено до цього одержувача сигналу (b):

QObject::disconnect (&a, SIGNAL(valueChanged(int)), &b, 0);

Отримуємо наступні варіанти дзвінка QObject::disconnect:

// Вимкнути все від сигналів, що надсилаються об'єктом a: QObject::disconnect (&a, 0, 0, 0); // Те саме, але як методу a: a.disconnect(); // Вимкнути все від сигналу SIGNAL(...), що надсилається об'єктом a: QObject::disconnect (&a, SIGNAL(...), 0, 0); // Те саме,але як методу a: a.disconnect (SIGNAL(...)); // Вимкнути цього одержувача сигналів b: QObject::disconnect (&a, 0, &b, 0); // Те саме,але як методу a: a.disconnect (&b);

При видаленні одного з об'єктів з'єднання Qt автоматично видаляє саме з'єднання.

5.4.4. Обмеження

Компілятор метаоб'єктів має ряд обмежень, які поширюються і на роботу з сигналами і слотами.

  1. moc не обробляє шаблони та макроси, тому шаблони класів не можуть визначати сигнали та слоти, а при оголошенні сигналів та слотів не можна використовувати макроси (у тому числі при вказівці параметрів).

    Макроси не можна використовувати в будь-яких ділянках коду, які повинні бути оброблені moc . Зокрема через них не можна вказати базовий клас.

    Однак деякі можливості препроцесора можна використовувати. Доступні прості умовні конструкції (з директивами #if, #ifdef, #ifndef, #else, #elif, #endif і спеціальним оператором defined). Для створення оголошень у moc є опція командного рядка -D. Утиліта qmake передає moc всі оголошення, перелічені у параметрі проекту DEFINES .

    Наприклад, moc правильно обробить

    #if 0 // Ігнорований блок #endif
    Блок #ifdef FOO // ... #endif

    також буде оброблений тільки якщо викликати moc -DFOO , або якщо до нього є рядок #define FOO .

  2. Типи мають бути вказані повністю, оскільки QObject::connect() порівнює їх буквально. Зокрема, якщо всередині класу Foo визначається перерахування Bar, то в аргументах сигналу потрібно вказувати Foo::Bar:

    class Foo: public QObject (Q_OBJECT enum Bar (a, b, c); signals: void somethingHappened (Foo::Bar x); );
  3. Як параметри сигналів та слотів не можна використовувати вказівники на функції. Наприклад,

    int (*fun)(int)

    не є допустимим аргументом. Можна використовувати typedef:

    typedef int (*fun)(int);

    Зазвичай замість покажчиків краще застосовувати наслідування та віртуальні функції.

  4. Вкладені класи не можуть містити сигнали та слоти.
  5. Сигнали та слоти, що повертають посилання, обробляються таким чином, якби вони повертали void .
  6. У розділах signals та slots можуть оголошуватися лише сигнали та слоти.

5.5. Властивості

Клас, що успадковує QObject може містити оголошення властивості за допомогою макросу Q_PROPERTY() :

Q_PROPERTY( type name

READ getFunction

Обов'язкові параметри макросу:

  • type- Тип якості;
  • name- Ім'я якості;
  • getFunction- Const-метод для зчитування значення; тип, що повертається, повинен бути type, type*або type&.

Чи не обов'язкові параметри макросу:

  • setFunction– метод для встановлення значення властивості, повинен повертати void та приймати лише один аргумент типу type, type*, або type&;
  • resetFunction– метод для встановлення значення якості за умовчанням, що залежить від контексту, повинен не мати аргументів та повертати void;

Способи може бути віртуальними чи успадкованими від базового класу. При множинному успадкування вони мають належати першому класу у списку.

Для необов'язкових атрибутів DESIGNABLE, SCRIPTABLE, STORED, USER допускається вказівка ​​булевих значень:

  • DESIGNABLE – чи показувати властивість у Qt Designer та подібних графічних програмах. За умовчанням true , також можна вказати булев метод.
  • SCRIPTABLE – чи має властивість бути видимим скриптовому движку. За умовчанням true , також можна вказати булев метод.
  • STORED – чи має властивість зберігатися за збереження стану об'єкта чи воно обчислюється через інші характеристики. Типово true .
  • USER - чи редагується властивість користувача. Зазвичай у класів, відповідних елементам управління, буває така властивість. За промовчанням false.

Наприклад, QWidget оголошує, серед інших, такі властивості:

Q_PROPERTY (QSize minimumSize READ minimumSize WRITE setMinimumSize) Q_PROPERTY(int minimumWidth READ minimumWidth WRITE setMinimumWidth STORED false DESIGNABLE false) Q_PROPERTY(int minimumHeight READ minimumHeight WRITE setMinimumWid

Властивість minimumSize має тип QSize і може бути отримана за допомогою QSize minimumSize() const і встановлена ​​за допомогою void setMinimumSize (const QSize&). minimumWidth і minimumHeight обчислюються через minimumSize , тому їм зазначено STORED false .

Приклад якості з атрибутом USER – text у QLineEdit:

Q_PROPERTY(QString text READ text WRITE setText USER true)

Для зчитування та запису властивості використовуються методи:

QVariant QObject::property (const char * name) const; bool QObject::setProperty (const char * name, const QVariant & value);

property() повертає значення якості або неправильний варіант QVariant, якщо такої властивості немає.

setProperty() повертає true , якщо об'єкт має вказану властивість з типом, сумісним з переданим значенням. В іншому випадку повертається false.

Якщо в класі немає зазначеної властивості, то додається динамічна властивістьоб'єкт. Перелік динамічних властивостей можна отримати за допомогою

QList QObject::dynamicPropertyNames() const;

Розглянемо приклад використання властивостей. Нехай клас MyClass має рядкову властивість text (типу QString):

Лістинг 4.1. Оголошення класу MyClass із властивістю text
// myclass.h #include #include class MyClass: public QObject ( Q_OBJECT Q_PROPERTY(QString text read text WRITE setText) public: MyClass(QString text, QObject *parent = 0); QString text() const; void setText(const QString& text); private: QString text_; ) ;
Лістинг 4.2. Визначення методів класу MyClass із властивістю text
// myclass.cpp #include #include #include "myclass.h" MyClass::MyClass(QString text, QObject *parent) : QObject(parent) ( setText(text); ) QString MyClass::text() const ( return text_; ) void MyClass::setText( const QString& text) ( text_ = text; )

Робота з властивістю:

Лістинг 4.3. Робота із властивістю text об'єкта MyClass
// main.cpp #include #include #include #include #include #include #include "myclass.h" int main() ( MyClass str("foo"); qDebug()<< "text:" << str.text(); // Через метод: str.setText("bar"); qDebug() << "text:" << str.text(); // Через setProperty() / property(): str.setProperty("text", QVariant("baz")); QVariant prop = str.property("text"); qDebug() << "text:" << prop.toString(); // Добавление динамического свойства: str.setProperty("foo", QVariant("bob")); str.setProperty("bar", QVariant("slack")); QListd_props = str.dynamicPropertyNames(); QListIterator iter(d_props); // (Контейнери та ітератори ми ще розглянемо окремо) while (iter.hasNext()) ( const char* d_prop_name = iter.next().data(); QVariant d_prop = str.property(d_prop_name); qDebug()<< "" << d_prop_name << ":" << d_prop.toString(); } return 0; }

Програма має вивести на екран наступне:

text: "foo" text: "bar" text: "baz" foo: "bob" bar: "slack"

Зрозуміло, безпечніше та швидше викликати методи конкретного класу для зчитування та запису властивостей. property() і setProperty() потрібні в тому випадку, коли про клас нічого не відомо крім імен та типів властивостей.

Якщо класу не відомий навіть перелік властивостей і методів, можна використовувати метаобъект.

5.6. Робота з метаоб'єктами

Метаоб'єкт повертається методом

QObject::metaObject()

З його допомогою можна динамічно отримати інформацію про клас, як, наприклад, Java Reflecion API (EN).

5.6.1. Основна інформація

Ім'я класу повертає

const char * QMetaObject::className() const;

Вказівник на метаоб'єкт базового класу –

const QMetaObject* superClass() const;

5.6.2. Методи

Через систему метаоб'єктів доступні лише методи і конструктори, перед оголошеннями яких вказано макрос Q_INVOKABLE:

class MyClass: public QObject ( Q_OBJECT public: Q_INVOKABLE MyClass(); // видно системі метаоб'єктів Q_INVOKABLE void foo(); // видно void foo(); // не видно);

Для доступу до методів (у тому числі сигналів та слотів) використовуйте

int QMetaObject::methodCount() const; int QMetaObject::methodOffset() const; QMetaMethod QMetaObject::method (int index) const;

Методи та властивості класу проіндексовані. Доступ до методу індексу здійснюється через QMetaObject::method() .

Загальна кількість методів з урахуванням успадкованих повертає QMetaObject::methodCount() . Зміщенняметодів класу повертається QMetaObject::methodOffset() , воно показує, з якого індексу починаються методи цього класу. Зміщення збільшується при успадкування та показує кількість методів базових класів.

Приклад проходу методами:

const QMetaObject* m_obj = obj.metaObject(); for (int i = m_obj->methodOffset(); i< m_obj->methodCount(); i++) (qDebug()<< m_obj->method(i).signature(); )

Якби ми почали з індексу 0, то отримали методи всіх базових класів, у тому числі QObject:

destroyed(QObject*) destroyed() deleteLater() _q_reregisterTimers(void*) ...

Методи, що починаються з _q_, використовуються всередині Qt і не є частиною API.

Конструктори зазначаються окремо:

QMetaMethod QMetaObject::constructor (int index) const; int QMetaObject::constructorCount() const;

Наприклад, отримаємо перелік конструкторів QObject:

Лістинг 5. Виведення конструкторів QObject через систему метаоб'єктів
#include #include #include #include int main() ( QObject obj; const QMetaObject* m_obj = obj.metaObject(); for (int i = 0; i< m_obj->constructorCount(); i++) (qDebug()<< m_obj->constructor(i).signature(); ) return 0; )

Результат:

QObject(QObject*) QObject()

Індекс методу, сигналу, слота або конструктора можна отримати за його сигнатурою:

int QMetaObject::indexOfConstructor (const char * constructor) const; int QMetaObject::indexOfMethod (const char * method) const; int QMetaObject::indexOfSignal (const char * signal) const; int QMetaObject::indexOfSlot (const char * slot) const;

Для конструкторів, методів чи сигналів очікуються нормалізовані сигнатури. Їх можна отримати за допомогою статичного методу

static QByteArray QMetaObject::normalizedSignature (const char * method);

Наприклад,

QMetaObject::normalizedSignature ("int * foo(const QString &, QObject *)")

повертає " int * foo (QString, QObject *) ".

Аналогічно працює

static QByteArray QMetaObject::normalizedType (const char * type);

Це текстове приведення до канонічного вигляду, яке використовується, зокрема, під час перевірки сумісності сигналу і слота.

5.6.3. Властивості

Аналогічно можна працювати із властивостями.

int QMetaObject::propertyCount() const; int QMetaObject::propertyOffset() const; QMetaProperty QMetaObject::property (int index) const;const QMetaObject* m_obj = obj.metaObject(); for (int i = m_obj->propertyOffset(); i< m_obj->>propertyCount(); i++) (qDebug()<< m_obj->property(i).name(); )

(Якщо переглянути всі властивості, включаючи успадковані, то ви побачите щонайменше objectName з QObject .)

Індекс властивості можна отримати на його ім'я:

int QMetaObject::indexOfProperty (const char * name) const;

5.6.4. Перерахування

Перерахування реєструються у класі з допомогою макросу Q_ENUMS() .

Перерахування, значення якого можна комбінувати за допомогою побітового АБО, називається прапорі слід реєструватися за допомогою Q_FLAGS() .

QMetaEnum QMetaObject: enumerator (int index) const; int QMetaObject::enumeratorCount() const; int QMetaObject::enumeratorOffset() const;
Лістинг 6.1. Клас MyClass з перерахуванням Type та прапором Mode
class MyClass ( Q_OBJECT Q_ENUMS(Type) Q_FLAGS(Mode) public: enum Type ( A, B, C ); enum Mode ( Read = 0x1, Write = 0x2, Execute = 0x4 ); // ... );

Прапори використовуються таким чином:

int mode = MyClass::Read | MyClass::Write; // ... if (mode & MyClass::Write) // Чи встановлено прапор Write? ( // ... )

Динамічна робота з перерахуваннями:

Лістинг 6.2. Виведення перерахувань та прапорів MyClass через систему метаоб'єктів
MyClass obj; const QMetaObject* m_obj = obj.metaObject(); for (int i = m_obj->enumeratorOffset() ; i< m_obj->enumeratorCount(); i++) ( QMetaEnum me = m_obj->enumerator(i); if (me.isValid()) // Є ім'я ( if (me.isFlag()) // Прапор ( qDebug()<< "" << me.scope() << "::" << me.name(); } else { qDebug() << me.scope() << "::" << me.name(); } } }

Результат праці:

MyClass :: Type MyClass :: Mode

Індекс перерахування можна отримати на його ім'я:

int QMetaObject::indexOfEnumerator (const char * name) const;

5.6.5. CLASSINFO

За допомогою макросу Q_CLASSINFO() до метаоб'єкта можна додавати пари ім'я-значення. Наприклад,

Лістинг 7.1. Клас MyClass з CLASSINFO
class MyClass (Q_OBJECT Q_CLASSINFO("author", "Bob Dobbs") Q_CLASSINFO("version", "0.23") // ...);

Ці пари успадковуються, і їх можна отримати з метаоб'єкта за тією самою схемою:

QMetaClassInfo QMetaObject:: classInfo (int index) const; int QMetaObject::classInfoCount() const; int QMetaObject::classInfoOffset() const;

Для прикладу вище:

Лістинг 7.2. Висновок CLASSINFO класу MyClass
MyClass obj; const QMetaObject* m_obj = obj.metaObject(); for (int i = m_obj->classInfoOffset(); i< m_obj->classInfoCount(); i++) ( QMetaClassInfo mci = m_obj->classInfo(i); qDebug()<< mci.name() << ":" << mci.value(); }

Результат:

Індекс CLASSINFO можна отримати на його ім'я:

int QMetaObject::indexOfClassInfo (const char * name) const;

5.6.6. Виклик конструкторів та методів

Передача аргументів здійснюється через об'єкти QGenericArgument та QGenericReturnArgument. Вони створюються макросами Q_ARG і Q_RETURN_ARG.

// константна посилання передачі значення: Q_ARG (T, const T& value) // посилання повернення значення: Q_RETURN_ARG (T, T& value)

Приклад використання:

Q_ARG(QString, "foo") Q_ARG(int, 23) Q_RETURN_ARG(QString, str)

Для створення нового екземпляра класу використовується метод метаоб'єкта newInstance(), якому можна передати до 10 аргументів.

QObject* QMetaObject::newInstance (QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument(Q), QGenericArgument() ricArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) const;

У разі помилки повертається 0.

Для виклику методу використовується invokeMethod() :

static bool QMetaObject::invokeMethod (QObject* obj, const char * member, Qt::ConnectionType typ, QGenericReturnArgument ret, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument() val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument(), QGenericArgument(), QGenericArgument(), QGenericArgument() 9 = QGenericArgument());
  • obj- Покажчик на об'єкт;
  • member- Ім'я методу;
  • type– тип виклику:
    • Qt::DirectConnection – негайно,
    • Qt::QueuedConnection – на початку виконання QCoreApplication::exec() ,
    • Qt::AutoConnection - синхронно, якщо об'єкт знаходиться в тому ж потоці, і асинхронно в іншому випадку;
  • ret– значення, що повертається;

Під час асинхронного виклику значення не може бути обчислене.

Є перевантажені версії invokeMethod() . Якщо ви не вкажете тип дзвінка, використовуватиметься Qt::AutoConnection . Якщо ви не вкажете значення, що повертається, то воно буде проігноровано.

Ті ж можливості надає клас QMetaMethod:

bool QMetaMethod::invoke (QObject* об'єкт, Qt::ConnectionType connectionType, QGenericReturnArgument returnValue, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGeneric = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument(), QGenericArgument(), QGenericArgument(), QGenericArgument()

Так само, тип з'єднання та/або значення, що повертається, можна не вказувати.

Асинхронний виклик використовується в тому випадку, коли обчислення займають занадто багато часу, тому їх результат не очікується у точці виклику. Подібні обчислення зазвичай поміщають окремий потік, тому за умовчанням (Qt::AutoConnection) методи об'єктів із зовнішніх потоків викликаються асинхронно.

Розглянемо наступний клас:

Лістинг 8.1. Клас MyClass з конструктором та методами, доступними системі метаоб'єктів
class MyClass: public QObject ( Q_OBJECT public: Q_INVOKABLE MyClass (QString text, QObject *parent = 0); Q_INVOKABLE QString text() const; Q_INVOKABLE void setText (const QString& text); private: QString text_; );

Звернення до конструктора та методів:

Лістинг 8.2. Виклик конструкторів та методів класу MyClass через систему метаоб'єктів
MyClass foo ("foo"); const QMetaObject* m_foo = foo.metaObject(); // Створити новий екземпляр: MyClass * bar = qobject_cast (m_foo->newInstance(Q_ARG(QString,"bar"))); if (!bar) (qCritical()<< "Can"t invoke constructor!"; } else { bar->setParent(&foo); qDebug()<< bar->text(); // "bar") // Викликати метод: if (!QMetaObject::invokeMethod (&foo, "setText", Q_ARG(QString,"baz"))) qCritical()<< "Can"t invoke method!"; QString val; // Вызвать метод и получить возвращенное значение: if (!QMetaObject::invokeMethod (&foo, "text", Q_RETURN_ARG(QString, val))) qCritical() << "Can"t invoke method!"; qDebug() << val; // "baz"

text() і setText() викликаються таким чином лише як простий приклад роботи з QMetaObject::invokeMethod() . Як ви вже знаєте, ці два методи мають бути пов'язані з властивістю.

Також зверніть увагу, що text() повертає QString, але не const QString&. Інакше система метаоб'єктів вважала б, що text() повертає void .

Висновок

Для ефективної роботи з класами на стадії виконання Qt використовує спеціальну об'єктну модель, в якій за допомогою успадкування від QObject та генерування коду компілятором метаоб'єктів реалізовано:

  • ієрархії об'єктів;
  • спеціальний аналог dynamic_cast, що не залежить від RTTI;
  • система сигналів та слотів;
  • система властивостей об'єктів;
  • динамічна робота із класами.

У наступній статті ми розглянемо типи, варіанти, посилання та розділення даних.