![Программирование в qt creator. Написание простой программы с помощью Qt Creator](https://i1.wp.com/cppstudio.ru/wp-content/uploads/2017/05/new_projeckt_1.png)
Программирование в qt creator. Написание простой программы с помощью Qt Creator
Первая часть статьи. Базовый обзор возможностей Qt и запуск первой Windows программы.
Приветствую тебя дорогой читатель. В этой статье мы поговорим о том как в QT можно создать первое оконное приложение используя только команды и главный файл main.cpp. Данная статья будет разделена на 2 части. В первой части мы с вами создадим само приложение и запустим его на компиляцию при помощи компилятора MinGW с очень короткими комментариями по коду, а во второй части мы разберем весь код до последней детали чтобы у вас сформировалось полное базовое понимание о том как все это работает. Итак пожалуй начнем. Первое что нам необходимо сделать это запустить QT Creator и создать проект. Я думаю нет необходимости объяснять как запустить QT Creator, так что мы начнем сразу с создания проекта.
Видео урок по данной статье
После того как QT запустился нажмите на кнопку новый проект как показано на скриншоте ниже.
новый проект , у вас откроется диалоговое окно, где будет предложен выбор что именно вы ходите создать. Вам необходимо выбрать приложение Qt Widgets как показано ниже на скриншоте.
После того как вы нажали кнопку выбрать… У вас откроется следующее окно выбора дополнительных параметров, где вам будет предложено выбрать имя вашего проекта и где он будет храниться. ВАЖНО: Не допускайте русских символов в пути где у вас будет храниться ваш проект, Qt не дружит с русскими символами. Эта мера необходима чтобы в дальнейшем избежать ошибок при сборке проекта. Ниже приведен пример как правильно создать имя и путь к проекту Qt.
После того как вы сделали все необходимые настройки, нажмите на кнопку далее.
Далее Qt нам предлагает выбрать комплект при помощи которого мы будем разрабатывать наш проект, я выбрал комплект Desktop Qt 5.8.0 с компилятором от MinGW 32 bit. Вы можете оставить значение по умолчанию или сделать свой выбор комплекта или даже комплектов для разработки вашей программы.
После того как вы определились с выбором, нажмите кнопку Далее . В следующем диалоговом окне мы оставляем все по умолчанию и просто нажимаем на кнопку далее.
После этого у нас откроется последнее окно в котором будет собрана вся суммарная информация по создаваемому проекту, также в этом окне нам будет предложено создать наш проект как подпроект нашего предыдущего и добавить под контроль версии проекта. Но так как у нас нет ни того ни другого, мы просто нажимаем кнопку завершить как показано на скриншоте ниже.
После того как наш конструктор приложений завершился пришло самое время настроить наш проект так чтобы у нас была возможность выполнить простейшую программу для вывода текста на экран и проверки правильности работы нашего фреймворка. Для начала нам необходимо удалить из нашего проекта все файлы кроме файла main.cpp — вот в этом файле мы и будем в дальнейшем прописывать код для запуска окна с выводом текста в оконном режиме. Вам необходимо оставить все так же как показано на скриншоте ниже после удаления лишних файлов из проекта.
Скриншот до удаления файлов из проекта:
Чуть ниже вы можете увидеть скриншот созданного проекта конструктором Qt без изменений.
До удаления лишних файлов из проекта
Скриншот после удаления файлов из проекта:
На текущем этапе я пока не приводил никаких действий с файлом кроме удаления лишних файлов, если вы не догадались как можно удалить файл, то для этого достаточно выделить его левой кнопкой мыши чтобы на нем загорелась синяя обводка, после этого на этот файл наводим мышку и нажимаем правой клавишей мыши и выбираем пункт удалить, в открывшемся диалоговом окне ставим галочку удалить навсегда и подтверждаем удаление файла. После проделанной операции у вас успешно будет удален файл из проекта. После того как вы удалили все ненужные файлы из проекта, откройте файл main.cpp двойным щелчком левой кнопки мыши и удалите из него весь код который там есть. В общем у вас должно получиться точно так же как на скриншоте ниже.
Разработка простейшего кода для оконного приложения:
Теперь пришло самое время создать код нашей простейшей программы в которой у нас будет запускаться окно и в данном окне у нас будет выводиться текст
на Русском языке без проблем с кодировкой. Данная проблема уже была предусмотрена в Qt и решена внутренними инструментами.
Для начала добавим следующий код в наш файл main.cpp
Запустите ваш код при помощи зеленого треугольника слева или сочетанием горячих клавиш Ctrl+R. В итоге у вас должно получиться окно с текстом Привет мир!!!
На этом первая часть статьи завершена, подробный разбор команд в данной программе и еще некоторые дополнительные возможности Qt на примере текущей программы мы разберем во второй части.
Вторая часть статьи. Подробный разбор кода:
До текущего момента мы с вами разобрались как можно создать простейшую программу которая только в окне выводит текст не разбираясь в подробностях кода. Теперь пришло самое время разобраться с теми командами которые мы использовали в нашей программе. Первое что мы с вами сделали, это подключили библиотеку QtWidgets — Данная библиотека включает в себя около 300 различных библиотек направленных на создания оконных приложений. Еще QtWidgets можно назвать складом строительных материалов для различных оконных приложений. Можно было конечно подключить отдельно библиотеки такие как QLabel для создания окон Windows и QApplication для обработки строк с различными аргументами. Но у нас стал бы код чуть длиннее и чуть труднее читаем. А теперь представьте если бы вы создавали программу в которой куча разных функций. И вы бы подключали к каждой функции свою библиотеку. Спустя какое то время вы бы с трудом смогли вспомнить для чего подключено такое большое количество библиотек. Вот как раз в таких случаях и выручают большие библиотеки которые содержат в себе уже весь необходимый строительный материал для создания к примеру оконных программ как QtWidgets.
Вот собственно говоря весь код нашей программы, чуть выше мы с вами уже разобрали библиотеку QtWidgets
#include
#include //входит около 300 различных классов для разработки оконных приложений. //В том числе и такие как QApplication и QLabel. int main (int argc , char * * argv ) { QApplication app (argc , argv ) ; QLabel lbl ("Привет мир!!!" ) ; // Создаем конструктором окно с текстом Привет мир!!! lbl . show () ; // Обращаемся к методу(функции) show() для вывода информации на экран return app . exec () ; // Проверяем сигнал закрытия окна через объект app при помощи метода(функции) //exec() которая перехватывает различные действия происходящие в программе. //Это необходимо для корректного завершения программы, чтобы после закрытия //окна программы она не занимала место в оперативной памяти и не напрягала //процессор в фоновом режиме. |
Теперь давайте разберем наш пример. Сначала создается объект класса QApplication, который осуществляет контроль и управление приложением. Для его создания в конструктор этого класса необходимо передать два аргумента. Первый argc аргумент представляет собой информацию о количестве аргументов в командной строке, из которой происходит обращение к программе, а второй argv - это указатель на массив символьных строк, содержащих аргументы, по одному в строке. Любая использующая Qt программа с графическим интерфейсом должна создавать только один объект этого класса, и он должен быть создан до использования операций, связанных с пользовательским интерфейсом.
Затем создается объект класса QLabel. После создания элементы управления Qt по умолчанию невидимы, и для их отображения необходимо вызвать метод show(). Объект класса QLabel является основным управляющим элементом приложения, что позволяет завершить работу приложения при закрытии окна элемента. Если вдруг окажется, что в созданном приложении имеется сразу несколько независимых друг от друга элементов управления, то при закрытии окна последнего такого элемента управления завершится и само приложение. Это правильно, иначе приложение осталось бы в памяти компьютера и расходовало бы его ресурсы.
Наконец, в последней строке программы приложение запускается вызовом
QApplication::ехес(). С его запуском приводится в действие цикл обработки событий, определенный в классе QCoreApplication, являющемся базовым для QGuiApplication, от которого унаследован класс QApplication. Этот цикл передает получаемые от системы события на обработку соответствующим объектам. Он продолжается до тех пор, пока либо не будет вызван статический метод QCoreApplication::exit (), либо не закроется окно последнего элемента управления. По завершению работы приложения метод QApplication::ехес() возвращает значение целого типа, содержащее код, информирующий о его завершении. После того как вы создали весь код и запустили программу – у вас должен получиться следующий результат.
Теперь пришло самое время разобраться как работают теги из HTML(язык гипертекстовой разметки) и CSS(каскадные таблицы стилей).
Когда вы создаете объект и передаете конструктору информацию в двойных кавычках, то конструктор автоматически создает окно при помощи QLabel и создает там текстовое поле в котором собственно и будет выводиться ваш текст. Благодаря новым возможностям от Qt есть возможность использовать различные теги от HTML и CSS. Такая возможность позволяет оформлять различные элементы программы в различных стилях что очень сильно помогает в разработке дизайна программ. Главное не забывать о том что в HTML и CSS используются такие спец. Символы как точка с запятой и двойные кавычки в для написания нескольких свойств в CSS для текста или какого то объекта. В Qt придется забыть о данных знаках при написания кода оформления в двойных кавычках так как это может вызвать ошибку при компиляции. А вот что касается простеньких свойств в HTML и CSS которые имеют не более 1 параметра, то здесь работает все корректно. Ниже привел пример оформления, с кодом вы можете ознакомится на скриншоте.
Чуть выше на скриншоте вы можете увидеть как буквально в 1 строку кода мы оформили текст в разной цветовой гамме, так же задали размер заголовка текста и добавили пару переносов строки. А теперь судите сами на сколько упрощает жизнь при оформлении программы при использовании языка гипертекстовой разметки и каскадных таблиц стилей.
На этом пожалуй все что я хотел рассказать в данной статье, пишите в комментариях ваши мысли, предложения. Комментарии буду публиковаться все кроме спама и не обоснованной критики. Все остальные комментарии будут опубликованы.
Так же большая просьба написать в комментариях, насколько бы вам было интересно начать параллельное изучение HTML и CSS для Qt.
Рис. A.1.
После перезагрузки ОС Windows для обращения к компилятору достаточно будет указывать его имя - g++ .
Таким образом, в ОС Linux для работы с компилятором в командной строке необходимо запустить Терминал, а в ОС Windows – командную строку. После чего работа с компилятором g++ с ОС Windows и Linux идентична.
Рассмотрим опции компилятора командной строки, необходимые для компиляции и запуска простейших программ.
Для того, чтобы создать исполняемый файл из текста программы на C++ , необходимо выполнить команду
Здесь name.cpp - имя файла с текстом программы. В результате будет создан исполняемый файл со стандартным именем a.out. Для того, чтобы создать исполняемый файл с другим именем, необходимо выполнить команду
g++ -o nameout name.cpp
Здесь name.cpp - имя файла с текстом программы, nameout - имя исполняемого файла.
При использовании компилятора g++ после компиляции программы автоматически происходит компоновка программы (запуск компоновщика make). Чтобы исключить автоматическую компоновку программы, следует использовать опцию -c. В этом случае команда будет иметь вид g++ -c name.cpp
Рис. A.2.
Рис. A.3.
Технология работы с компилятором g++ может быть такой: набираем текст программы в стандартном текстовом редакторе, потом в консоли запускаем компилятор, после исправления синтаксических ошибок запускаем исполняемый файл. После каждого изменения текста программы надо сохранить изменения в файле на диске, запустить компилятор, и только после этого запускать программу (исполняемый файл). Очень важно не забывать сохранять текст программы, иначе при запуске компилятора будет компилироваться старая версия текста программы.
Рис. A.4.
Рис. A.5.
Компилятор g++ эффективен при разработке больших комплексов программ, он позволяет собирать приложения из нескольких файлов, создавать библиотеки программ. Рассмотрим процесс создания и использования библиотеки решения задач линейной алгебры (см. п. 6.4, задачи 6.10 - 6.12):
int SLAU(double **matrica_a, int n, double *massiv_b, double *x) - функция решения системы линейных алгебраических уравнений;
int INVERSE(double **a, int n, double **y) - функция вычисления обратной матрицы;
double determinant(double **matrica_a, int n) - функция вычисления определителя.
Для создания библиотеки создадим заголовочный файл slau.h и файл slau.cpp , в который поместим тексты всех трёх функций решения задач линейной алгебры.
Текст файла slau1.h:
int SLAU(double ** matrica_a, int n, double *massiv_b, double *x); int INVERSE(double **a, int n, double **y); double determinant (double ** matrica_a, int n);
Текст файла slau1.cpp :
#include В качестве тестовой задачи напишем главную функцию, которая предназначена для решения системы линейных алгебраических уравнений. #include Теперь необходимо из этих текстов создать работающее приложение. Рассмотрим это поэтапно. После разработки библиотеки линейной алгебры (пример) slau1
, можно использовать её в различных программах при вычислении определителя, обратной матрицы и решения систем линейных алгебраических уравнений. При разработке программ с большим количеством вычислений, компилятор g++
позволяет оптимизировать программы по быстродействию. Для получения оптимизированных программ можно использовать ключи -O0, -O1, -O2, -O3, -Os
: Для разработки программ на различных языках программирования можно использовать текстовый редактор Geany
. Редактор Geany
входит в репозитории большинства дистрибутивов Linux, его установка осуществляется стандартным для вашего дистрибутива образом (в debian-подобных ОС с помощью команды apt-get install geany
). Для установки его в Windows необходимо скачать со страницы http://www.geany.org/Download/Releases инсталляционный файл и установить программу стандартным способом. Разработка программ с использованием Geany более эффективна. Окно Geany представлено на . Последовательно рассмотрим основные этапы разработки программы с использованием Geany
. Параметры компилятора определяются автоматически после выбора шаблона (Файл -> Создать из шаблона). Однако команды компиляции и сборки по умолчанию можно изменить, используя команду Сборка -> Установить параметры сборки (см. ). Здесь %f
- имя компилируемого файла, %e
- имя файла без расширения. Рис. A.6.
Рис. B.1.
Задача B.2.
Построить графики функций #include Рис. B.2.
Задача B.3.
Построить в одном графическом окне графики функций: Результаты вывести на экран и в файл. Рис. B.3.
#include Рис. B.4.
Задача B.4.
Построить график функций . Нетрудно заметить, что функция не существует в точке ноль. Поэтому построим её график на двух интервалах [-2;-0.1] и , исключив точку разрыва из диапазона построения. Текст программы с подробными комментариями приведён далее. Решение задачи представлено на . #include Рис. B.5.
Задача B.5.
Построить график функции Функция имеет разрыв в точках -1 и 3. Построим её график на трёх интервалах [-5; -1.1], [-0.9;2.9] и , исключив точки разрыва из диапазона построения. Текст программы с подробными комментариями приведён далее. Решение задачи представлено на . #include Рис. B.6.
Задача B.6
. Построить график функции . График задан в параметрической форме и представляет собой эллипс. Выберем интервал построения графика , ранжируем переменную на этом интервале, сформируем массивы и и построим точечный график. Текст программы и результаты её работы () представлены далее. #include Рис. B.7.
Задача B.7.
Построить график функции: В данном случае необходимо построить график функции двух аргументов. Для этого нужно сформировать матрицу при изменении значений аргументов и и отобразить полученные результаты. #include Рис. B.9.
График к задаче B.8 В завершении приведём решение реальной инженерной задачи с использованием MathGL
. Задача B.9.
В "Основах химии" Д. И. Менделеева приводятся данные о растворимости азотнокислого натрия в зависимости от температуры воды. Число условных частей , растворяющихся в 100 частях воды при соответствующих температурах, представлено в таблице. Текст программы решения задачи с комментариями приведён ниже. #include После запуска программы на экране пользователь увидит следующие значения X
0 4 10 15 21 29 36 51 68
Y
66.7 71 76.3 80.6 85.7 92.9 99.4 113.6 125.1
k=3
a=67.5078 b=0.87064
Xr Yr
25 89.2738
32 95.3683
45 106.687 Графическое решение задачи, полученное с помощью средств библиотеки MathGL
, представлено на . Рис. B.11.
Для изучения всех возможностей MathGL
, авторы советуют обратиться к документации по MathGL
. При освоении библиотеки MathGL
следует помнить, что логика и синтаксис библиотеки напоминает синтаксис Scilab
и Octave
. Простой пример для ручной сборки
Для лучшего понимания Qt будет правильным выполнить ручную сборку в консоли хотя бы простейшего примера. Методика сборки одинакова для проектов любого размера и важно понимать, как она выполняется. Рассмотрим простейший пример GUI-приложения, которое выполняет открытие пустого окна на экране. Выделим директорию на диске, где создадим файл main.cpp
со следующим содержимым. В пример добавлена нумерация строк для упрощения дальнейших комментариев к примеру. Следует понимать, что нумерация строк не должна содержаться в рабочем файле, так как она не входит в допустимый синтаксис выражений на языке C++. 01. #include В строках 01 и 02 выполняется включение заголовочных файлов QApplication
и QWidget
, в которых, кроме прочего, содержится объявление классов QApplication
и QWidget
. В нашем примере мы создаем экземпляры этих классов. Следует заметить удобную традицию именования заголовочных файлов используемую в Qt. Начиная с четвертой версии Qt, если вам потребовался класс с именем Qxxx
, то, скорее всего, его определение находится в заголовочном файле Qxxx
. В данном примере, класс QApplication
интересует нас как организатор цикла сбора сообщений, которые будут приходить в окно нашего GUI-приложения. См. строку 11 (запуск цикла обработки оконных сообщений). Класс QApplication
реализован по подобию синглтона (Singleton). Для тех, кто не знаком с этим термином из теории шаблонов проектирования (или, паттернов, от англ. patterns
) дадим небольшое пояснение. Суть синглтона заключается в том, что реализация класса предотвращает возможность создания более одного экземпляра данного класса. Эта особенность нам важна потому, что из нее следует возможность определения внутри библиотеки Qt глобальной переменной с указателем на единственный экземпляр данного класса. Смотрите описание для символа qApp
в справке Qt. В библиотеке Qt используется терминология виджета (от англ. widget
— штуковина, приспособление), как элемента GUI-интерфейса. Минимальный общий набор свойств таких элементов представлен классом QWidget
. В строках 06 и 07 создаются объекты классов приложения и виджета. В обоих случаях выполняется статическое создание объектов. Время существования таких объектов ограничивается операторным блоком { … } в котором они созданы. Как только исполнение кода программы дойдет до закрывающей скобки, то оба этих объекта автоматически уничтожатся. Таким образом, в данном примере мы не будет задумываться о способе уничтожения объектов Qt и рассмотрим этот вопрос позже. При создании объекта QApplication
используется параметризованный конструктор в который передаются аргументы запуска приложения (копируются из аргументов функции main()). Внутри объекта класса выполняется парсинг этих аргументов. Класс приложения поддерживает ряд параметры запуска, которые можно уточнить по справке для соответствующего конструктора QApplication
. Параметры запуска не входящие в этот список следует анализировать самостоятельно. В конце этого урока, разместив в главном окне приложения несколько элементов управления мы предложим провести эксперимент с параметром запуска -style, для которого, в любой сборке Qt, возможны значения: motif
, windows
, platinum
. В строке 08 выполняется изменение одного из атрибутов объекта виджета. С помощью метода setWindowTitle() мы устанавливаем свой текст для заголовка нашего будущего окна. Обратите внимание, что изображение строки обернуто вызовом функции трансляции tr()
. Это требование интернационализации приложения. По всем подобным оберткам, специальными средствами Qt, могут быть созданы специальные файлы с переводами на разные языки, которые можно будет использовать в приложении для выполнения автоматических замен. Обычно, такие файлы включают в сборку приложения в виде ресурсов. В строке 10 выполняется открытие окна приложения. До версии Qt4, перед открытием окна выполнялась явное объявление виджета как главного виджета приложения. Это делалось с помощью следующего кода. App.setMainWidget(&wgt);
Начиная с версии Qt4 выполнение такой связи выполняется автоматически через обращение к глобальному указателю qApp
на экземпляр класса приложения. В строке 11 выполняется запуск цикла обработки сообщений операционной системы направленный в окно приложения. Цикл завершается при выполнении какой-нибудь из команд закрытия приложения. Код закрытия приложения возвращается методом exec() при завершении метода. Именно этот код становится кодом возврата функции main() за счет передачи его через оператор return
. Теперь попробуем выполнить сборку приложения. Проще всего это будет сделать в Linux. Для этого надо будет просто открыть консоль и выполнить некоторые команды, которые мы сейчас опишем. Для Windows такая работа может потребовать установку путей на директории размещения утилиты qmake из Qt SDK. Данная утилита реализует правила системы сборки проектов QMake. Для начала следует узнать, что нам доступно из консоли. Если мы в консоли bash (*nix), то сделать это достаточно просто. Наберите команду qmake
и нажмите два раза табуляцию. Мы должны увидеть список всех команд, которые начинаются с сочетания qmake
. Например, в моем случае я вижу две команды: qmake
и qmake-qt4
. Это говорит о том, что у меня из репозитория установлены две версии библиотеки. Команда qmake
соответствует версии Qt5 (действует умолчание для последней версии), а команда qmake-qt4
соответствует, соответственно Qt4. Теперь, в зависимости от того, какой командой я воспользуюсь, я буду делать сборку либо с использованием версии Qt5, либо с использованием версии Qt4. Если все у нас в системе настроено нормально и версии Qt SDK удачные, то сборка проекта должна быть выполнена с помощью следующих трех команд.
$ qmake -project
$ qmake
$ make
С помощью первой команды будет создан проектный файл. Проектный файл имеет суффикс .pro
. В контексте Windows правильно будет сказать "расширение pro
". Понятия суффикс имени файла
и расширение файла
означают совершенно разные вещи, хотя и кажутся похожими. Следите за правильностью употребления. Вторая команда должна создать файл сценария компиляции — Makefile. Третья команда должна выполнить сценарий компиляции, в результате которого должен получиться исполняемый файл приложения. Если этого не произошло, то попробуем найти проблему. Откройте проектный файл. Попробуйте найти там следующую строку. QT += gui
Если такой строки там нет, то следует ее добавить, иначе в зависимости от версии SDK проект может быть воспринят как консольный и в него не будут включены пути на заголовочные файлы классов GUI. Это приведт к тому, что при компиляции будут выведены ошибки о том, что включенные заголовочные файлы не найдены. Следует иметь в виду, что если вы имеете дело с Qt SDK версии 5, то данное определение должно еще включать группу widgets
, как показано ниже. QT += gui widgets
Откройте QtCreator. Для создания нового проекта запустим мастер создания проекта из меню "File->New File or Project …". В открывшемся окне первой страницы мастера предлагается выбрать шаблон будущего проекта. Для группы проектов "Application" выберем вариант "Qt GUI Application" и щелкнем по кнопке "Choose" для перехода на следующую страницу мастера. На второй странице мастера создания проекта предлагается выбрать имя проекта и директорию его размещения. По указанному имени проекта будет создана поддиректория в которой будут размещены файлы проекта. Создание поддиректории будет выполнено в указанной директории размещения. Таким образом, имя проекта должно определяться правилами которым должно подчиняться имя директории. Однако, чтобы избежать проблем, не используйте русских букв и пробелов. Используйте английские буквы, цифры и символы подчеркивания и тире (знак минуса). Пусть наш проект будет называться app1
. Запишем его в строку name
, а в строке выбора директории проекта укажем директорию, где и в дальшейшем будем создавать проекты на Qt. Следует избегать путей с русскими буквами и пробелами. Рано или поздно, они могут вызвать проблемы. Если необходимо запомнить этот путь для следующего раза, установите флажок "Use as default project location". Щелкнем по кнопке "Next" для перехода на следующую страницу мастера. На третьей странице мастера создания проекта предлагается выбрать Qt SDK из списка найденных и зарегистрированных в QtCreator. Выберите вариант Qt4. Для выбранной версии SDK, следует определить профили сборки проекта. Предлагаются варианты "Release" и "Debug" Сборка "Release" не содержит отладочных символов в исполняемом файле и рекомендуется для передаче в реальное использование. Во всех остальных случаях, удобнее использовать сборку "Debug". В текущем примере, выбор сборки не имеет значения. Можно оставить включенным оба профиля. Справа от названия профиля расположено поле ввода в котором записан путь по которому будет выполняться соответствующая сборка. Часто эти пути редактируют исходя из разных традиций. В нашем примере мы можем оставить эти пути без изменений. Щелкнем по кнопке "Next" для перехода на следующую страницу мастера. На четвертой странице мастера создания проекта предлагается выбрать имя класса для главной формы проекта, базовый класс, от которого следует унаследовать главную форму проекта и имена файлов, где будет размещен интерфейс и реализация создаваемого класса главной формы. Кроме этого, на странице следует указать будет ли использовано визуальное проектирование формы. Так как мы не будем использовать визуальное проектирование, то следует проследить, чтобы флажок "Generate form" был снят. Наиболее интересным на четвертой странице мастера создания проекта является выбор базового класса для создания класса формы. Предлагаются три варианта. Для нашего примера, выберем в качестве базового класса, простейший вариант — QWidget
. Имена для главного класса формы и для файлов, где будет размещаться его интерфейс и реализация можно оставить значениями по-умолчанию. Щелкнем по кнопке "Next" для перехода на следующую страницу мастера. Пятая страница мастера создания проекта позволяет определить связь создаваемого проекта с уже открытыми проектами и указать выбор какой-нибудь доступной системы управления версиями проекта. В текущем примере нас не интересуют данные возможности, поэтому нам следует завершить работу мастера щелкнув кнопку "Finish". В разных версиях QtCreator шаги по созданию проекта могут несколько отличаться, но, по сути, здесь изложены главные моменты, которые следует понимать при создании GUI-проекта. Текущее описание делалось на основе версии QtCreator 2.7.0. После выполнения мастера создания проекта в QtCreator откроется вид с созданным проектом. Выполните запуск проекта, чтобы проверить настройки среды разработки. Если все в порядке, то созданный шаблон проекта должен откомпилироваться и запуститься. Для обычного запуска проекта можно воспользоваться щелчком по кнопке с изображением зеленого треугольника, размещенной на панели инструментов. Кнопка с изображением зеленого треугольника с жуком выполняет функции запуска в режиме отладке. В этом случае, если в проекте установлены точки останова, то при их достижении, выполнение запущенного приложения остановится и в редакторе отладке подсветится строка на которой был выполнен останов. QtCreator заметно отличается своим дизайном от других сред разработки. Однако, его дизайн удивительно эргономичен. Кроме прочего, в эпоху широких экранов, очень выгодно смотрится панель инструментов расположенная слева в главном окне среды разработки. В нижней части панели инструментов, как уже говорилось, расположены кнопки запуска приложения, а в верхней — кнопки выбора вкладок для главного окна среды разработки. Рассмотрим список основных вкладок. Возьмем проект, который мы создали в предыдущем разделе с помощью мастера среды разработки QtCreator. Созданный проект включает в себя следующие файлы. Возможно, имена файлов в вашем проекте несколько отличаются. Это может быть либо потому, что вы их явно указали другими, при создании проекта, либо значения по умолчанию для вашей версии QtCreator иные. Откроем файл реализации главного окна приложения — widget.cpp
. Изменим его до состояния представленного в следующем примере. Помните, что номера строк указаны только для удобства комментариев. 01. #include В строках 01-03 выполняется включение файлов с интерфесами следующих классов виджетов. Тело конструктора класса Widget
содержит две строки настройки атрибутов окна (строки 10-11) и 8 строк создания и размещения на поле окна трех других виджетов (строки 13-20). Настройка атрибутов окна состоит из команду установки имени окна приложения и минимального размера окна приложения. Для задания минимального размера использован метод, принимающий значения ширины и высоты окна в пикселях. Строка 13 содержит создание экземпляра класса QLabel. Объект создается динамически, через оператор new. Для создания объекта используется конструктор, первым параметром которого указывается строка, которую должен изображать создаваемый объект. Вторым параметром данного конструктора следует указать адрес объекта, который станет владельцем создаваемого объекта метки. В качестве адреса владельца указано значение this
. По правилам языка C++, this
является указателем на объект внутри которого он используется. Т.е., в данном контексте, это указатель на созданный экземпляр класса Widget
. Таким образом, в строке 13 создается объект класса метки, который должен изображать указанный текст и владельцем которого назначается текущий объект. Здесь пришло время рассказать о цепях владения, которые реализованы в системе классов Qt для решения проблемы уничтожения объектов для предотвращения случайных утечек памяти. Следует вспомнить, что объекты созданные динамически, т.е. с помощью оператора new
, размещаются в специальной области памяти, называемой кучей (heap)
и которые живут в куче до тех пор пока не будут явно уничтожены оператором delete
. Если программист не отслеживает уничтожение объектов, которые стали ненужными и не вызывает для их уничтожения оператор delete
, то это становится причиной утечки памяти в приложении, которая является серьезной проблемой для ряда языков программирования в группу которых входит язык C++. Существует несколько известных схем автоматического слежения за уничтожением динамически созданных объектов. Один из них заключается в использовании умных указателей, о которых, следует рассказать позже. Другой способ заключается в создании цепей владения, о котором мы сейчас расскажем. Третьим способом является создание подсистемы уборки мусора, которая должна отлеживать ненужные объекты и уничтожать их. Последний способ, традиционный для ядер многих современных языков, в C++ практически не используется. Гораздо более популярными в традициях языка C++ являются первые два способа. Итак, цепи владения реализуют следующую простую идею. Создается некоторый объект, за уничтожением которого мы обязуемся следить. Проще всего создать такой объект статическим определением и тогда он уничтожится автоматически, когда исполнение программы достигнет конца операторного блока { … } в котором он был определен. Далее, при динамическом создании других объектов будем назначать для них объекты владельцев. В обязанности владельцев будет входить уничтожении владеемых объектов в теле собственного деструктора. Напомним, что деструктор — это особый метод, который вызывается при уничтожении объекта. Таким образом, можно построить такую цепь владения относительно первого объекта, все элементы которой будут автоматически уничтожены при уничтожении первого объекта. При организации такой схемы следует лишь правильно учитывать время жизни объекта, который назначается владельцем другого объекта, чтобы объекты не были уничтожены преждевременно. Именно такая схема владения реализована в системе классов Qt. При создании многих классов из этой библиотеки можно воспользоваться конструктором, который единственным или последним параметром принимает указатель на объект, который назначается владельцем к создаваемому объекту. Параметр этот описывается как параметр со значением по умолчанию, которое определяется как ноль. Таким образом, если адрес владельца не указывается, то параметр получает значение ноль и схема владения для такого объекта отключается. В этом случае следует помнить о явном уничтожении такого объекта. При реализации схем цепей владения, в некоторых библиотеках используется параметр с именем owner
, что с английского языка переводится как владелец
. Однако в библиотеке Qt такой параметр называется parent
, что переводится с английского как родитель
. В результате этого, у некоторых новичков возникает недопонимание образованное тем, что понятие "родитель" традиционо относится к цепям наследования в ООП, однако цепи наследования и цепи владения не имеют ничего общего. Будьте внимательны и не становитесь жертвой заблуждения в этом вопросе. Вернемся еще раз к строке 13. Там мы создали объект владельцем которого назначен текущий объект главного окна приложения. Переменная plb
, которая хранит адрес созданного объекта будет уничтожена автоматически при достижении конца кода конструктора. Однако объект который был распределен в памяти останется жить и далее, и будет жить до тех пор, пока объект главного окна приложения не будет уничтожен. При уничтожении объекта главного окна будут автоматически уничтожены все объекты, которыми объект окна владеет. В строке 14 мы обращаемся к методу задания атрибутов геометрии, которые определяют размещение настраиваемого объекта относительно своего владельца. Первым и вторым значением указываются горизонтальная и вертикальная координата для верхнего левого угла объекта. Третьим и четвертым значением указывается ширина и высота настраиваемого объекта. Если вы добились того, что пример, созданный в предыдущем разделе собрался и запустился без ошибок, то должен выполниться и этот пример, который является расширением предыдущего. Используя данное приложение можно поэкспериментировать с параметрами запуска приложения. Проще всего это сделать в консоли. Зайдите в директорию сборки приложения и выполните следующие варианты запуска приложения с параметрами. В каждом из запусков должна быть заметна разница в стиле прорисовки виджетов.
$ ./app1 -style=motif
$ ./app1 -style=windows
$ ./app1 -style=platinum
В данном примере, исполняемый файл приложения указан именем app1
. Возможно, что в вашем случае, исполняемый файл имеет другое имя. В операционной системе Windows, исполняемые файлы имеют расширение exe
. Кроме того, в операционной системе Windows, выполнить запуск исполняемого файла из текущей директории можно без указания относительного пути, т.е. без указания (./
) — символ точки является синонимом текущей директории, а символ прямого слеша — символом разделителя в записи файловых путей. Также, следует иметь в виду, что символ доллара — это символ стандартного приглашения в консоли *nix для обычного пользователя и набирать его частью команды не нужно. В консоли Windows, символом приглашения обычно является символ угловой скобки (>
). Параметры запуска, также можно указать при запуске приложения из среды разработки QtCreator. Для этого щелкните на панели инструментов слева по значку Projects
. Откроется соответствующая вкладка с настройками. В верхней части окна можно видеть иерархическую систему вкладок. Вкладки самого верхнего уровня определяют проект, так как в среде разработки могут быть открыты несколько проектов. Следующий уровень вкладок, кроме прочего, должен содержать вкладку Build&Run
, которая нам нужна. В этой вкладке далее следует выбор версии Qt SDK, для случая, если вы делаете сборку проекта сразу под несколько версий. Если вы собираете проект под одну версию, то выбор будет состоять из одного элемента. Внутри виджета выбора версии Qt SDK, расположены две стилизованные кнопки с закругленными краями — кнопки Build
и Run
. Щелкните по кнопке Run
, чтобы выбрать соответствующую группу настроек. Там, в одноименной группе параметров Run
вы найдете однострочное поле ввода напротив метки Arguments
. Это и есть конечная цель нашего выбора. Запишем туда следующую строку. Style=motif
Запустите приложение. Потом попробуйте другие значения: windows
и platinum
. Напомним, что объект класса QApplication
поддерживает список из пары десятков параметров запуска, о которых можно прочитать в справке по соответствующим конструкторам класса. Работая над примером, познакомьтесь со справочной информацией по используемым в примере классам. Попробуйте добавить на форму другие экземпляры классов метки, однострочного поля ввода и кнопки. Кроме этого, попробуйте познакомиться со справкой и добавить на форму объекты следующих классов. В заключении урока следует заметить, что используемый способ "жесткого" размещения объектов на поле формы, через явное указание геометрии размещения не является традиционным и рекомендуемым в библиотеке классов Qt. В следующих уроках будут рассмотрены менеджеры компоновки, которые являются современными и удобными средствами размещения виджетов на форме. Программирование с Qt Существуют версии 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 или другой совместимой
графической подсистемы. Нам потребуется установить среду разработки Qt. Программное обеспечение
распространяется на условиях свободной лицензии GPL 3.0 или LGPL 2.1. Его
можно получить по адресу http://www.qtsoftware.com/downloads (EN). В репозиториях популярных дистрибутивов 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
выполните следующие команды: Прежде чем собирать Qt, запустите скрипт configure . Полный
набор его опций выдается по команде./configure -help , но
обычно можно использовать типовые настройки. Параметр -prefix задает каталог для установки (по умолчанию
используется /usr/local/Trolltech/Qt-4.5.0). Также имеются
ключи для инсталляции различных компонентов (исполняемых файлов,
библиотек, документации, и т.д.) в разные директории. При запуске скрипт требует подтвердить согласие пользователя с условиями
лицензии GPL / LGPL. После выполнения можно запустить сборку и установку при помощи команд: Имейте в виду, что компиляция занимает много времени, а для установки Qt
могут потребоваться права суперпользователя (файлы записываются в
/usr/local/). Если в дальнейшем вам понадобится в той же директории заново
сконфигурировать и пересобрать Qt, удалите все следы предыдущей
конфигурации при помощи make confclean , прежде чем снова
запускать./configure . Путь к исполняемым файлам Qt нужно добавить в переменную окружения PATH. В
оболочках bash, ksh, zsh и sh это можно сделать, дописав в файл
~/.profile следующие строки: В csh и tcsh нужно дописать в ~/.login строку: Если вы используете другую оболочку, то обратитесь к соответствующим
разделам документации. Кроме того, необходимо добавить строку
/usr/local/Trolltech/Qt-4.5.0/lib в переменную
LD_LIBRARY_PATH , если компилятор не поддерживает RPATH. Мы
используем GNU/Linux и GCC (EN), поэтому
пропускаем этот шаг. Затем с помощью утилиты qtdemo запустите демонстрационные
приложения для проверки работоспособности установленного инструментария. Недавно появилась кросс-платформенная среда разработки Qt Creator. На сайте
Qt Software можно найти полный SDK, включающий IDE (помимо библиотек и
основных средств разработчика). Загрузите бинарный файл
qt-sdk-linux-x86-opensource-xxx.bin и запустите мастер
установки: Если не собираетесь устанавливать SDK в домашнюю директорию, то запускайте
инсталлятор с правами суперпользователя. В состав Qt включены инструменты разработчика с графическим или консольным
интерфейсом. В их числе: Утилита qmake используется для автоматического генерирования
Makefile на различных платформах. В целом qmake ориентируется на Qt. Если вас интересуют
кросс-платформенные системы сборки более широкого назначения, то можете
обратиться к CMake, которая также поддерживает Qt. Новичкам стоит остановиться на qmake. Полную документацию по этой утилите вы можете найти в Qt Assistant. Также с
Qt поставляются страницы руководства, в том числе qmake(1)
(наберите в командной строке man qmake). Здесь мы приведем
основные указания, которые помогут вам собирать код примеров статьи, а
также свои простые проекты. В качестве примера создадим директорию myproject и добавим
туда файлы hello.h, hello.cpp и main.cpp . В
hello.h опишем прототип функции hello(): Реализацию hello() поместим в hello.cpp: Здесь qDebug() используется для вывода отладочной информации.
Ее можно убрать, объявив при компиляции символ
QT_NO_DEBUG_OUTPUT . Также имеется функция
qWarning() , выдающая предупреждения, и qFatal() ,
завершающая работу приложения после вывода сообщения о критической ошибке
в STDERR (то же самое, но без завершения работы, делает
qCritical()). В заголовочном файле Код основного приложения (здесь мы следуем соглашению, по которому
main() помещается в файл main.cpp): Чтобы создать файл проекта, запустите После этого должен появиться файл myproject.pro примерно
такого содержания: Оператор = используется для присвоения значений переменным, += добавляет
новую опцию к переменной, -= удаляет указанную опцию. TEMPLATE = app обозначает, что мы собираем приложение; для
библиотеки используется TEMPLATE = lib . TARGET – имя целевого файла (укажите
TARGET = foobar , чтобы получить исполняемый файл
foobar). DEPENDPATH – директории для поиска при разрешении
зависимостей. INCLUDEPATH – директории с заголовочными файлами. После запуска на основе myproject.pro в GNU/Linux будет создан обычный
Makefile: Опции qmake влияют на содержимое Makefile. Например,
qmake -Wall добавит к флагам компилятора -Wall –
вывод всех предупреждений. По команде make мы получим исполняемый файл
myproject , который выводит на экран строку «Hello,
World!». Эта схема может показаться слишком сложной, но в реальных проектах
qmake берет на себя большую часть работы по сборке (например,
запускает компилятор метаобъектов). Описанных выше инструментов достаточно для разработки приложений. Вы можете
использовать любимый текстовый редактор, например 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" и т.д. В нижней части окна при этом отображается поле с автоматическим
дополнением. Таблица 2. Комбинации клавиш для редактора Qt Creator
Во встроенном редакторе реализовано «умное» дополнение кода, вызываемое
комбинацией клавиш Ctrl+<Пробел> . База символов
составляется на основе заголовочных файлов проекта из
INCLUDEPATH . Для чтения документации в IDE предусмотрен отдельный режим
справки
. Чтобы получить контекстную помощь по классу или методу,
просто передвиньте текстовый курсор к имени и нажмите F1 .
Также полезна клавиша F2 , перемещающая к определению в
заголовочных файлах. Чтобы переключиться из режима справки или отладки в основной режим
редактирования, нажмите Esc . В режиме редактирования
Esc переводит фокус из дополнительных окон (например, вывода
компиляции или контекстной справки) на редактор. Если нажать Esc еще раз,
то дополнительные окна закрываются. Как и qmake , Qt Creator использует файлы в формате.pro , поэтому в IDE легко импортируются старые проекты,
созданные вручную. Также доступен мастер, при помощи которого можно
создать заготовку нового проекта. Сейчас Qt Creator активно разрабатывается, но если вам нужна классическая
IDE для Qt, работающая на различных платформах, то это лучший вариант. В Qt используется CamelCasing
: имена классов выглядят как
MyClassName , а имена методов – как myMethodName . При этом имена всех классов Qt начинаются с Q , например
QObject, QList или QFont . Большинству классов соответствуют заголовочные файлы с тем же именем (без
расширения.h), т.е. нужно использовать: Поэтому в дальнейшем мы не будем отдельно оговаривать, где объявлен тот или
иной класс. Методы для получения и установки свойств
(getter
и
setter
) именуются следующим образом: свойство
fooBar можно получить при помощи метода fooBar()
и установить при помощи setFooBar() . При разработке собственных приложений на Qt стоит придерживаться этого
стиля. Для эффективной работы с классами на стадии выполнения в Qt используется
специальная объектная модель, расширяющая модель C++. В частности,
добавляются следующие возможности: Многие объекты определяются значением сразу нескольких свойств, внутренними
состояниями и связями с другими объектами. Они представляют собой
индивидуальные сущности, и для них не имеет смысла операция буквального
копирования, а также разделение данных в памяти. В Qt эти объекты
наследуют свойства QObject . В тех случаях, когда объект требовалось бы рассматривать не как сущность, а
как значение (например, при хранении в контейнере) – используются
указатели. Иногда указатель на объект, наследуемый от
QObject , называют просто объектом. Инструментарий спроектирован так, что для QObject и всех его
потомков конструктор копирования и оператор присваивания недоступны – они
объявлены в разделе private через макрос
Q_DISABLE_COPY() : Будьте внимательны и не используйте конструкцию Foo bar = Foo (baz); Другие объекты (например, контейнеры и строки) полностью определяются
представляемыми данными, поэтому в соответствующих классах имеются
операция присваивания и конструктор копирования. Кроме того, объекты,
представляющие одинаковые данные, могут прозрачно для программиста
разделять их в памяти. Часть расширений реализована стандартными методами 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: Для динамического приведения QObject используется функция T qobject_cast (QObject *object); Она работает как стандартная операция dynamic_cast в C++, но
не требует поддержки со стороны системы динамической идентификации типов
(RTTI). Пусть у нас имеется класс MyClass1 , наследующий от
QObject и MyClass2 , наследующий от
MyClass1: Динамическое приведение иллюстрирует следующий код: Эти операции сработают корректно на стадии выполнения. Как и в случае с dynamic_cast , результат приведения можно
проверить: Система метаобъектов позволяет также проверить, наследует ли a класс
MyClass1: Однако предпочтителен предыдущий вариант с qobject_cast . Объекты классов, наследующих от QObject , могут быть
организованы в древовидную структуру. При удалении объекта Qt удаляет его
дочерние объекты
, которые в свою очередь удаляют свои
дочерние объекты, и т.д. Иными словами, удаление объекта приводит к
удалению всего поддерева, корнем которого он является. Пусть у нас имеются классы ClassA и ClassB: Здесь родительский объект устанавливается в конструкторе
QObject: QObject::QObject (QObject *parent = 0); Его можно установить в последующем при помощи метода
setParent() и получить при помощи parent() : void QObject::setParent (QObject *parent); Если создать в стеке по одному из объектов A и B, то сначала будет создан
A, потом B. В соответствии со стандартом C++, удаление происходит в
обратном порядке – сначала B, затем A: Если создать B в куче и назначить его дочерним объектом для A, то вместе с
A автоматически удалится B: Аналогично для более сложных деревьев: После удаления A удалится всё дерево. Таким образом, программист должен создавать объекты в куче и задавать
соответствующие иерархии, а заботу об управлении памятью Qt берет на себя. Если объект и его дочерние объекты созданы в стеке, то подобный порядок
удаления может привести к ошибкам. Здесь при выходе из области действия сначала будет удален объект A, так как
он был создан последним. При этом Qt удалит и его дочерний объект B. Но
потом будет сделана попытка удаления B, что приведет к ошибке. В другом случае проблем не будет, потому что при вызове деструктора
QObject объект удаляет себя из списка дочерних объектов
родительского объекта: Вообще говоря, дочерние объекты должны размещаться в куче. У каждого QObject есть свойство objectName , для
доступа к которому используются методы По умолчанию objectName – пустая строка. Через это свойство
объектам в дереве можно присвоить имена для последующего поиска. const QList – возвращает список дочерних объектов. T findChild (const QString& name = QString()) const; – возвращает дочерний объект с именем name
, который можно привести
к типу T, либо 0, если такой объект не найден. Без аргумента name
возвращает все дочерние объекты. Поиск осуществляется рекурсивно. QList – возвращает все дочерние объекты с именем name
, которые можно
привести к типу T
, либо пустой список, если таких объектов не
найдено. Без аргумента name
возвращает все дочерние объекты.
Поиск осуществляется рекурсивно. QList – аналогично, но с поиском по регулярному выражению regExp
. void dumpObjectTree(); – выводит отладочную информацию о дереве объектов с данным корнем. При создании графических пользовательских интерфейсов взаимодействие
объектов часто осуществляется через обратные вызовы, т.е. передачу кода
для последующего выполнения (в виде указателей на функции, функторов, и
т.д.). Также популярна концепция событий
и обработчиков
,
в которой обработчик действует как перехватчик события определенного
объекта. В Qt вводится концепция сигналов и слотов. Сигнал
отправляется при вызове соответствующего ему метода.
Программисту при этом нужно только указать прототип метода в разделе
signals . Слот
является методом, исполняемым при получении сигнала. Слоты
могут объявляться в разделе pulic slots, protected slots или
private slots . При этом уровень защиты влияет лишь на
возможность вызова слотов в качестве обычных методов, но не на возможность
подключения сигналов к слотам. Модель сигналов и слотов отличается от модели событий и обработчиков тем,
что слот может подключаться к любому числу сигналов, а сигнал может
подключаться к любому числу слотов. При отправке сигнала будут вызваны все
подключенные к нему слоты (порядок вызовов не определен). В качестве типичного примера слота рассмотрим метод получения свойства
(getter
). Методу установки свойства (setter
) при
этом будет соответствовать сигнал. Обратите внимание на макрос Q_OBJECT , сигнализирующий Qt о
том, что используются возможности системы метаобъектов. Ключевое слово 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 . Здесь при помощи 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; . Она нужна,
чтобы избежать проблем при циклических соединениях. Например, следующий
код сработает: QObject::connect возвращает true , если соединение
успешно установлено, и false в противном случае – например,
когда сигнал или слот не обнаружен, либо их сигнатуры несовместимы. Если добавить при помощи QObject::connect одинаковые
соединения, то слот будет вызываться несколько раз. Для отключения сигнала от слота используется
QObject::disconnect: При успешном отключении возвращается 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: При удалении одного из объектов соединения Qt автоматически удаляет само
соединение. У компилятора метаобъектов имеется ряд ограничений, которые
распространяются и на работу с сигналами и слотами. Макросы нельзя использовать в любых участках кода,
которые должны обрабатываться moc . В частности, через
них нельзя указать базовый класс. Однако некоторые
возможности препроцессора использовать можно. Доступны простые
условные конструкции (с директивами
#if, #ifdef, #ifndef, #else, #elif, #endif , а также
специальным оператором defined). Для создания
объявлений у moc имеется опция командной строки
-D . Утилита qmake передает
moc все объявления, перечисленные в параметре проекта
DEFINES . Например, moc правильно
обработает также будет обработан, только если вызвать moc -DFOO ,
либо если до него имеется строка
#define FOO . Типы должны быть указаны полностью, так как
QObject::connect() сравнивает их буквально. В
частности, если внутри класса Foo определяется
перечисление Bar , то в аргументах сигнала нужно
указывать Foo::Bar: В качестве параметров сигналов и слотов нельзя использовать
указатели на функции. Например, не является допустимым аргументом. Можно использовать typedef: Обычно вместо указателей лучше применять наследование и виртуальные
функции. Класс, наследующий QObject , может содержать объявление
свойства при помощи макроса Q_PROPERTY() : Q_PROPERTY(type name
READ getFunction
Обязательные параметры макроса: Не обязательные параметры макроса: Методы могут быть виртуальными либо унаследованными от базового класса. При
множественном наследовании они должны принадлежать первому классу в
списке. Для не обязательных атрибутов
DESIGNABLE, SCRIPTABLE, STORED, USER допускается указание
булевых значений: Например, 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
setMinimumHeight STORED false DESIGNABLE false) Свойство minimumSize имеет тип QSize и может быть
получено при помощи QSize minimumSize() const и установлено
при помощи void setMinimumSize
(const QSize&). minimumWidth и
minimumHeight вычисляются через minimumSize ,
поэтому для них указано STORED false . Пример свойства с атрибутом USER – text в QLineEdit: Для считывания и записи свойства используются методы: property() возвращает значение свойства либо неправильный
вариант
QVariant , если такого свойства нет. setProperty() возвращает true , если у объекта
есть указанное свойство с типом, совместимым с переданным значением. В
противном случае возвращается false . Если в классе нет указанного свойства, то добавляется динамическое
свойство
объекта. Перечень динамических свойств можно получить
при помощи Рассмотрим пример использования свойств. Пусть класс MyClass
имеет строковое свойство text (типа QString): Работа со свойством: Программа должна вывести на экран следующее: Разумеется, безопаснее и быстрее вызывать методы конкретного класса для
считывания и записи свойств. property() и
setProperty() нужны в том случае, когда о классе ничего не
известно кроме имен и типов свойств. Если для класса не известен даже перечень свойств и методов, можно
использовать метаобъект. Метаобъект возвращается методом QObject::metaObject() С его помощью можно динамически получить информацию о классе, как,
например, в Java Reflecion API (EN). Имя класса возвращает const char * QMetaObject::className() const; Указатель на метаобъект базового класса – const QMetaObject* superClass() const; Через систему метаобъектов доступны только те методы и конструкторы, перед
объявлениями которых указан макрос Q_INVOKABLE: Для доступа к методам (в том числе сигналам и слотам) используйте Методы и свойства класса проиндексированы. Доступ к методу по индексу
осуществляется через QMetaObject::method() . Общее число методов, с учетом наследованных, возвращает
QMetaObject::methodCount() . Смещение
методов класса
возвращается QMetaObject::methodOffset() , оно показывает, с
какого индекса начинаются методы данного класса. Смещение увеличивается
при наследовании и показывает число методов базовых классов. Пример прохода по методам: Если бы мы начали с индекса 0, то получили бы методы всех базовых классов,
в том числе QObject: Методы, начинающиеся с _q_ , используются внутри Qt и не
являются частью API. Конструкторы указываются отдельно: Например, получим перечень конструкторов QObject: Результат: Индекс метода, сигнала, слота или конструктора можно получить по его
сигнатуре: Для конструкторов, методов или сигналов ожидаются нормализованные
сигнатуры
. Их можно получить при помощи статического метода Например, возвращает " int*foo(QString,QObject*) ". Аналогично работает Это текстовое приведение к каноническому виду, используемое, в частности,
при проверке совместимости сигнала и слота. Аналогично можно работать со свойствами. (Если просмотреть все свойства, включая наследованные, то вы увидите, по
меньшей мере, objectName из QObject .) Индекс свойства можно получить по его имени: Перечисления регистрируются в классе при помощи макроса
Q_ENUMS() . Перечисление, значения которого можно комбинировать при помощи побитового
ИЛИ, называется флагом
и должно регистрироваться при помощи
Q_FLAGS() . Флаги используются следующим образом: Динамическая работа с перечислениями: Результат работы: Индекс перечисления можно получить по его имени: При помощи макроса Q_CLASSINFO() к метаобъекту можно добавлять
пары имя–значение. Например, Эти пары наследуются, и их можно получить из метаобъекта по той же схеме: Для примера выше: Результат: Индекс CLASSINFO можно получить по его имени: Передача аргументов осуществляется через объекты
QGenericArgument и QGenericReturnArgument . Они
создаются макросами Q_ARG и Q_RETURN_ARG . Пример использования: Для создания нового экземпляра класса используется метод метаобъекта
newInstance() , которому можно передать до 10 аргументов. В случае ошибки возвращается 0. Для вызова метода используется invokeMethod() : При асинхронном вызове значение не может быть вычислено. Имеются перегруженные версии invokeMethod() . Если вы не
укажете тип вызова, то будет использоваться
Qt::AutoConnection . Если вы не укажете возвращаемое значение,
то оно будет проигнорировано. Те же возможности предоставляет класс QMetaMethod: Точно так же, тип соединения и/или возвращаемое значение можно не
указывать. Асинхронный вызов используется в том случае, когда вычисления занимают
слишком много времени, поэтому их результат не ожидается в точке вызова.
Подобные вычисления обычно помещают в отдельный поток, поэтому по
умолчанию (Qt::AutoConnection) методы объектов из внешних
потоков вызываются асинхронно. Рассмотрим следующий класс: Обращение к конструктору и методам: text() и setText() вызываются таким образом лишь
в качестве простого примера работы с
QMetaObject::invokeMethod() . Как вы уже знаете, эти два
метода должны быть связаны со свойством. Также обратите внимание, что text() возвращает
QString , но не const QString& . Иначе бы
система метаобъектов считала, что text() возвращает
void . Для эффективной работы с классами на стадии выполнения Qt использует
специальную объектную модель, в которой при помощи наследования от
QObject и генерирования кода компилятором метаобъектов
реализованы: В следующей статье мы рассмотрим типы, варианты, ссылки и разделение
данных. Qt
— это кроссплатформенный фреймворк
для разработки ПО на языке программирования C++
(и не только). Также имеется и для Ruby — QtRuby
, для Python — PyQt
, PHP — PHP-Qt
и других языков программирования. Разрабатывается компанией Trolltech
с 1996 года. С использованием этого фреймворка написано множество популярных программ: 2ГИС для Android, Kaspersky Internet Security, Virtual Box, Skype, VLC Media Player, Opera и другие. KDE
— это одно из окружений рабочего стола со множеством программ для Linux написано с использованием фреймворка Qt. Qt
полностью объектно-ориентированная, кросс-платформенная. Дает возможность разрабатывать платформо-независимое ПО, написанный код можно компилировать для Linux, Windows, Mac OS X
и других операционных систем. Включает в себя множество классов для работы с сетью, базами данных, классы-контейнеры, а также для создания графического интерфейса и множество других(чуть ниже). Qt
использует MOC
(Meta Object Compiler) для предварительной компиляции программ. Исходный текст программы обрабатывается MOC, который ищет в классах программы макрос Q_OBJECT и переводит исходный код в мета-объектный код, после чего мета-объектный код компилируется компилятором C++. MOC расширяет функциональность фреймворка, благодаря ему добавляются такие понятия, как слоты и сигналы. В Qt
имеется огромный набор виджетов (Widget), таких как: кнопки, прогресс бары, переключатели, checkbox, и другие — они обеспечивают стандартную функциональность GUI (графический интерфейс пользователя). Позволяет использовать весь функционал пользовательского интерфейса — меню, контекстные меню, drag&drop. Как видно на картинке, программы имеют родной внешний вид в различных операционных системах. Qt
имеет среду разработки Qt Creator
. Она включает в себя Qt Designer
, с помощью которого можно создавать графический интерфейс. Визуальное создание интерфейса позволяет легко и просто создавать интерфейс, перетаскивая различные виджеты(выпадающие списки, кнопки, переключатели) на форму. Qt
поставляется вместе с Qt Assistant
— это огромный интерактивный справочник, содержащий в себе информацию по работе с Qt
. К сожалению полностью не переведен на русский. В состав Qt
также входит Qt Linguist
, которая позволяет локализировать приложение для разных языков. Библиотека Qt
состоит из различных модулей, которые подключаются при помощи директивы #include
. В состав входят: QtCore — классы ядра библиотеки Qt, они используются другими модулями. QtGui — модуль содержит компоненты графического интерфейса. QtNetwork — модуль содержит классы для работы с сетью. В него входят классы для работы с протоколами FTP, HTPP, IP и другими. QtOpenGL — модуль содержит классы для работы с OpenGL QtSql — содержит классы для работы с различными базами данных с использованием языка SQL. QtSvg — содержит классы, позволяющие работать с данными Scalable Vector Graphics (SVG) QtXml — классы для работы с XML QtScript — классы для работы с Qt Scripts Имеются и другие модули. В данный момент Qt
распрастраняется по 3-м лицензиям: Qt Commercial(собственическая), GNU GPL, GNU LGPL. В настоящее время Qt фреймворк
активно развивается. Имеет интуитивно понятное API, огромную документацию с большим количеством примеров, мощнейшую среду разработки QtCreator
и дополнительный инструментарий. Этот фреймворк стоит Вашего внимания. и
в одной графической области.
на интервале [-5;5].
0
4
10
15
21
29
36
51
68
66.7
71.0
76.3
В первой строке файла хранится количество экспериментальных точек, в следующих двух строках - массивы абсцисс и ординат экспериментальных точек. В четвёртой строке хранится количество (3) и точки (25, 32, 45), в которых необходимо вычислить ожидаемое значение. Пример создания шаблона GUI-приложения из QtCreator
Простой пример размещения виджетов в окне приложения
Часть 1. Введение. Инструменты разработчика и объектная модель
Серия контента:
1. Введение
2. Установка Qt
2.1. Базовые библиотеки и инструменты
2.2. SDK
3. Инструменты разработчика
3.1. qmake
Листинг 1.1. Объявления функций программы
«Hello, World!»
// hello.h
void hello();
Листинг 1.2. Реализации функций программы «Hello,
World!»
// hello.cpp
#include Листинг 1.3. Функция main() программы «Hello, World!»
// main.cpp
#include "hello.h"
int main()
{
hello();
return 0;
}
####################################
# Automatically generated by qmake
####################################
TEMPLATE = app
TARGET =
DEPENDPATH += .
INCLUDEPATH += .
# Input
HEADERS += hello.h
SOURCES += hello.cpp main.cpp
####### 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:
3.2. Qt Creator
Рисунок 5. Поле для навигации в Qt
Creator
Ctrl+[
Перейти к началу
блока
Ctrl+]
Перейти к концу
блока
Ctrl+U
Выделить блок
Ctrl+Shift+U
Снять
выделение блока
Ctrl+I
Выровнять блок
Ctrl+<
Свернуть блок
Ctrl+>
Развернуть блок
Ctrl+/
Закомментировать
блок
Ctrl+Shift+
Переместить
строку вверх
Ctrl+Shift+↓
Переместить
строку вниз
hift+Del
SУдалить строку
4. Стиль Qt
5. Объектная модель
5.1. Система метаобъектов
5.2. qobject_cast
5.3. Деревья объектов
Листинг 2.1. Объявление MyClass для программы,
демонстрирующей порядок создания и удаления объектов
// myclass.h
#include Листинг 2.2. Определение методов MyClass для
программы, демонстрирующей порядок создания и удаления объектов
// myclass.cpp
#include
QObject* QObject::parent() const;Листинг 2.3. Создание экземпляров MyClass в стеке
// main.cpp
#include "myclass.h"
int main()
{
MyClass a ("A");
MyClass b ("B");
return 0;
}
Листинг 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 = new MyClass ("B", &a);
MyClass *c = new MyClass ("C", &a);
MyClass *d = new MyClass ("D", c);
MyClass *e = new MyClass ("E", c);
return 0;
}
5.4. Сигналы и слоты
5.4.1. Объявление сигналов и слотов,
отправка сигналов
Листинг 3.1. Класс MyClass со слотом void setValue
(int x) и сигналом void valueChanged (int x)
// myclass.h
#include Листинг 3.2. Реализация методов класса MyClass со
слотом void setValue (int x) и сигналом void valueChanged (int x)
// myclass.cpp
#include 5.4.2. Подключение сигнала к слоту
Листинг 3.3. Подключение сигнала void
MyClass::valueChanged (int x) к слоту void MyClass::setValue (int x)
// main.cpp
#include Листинг 3.4. Циклическое соединение сигналов void
MyClass::valueChanged (int x) со слотами void MyClass::setValue (int
x)
// main.cpp
#include 5.4.3. Отключение
5.4.4. Ограничения
Блок #ifdef FOO
// ...
#endif
5.5. Свойства
Листинг 4.1. Объявление класса MyClass со свойством
text
// myclass.h
#include Листинг 4.2. Определение методов класса MyClass со
свойством text
// myclass.cpp
#include Листинг 4.3. Работа со свойством text объекта MyClass
// main.cpp
#include 5.6. Работа с метаобъектами
5.6.1. Основная информация
5.6.2. Методы
Листинг 5. Вывод конструкторов QObject через систему
метаобъектов
#include 5.6.3. Свойства
5.6.4. Перечисления
Листинг 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
};
// ...
};
Листинг 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();
}
}
}
5.6.5. CLASSINFO
Листинг 7.1. Класс MyClass с CLASSINFO
class MyClass
{
Q_OBJECT
Q_CLASSINFO("author", "Bob Dobbs")
Q_CLASSINFO("version", "0.23")
// ...
};
Листинг 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();
}
5.6.6. Вызов конструкторов и методов
Листинг 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Заключение
Состав библиотеки Qt
Заключение
Для вас это может быть интересно: