Автор: Jasmin Blanchette
Перевод: Andi Peredri
Эта статья дает ответы на наиболее часто задаваемые вопросы, касающиеся внешнего вида Qt-виджетов. В частности, мы рассмотрим, как изменить внешний вид стандартных Qt-виджетов с помощью наследования от QStyle и как использовать фильтры событий для рисования в контексте другого виджета.
Неофициальный перевод статьи
Look 'n' Feel Q & A выполнен с любезного разрешения
Trolltech.
Мне бы хотелось изменить способ отображения "горячих" клавиш в меню. Например, для элемента меню выполнения обратного поиска Find Again показать сочетание клавиш Ctrl+Shift+G как [Shift]Ctrl+G. Можно ли как-то изменить способ отображения "горячих" клавиш в выпадающем меню?
Для указания своего текста в элементах меню вы должны использовать символ табуляции ('\t'). Например:
editMenu->insertItem(tr("F&ind Again\t[Shift]Ctrl+G"));
Если вы используете QAction, то должны написать так:
findAgainAct->setMenuText(tr("F&ind Again\t[Shift]Ctrl+G"));

Основная идея реализации задаваемых таким образом "горячих" клавиш заключается в том, что вместо использования классов
QAccel или
QAction вы должны переопределить метод
QWidget::keyEvent().
Я использую Qt на устройстве с сенсорным экраном. Как изменить ширину вертикальных полос прокрутки и высоту горизонтальных полос прокрутки, чтобы стало удобнее пользоваться виджетом
QListBox?
Попробуйте воспользоваться функцией
QApplication::setGlobalStrut(). Она задает минимальный размер
QSize для всех GUI-элементов. Например, после следующего вызова:
QApplication::setGlobalStrut(QSize(30, 30));
ширина ваших вертикальных полос прокрутки и высота горизонтальных полос прокрутки станут равны, как минимум, 30 пикселам. Также вызов этой функции приведет к изменению размеров других GUI-элементов: кнопок, строк меню и элементов списка.
Если вам необходимо изменить размеры лишь элементов
QScrollBar, то создайте свой стиль и переопределите метод
QStyle::pixelMetric() таким образом, чтобы для параметра QStyle::PM_ScrollBarExtent возвращалось значение 30.
Я попытался изменить внешний вид горизонтального заголовка виджета
QTable. Для этого я наследовал от
QHeader и переопределил метод paintSection(). Как мне теперь сообщить QTable, чтобы для заголовка он использовал мой класс вместо стандартного QHeader?
К сожалению, в настоящей версии Qt QTable не может быть использован с другими подклассами QHeader. Однако есть два обходных пути:
1. Вы можете создать свой собственный стиль, например, на базе
QWindowsStyle, и переопределить метод QStyle::drawPrimitive() следующим образом:
void MyStyle::drawPrimitive(PrimitiveElement element,
QPainter *painter, const QRect &rect,
const QColorGroup &colorGroup, SFlags flags,
const QStyleOption &opt) const
{
if (element == PE_HeaderSection) {
// код отрисовки
} else
QWindowsStyle::drawPrimitive(element, painter, rect, colorGroup, flags, opt);
}
2. Чтобы самостоятельно отрисовывать горизонтальный заголовок QHeader, вы можете переопределить фильтр событий вашего подкласса QTable:
bool MyTable::eventFilter(QObject *targetObj, QEvent *event)
{
if (targetObj == (QObject *)horizontalHeader() &&
event->type() == QEvent::Paint)
{
QPainter painter(horizontalHeader());
// код отрисовки
return true;
} else
return QTable::eventFilter(targetObj, event);
}
Возможно, более лучшее решение появится в Qt 4.
Как изменить ширину разделителя
QSplitter?
В Qt 3.2 и более поздних версиях вы можете использовать QSplitter::setHandleWidth(). В ранних версиях Qt вам необходимо создать свой собственный стиль и переопределить метод pixelMetric():
int MyStyle::pixelMetric(PixelMetric metric, const QWidget *widget) const
{
if (metric == PM_SplitterWidth) {
return 6;
} else {
return QWindowsStyle::pixelMetric(metric, widget);
}
}
Наше приложение поддерживает платформы Windows, Linux, Solaris и Mac OS X, и при этом использует стили, свойственные каждой из этих систем. Однако мы хотели бы немного изменить внешний вид виджета
QTabWidget и сделать его одинаковым на всех платформах. Как это можно сделать без наследования от всех стандартных классов стилей (QWindowsStyle,
QMotifStyle и т.д.) ?
Это зависит от того, что именно вы хотите изменить. Одним из предложенных Qt-разработчиками решений является использование класса ProxyStyle, унаследованного непосредственно от Style и перенаправляющего вызовы виртуальных функций на соответствующие вызовы стандартных классов стилей:
class ProxyStyle : public QStyle
{
public:
ProxyStyle(const QString &baseStyle)
{ style = QStyleFactory::create(baseStyle); }
void polish(QWidget *w) { style->polish(w); }
void unPolish(QWidget *w) { style->unPolish(w); }
int pixelMetric(PixelMetric metric, QWidget *widget) const
{ return style->pixelMetric(metric, widget); }
...
private:
QStyle *style;
};
Затем достаточно наследовать ProxyStyle и реализовать класс с желаемым поведением:
class MyStyle : public ProxyStyle
{
public:
MyStyle(const QString &baseStyle);
int pixelMetric(PixelMetric metric, const QWidget *widget) const;
};
int MyStyle::pixelMetric(PixelMetric metric, const QWidget *widget) const
{
if (metric == PM_SplitterWidth)
return 6;
return ProxyStyle::pixelMetric(metric, widget);
}
Впоследствии этот класс используется следующим образом:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
app.setStyle(new MyStyle(app.style().name()));
...
return app.exec();
}
Благодаря прокси-технике, необходимое поведение реализуется лишь в одном классе MyStyle. Далее объект этого класса используется вместо стандартных стилей, и все нереализованные в MyStyle методы подменяются соответствующими методами из стандартных стилей.
Таким образом, прокси-техника в некоторой степени компенсирует ограничения языка: в C++ можно наследовать только от классов, а не от объектов. Подробнее прокси-классы рассмотрены в Design Patterns (ISBN 0-201-63361-2).
При использовании прокси-классов нужно помнить об одной тонкости. Некоторые виртуальные функции стандартных стилей Qt реализованы посредством других виртуальных функций, например, метод QWindowsStyle::drawComplexControl() для отрисовки стрелочек виджета QComboBox вызывает метод QWindowsStyle::drawPrimitive(). Если в классе MyStyle (который унаследован от ProxyStyle) вы переопределите метод drawPrimitive(), то он будет проигнорирован в классе QWindowsStyle, так как последний будет использовать свою функцию drawPrimitive(). (Это объясняется тем, что MyStyle не наследует QWindowsStyle, а лишь включает его.) В зависимости от того, какой именно виджет вы захотите изменить, вам также может понадобиться переопределить метод drawComplexControl().
У меня проблема со стилем Windows XP. При настройке палитры и выборе зеленого цвета для компоненты Button не происходит смена цвета кнопки QPushButton. Но в то же время смена цвета осуществляется корректно после смены текущей темы Windows на "Windows Classic". Как это исправить? Это ошибка в стиле?
Стиль Windows XP в Qt использует Windows-механизм тем. Для отрисовки кнопок и других компонентов тема XP использует изображения, игнорируя при этом цвета палитры. Такое поведение "зашито" в код Windows XP, и Qt здесь бессильна. (В Mac OS X наблюдается такая же проблема со стилем Mac.)
 |
|
 |
Если вам действительно нужна кнопка зеленого цвета, вы всегда можете сменить ее стиль на QWindowsStyle с помощью QWidget::setStyle(). Хотя при этом она будет отличаться от других кнопок в вашем приложении.
Я использую Qt/Mac, и хотел бы поинтересоваться, в чем отличия между стилями
QAquaStyle и
QMacStyle?
Впервые стиль QAquaStyle появился в Qt 3.0, когда стала поддерживаться платформа Qt/Mac. Он эмулировал Apple-тему "Aqua". В Qt 3.1 ему на смену пришел стиль QMacStyle, который стал использовать для отрисовки Appearance Manager. Сейчас стиль QAquaStyle поставляется вместе с Qt/Mac лишь для совместимости и может быть удален в новых версиях Qt.