QStyle类用法总结(二)

1.前言

       为了更容易搞懂Qt自定义风格绘制,在平时开发中,实现定制自己风格的UI,本人推出有关QStyle自定义风格系列文章。文章链接如下: 

  1.  《QStyle类用法总结(一)》。对Qt自定义风格简单描述,对QStyle及其相关类作了概念性的描述。
  2. QStyle类用法总结(二)》。对QStyle及其相关类作了详细描述。
  3. QStyle类用法总结(三)》。对QStyle中的各widget元素的层次继承树做了详细描述。
  4. QProxyStyle用法简述》。通过一个简单的例子,演示了QProxyStyle类用法。
  5. QStyle实现自绘界面项目实战(一)》通过项目实战来理解前几篇提到的内容,理解QStyle及其子类在自绘时的工作机制。
  6. QStyle实现自绘界面项目实战(二)》通过项目实战来理解前几篇提到的内容,理解QStyle及其子类在自绘时的工作机制。

2.自定义一种风格

        为了自定义风格,继承QProxyStyle类,重写该类中的某些虚函数。QProxyStyle类允许指定某个基础风格或者当基础风格没指定时,会自动采用应用程序的风格。Qt提供的风格框架模板为基础风格提供全面控制且能够为自定义风格工作的很好。

3.QStyle类的实现

   QStyle类的API包含绘制widgets的函数,某些静态函数能够做常用和复杂的任务(如:计算QSlider中handles的位置),某些函数能在绘制时候做些必要的计算(如:计算widgets的尺寸大小)。风格也能布局widgets中的内容,另外它能创建QPalette对象,在该对象中包含用于绘制widget的画刷信息。

     QStyle负责绘制图形元素。图形元素是一个widget或widget的一部分,例如:窗体的边框、滚动条。

大部分QStyle类的绘制函数接受4个参数:

  1. 一个枚举值,该枚举值指定哪种类型的图形元素被绘制。
  2. 一个QStyleOption 类型参数。该参数指示怎么绘制、在哪个地方绘制条目1中提到的图形元素。
  3. 一个QPainter类型参数。该参数被用来绘制条目1中提到的图形元素。
  4. 一个QWidget类型参数。该参数是可选的。绘制过程在该参数表示的widget上执行。
     

        当一个widget向style请求绘制一个图形元素时,它提供style一个QStyleOption类型对象,在该对象中包含绘制的一些必要信息。多亏了QStyleOption 类型对象,这使得用QStyle绘制widgets且不用链入任何代码。这使得在任何绘图设备上用QStyle绘制函数进行绘制变为可能,例如:你可以在任何widget上绘制一个combobox,而不仅仅是在QComboBox上。一个widget对象作为最后一个参数被传入函数。在这种情况下,QStyle类可以在传入的widget对象执行一些特殊效果(如:实现mac操作系统上的按钮动画),但这个widget对象不是必要的。事实上,你可以用QStyle在任何绘图设备上进行绘制,不仅仅是widget。

4.风格元素

    一个风格元素是GUI(图形化用户界面接口)图形化的一部分。一个widget由许多层次化(或树状)的风格元素组成。例如:当一个style接收到一个绘制按钮的请求时(例如:该请求来自QPushButton),它绘制标签(文本和icon)、button bevel、具有焦点的边框。下图是push button的概念性层次化树状图解,在后文的描述中,我们将会看到QPushButton元素树的更详细的图解描述。

 图1 QPushButton元素层次树概念图

      widgets能调用style多次,从而绘制不同的图形元素。例如:在绘制QTabWidget时,在绘制tabs、frame时,分别独自调用style,从而绘制出tabs、frame。

     有三种类型的图形元素:

  • primitive elements
  • control elements
  • complex control elements

         这些图形元素在QStyle::PrimitiveElement、QStyle::ControlElement、QStyle::ComplexControl枚举中被定义。每个枚举值有一个前缀标识它们的类型:CC_ 表示complex elements,CE_表示control elements,PE_表示primitive elements。

4.1.Primitive Elements

        Primitive Elements是被许多widgets使用、大众化GUI元素。例如:边框、按钮斜角(面)、spin box中的箭头、滚动条滚动块、组合框中的boxes。Primitive Elements不能单独自己存在,它是QStyle::ControlElement枚举值定义的控件的某部分,如:checkbox中的checkbox indicator和button中的button bevel。Primitive Elements在QStyle::PrimitiveElement枚举中定义,该枚举定义了各种各样的Primitive Elements。QStyle::PrimitiveElement枚举如下:


QStyle::PE_FrameStatusBar
QStyle::PE_PanelButtonCommand
QStyle::PE_FrameDefaultButton
QStyle::PE_PanelButtonBevel
QStyle::PE_PanelButtonTool
QStyle::PE_PanelLineEdit
QStyle::PE_IndicatorButtonDropDown
QStyle::PE_FrameFocusRect
QStyle::PE_IndicatorArrowUp
QStyle::PE_IndicatorArrowDown
QStyle::PE_IndicatorArrowRight
QStyle::PE_IndicatorArrowLeft
QStyle::PE_IndicatorSpinUp
QStyle::PE_IndicatorSpinDown
QStyle::PE_IndicatorSpinPlus
QStyle::PE_IndicatorSpinMinus
QStyle::PE_IndicatorItemViewItemCheck
QStyle::PE_IndicatorCheckBox
QStyle::PE_IndicatorRadioButton
QStyle::PE_IndicatorDockWidgetResizeHandle
QStyle::PE_Frame
QStyle::PE_FrameMenu
QStyle::PE_PanelMenuBar
QStyle::PE_PanelScrollAreaCorner
QStyle::PE_FrameDockWidget
QStyle::PE_FrameTabWidget
QStyle::PE_FrameLineEdit
QStyle::PE_FrameGroupBox
QStyle::PE_FrameButtonBevel
QStyle::PE_FrameButtonTool
QStyle::PE_IndicatorHeaderArrow
QStyle::PE_FrameStatusBarItem
QStyle::PE_FrameWindow
QStyle::PE_IndicatorMenuCheckMark
QStyle::PE_IndicatorProgressChunk
QStyle::PE_IndicatorBranch
QStyle::PE_IndicatorToolBarHandle
QStyle::PE_IndicatorToolBarSeparator
QStyle::PE_PanelToolBar
QStyle::PE_PanelTipLabel
QStyle::PE_FrameTabBarBase
QStyle::PE_IndicatorTabTear
QStyle::PE_IndicatorTabTearLeft
QStyle::PE_IndicatorTabTearRight
QStyle::PE_IndicatorColumnViewArrow
QStyle::PE_Widget
QStyle::PE_CustomBase
QStyle::PE_IndicatorItemViewItemDrop
QStyle::PE_PanelItemViewItem
QStyle::PE_PanelItemViewRow
QStyle::PE_PanelStatusBar
QStyle::PE_IndicatorTabClose
QStyle::PE_PanelMenu

      如:QStyle::PE_PanelButtonTool表示面板上的一个QToolButton控件、QStyle::PE_PanelLineEdit表示面板上的QLineEdit控件,其它各个枚举值的具体含义请参考Qt Assist。

4.2.Control Elements

       control element表示某个子控件元素,用来向用户执行某个动作或显示某些信息例如:push buttons、check boxes、 表视图和树视图中的header sections。control element可以是完整的widget,比如:push buttons类型按钮;它也可以是widget的某部分,例如:tab bar中的tabs和sliders中的scroll bar。control element由QStyle::ControlElement枚举定义,如下:


QStyle::CE_PushButton
QStyle::CE_PushButtonBevel
QStyle::CE_PushButtonLabel
QStyle::CE_DockWidgetTitle
QStyle::CE_Splitter
QStyle::CE_CheckBox
QStyle::CE_CheckBoxLabel
QStyle::CE_RadioButton
QStyle::CE_RadioButtonLabel
QStyle::CE_TabBarTab
QStyle::CE_TabBarTabShape
QStyle::CE_TabBarTabLabel
QStyle::CE_ProgressBar
QStyle::CE_ProgressBarGroove
QStyle::CE_ProgressBarContents
QStyle::CE_ProgressBarLabel
QStyle::CE_ToolButtonLabel
QStyle::CE_MenuBarItem
QStyle::CE_MenuBarEmptyArea
QStyle::CE_MenuItem
QStyle::CE_MenuScroller
QStyle::CE_MenuTearoff
QStyle::CE_MenuEmptyArea
QStyle::CE_MenuHMargin
QStyle::CE_MenuVMargin
QStyle::CE_ToolBoxTab
QStyle::CE_SizeGrip
QStyle::CE_Header
QStyle::CE_HeaderSection
QStyle::CE_HeaderLabel
QStyle::CE_ScrollBarAddLine
QStyle::CE_ScrollBarSubLine
QStyle::CE_ScrollBarAddPage
QStyle::CE_ScrollBarSubPage
QStyle::CE_ScrollBarSlider
QStyle::CE_ScrollBarFirst
QStyle::CE_ScrollBarLast
QStyle::CE_RubberBand
QStyle::CE_FocusFrame
QStyle::CE_ItemViewItem
QStyle::CE_CustomBase
QStyle::CE_ComboBoxLabel
QStyle::CE_ToolBar
QStyle::CE_ToolBoxTabShape
QStyle::CE_ToolBoxTabLabel
QStyle::CE_HeaderEmptyArea
QStyle::CE_ShapedFrame

   QStyle::SubElement枚举定义了可利用的sub elements。该枚举被control element用来计算其某个子元素的最小外包围矩形。sub elements不像primitive、control、complex elements,它不是一个用来绘制的图形元素, 而是某个子元素占据的区域,即矩形。QStyle::SubElement枚举表示widget中的某个子区域。在实现风格时,Qt通过这些子区域就知道需要绘制widget的哪些部分。QStyle::SubElement如下:


QStyle::SE_PushButtonContents
QStyle::SE_PushButtonFocusRect
QStyle::SE_PushButtonLayoutItem
QStyle::SE_CheckBoxIndicator
QStyle::SE_CheckBoxContents
QStyle::SE_CheckBoxFocusRect
QStyle::SE_CheckBoxClickRect
QStyle::SE_CheckBoxLayoutItem
QStyle::SE_DateTimeEditLayoutItem
QStyle::SE_RadioButtonIndicator
QStyle::SE_RadioButtonContents
QStyle::SE_RadioButtonFocusRect
QStyle::SE_RadioButtonClickRect
QStyle::SE_RadioButtonLayoutItem
QStyle::SE_ComboBoxFocusRect
QStyle::SE_SliderFocusRect
QStyle::SE_SliderLayoutItem
QStyle::SE_SpinBoxLayoutItem
QStyle::SE_ProgressBarGroove
QStyle::SE_ProgressBarContents
QStyle::SE_ProgressBarLabel
QStyle::SE_ProgressBarLayoutItem
QStyle::SE_FrameContents
QStyle::SE_ShapedFrameContents
QStyle::SE_FrameLayoutItem
QStyle::SE_HeaderArrow
QStyle::SE_HeaderLabel
QStyle::SE_LabelLayoutItem
QStyle::SE_LineEditContents
QStyle::SE_TabWidgetLeftCorner
QStyle::SE_TabWidgetRightCorner
QStyle::SE_TabWidgetTabBar
QStyle::SE_TabWidgetTabContents
QStyle::SE_TabWidgetTabPane
QStyle::SE_TabWidgetLayoutItem
QStyle::SE_ToolBoxTabContents
QStyle::SE_ToolButtonLayoutItem
QStyle::SE_ItemViewItemCheckIndicator
QStyle::SE_TabBarTearIndicator
QStyle::SE_TabBarTearIndicatorLeft
QStyle::SE_TabBarTearIndicatorRight
QStyle::SE_TabBarScrollLeftButton
QStyle::SE_TabBarScrollRightButton
QStyle::SE_TreeViewDisclosureItem
QStyle::SE_DialogButtonBoxLayoutItem
QStyle::SE_GroupBoxLayoutItem
QStyle::SE_CustomBase
QStyle::SE_DockWidgetFloatButton
QStyle::SE_DockWidgetTitleBarText
QStyle::SE_DockWidgetCloseButton
QStyle::SE_DockWidgetIcon
QStyle::SE_ComboBoxLayoutItem
QStyle::SE_ItemViewItemDecoration
QStyle::SE_ItemViewItemText
QStyle::SE_ItemViewItemFocusRect
QStyle::SE_TabBarTabLeftButton
QStyle::SE_TabBarTabRightButton
QStyle::SE_TabBarTabText
QStyle::SE_ToolBarHandle

 如:QStyle::SE_PushButtonContents表示一个PushButton中的文本或文本和icon(如果有icon)或文本和pixmap(如果有pixmap)占据的区域;QStyle::SE_RadioButtonContents表示一个RadioButton中文本占据的区域。其它各个枚举值参考Qt Assist。

注意和Primitive Elements的区别:

        QStyle::PrimitiveElement枚举值表示控件中的某个子元素,如:checkbox中的checkbox indicator、button中的button bevel;而QStyle::SubElemen表示控件中某个子区域范围,可以理解为一个矩形。如:checkbox中的checkbox indicator占据的外包围矩形。在绘制图形元素时,Qt从QStyle::SubElement枚举值中知道了需要绘制的范围,从而只绘制控件的指定范围。

4.3.Complex Control Elements

     Complex Control Elements包含子控件,即是由多个子控件组成。Complex Control的动作行为取决于用户用鼠标在Complex Control不同地方进行交互或不同地方按下键盘而不同。该动作行为与鼠标在哪个子控件(如果该控件有子控件)上方或者鼠标被按下有关。例如:具有滚动条的combo box就是一个Complex Control,其由combo box和scroll bars组成,如下:

                                                            图 2: 具有滚动条的combo box的Complex Control元素

      你能用鼠标移动滑块或按下滚动条箭头。Complex Control Elements中的子控件被定义在QStyle::SubControl枚举中,QStyle::SubControl定义如下:


QStyle::SC_None
QStyle::SC_ScrollBarAddLine
QStyle::SC_ScrollBarSubLine
QStyle::SC_ScrollBarAddPage
QStyle::SC_ScrollBarSubPage
QStyle::SC_ScrollBarFirst
QStyle::SC_ScrollBarLast
QStyle::SC_ScrollBarSlider
QStyle::SC_ScrollBarGroove
QStyle::SC_SpinBoxUp
QStyle::SC_SpinBoxDown
QStyle::SC_SpinBoxFrame
QStyle::SC_SpinBoxEditField
QStyle::SC_ComboBoxEditField
QStyle::SC_ComboBoxArrow
QStyle::SC_ComboBoxFrame
QStyle::SC_ComboBoxListBoxPopup
QStyle::SC_SliderGroove
QStyle::SC_SliderHandle
QStyle::SC_SliderTickmarks
QStyle::SC_ToolButton
QStyle::SC_ToolButtonMenu
QStyle::SC_TitleBarSysMenu
QStyle::SC_TitleBarMinButton
QStyle::SC_TitleBarMaxButton
QStyle::SC_TitleBarCloseButton
QStyle::SC_TitleBarLabel
QStyle::SC_TitleBarNormalButton
QStyle::SC_TitleBarShadeButton
QStyle::SC_TitleBarUnshadeButton
QStyle::SC_TitleBarContextHelpButton
QStyle::SC_DialHandle
QStyle::SC_DialGroove
QStyle::SC_DialTickmarks
QStyle::SC_GroupBoxFrame
QStyle::SC_GroupBoxLabel
QStyle::SC_GroupBoxCheckBox
QStyle::SC_GroupBoxContents
QStyle::SC_MdiNormalButton
QStyle::SC_MdiMinButton
QStyle::SC_MdiCloseButton
QStyle::SC_All

      该枚举定义了子控件。子控件是构成complex control的元素,是complex control内部一个子控件元素。所谓复杂控件(complex control)、及复杂控件中的子控件,我们可以举出很多这样的例子,如:QSlider就是一个复杂控件,QSlider中的滑块、刻度线、滑块能移动的区域就是子控件。又比如QSpin是复杂控件,QSpin中的上(增加)箭头部件、下(减少)箭头部件、边框、编辑框就是子控件。QStyle::SC_SliderHandle表示QSlider中的滑块手柄,每个子部件在QStyle::SubControl枚举中都有定义。

        为了进行绘制,style需要提供给widgets一些信息,这些信息是有关鼠标在widgets的哪个子控件操作的相关信息。例如:QScrollBar需要知道用户是否按下了slider(即滑块)、slider groove(滑块槽)或其中的某个按钮。

      Complex Control Elements中的 sub controls(子控件)和前文描述的control elements(控件元素)不同。你不能用style绘制sub controls(子控件);style仅仅只会计算将要被绘制的  sub controls(子控件)的最小外包围矩形。通常,complex elements 用control和primitive elements绘制它们的sub controls(子控件),这种方法被频繁地用于Qt内置的styles。例如:Qt用PE_IndicatorCheckBox绘制group boxes(组框)内的check box(复选框,该复选框是CC_GroupBox的一个sub control即子控件)。一些sub control(子控件)本身就是control element(控件元素),例如:滚动条滑块(SC_SCrollBarSlider和CE_ScrollBarSlider,即SC_SCrollBarSlider和CE_ScrollBarSlider都表示滚动条滑块)。

4.5.三者的区别

  • QStyle::PrimitiveElement表示的是某个单一控件(即4.2节提到的Control Elements)中的某个子元素部分,如:checkbox中的checkbox indicator、button中的button bevel、text、icon。其表示的是子元素,该子元素所在控件是某个单一控件。
  • Complex Control Elements是复杂控件,其由多个控件组成,其是多个控件组成的复合体。如:Slider本身应该不是一个控件,其是由滑块、刻度线、滑块能移动的区域这些子控件组成的一个复合体,为了编程及表述的方便,我们才把Slider称为控件。
  • QStyle::SubElement是表示widget中某个矩形区域,该区域告知Qt在实现风格绘制要绘制的区域。

4.6.其它和风格相关的知识点

       Pixel metrics常常在绘制的时候被用来当作测量单位,其值以屏幕像素为单位且和风格相关的。QStyle::PixelMetric枚举定义了各种可利用的像素度量指标。一个像素度量指标是指一个像素占据的尺寸,其和风格相关,也就是说,在不同风格中,其值可能不同,比如:在QWindowStyle风格下一个像素占据的尺寸为8,而在QMacStyle风格下一个像素占据的尺寸可能为12。QStyle::PixelMetric枚举如下:


QStyle::PM_ButtonMargin
QStyle::PM_DockWidgetTitleBarButtonMargin
QStyle::PM_ButtonDefaultIndicator
QStyle::PM_MenuButtonIndicator
QStyle::PM_ButtonShiftHorizontal
QStyle::PM_ButtonShiftVertical
QStyle::PM_DefaultFrameWidth
QStyle::PM_SpinBoxFrameWidth
QStyle::PM_ComboBoxFrameWidth
QStyle::PM_MDIFrameWidth
QStyle::PM_MdiSubWindowFrameWidth
QStyle::PM_MDIMinimizedWidth
QStyle::PM_MdiSubWindowMinimizedWidth
QStyle::PM_LayoutLeftMargin
QStyle::PM_LayoutTopMargin
QStyle::PM_LayoutRightMargin
QStyle::PM_LayoutBottomMargin
QStyle::PM_LayoutHorizontalSpacing
QStyle::PM_LayoutVerticalSpacing
QStyle::PM_MaximumDragDistance
QStyle::PM_ScrollBarExtent
QStyle::PM_ScrollBarSliderMin
QStyle::PM_SliderThickness
QStyle::PM_SliderControlThickness
QStyle::PM_SliderLength
QStyle::PM_SliderTickmarkOffset
QStyle::PM_SliderSpaceAvailable
QStyle::PM_DockWidgetSeparatorExtent
QStyle::PM_DockWidgetHandleExtent
QStyle::PM_DockWidgetFrameWidth
QStyle::PM_DockWidgetTitleMargin
QStyle::PM_MenuBarPanelWidth
QStyle::PM_MenuBarItemSpacing
QStyle::PM_MenuBarHMargin
QStyle::PM_MenuBarVMargin
QStyle::PM_ToolBarFrameWidth
QStyle::PM_ToolBarHandleExtent
QStyle::PM_ToolBarItemMargin
QStyle::PM_ToolBarItemSpacing
QStyle::PM_ToolBarSeparatorExtent
QStyle::PM_ToolBarExtensionExtent
QStyle::PM_TabBarTabOverlap
QStyle::PM_TabBarTabHSpace
QStyle::PM_TabBarTabVSpace
QStyle::PM_TabBarBaseHeight
QStyle::PM_TabBarBaseOverlap
QStyle::PM_TabBarScrollButtonWidth
QStyle::PM_TabBarTabShiftHorizontal
QStyle::PM_TabBarTabShiftVertical
QStyle::PM_ProgressBarChunkWidth
QStyle::PM_SplitterWidth
QStyle::PM_TitleBarHeight
QStyle::PM_IndicatorWidth
QStyle::PM_IndicatorHeight
QStyle::PM_ExclusiveIndicatorWidth
QStyle::PM_ExclusiveIndicatorHeight
QStyle::PM_MenuPanelWidth
QStyle::PM_MenuHMargin
QStyle::PM_MenuVMargin
QStyle::PM_MenuScrollerHeight
QStyle::PM_MenuTearoffHeight
QStyle::PM_MenuDesktopFrameWidth
QStyle::PM_HeaderMarkSize
QStyle::PM_HeaderGripMargin
QStyle::PM_HeaderMargin
QStyle::PM_SpinBoxSliderHeight
QStyle::PM_ToolBarIconSize
QStyle::PM_SmallIconSize
QStyle::PM_LargeIconSize
QStyle::PM_FocusFrameHMargin
QStyle::PM_FocusFrameVMargin
QStyle::PM_IconViewIconSize
QStyle::PM_ListViewIconSize
QStyle::PM_ToolTipLabelFrameWidth
QStyle::PM_CheckBoxLabelSpacing
QStyle::PM_RadioButtonLabelSpacing
QStyle::PM_TabBarIconSize
QStyle::PM_SizeGripSize
QStyle::PM_MessageBoxIconSize
QStyle::PM_ButtonIconSize
QStyle::PM_TextCursorWidth
QStyle::PM_TabBar_ScrollButtonOverlap
QStyle::PM_TabCloseIndicatorWidth
QStyle::PM_TabCloseIndicatorHeight
QStyle::PM_ScrollView_ScrollBarSpacing
QStyle::PM_ScrollView_ScrollBarOverlap
QStyle::PM_SubMenuOverlap
QStyle::PM_TreeViewIndentation
QStyle::PM_HeaderDefaultSectionSizeHorizontal
QStyle::PM_HeaderDefaultSectionSizeVertical
QStyle::PM_TitleBarButtonIconSize
QStyle::PM_TitleBarButtonSize
QStyle::PM_CustomBase

如:QStyle::PM_ButtonMargin表示QPushButton中的Label和边框之间的空白占据的距离;QStyle::PM_SliderLength表示slider的长度。这些值在不同style中可能会不同。其它各个枚举值参考Qt Assist。

         风格通常有一套标准图片图标,如:消息提示框或文件对话框中的警告、问询、错误图标。QStyle提供QStyle::StandardPixmap枚举,该枚举定义了这些标准图标。《Qt的QStyle类的标准图标汇总》这篇博文通过编程展示了QStyle::StandardPixmap枚举中的每个图标,敬请参考。

       Qt style 计算布局器中widget之间的间距。 Qt style有两种方法处理这种计算。第一种方法是:可以通过设置PM_LayoutHorizontalSpacing和M_LayoutVerticalSpacing来处理这种计算。另外一种方法是:如果需要对布局器中的某部分进行精细的控制,可以通过重载QStyle::layoutSpacing() 和QStyle::layoutSpacingImplementation()方法。可以根据不同的控件类型(该类型在QSizePolicy::ControlType定义)和不同的尺寸策略(通过QSizePolicy::Policy枚举定义),利用这些函数从而计算出间距。

5. 风格选项

QStyleOption子类如下:

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

         在对每个单独的elements进行style绘制时,这些子类包含绘制时需要用到的所有信息。这些Style options通常在栈中被实例化,在调用方的QStyle相关函数中对实例化对象的数据成员进行填充赋值。采用哪个Style options子类,这是由style绘制的对象决定的,例如:绘制QStyle::PE_FrameFocusRect时,将采用QStyleOptionFocusRect子类来绘制、将其作为函数的参数。也可以通过继承、子类化Style options来创建自己的风格选项。为了提高绘制效率和性能,Style options类的数据成员都采用public的访问接口。

          widgets可能处某种状态,该状态由QStyleOption对象中类型为QStyle::State的数据成员来描述。这些状态在QStyle::State枚举中定义,如下:


QStyle::State_None
QStyle::State_Active
QStyle::State_AutoRaise
QStyle::State_Children
QStyle::State_DownArrow
QStyle::State_Editing
QStyle::State_Enabled
QStyle::State_HasEditFocus
QStyle::State_HasFocus
QStyle::State_Horizontal
QStyle::State_KeyboardFocusChange
QStyle::State_MouseOver
QStyle::State_NoChange
QStyle::State_Off
QStyle::State_On
QStyle::State_Raised
QStyle::State_ReadOnly
QStyle::State_Selected
QStyle::State_Item
QStyle::State_Open
QStyle::State_Sibling
QStyle::State_Sunken
QStyle::State_UpArrow
QStyle::State_Mini
QStyle::State_Small

        比如:QStyle::State_Active表示widget处于激活状态,QStyle::State_Enabled表示widget处于可用状态。其它枚举值的含义参见Qt Assist。

        当绘制primitive elements时,这些枚举描述了primitive elements的处于的状态,如:禁用、激活、可用状态。需要说明的是:并不是所有的primitive elements都用到上面的枚举值,且对于不同的绘制项,上面的枚举可能表示不同含义。

      就像前文提到的:某些状态枚举值含义因不同widget而含义不同(暂且称为个性化状态),但是某些枚举值对所有widget含义都是相同的(暂且称为大众化状态),比如:QStyle::State_Disabled。QStyleOption::initFrom()函数初始化大众化的状态,剩下的个性化状态由每个widget自己设置。如:我们经常看到如下初始化大众化状态代码像下面那样:

  void MyWidget::paintEvent(QPaintEvent * /* event */)
  {
      QPainter painter(this);
 
      QStyleOptionFocusRect option;
      option.initFrom(this); // 初始化大众化的状态
      option.backgroundColor = palette().color(QPalette::Background);
 
      style()->drawPrimitive(QStyle::PE_FrameFocusRect, &option, &painter, this);
  }

       在大部分情况下,style options包含被绘制的widget的调色板、最小外包围矩形。大部分widgets特化了style options,即使用QStyleOption特定的子类来绘制。比如:QPushButton 和QCheckBoxQStyleOptionButtonQStyleOptionButton作为它们的style options。QStyleOptionButton包含按钮需要用到的文本、icon、和icon的尺寸大小。

      当实现QStyle相关函数(该函数一般都会有QStyleOption类型的参数)时,通常应该将QStyleOption类型的参数转换为相应的特化style options,比如:对按钮来说,将QStyleOption类型的参数转换为QStyleOptionButton。出于安全考虑,应该用qstyleoption_cast()执行这种转换,这样可以确保指针类型是正确的。如果被转换对象不是正确类型,则qstyleoption_cast()函数就会返回nullptr,例如:


  const QStyleOptionFocusRect *focusRectOption =
          qstyleoption_cast<const QStyleOptionFocusRect *>(option);
  if (focusRectOption) {
      ...
  }

下面代码片段演示了如何在自定义widget的paintEvent()函数用QStyle绘制一个带焦点的矩形

void MyWidget::paintEvent(QPaintEvent *event)
  {
      QPainter painter(this);
      ...

      // QStyleOptionFocusRect是QStyleOption的一个子类,该子类专为绘制带焦点矩形而存在。
      QStyleOptionFocusRect option(1);

     // 为this表示的widget初始化状态、布局器方向、调色板、字体尺寸和styleObject对象的成员。
      option.init(this);  
      option.backgroundColor = palette().color(QPalette::Window);

      style().drawPrimitive(QStyle::PE_FrameFocusRect, &option, &painter,
                            this);
  }

下面的例子展示了如何从Qt已有的style,继承出符合自己图形元素外观的风格:

class CustomStyle : public QProxyStyle
  {
      Q_OBJECT

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

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


 
  void CustomStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
                                  QPainter *painter, const QWidget *widget) const
  {
       // 如果要绘制的窗体元素是上下箭头
      if (element == PE_IndicatorSpinUp || element == PE_IndicatorSpinDown) 
       {
          QPolygon points(3);
 
          // option->rect()表示上下箭头占据的最小外包围矩形
          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);  
 
               // 将箭头右端点绘制在左端点向右w处,和左端点高度一致的地方。
              points[1] = QPoint(x + w, y + h);
 
              // 将箭头上端点绘制在左端点和右端点之间且比左右端点朝上h的地方
              points[2] = QPoint(x + w / 2, y);
          } 
         else   // 如果是下箭头
            { // PE_SpinBoxDown
   
            // 将左端点绘制在矩形左上顶点处
              points[0] = QPoint(x, y);
 
              // 将右端点绘制在和左端点同一高度,但比左端点向右w即矩形宽度的1/2处
              points[1] = QPoint(x + w, y);
 
              // 将箭头下端点绘制在左右端点的中间且朝下为矩形高度的一半处
              points[2] = QPoint(x + w / 2, y + h);
          }
 
          // 当QSpinBox可用和不可用时,用不同的画笔、画刷绘制,以展示可用或不可用的状态
          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);
      }
  }

        为了实现绘制上下箭头,QSpinBox 用 PE_IndicatorSpinUp 和 PE_IndicatorSpinDown表示上下箭头。上面代码实现绘制上下箭头的drawPrimitive函数的重写,从而实现自己想要的箭头的样式。码绘制出的pin box的箭头效果类似如下:

图3:自绘制的spin box的上下箭头

6.风格函数

QStyle类的相关函数执行窗体部分区域绘制,像:drawItemText(), drawItemPixmap(), drawPrimitive(), drawControl(), drawComplexControl()函数等。

大部分QStyle类的绘制函数接受4个参数:

  1. 一个枚举值,该枚举值指定哪种类型的图形元素被绘制。
  2. 一个QStyleOption 类型参数。该参数指示怎么绘制、在哪个地方绘制条目1中的提到的图形元素。
  3. 一个QPainter类型参数。该参数被用来绘制条目1中的提到的图形元素。
  4. 一个QWidget类型参数。该参数是可选的。绘制过程在该参数表示的widget上执行。

并不是所有的widget会发送一个指针给自己。如果style option发送给函数的参数种并没有包含你想要的绘制信息,你应该检查widget的实现是否发送了一个指向自己的指针,如下:

  const QSpinBox *spinBox = qobject_cast<const QSpinBox *>(widget);
      if (spinBox)  // 是QSpinBox
      {
      ...
      }
  •  drawPrimitive函数

该函数定义如下:

[pure virtual] void QStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const

功能说明如下:

    该函数用指定的painter和style options绘制图元元素,即上文提到PE_开头的元素。

  • drawControl函数

该函数定义如下:

[pure virtual] void QStyle::drawControl(QStyle::ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const

 该函数用指定的painter和style options绘制元素,即上文提到CE_开头的元素。

  • drawComplexControl函数
[pure virtual] void QStyle::drawComplexControl(QStyle::ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget = nullptr) const

该函数用指定的painter和style options绘制复杂控件,即上文提到CC_开头的元素。 

     QStyle类提供很多帮助函数。这些函数在绘制窗体元素时经常被用到。drawItemText()函数在指定的矩形内绘制文本,该函数有一个QPalette对象为参数。drawItemPixmap()函数用于在指定的矩形内绘制并对其一个pixmap对象。

       其它的 QStyle类函数在绘制时做了很多计算功能。widgets用这些函数计算size hints和外包围矩形。

  • subElementRect
[pure virtual] QRect QStyle::subElementRect(QStyle::SubElement element, const QStyleOption *option, const QWidget *widget = nullptr) const

       该函数接收一个QStyle::SubElement类型的子元素,并为类型为QStyleOption的option参数指定的绘制元素返回一个子区域(以矩形表示),这样QStyle就知道接下来需要绘制的不同子元素在什么区域。返回的子区域矩形以屏幕坐标表示。widget参数是可选的,通常被用来帮助检测子区域。QStyleOption类型为option参数能够用qstyleoption_cast()函数转为合适的类型,如:QStyleOptionFocusRect

  • subControlRect

       该函数被用来计算复杂控件中的子控件占据的外包围矩形。当实现新style时,应该重写该函数以计算外包围矩形,这样算出的矩形就和其祖先类占据的矩形不同,从而实现定制化的风格。

[pure virtual] QRect QStyle::subControlRect(QStyle::ComplexControl control, const QStyleOptionComplex *option, QStyle::SubControl subControl, const QWidget *widget = nullptr) const

       返回指定复杂控件(由第1个参数)中的子控件(第3个参数指定)占据的矩形,这个矩形以屏幕坐标表示。参数option指定了绘制复杂控件需要用到的一些绘制信息,如:复杂控件的状态、调色板、方向布局器等,其指向QStyleOptionComplex类或其子类,该参数能够用qstyleoption_cast()函数转为合适的类型。widget参数是可选的,能包含一些附加信息。当实现一种新风格时,需要重写该函数且计算出的一个和基类不同的外包围矩形。

  •  pixelMetric()
[pure virtual] int QStyle::pixelMetric(QStyle::PixelMetric metric, const QStyleOption *option = nullptr, const QWidget *widget = nullptr) const

      该函数返回像素度量。这是一个和风格相关的且以屏幕像素为单位的大小尺寸。它接受一个QSQtyle::PixelMetric类型的枚举值,返回以正确的尺寸度量。

  • hitTestComplexControl
 QStyle::SubControl QStyle::hitTestComplexControl(QStyle::ComplexControl control, const QStyleOptionComplex *option, const QPoint &position, const QWidget *widget = nullptr) const

     当鼠标光标在一个复杂控件上方悬浮时,该函数返回光标所在复杂控件的子控件,即鼠标落在复杂控件中的哪个子控件上。通常,利用子控件的subControlRect()返回的外包围矩形,然后判断矩形是否包含鼠标光标(即通过QRect的contains函数判断)。

     QStyle提供了polish()unpolish()函数。所有widgets在被显示之前,都会调用polish()函数,在隐藏之后,会调用unpolish()函数。可以利用这些函数在widget上设置一些属性或者做些其它工作。例如:如果你需要知道鼠标是否在widge上悬浮,可以通过QWidget::setAttribute()函数设置Qt::WA_Hover属性,之后,State_MouseOver状态标志将会在widget的QStyleOption参数中设置。

     QStyle有很多静态帮助函数,这些函数能够处理一些常用和困难的工作。它们能够依据slider的值从而计算出slider滑块位置,变换外包围矩形、以布局器相反的方向绘制文本。

       自定义风格时,常用的做法是:对不同于基类风格的子类元素,重写QStyle的虚函数,对于其它的、和基类风格相同的子类元素,则简单地使用从基类继承过来的实现即可。

7. 调色板 

     每种style提供了颜色,其实就是调色板(QPalette)下的QBrush,它被用于绘制widgets。调色板(QPalette)中有表示widget不同状态下 的颜色集合QPalette::ColorGroup,如下:


QPalette::Disabled
QPalette::Active
QPalette::Inactive
QPalette::Normal

      分别表示widget禁用、激活、非激活、正常 状态下的颜色表示。状态能通过State_Active、 State_Enabled state枚举标志(在QStyle::State中定义)查询到。每个集合包含某个颜色角色,这些颜色角色在QPalette::ColorRole枚举中定义。颜色角色描述了在某种情况下哪些颜色应该被使用。

     QPalett 为widget提供调色板支持,为widget在不同状态、不同颜色角色提供保存颜色支持。某个style中的调色板可通过standardPalette()函数返回。新的style通过QApplication::setStyle()或QWidget::setStyle()设置到应用程序或widget上时,标准调色板不会自动安装,所以需要通过QApplication::setPalette()或QWidget::setPalette()人为设置。

         不推荐使用颜色硬编码。应用程序和每个widgets能设置它们自己的调色板,也能够用它们自己style中的调色板用于绘制。更多调色板知识参见《QPalette类的使用和说明

8.实现style遇到的问题

      当实现styles时,很多问题需要考虑到。需要通览widgets的实现代码、widgets基类及祖先类代码。这是因为widget实现风格各自不同(所以才叫自定义风格),这种不同是通过重写基类或祖先类的虚函数来实现,这个过程能够影响到绘制的状态,比如:改变QPainter状态而没恢复到改变之前的状态、绘制某些元素而没有用合适的像素度量尺寸或子元素。

        当实现自定义风格时,建议不要更改由QStyle::sizeFromContents()函数返回的和widget有关的尺寸大小,尽量在QCommonStyle函数的实现去修改。如果在实现自定义风格时一些更改不可避免,应该尽可能让这些更改的范围很小,因为如果widget的布局等看起来变化相当的大,应用程序的开发实现可能很困难的。

9.设计和实现

       在设计实现风格时,第一步是选择一个基类,通常选择QCommonStyle的子类,因为它不仅仅实现了绘制功能,还实现了我们想要的大部分功能。

        自定义风格的实现最好限制在一个类中。因为在一个类文件中实现风格很方便。通过在某些函数中切换需要绘制的元素的标识符,从而实现不同元素的绘制,从而保证这些函数数量最少,这将导致某些函数的函数体很大,代码很长,但代码分布在很多switch语句中,因为绘制每个元素而被switch切分为几段,所以这些绘制的函数虽然很长,很庞大,但依然容易阅读、维护。

10.QCheckBox风格实现

         在本节,通过详细讲解QCheckBox的风格实现,从而使大家知道风格实现的机制,这将会给大家在实现其它widgets风格时给予很好的启发。对于其它widget风格的实现,不作深入分析,和QCheckBox的风格实现大同小异,当实现其它widgets风格时,可以参考这里的QCheckBox的风格实现。下面代码块是对QCommonStyle类代码进行了编辑更改,移除了某些和QCheckBox的风格实现无直接关联的代码,我们看看QCheckBox是怎样构建它自己的风格选项 QStyleOptionButton的。


      opt.initFrom(q);
          if (down)
          opt.state |= QStyle::State_Sunken;
      if (tristate && noChange)
          opt.state |= QStyle::State_NoChange;
      else
          opt.state |= checked ? QStyle::State_On :
          QStyle::State_Off;
      if (q->testAttribute(Qt::WA_Hover) &&  q->underMouse()) {
          if (hovering)
          opt.state |= QStyle::State_MouseOver;
          else
          opt.state &= ~QStyle::State_MouseOver;
      }
      opt.text = text;
      opt.icon = icon;
      opt.iconSize = q->iconSize();

        解释说明:

  1. 首先opt是一个QStyleOption的子类QStyleOptionButton类型的对象。QStyleOptionButton包含QStyle绘制函数在绘制QPushButton、 QCheckBox、 QRadioButton三种类型按钮时需要的信息。opt的initFrom函数初始化所有widget常用的一些属性信息,如:状态、布局方向、调色板、字体、外包围矩形等信息。
  2. 当用户按下按钮方框时,QStyleOption类的down变量就变为true;那么就将opt.state设置为QStyle::State_Sunken状态,即让按钮呈现凹下或被按下状态。
  3. 当checkbox是一个三态按钮且局部被勾选时,则QStyle::State_NoChange状态位被设置。
  4. 当checkbox不是一个三态按钮且被checked为true时,则QStyle::State_On状态为被设置。否则QStyle::State_Off状态位被设置。
  5. 如果鼠标悬浮在checkbox上方且widget设置过Qt::WA_Hover属性(一般通过在QStyle::polish函数中调用QWidget::setAttribute()函数设置),则QStyle::State_MouseOver状态标志被设置,否则被取消。
  6. 接下来设置按钮中的文本、icon、icon大小。

       QStyleOptioninitFrom()函数安装了所有widgets都具有的通用属性,代码实现如下:


      state = QStyle::State_None;
      if (widget->isEnabled())
          state |= QStyle::State_Enabled;
      if (widget->hasFocus())
          state |= QStyle::State_HasFocus;
      if (widget->window()->testAttribute(Qt::WA_KeyboardFocusChange))
          state |= QStyle::State_KeyboardFocusChange;
      if (widget->underMouse())
          state |= QStyle::State_MouseOver;
      if (widget->window()->isActiveWindow())
          state |= QStyle::State_Active;
  #ifdef QT_KEYPAD_NAVIGATION
      if (widget->hasEditFocus())
          state |= QStyle::State_HasEditFocus;
  #endif

      direction = widget->layoutDirection();
      rect = widget->rect();
      palette = widget->palette();
      fontMetrics = widget->fontMetrics();

说明:

       当widget是可用状态时,State_Enabled标志位被设置;当widget具有键盘焦点时,State_HasFocus标志位被设置;当widget是某个活动窗体的子窗体时,State_Active标志位被设置;如果widget设置过WA_HoverEnabled标志且鼠标在widget上方时,则State_MouseOver标志位被设置;当keypad navigation被启用且widget有编辑焦点,则State_HasEditFocus标志位被设置。接下来设置了widget的布局器方法、外包围矩形、调色板、字体尺寸信息。经过上面代码设置后,checkbox状态如下图:

图4:自绘制的check box

 上图中的checkbox在style option中将会有如下状态标志:

状态标志

是否设置

State_Sunken

Yes

State_NoChange

No

State_On

Yes

State_Off

No

State_MouseOver

Yes

State_Enabled

Yes

State_HasFocus

Yes

State_KeyboardFocusChange

No

State_Active

Yes

QCheckBoxQWidget::paintEvent()函数中用QStyleOption类型的对象opt和QStylePainter类型对象p绘制它自己。QStylePainter类是绘制风格元素非常方便的类,它封装QStyle中许多用来绘图的函数。QCheckBox绘制它自己的代码如下:


      QStylePainter p(this);
      QStyleOptionButton opt = d->getStyleOption();
      p.drawControl(QStyle::CE_CheckBox, opt);

QCommonStyle处理CE_CheckBox元素。QCheckBox有两个子元素:PE_CheckBoxIndicator(表示CheckBox的指示器,即CheckBox中的选中或取消选中的方框)和CE_CheckBoxLabel(表示CheckBox的文本内容)。QCommonStyle实现了这些子元素的外包围矩形(由SE_CheckBoxIndicator和SE_CheckBoxContents枚举值指定)。QCommonStyle实现代码如下:


      QStyleOptionButton subopt = *btn;
      subopt.rect = subElementRect(SE_CheckBoxIndicator, btn, widget);
      drawPrimitive(PE_IndicatorCheckBox, &subopt, p, widget);
      subopt.rect = subElementRect(SE_CheckBoxContents, btn, widget);
      drawControl(CE_CheckBoxLabel, &subopt, p, widget);

      if (btn->state & State_HasFocus) {
          QStyleOptionFocusRect fropt;
          fropt.QStyleOption::operator=(*btn);
          fropt.rect = subElementRect(SE_CheckBoxFocusRect, btn, widget);
          drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
      }

       从上面代码可以看到,QCommonStyle获取到Check Box的两个子元素的外包围矩形,然后绘制它们。如果Check Box具有焦点,则绘制焦点边框。

        疑问:很多读者可能会问,为何绘制第1个子元素时,调用的是drawPrimitive,而绘制第2个子元素时调用的是drawControl?为何不是都调用drawPrimitive或都调用drawControl?这和Check Box控件的元素层次结构有关(参见4节 “风格元素”,元素层次描述)。Check Box中的子元素树状层次结构如下:

图5:check box子元素层次树

       位于层次树叶子节点的才是构成check box的子控件,如:上图的 PE_IndicatorCheckBox、CE_CheckBoxLabel、PE_FrameFocusRect。以PE开头的子元素是CE_CheckBox控件的一部分,是调用drawPrimitive函数绘制的,如: PE_IndicatorCheckBox、PE_FrameFocusRect。以CE开头的子元素表示其同CE_CheckBox一样,也是一个控件元素,只不过其作为CE_CheckBox控件的子元素而存在,也就是说CE_CheckBoxLabel还可以向下分出叶子节点来。CE_开头的元素调用drawControl函数绘制的,如:CE_CheckBoxLabel。以SE开头的树节点,如:SE_CheckBoxIndicator、E_CheckBoxContents、SE_CheckBoxFocusRect都不是子控件,而是表示子控件(PE_IndicatorCheckBox、CE_CheckBoxLabel、PE_FrameFocusRect)占据的外包围矩形,外包围矩形的计算和获取是通过调用subElementRect函数实现的。Qt更多widgets的层次树参见后文描述。

     让我们来看看QCommonStyle是怎么绘制CE_CheckboxLabel的,代码如下:

 const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt);
      uint alignment = visualAlignment(btn->direction, Qt::AlignLeft | Qt::AlignVCenter);

      if (!styleHint(SH_UnderlineShortcut, btn, widget))
          alignment |= Qt::TextHideMnemonic;
      QPixmap pix;
      QRect textRect = btn->rect;
      if (!btn->icon.isNull()) {
          pix = btn->icon.pixmap(btn->iconSize, btn->state & State_Enabled ? QIcon::Normal : QIcon::Disabled);
          drawItemPixmap(p, btn->rect, alignment, pix);
          if (btn->direction == Qt::RightToLeft)
              textRect.setRight(textRect.right() - btn->iconSize.width() - 4);
          else
              textRect.setLeft(textRect.left() + btn->iconSize.width() + 4);
      }
      if (!btn->text.isEmpty()){
          drawItemText(p, textRect, alignment | Qt::TextShowMnemonic,
              btn->palette, btn->state & State_Enabled, btn->text, QPalette::WindowText);
      }

         visualAlignment()函数根据check box的layout direction属性,调整其上的文本对齐方式。如果check box设置过icon,则接下来绘制这个icon并调为text调整了间隙。以对其方式、布局方向、和文本提示(这里是指热键是否显示,热键是否加下划线)、调色板颜色为参数调用drawItemText()绘制check box上的文本。

        从上面可以看到,绘制check box中的文本,显得有点复杂,幸运的是,这些绘制文本的工作都由基类来处理,在平时我们不需要过多关注,这里只是拿出来说说其内部的实现机制而已。

        上面分析了QCommonStyle是怎么绘制CE_CheckboxLabel的,让我们再来看看

QCommonStyle是怎么绘制CE_CheckBoxIndicator(即用于勾选的方框)的,代码如下:


          case PE_IndicatorCheckBox: {
              painter->save();
              drawButtonBackground(option, painter, true);

              if (option->state & State_Enabled &&
                  option->state & State_MouseOver &&
                  !(option->state & State_Sunken)) {
                  painter->setPen(option->palette.color(QPalette::Button));
                  QRect rect = option->rect.adjusted(1, 1, -2, -2);
                  painter->drawRect(rect);
                  rect = rect.adjusted(1, 1, -1, -1);
                  painter->drawRect(rect);
              }

              if (option->state & State_On) {
                  QImage image(":/images/checkboxchecked.png");
                  painter->drawImage(option->rect.topLeft(), image);
              }
              painter->restore();
              break;

        我们先将painter保存起来。虽然这不是必须的,但当调用完该段代码后,QCommonStyle需要painter的状态还是同调用本代码之前的状态一样(这也可以通过调用某些函数实现,而不一定必须调用save和下面的restore)。然后调用drawButtonBackground()函数绘制了check box indicator 。drawButtonBackground()是一个帮助函数,该函数绘制背景色和push buttons、check boxes的边框,等下我们将会分析drawButtonBackground()函数。然后检测鼠标是否悬浮在check box上。如果是,则绘制出box未处于按下状态下的边框。

         在下面代码,我们为check box indicator贴了一张PNG图片。我们也检查widget是否是不可用状态。如果是不可用状态,用另一张图片贴到check box indicator上且用一个不可用的颜色绘制。

  void JavaStyle::drawButtonBackground(const QStyleOption *option,
                                       QPainter *painter, bool isCheckbox) const
  {
      QBrush buttonBrush = option->palette.button();
 
      // 按钮是否则处于下压,凹下状态
      bool sunken = option->state & State_Sunken;

      // 按钮是否处于禁用状态
      bool disabled = !(option->state & State_Enabled);

      // 按钮是否选中
      bool on = option->state & State_On;
     
      // 按钮不是下压且是可用状态且是pushbutton、radiobutton、Checkbox三种类型按钮中的某种按钮
      if (!sunken && !disabled && (!on || isCheckbox))

         // 以按钮所在外包围矩形创建一个渐变画刷
          buttonBrush = gradientBrush(option->rect);

          // 用渐变画刷填充按钮所在外包围矩形
          painter->fillRect(option->rect, buttonBrush);

          // 将按钮外包围矩形底边向上、右边向左减少一个像素,即外包围矩形底边、宽度都减少1像素。
          QRect rect = option->rect.adjusted(0, 0, -1, -1);

          // 如果是禁用状态,则用调色板禁用画笔
          if (disabled)
              painter->setPen(option->palette.color(QPalette::Disabled,
                                                    QPalette::WindowText));
          else // 如果是可用,则用调色板QPalette::Mid颜色
              painter->setPen(option->palette.color(QPalette::Mid));

          painter->drawRect(rect);

          if (sunken && !disabled) {
              drawSunkenButtonShadow(painter, rect,
                     option->palette.color(QPalette::Mid),
                     option->direction == Qt::RightToLeft);
      }
  }

  • 7
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: QStyle 是 Qt 框架中的,用于定义控件的外观和行为。如果想将 QStyle 对象转换为字符串,可以使用 QStyle 的 metaObject() 方法获取其元对象,然后调用元对象的 className() 方法获取名。 示例代码如下: ```c++ QStyle* myStyle = new QStyle(); QString styleName = myStyle->metaObject()->className(); qDebug() << styleName; // 输出 "QStyle" ``` 这样就可以将 QStyle 对象转换为字符串了。 ### 回答2: QStyle是Qt框架中的一个,用于提供用户界面的样式和外观。它可以用来定制应用程序窗口、按钮、文本框等控件的外观和风格。 要将QStyle对象转换为字符串,我们可以使用QStyle 的typename()方法。typename()方法返回一个QString,包含了QStyle型名。 以下是一个示例代码: ```cpp #include <QStyle> #include <QDebug> int main() { QStyle* style = new QStyle(); // 创建一个QStyle对象 QString styleTypeName = style->typeName(); // 将QStyle对象转换为字符串 qDebug() << "QStyle型名:" << styleTypeName; delete style; return 0; } ``` 运行这段代码后,会输出似以下的结果: ``` QStyle型名: QStyle ``` 这样就实现了将QStyle对象转换为字符串的功能。请注意,QStyle对象的字符串表示仅包含其型名,而不包含其他属性或方法。如果需要获取更多关于QStyle对象的信息,可以使用其他成员函数和属性来检索。 ### 回答3: QStyle是Qt框架中的一个,用于界面风格的管理和绘制。要将QStyle对象转换成字符串,可以使用QStyle的objectName属性来获取其名称,并通过QString的toStdString()方法将其转换为标准字符串。 下面是一个示例代码: ```cpp QStyle* style = QApplication::style(); // 获取当前应用程序的界面风格 QString styleName = style->objectName(); // 获取QStyle对象的名称 std::string styleStr = styleName.toStdString(); // 将QString转换为标准字符串 ``` 在这个例子中,首先获取当前应用程序的界面风格,然后使用objectName属性获取其名称,并将其转换为标准字符串。最终得到的styleStr就是QStyle对象的字符串表示。 需要注意的是,上述代码只能在已经创建了QApplication对象之后才能正常运行,因为QStyle的获取依赖于QApplication。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值