QT界面 理解QStyle和QStyleOption以及QStyleFactory

QStyleOption类和QStyle类简介

QStyleOption类存储QStyle函数使用的参数。QStyleOption及其子类包含了QStyle函数绘制图形元素所需的所有信息。

由于性能原因,成员函数很少,并且对成员变量的访问是直接的(即。,使用。或- >操作符)。这种低级的感觉使结构易于使用,并强调这些只是样式函数使用的参数。

QStyle函数的调用者通常在堆栈上创建QStyleOption对象。结合Qt对QString、QPalette和QColor等类型的隐式共享的广泛使用,可以确保没有不必要的内存分配。

QStyle类是一个抽象基类,它封装GUI的外观和感觉。

Qt包含一组QStyle子类,它们模拟Qt支持的不同平台的样式(QWindowsStyle、QMacStyle等)。默认情况下,这些样式构建在Qt GUI模块中。样式也可以作为插件提供。

Qt的内置小部件使用QStyle来执行几乎所有的绘图,确保它们看起来与等效的本地小部件完全相同。

QStyle类

设置风格:

可以使用QApplication::setStyle(QStyle *style)函数设置整个应用程序的样式。它也可以由应用程序的用户指定,使用样式命令行选项: 。

./myapplication -style windows

 如果没有指定样式,Qt将为用户的平台或桌面环境选择最合适的样式。还可以使用QWidget::setStyle(QStyle *style)函数在单个小部件上设置样式。

QStyle类的简要:

如果您正在开发自定义小部件,并希望它们在所有平台上看起来都很好,那么可以使用QStyle函数来执行小部件绘图的部分,例如drawItemText()、drawItemPixmap()、drawPrimitive()、drawControl()和drawComplexControl()。

大多数QStyle绘制函数有四个参数:

1.枚举值,指定要绘制哪个图形元素

2.QStyleOption,指定如何和在哪里呈现该元素

3.用于绘制元素的QPainter

4.执行绘图的QWidget(可选)

QStyle从QStyleOption中获取呈现图形元素所需的所有信息。如果样式需要小部件执行特殊效果(比如macOS上的动画默认按钮),则将小部件作为最后一个参数传递,但这不是必须的。实际上,通过正确设置QPainter,您可以使用QStyle来绘制任何绘图设备,而不仅仅是控件。QStyleOption为可以绘制的各种图形元素类型有不同的子类。例如,PE_FrameFocusRect需要一个QStyleOptionFocusRect参数。为了确保绘图操作尽可能快,QStyleOption及其子类具有公共数据成员。有关如何使用的详细信息,请参阅QStyleOption类文档。

为了方便,Qt提供了QStylePainter类,它结合了QStyle、QPainter和QWidget。这使得下面写作替换原有的成为可能:

QStylePainter painter(this);
...
painter.drawPrimitive(QStyle::PE_FrameFocusRect, option);

取代下面代码

QPainter painter(this);
...
style()->drawPrimitive(QStyle::PE_FrameFocusRect, &option, &painter, this);

自定义界面:

通过创建自定义样式,您可以为应用程序创建自定义外观。创建自定义样式有两种方法。在静态方法中,您要么选择一个现有的QStyle类,子类化它,并重新实现虚拟函数来提供自定义行为,或者从头创建一个完整的QStyle类。在动态方法中,您可以在运行时修改系统样式的行为。下面描述静态方法。动态方法描述在QProxyStyle中。

静态方法的第一步是选择Qt提供的一种样式,您将从中构建自定义样式。您对QStyle类的选择将取决于哪个样式与您所期望的样式最相似。您可以使用的最普通的类是QCommonStyle(不是QStyle)。这是因为Qt要求它的样式是QCommonStyles。

根据您想要更改的基本样式的哪些部分,您必须重新实现用于绘制接口部分的函数。为了说明这一点,我们将修改QWindowsStyle绘制的旋转框箭头的外观。箭头是由draw原语()函数绘制的基本元素,因此我们需要重新实现该函数。

我们需要以下类声明:

  class CustomStyle : public QProxyStyle
  {
      Q_OBJECT

  public:
      CustomStyle();
      ~CustomStyle() {}

      void drawPrimitive(PrimitiveElement element, const QStyleOption *option,
                         QPainter *painter, const QWidget *widget) const;
  };

要绘制上下箭头,QSpinBox使用PE_IndicatorSpinUp和PE_IndicatorSpinDown原始元素。下面介绍如何重新实现draw原语()函数,以便以不同的方式绘制它们:

  void CustomStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
                                  QPainter *painter, const QWidget *widget) const
  {
      if (element == PE_IndicatorSpinUp || element == PE_IndicatorSpinDown) {
          QPolygon points(3);
          int x = option->rect.x();
          int y = option->rect.y();
          int w = option->rect.width() / 2;
          int h = option->rect.height() / 2;
          x += (option->rect.width() - w) / 2;
          y += (option->rect.height() - h) / 2;

          if (element == PE_IndicatorSpinUp) {
              points[0] = QPoint(x, y + h);
              points[1] = QPoint(x + w, y + h);
              points[2] = QPoint(x + w / 2, y);
          } else { // PE_SpinBoxDown
              points[0] = QPoint(x, y);
              points[1] = QPoint(x + w, y);
              points[2] = QPoint(x + w / 2, y + h);
          }

          if (option->state & State_Enabled) {
              painter->setPen(option->palette.mid().color());
              painter->setBrush(option->palette.buttonText());
          } else {
              painter->setPen(option->palette.buttonText().color());
              painter->setBrush(option->palette.mid());
          }
          painter->drawPolygon(points);
      } else {
      QProxyStyle::drawPrimitive(element, option, painter, widget);
      }
  }

注意:我们不使用小部件参数,除非将其传递给QWindowStyle::drawPrimitive()函数。如前所述,关于要绘制什么以及应该如何绘制的信息是由QStyleOption对象指定的,因此不需要询问小部件。

如果您需要使用小部件参数来获取其他信息,请小心确保它不是0,并且在使用它之前它是正确的类型。例如:

const QSpinBox *spinBox = qobject_cast<const QSpinBox *>(widget);
      if (spinBox) {
      ...
      }

**在实现自定义样式时,您不能仅仅因为枚举值被称为PE_IndicatorSpinUp或PE_IndicatorSpinDown就认为小部件是一个QSpinBox。

警告:Qt样式表目前不支持自定义QStyle子类。在将来的某个版本会解决这个问题。

在Qt应用程序中有几种使用自定义样式的方法。最简单的方法是在创建QApplication对象之前将自定义样式传递给QApplication::setStyle()静态函数:

  #include <QtWidgets>

  #include "customstyle.h"

  int main(int argc, char *argv[])
  {
      QApplication::setStyle(new CustomStyle);
      QApplication app(argc, argv);
      QSpinBox spinBox;
      spinBox.show();
      return app.exec();
  }

在项视图中使用样式

视图中的项的绘制是由委托执行的。Qt的默认委托QStyledItemDelegate也用于计算项目的边界矩形,以及QStyledItemDelegate支持的各种项目数据角色的子元素。查看QStyledItemDelegate类描述,以找出支持哪些数据类型和角色。您可以在模型/视图编程中阅读更多关于项目数据角色的内容。

当QStyledItemDelegate绘制项时,它会绘制CE_ItemViewItem,并使用CT_ItemViewItem计算它们的大小。还要注意,它使用SE_ItemViewItemText设置编辑器的大小。在实现自定义项目视图绘制样式时,需要检查QCommonStyle的实现(以及样式继承的任何其他子类)。通过这种方式,您可以发现哪些和如何绘制其他样式元素,然后您可以重新实现应该以不同方式绘制的元素的绘制。

下面使用一个小例子来说明:

      switch (element) {
          case (PE_PanelItemViewItem): {
              painter->save();

              QPoint topLeft = option->rect.topLeft();
              QPoint bottomRight = option->rect.topRight();
              QLinearGradient backgroundGradient(topLeft, bottomRight);
              backgroundGradient.setColorAt(0.0, QColor(Qt::yellow).lighter(190));
              backgroundGradient.setColorAt(1.0, Qt::white);
              painter->fillRect(option->rect, QBrush(backgroundGradient));

              painter->restore();
          break;
          }
          default:
              QProxyStyle::drawPrimitive(element, option, painter, widget);
      }

原始元素PE_PanelItemViewItem负责绘制项目的背景,它来自QCommonStyle的CE_ItemViewItem实现。

要添加对绘制新数据类型和项数据角色的支持,需要创建一个自定义委托。但是,如果您只需要支持由默认委托实现的数据类型,则自定义样式不需要附带的委托。QStyledItemDelegate类描述提供了关于自定义委托的更多信息。项目视图标题的绘制也由样式完成,对标题项的大小以及行和列的大小进行控制。

 QStyleOption子类:

QStyleOptionButton, QStyleOptionComplex, QStyleOptionDockWidget, QStyleOptionFocusRect, QStyleOptionFrame, QStyleOptionGraphicsItem, QStyleOptionHeader, QStyleOptionMenuItem, QStyleOptionProgressBar, QStyleOptionRubberBand, QStyleOptionTab, QStyleOptionTabBarBase, QStyleOptionTabWidgetFrame, QStyleOptionToolBar, QStyleOptionToolBox, and QStyleOptionViewItem

posted @ 2018-07-09 20:31  在左手  阅读(14685)  评论(0编辑  收藏  举报