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, а внешний вид окна -- на рис.
Рис. Перерасчёт геометрии в
методе resizeEvent
Листинг 8. Перерасчёт геометрии в методе resizeEvent
(файл
examples-qt/03/03.h
)
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 };
MyDialog
, в качестве базового использовали
стандартный класс диалога библиотеки Qt.
resizeEvent
.
resizeEvent
(файл examples-qt/03/03.cpp
) 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 }
this
.
setMinimumSize
использовать метод setFixedSize
, то
пользователь не сможет изменить ни ширину, ни высоту окна.
Рис. Внешний вид окна и
элементов управления в системе Windows при запуске программы с
параметром-style=Windows
, WindowsXP
,
Motif
, CDE
, Plastique
и
Cleanlooks
На рис. показано, как изменяется внешний вид элементов диалога, если при
запуске программы, текст которой приведён в листингах 8 и 9, указать в командной
строке параметр -style=ИмяСтиля
. Обратите внимание, что при одних и
тех же размерах окна, устанавливаемых сразу после запуска программы, в режиме
WindowsXP текстовая метка "Один" оказалась обрезанной на последней букве, а для
стиля Motif высота всех полей ввода и ширина кнопок оказалась на грани
критической: текстовые надписи еле "влазят" в отведённые для них границы из-за
увеличенной толщины декоративных элементов. В системе Linux, где размер шрифта
по умолчанию выбирается обычно больше, чем в Windows (из-за традиционно худшего
качества отображения шрифтов), это различие компоновки элементов диалога для
различных стилей ещё более заметно.
QHBoxLayout
,
QVBoxLayout
и QGridLayout
, которые специально
предназначены для управления положением и размерами элементов в окне. Первый
позволяет располагать элементы друг за другом по горизонтали, второй -- по
вертикали, а третий размещает виджеты в ячейках воображаемой таблицы, причём
каждый элемент может занимать несколько смежных ячеек по вертикали и/или
горизонтали.
В листинге 10 показаны строки, которые потребуется дописать к тексту
конструктора диалога (см. листинг 9), если мы захотим использовать
менеджеры размещения. Здесь все элементы располагаются в сетке
QGridLayout
, а для зависимых переключателей и кнопок создаются
отдельные менеджеры QHBoxLayout
. Разумеется, метод
resizeEvent
теперь не нужен.
Листинг 10. Менеджеры размещения (файл
examples-qt/04/04.cpp
)
// Создаём менеджер размещения: 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
-- элемент "старается" принять
максимально возможный доступный ему размер, но при необходимости может и
сжиматься.