1 // Перерасчёт геометрии всех элементов при изменении размеров окна 2 3 #include <QtGui>
4 5 class MyDialog : public QDialog { 6 Q_OBJECT 7 8 public: 9 MyDialog();
10 11 protected: 12 virtual void resizeEvent(QResizeEvent *event);
13 14 private: 15 QLabel *lb; // Текстовая метка. 16 QLineEdit *le; // Строковое поле ввода. 17 QComboBox *cb; // Поле ввода с раскрывающимся списком. 18 QSpinBox *sb; // Целочисленное поле ввода с кнопками 19 // инкремента/декремента. 20 QCheckBox *chb; // Независимый переключатель 21 // с двумя состояниями. 22 QDoubleSpinBox *dsb; // Поле ввода вещественного значения 23 // с кнопками инкремента/декремента. 24 QDateTimeEdit *dte; // Поле ввода даты и времени. 25 QGroupBox *gb; // Рамка с надписью вокруг группы элементов. 26 QRadioButton *rb1; // Три 27 QRadioButton *rb2; // зависимых 28 QRadioButton *rb3; // переключателя. 29 QPushButton *btn1; // Кнопка "Сохранить". 30 QPushButton *btn2; // Кнопка "Отменить". 31 };
(5) Объявили класс MyDialog, в качестве базового использовали стандартный класс диалога библиотеки Qt. (9) Конструктор диалога. (15-30) Указатели на все визуальные элементы необходимо сделать членами класса, чтобы иметь к ним доступ из метода resizeEvent.
1 // Перерасчёт геометрии всех элементов при изменении размеров окна 2 3 #include "03.h" 4 5 MyDialog::MyDialog() { 6 7 QTextCodec *codec = QTextCodec::codecForName("CP1251");
8 QTextCodec::setCodecForTr(codec);
9 10 lb = new QLabel(tr("Метка:"), this);
11 12 le = new QLineEdit(tr("Строка"), this);
13 14 cb = new QComboBox(this);
15 cb->
addItem(tr("Первый"));
16 cb->
addItem(tr("Второй"));
17 cb->
addItem(tr("Третий"));
18 cb->
setCurrentIndex(2);
19 cb->
setEditable(true);
20 cb->
setInsertPolicy(QComboBox::InsertAtBottom);
21 22 sb = new QSpinBox(this);
23 sb->
setValue(5);
24 25 chb = new QCheckBox(tr("Пометка"), this);
26 chb->
setCheckState(Qt::Checked);
27 28 dsb = new QDoubleSpinBox(this);
29 dsb->
setMaximum(200.0);
30 dsb->
setDecimals(2);
31 dsb->
setSingleStep(0.25);
32 dsb->
setValue(100.25);
33 34 dte = new QDateTimeEdit( 35 QDateTime(QDate(2007, 2, 5), QTime(13, 35, 55, 10)), 36 this);
37 38 gb = new QGroupBox(tr("Выбрать одно из трёх:"), this);
39 40 rb1 = new QRadioButton(tr("Один"), gb);
41 42 rb2 = new QRadioButton(tr("Два"), gb);
43 44 rb3 = new QRadioButton(tr("Три"), gb);
45 46 rb2->
setChecked(true);
47 48 btn1 = new QPushButton(tr("Сохранить"), this);
49 50 btn2 = new QPushButton(tr("Отменить"), this);
51 52 setMinimumSize(160, 205);
53 } 54 55 void MyDialog::resizeEvent(QResizeEvent* /* event */ ) { 56 int dw = width() - minimumWidth();
57 int dh = height() - minimumHeight();
58 59 lb->
setGeometry(5, 6, 45, 24);
60 le->
setGeometry(55, 6, 100+dw, 24);
61 62 cb->
setGeometry(5, 36, 80+dw*2/3, 24);
63 sb->
setGeometry(95+dw*2/3, 36, 60+dw/3, 24);
64 65 chb->
setGeometry(5, 65, 70, 24);
66 dsb->
setGeometry(95, 65, 60+dw, 24);
67 68 dte->
setGeometry(5, 95, 150+dw, 24);
69 70 gb->
setGeometry(5, 120, 150+dw, 40);
71 rb1->
setGeometry(5, 15, 45+dw/3, 24);
72 rb2->
setGeometry(55+dw/3, 15, 45+dw/3, 24);
73 rb3->
setGeometry(105+dw*2/3, 15, 45+dw/3, 24);
74 75 btn1->
setGeometry(5+dw/4, 170+dh, 70+dw/4, 29);
76 btn2->
setGeometry(85+dw/2, 170+dh, 70+dw/4, 29);
77 } 78 79 int main(int argc, char *argv[]) { 80 81 QApplication app(argc, argv);
82 83 MyDialog *dlg = new MyDialog();
84 dlg->
show();
85 86 return app.exec();
87 }

// Создаём менеджер размещения: 52 QGridLayout *mainlay = new QGridLayout();
// Размер полей вокруг сетки элементов и // интервалы между ячейками сетки: 53 mainlay->
setMargin(2);
54 mainlay->
setSpacing(3);
55 // Размещаем элементы: 56 mainlay->
addWidget(lb, 0, 0);
57 mainlay->
addWidget(le, 0, 1, 1, 2);
58 59 mainlay->
addWidget(cb, 1, 0, 1, 2);
60 mainlay->
addWidget(sb, 1, 2);
61 62 mainlay->
addWidget(chb, 2, 0);
63 mainlay->
addWidget(dsb, 2, 1, 1, 2);
64 65 mainlay->
addWidget(dte, 3, 0, 1, 3);
66 // Менеджер размещения для радиокнопок: 67 QHBoxLayout *hbl = new QHBoxLayout();
68 hbl->
addWidget(rb1, 1);
69 hbl->
addWidget(rb2, 1);
70 hbl->
addWidget(rb3, 1);
71 gb->
setLayout(hbl);
72 mainlay->
addWidget(gb, 4, 0, 1, 3);
73 // Менеджер размещения для кнопок диалога: 74 QHBoxLayout *btns = new QHBoxLayout();
75 btns->
addStretch(1);
76 btns->
addWidget(btn1, 2);
77 btns->
addWidget(btn2, 2);
78 btns->
addStretch(1);
79 mainlay->
addLayout(btns, 6, 0, 1, 3);
80 // Растяжимость колонок и строк: 81 mainlay->
setColumnStretch(1, 1);
82 mainlay->
setColumnStretch(2, 1);
83 mainlay->
setRowStretch(5, 1);
84 setLayout(mainlay);
85 }
Для вставки любого виджета в нужную ячейку QGridLayout предназначен метод addWidget, при вызове которого указываются номер строки и столбца (нумерация начинается с нуля), а также, если требуется, количество соседних строк и столбцов, которые будет занимать элемент. Растяжимостью строк и столбцов сетки управляют методы setRowStretch и setColumnStretch, первый параметр которых указывает номер строки (столбца), а второй -- коэффициент растяжения. Чем он больше, тем сильнее будет растягиваться/сжиматься данная строка по вертикали (или столбец по горизонтали) по сравнению с остальными строками (столбцами) при изменении размеров всего окна.
Разумеется, в данном случае можно предложить и другие варианты, например, на внешнем уровне использовать QVBoxLayout и заполнять его менеджерами QHBoxLayout, в которые вставлять элементы каждой горизонтальной строки диалога, подобно тому, как это сделано с радиокнопками в нашем примере.
Если нас не удовлетворяет то, как ведут себя какие-либо элементы диалога при изменении размеров окна, то можно попытаться вызвать для них метод setSizePolicy, первый параметр которого задаёт политику изменения ширины элемента, а второй -- его высоты. Каждый параметр может принимать одно из значений:
QSizePolicy::Fixed -- размер элемента в данном направлении не изменяется; QSizePolicy::Minimum -- "идеальным" размером элемента считается его минимальный размер. Элемент может растягиваться, но не может сжиматься; QSizePolicy::Maximum -- "идеальным" размером элемента считается его максимальный размер. Элемент может сжиматься, но не может растягиваться; QSizePolicy::Preferred -- элемент "старается" поддерживать некоторый предпочтительный для него размер, но при необходимости может растянуться или сжаться; QSizePolicy::Expanding -- элемент "старается" принять максимально возможный доступный ему размер, но при необходимости может и сжиматься.
В Qt имеются классы QHBoxLayout, QVBoxLayout и QGridLayout, которые специально предназначены для управления положением и размерами элементов в окне. Первый позволяет располагать элементы друг за другом по горизонтали, второй -- по вертикали, а третий размещает виджеты в ячейках воображаемой таблицы, причём каждый элемент может занимать несколько смежных ячеек по вертикали и/или горизонтали.
В листинге 10 показаны строки, которые потребуется дописать к тексту конструктора диалога (см. листинг 9), если мы захотим использовать менеджеры размещения. Здесь все элементы располагаются в сетке QGridLayout, а для зависимых переключателей и кнопок создаются отдельные менеджеры QHBoxLayout. Разумеется, метод resizeEvent теперь не нужен.
С помощью метода setGeometry(int x, int y, int w, int h) или setGeometry(const QRect&) можно задать положение и размер любого визуального элемента в пикселах. Для установки размеров без изменения положения может использоваться метод resize(int w, int h) или resize(const QSize&). Наоборот, для перемещения элемента в нужную позицию с сохранением прежних размеров служит метод move(int x, int y) или move(const QPoint&).
Недостатком жёсткого варианта размещения элементов интерфейса является то, что пользователь не может изменить размер окна диалога (или изменение размеров окна не влияет на взаимное положение и размеры всех его элементов). В результате при низком разрешении монитора всё выглядит слишком крупно, а то и вовсе не помещается на экран, при высоком-- наоборот, слишком мелко. Кроме того, в различных операционных системах используются разные шрифты, поэтому надписи и поля ввода, прекрасно смотревшиеся в одной системе, при переносе на другую платформу могут не уместиться в прежних границах. К тому же в солидных программных продуктах принято давать пользователю возможность настраивать интерфейс программы по своему вкусу, в частности, изменять гарнитуру и размер шрифта. А при локализации (переводе интерфейса программы на другой язык) всё ещё больше усложняется.
Очевидный (но не лучший) способ решить хотя бы некоторые из перечисленных проблем заключается в том, чтобы в виртуальном методе resizeEvent, автоматически выполняемом при изменении размеров окна, пересчитывать размеры и положение всех элементов. Пример программирования такого диалога приведён в листингах 8 и 9, а внешний вид окна -- на рис.
