QStyle draw functions 範例
透過一個簡單的範例,展示 drawPrimitive 畫出來的元件,以及 SubElement 在實際案例的作用。
Categories:
這個範例將展示
- 畫出部分 Primitive Element ,了解他們個別長什麼樣子:
PE_IndicatorArrowUp
,PE_IndicatorItemViewItemCheck
,PE_IndicatorCheckBox
,PE_IndicatorRadioButton
,PE_IndicatorArrowRight
,PE_IndicatorProgressChunk
,PE_FrameMenu
。 - 如何使用 SubElement 與
subElementRect
Primitive Element
這裡使用最簡單的 QStyleOption
,讓每個 element 都套用相同的設定。事實上有些 element 需要搭配特定的 option,在 drawPrimitive 裡面有表格說明。
for i, pe in enumerate(styles_name):
name = pe
pe = eval("QStyle."+pe)
# use the most general option. which option should be used for each item are
# written in https://doc.qt.io/qt-6/qstyle.html#drawPrimitive
option = QStyleOption()
option.initFrom(self)
option.rect = QRect(10, 10+20*i, 20, 20)
self.style().drawPrimitive(pe, option, painter,)
painter.drawText(40, 10+20*(i+1), name)
Control Element 與 SubElement
模仿 Styles and Style Aware Widgets 的一段範例,畫出 checkbox 與 checkbox label。需要注意的地方是
btn_opt
原本的rect
很重要,有初始位置的概念在,會影響subElementRect
算出來的結果。subElementRect(QStyle.SE_CheckBoxContents, ...)
並沒有包含 text 的空間,原本算出來的rect
寬度只有1
,需要手動算文字寬度加上去,才能完整顯示文字。目前不清楚對 QCommonStyle 而言,調整文字寬度的地方在哪。
def draw_ce(self, ):
painter = QPainter(self)
# A QCheckBox, draws a PE_IndicatorCheckBox, a CE_CheckBoxLabel and a PE_FrameFocusRect.
btn_opt = QStyleOptionButton()
btn_opt.rect = QRect(10, 180, 20, 20)
subopt = QStyleOptionButton()
subopt.rect = self.style().subElementRect(QStyle.SE_CheckBoxIndicator, btn_opt, self)
print(subopt.rect)
self.style().drawPrimitive(QStyle.PE_IndicatorCheckBox, subopt, painter, self)
# Draw the checkbox label
subopt = QStyleOptionButton(btn_opt)
subopt.text = "This is label for checkbox"
font_metrics = QFontMetrics(subopt.fontMetrics)
text_width = font_metrics.width(subopt.text)
subopt.rect = self.style().subElementRect(QStyle.SE_CheckBoxContents, btn_opt, self)
print("Before set text width", subopt.rect)
subopt.rect.setWidth(subopt.rect.width() + text_width)
print("After set text width", subopt.rect)
self.style().drawControl(QStyle.CE_CheckBoxLabel, subopt, painter, self)
完整程式
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class App(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def draw_pe(self, ):
painter = QPainter(self)
styles_name = ( "PE_IndicatorArrowUp", "PE_IndicatorItemViewItemCheck", "PE_IndicatorCheckBox",
"PE_IndicatorRadioButton", "PE_IndicatorArrowRight", "PE_IndicatorProgressChunk",
"PE_FrameMenu"
)
for i, pe in enumerate(styles_name):
name = pe
pe = eval("QStyle."+pe)
# use the most general option. which option should be used for each item are
# written in https://doc.qt.io/qt-6/qstyle.html#drawPrimitive
option = QStyleOption()
option.initFrom(self)
option.rect = QRect(10, 10+20*i, 20, 20)
self.style().drawPrimitive(pe, option, painter,)
painter.drawText(40, 10+20*(i+1), name)
def draw_ce(self, ):
painter = QPainter(self)
# A QCheckBox, draws a PE_IndicatorCheckBox, a CE_CheckBoxLabel and a PE_FrameFocusRect.
btn_opt = QStyleOptionButton()
btn_opt.rect = QRect(10, 180, 20, 20)
subopt = QStyleOptionButton()
subopt.rect = self.style().subElementRect(QStyle.SE_CheckBoxIndicator, btn_opt, self)
print(subopt.rect)
self.style().drawPrimitive(QStyle.PE_IndicatorCheckBox, subopt, painter, self)
# Draw the checkbox label
subopt = QStyleOptionButton(btn_opt)
subopt.text = "This is label for checkbox"
font_metrics = QFontMetrics(subopt.fontMetrics)
text_width = font_metrics.width(subopt.text)
subopt.rect = self.style().subElementRect(QStyle.SE_CheckBoxContents, btn_opt, self)
print("Before set text width", subopt.rect)
subopt.rect.setWidth(subopt.rect.width() + text_width)
print("After set text width", subopt.rect)
self.style().drawControl(QStyle.CE_CheckBoxLabel, subopt, painter, self)
def paintEvent(self, event):
super().paintEvent(event)
self.draw_pe()
self.draw_ce()
def initUI(self):
layout = QVBoxLayout()
self.setLayout(layout)
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('PE_FrameFocusRect Example')
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyle('Windows')
ex = App()
ex.show()
sys.exit(app.exec_())