Создание графических интерфейсов пользователя с использованием Qt 5.12 (1245345), страница 7
Текст из файла (страница 7)
Размещение виджетов-редакторов в окне вручную.#include <QApplication>#include <QWidget>#include <QLineEdit>int main(int argc, char *argv[]){QApplication app(argc, argv);QWidget window;window.setWindowTitle("Main Window");window.setObjectName("window");QLineEdit *edit1=new QLineEdit("Edit1",&window);QLineEdit *edit2=new QLineEdit("Edit2",&window);edit1->setGeometry(20,20,60,60);edit2->setGeometry(120,20,60,60);window.show();return app.exec();}В результате на экране появляется окно, содержащее оба однострочных редактора(рисунок 2.1, а).Рисунок 2.1 – Вид окна с двумя однострочными редакторами в нормальном (а) и в свернутом состоянии (б)Однако на рисунке 2.1, б видно, что попытка уменьшения размера окна приводит кнарушению внешнего вида, в результате которого виджеты вообще могут исчезнуть изполя зрения.
Следовательно, при ручной компоновке пришлось бы программировать, какдолжен изменяться внешний вид окна при изменении его размеров.В отличие от ручного варианта при компоновке с использованием менеджеров компоновки осуществляется автоматическая перестройка внешнего вида окна в зависимостиот его размеров.В Qt предусмотрены следующие элементы компоновки: QVBoxLayout – вертикальный компоновщик – управляет расположением виджетов в окне по вертикали; QHBoxLayout – горизонтальный компоновщик – управляет расположениемвиджетов в окне по горизонтали; QGridLayout – табличный компоновщик – управляет расположением виджетовв направляющей двумерной сетке – матрице или таблице.Пример 2.3. Автоматическая компоновка виджетов в окне#include#include#include#include<QApplication><QWidget><QLineEdit><QHBoxLayout>int main(int argc, char *argv[]){QApplication app(argc, argv);QWidget window;window.setWindowTitle("Main Window");window.setObjectName("window");QLineEdit *edit1=new QLineEdit("Edit1",&window);QLineEdit *edit2=new QLineEdit("Edit2",&window);QHBoxLayout *layout = new QHBoxLayout; // выравнивание по// горизонталиlayout->setContentsMargins(5,5,5,5);// внешние поля окнаlayout->setSpacing(5);// просвет между виджетамиwindow.setLayout(layout); // связывание layout с виджетом окна// задание порядка следования элементовlayout->addWidget(edit1);layout->addWidget(edit2);window.show();return app.exec();}В результате работы приложения получаем интерфейс, который при изменении размеров окна сохраняет пропорции (рисунок 2.2, а-б).Рисунок 2.2 – Внешний вид интерфейса при автоматической компоновке виджетов:а – исходное окно; б – окно после уменьшения размеров; в – окно при смене типа компоновщикаЗамена элемента горизонтальной компоновки на вертикальную приводит к тому, чтоокошки редакторов размещаются вертикально, один над другим (рисунок 2.2, в).QVBoxLayout *layout = new QVBoxLayout;Табличная компоновка предполагает задание координат размещения компонентов сточностью до клетки.
При этом допускается размещать виджет в нескольких клетках. Длядобавления виджетов в менеджер компоновки используют специальный метод, позволяющий указать область таблицы, которую должен занимать элемент:QGridLayout::addWidget(QWidget *widget, // размещаемый виджетint fromRow, int fromColumn, // координаты верхней левой ячейкиint rowSpan, int columnSpan, // количество ячеек по горизонтали и// вертикали соответственноQt::Alignment alignment=0); // спороб выравниванияПример 2.4.
Применение табличного компоновщика.#include <QApplication>#include <QWidget>#include <QLineEdit>#include <QGridLayout>int main(int argc, char *argv[]){QApplication app(argc, argv);QWidget window;window.setWindowTitle("Main Window");window.setObjectName("window");QLineEdit *edit1=new QLineEdit("Edit1",&window);QLineEdit *edit2=new QLineEdit("Edit2",&window);QLineEdit *edit3=new QLineEdit("Edit3",&window);QGridLayout *layout = new QGridLayout; // выравнивание по сеткеlayout->setContentsMargins(5,5,5,5); // устанавливаем внешние поляlayout->setSpacing(5); // устанавливаем интервал между виджетамиwindow.setLayout(layout); // связываем layout с виджетом окнаlayout->addWidget(edit1,0,0,1,2);layout->addWidget(edit2,1,0,1,1);layout->addWidget(edit3,1,1,1,1);window.show();return app.exec();}Результат работы программы показан на рисунке 2.3.Рисунок 2.3 – Результат применения табличной компоновкиДля реализации «поджатия» виджетов одного к другому используют «пружины».Виджеты, поджатые пружиной, при увеличении размеров окна остаются рядом.Для добавления пружины используют метод addStretch класса QBoxLayout:voidQBoxLayout::addStretch(int stretch=0);Пример 2.5.
Применение пружины.…QVBoxLayout *layout = new QVBoxLayout;layout->addWidget(edit1);layout->addWidget(edit2);layout->addStretch();…На рисунке 2.4 показан эффект использования пружины: размер окон редакторов ирасстояние между строчными редакторами минимально и независит от размеров окна.Рисунок 2.4 – Использование «пружины» для поджатия виджетовСледует иметь в виду, что управление размерами виджетов, осуществляемое менеджерами компоновки, регулируется параметрами растяжения и политиками, отдельно задаваемыми по горизонтали и вертикали [1].Разделители. Вместо менеджеров компоновки, которые обычно используют политики пропорционального изменения размеров виджетов при изменении размеров форм,можно использовать разделители QSplitter, которые позволяют регулировать размерывиджетов по желанию пользователя.Разделители бывают вертикальными и горизонтальными.
Их применяют в качествеобъекта основы окна или его фрагмента.Пример 2.6. Применение разделителя.#include <QApplication>#include <QSplitter>#include <QLineEdit>int main(int argc, char *argv[]){QApplication app(argc, argv);QSplitter splitter(Qt::Horizontal);splitter.setWindowTitle("Main Window");QLineEdit *edit1=new QLineEdit("Edit1",&splitter);QLineEdit *edit2=new QLineEdit("Edit2",&splitter);splitter.show();return app.exec();}Результат работы программы представлен на рисунке 2.5. Линия между двумяредакторами – ползунок, потянув за который можно изменить соотношение областей,отведенных под каждый редактор.Рисунок 2.5 – Применение разделителяКомпоновщики всех рассмотренных типов могут вкладываться один в другой в соответствии с реализуемой схемой компоновки окна.
Однако при добавлении компоновщика в контейнер другого компоновщика используется не метод addWidget(), а методaddLayout(), например:layout2->addLayout(layout1);Вложение различных видов компоновщиков, пружин и ползунков позволяет реализовать практически любые варианты компоновки окна приложения.2.3 Механизм слотов и сигналовПри использовании средств библиотеки Qt передача сообщений внутри приложенияреализуется механизмом слотов и сигналов. Это – наиболее важный механизм Qt, отличающий его от других библиотек интерфейсных элементов C++, например «родной» библиотеки Visual C++ – MFC.Как уже упоминалось ранее, механизм реализуется метакомпилятором MOC, который генерирует соответствующий код на «чистом» С++.По правилам Qt любой виджет может посылать сигналы другим виджетам, сообщаяим об изменениях, произошедших с ним в процессе функционирования.
Чаще всего причиной формирования сигнала бывают действия пользователя. Например, объекты классаQPushButton посылают приложению сигнал clicked(), когда пользователь щелкаетмышкой по реализуемой объектом класса кнопке. Причинами генерации сигналов могутбыть и достижения каких-либо значений, срабатывания таймеров, действия операционнойсистемы или других приложений.Посредством специального оператора Qt connect каждый сигнал может бытьподключен к одному или нескольким слотам других виджетов.
Тогда каждый раз при получении сигнала в виджетах-адресатах будет активизироваться соответствующий обработчик сигналов – слот или последовательно несколько слотов.Слотом может объявляться любой (перегруженный, виртуальный, общий, защищенный, закрытый) метод, что позволяют подключать его к сигналу. Метод-слот также сохраняет возможность традиционного вызова, не связанного с сигналом.Точно так же, как один сигнал может быть подключен к нескольким слотам, к одному слоту может быть подключено несколько сигналов. В этом случае приложение одинаково реагирует на указанные при подключении сигналы.Соединяемые сигналы и слоты должны иметь идентичные сигнатуры, т.е.
количество и типы входных аргументов. Исключением является случай, когда сигнал имеетбольшее число аргументов, чем слот. В этом случае "лишние" аргументы просто не передаются в слот. Если типы входных аргументов не совместимы или сигнал или слот неопределены, Qt выдаст предупреждение во время выполнения программы. Точно так же Qtотреагирует, если в сигнатуры сигналов или слотов в макросе connect()включены имена аргументов.2.3.1 Создание новых слотов и установка связи сигналов со слотамиПрограммист, применяющий средства Qt, имеет возможность не только использовать предопределенные сигналы и слоты, но и создавать новые.
При этом следует следить,чтобы классы, определяющие новые сигналы и слоты, обязательно включали макросQ_OBJECT, обрабатываемый MOC.Пример 2.7. Приложение «Возведение числа в квадрат». Создание новых слотов.На рисунке 2.6 представлено окно создаваемого приложения в разные моменты времени.
В момент запуска приложения кнопка Следующее не активна, поскольку нажатиена нее бессмысленно. При выдаче результата эта кнопка становится доступной, но строчный редактор, используемый для ввода значения, блокируется, чтобы предотвратить изменение исходных данных и, как следствие, появление на экране результата, не связанного с исходными данными.Ввод будем считать завершенным, если пользователь нажимает клавиши Enter илиTab клавиатуры или щелкает мышью вне поля ввода. При этом строчный редактор теряетфокус ввода. При вводе неправильных (например буквенных) исходных данных приложение должно выдавать сообщение об ошибке в специальном окне.Рисунок 2.6 – Окно приложения: при запуске программы (а), при выдаче результата (б)Создание приложения начинаем с описания класса окна Win.
Этот класс, кроме рамки, меток, строчных редакторов и кнопок должен включать: объект класса QTextCodec – для русификации интерфейса; менеджеры вертикальной и горизонтальной компоновки.Поскольку в качестве сигнала завершения ввода мы собираемся использоватьreturnPressed()– сигнал нажатия клавиши Enter, кроме класса окна нам понадобитсяописать специальный класс-валидатор StrValidator, наследуемый от классаQValidator.Объект этого класса, включающего метод проверки вводимой строки validate(),передается строчному редактору, осуществляющему ввод. При завершении ввода этот метод вызывается автоматически. Если этот метод возвращает Acceptable, то редакторввода генерирует сигналы editingFinished()– завершение редактирования иreturnPressed()– сигнал нажатия клавиши Enter.
В противном случае эти сигналы негенерируются. Метод validate()разрабатываемого нами приложения будет всегдапринимать вводимую строку. Проверка этой строки будет осуществляться позднее.Окончательно получаем следующий файл win.h:#ifndef win_h#define win_h#include <QTextCodec>#include <QFrame>#include <QLabel>#include <QLineEdit>#include <QPushButton>#include <QValidator>class Win:public QWidget// класс окна{Q_OBJECT // макрос Qt, обеспечивающий корректное создание сигналов и слотовprotected:QTextCodec *codec;QFrame *frame;// рамкаQLabel *inputLabel;// метка вводаQLineEdit *inputEdit;// строчный редактор вводаQLabel *outputLabel;// метка выводаQLineEdit *outputEdit;// строчный редактор выводаQPushButton *nextButton; // кнопка СледующееQPushButton *exitButton; // кнопка Выходpublic:Win(QWidget *parent = 0); // конструкторpublic slots:void begin();// метод начальной настройки интерфейсаvoid calc();// метод реализации вычислений};class StrValidator:public QValidator // класс компонента проверки ввода{public:StrValidator(QObject *parent):QValidator(parent){}virtual State validate(QString &str,int &pos)const{return Acceptable; // метод всегда принимает вводимую строку}};#endifКак следует из текста класс окна добавляет к множеству стандартно объявленныхслотов еще два слота – методы начальной настройки и реализации вычислений:public slots:void begin();void calc();// метод начальной настройки интерфейса// метод реализации вычисленийпосле этого указанные методы могут подключаться к сигналам с использованиемоператора Qt connect.Файл реализации win.cpp содержит описание трех методов класса окна.