Qt for Python リファレンスガイド QPainter
QPainter
では、Qtの基本でしばしば紹介されるペインターについてちょっと見ていきたいと思います。
「QPainterはGUIプログラムが要求する描画のほとんどを行うための、高度に洗練された関数を提供します。単純な線から、①pieやchordのような複雑な図形の全てを描くことができます。②普通に、自然の座標軸内で描きますが、ビュー変換とワールド変換も行います。QPainterはQPaintDeviceクラスを継承しているすべてのオブジェクト上で実行することができます。」
①pieとchord
pie
rectangle = QRectF(10.0, 20.0, 80.0, 60.0)
startAngle = 30 * 16
spanAngle = 120 * 16
painter = QPainter(self)
painter.drawPie(rectangle, startAngle, spanAngle)
rectangle = QRectF(10.0, 20.0, 80.0, 60.0)
startAngle = 30 * 16
spanAngle = 120 * 16
painter = QPainter(self)
painter.drawChord(rect, startAngle, spanAngle)
②ビュー変換とワールド変換
別記事で扱う予定です。
「①QPainterの一般的な使用はウィジェットのペイントイベント内です。ペインターの構成とカスタマイズ(例えば、ペンとブラッシュ)を行い、それから描きます。②描いた後はQPainterオブジェクトを破壊することを忘れないでください。例えば、」
def paintEvent(self, arg__0):
painter = QPainter(self)
painter.setPen(Qt.blue)
painter.setFont(QFont("Arial", 30))
painter.drawText(rect(), Qt.AlignCenter, "Qt")
①setPen, setFont等でQPainterオブジェクトの構成とカスタマイズを行い、それからdrawTextで描いています。ここでは"Qt"という文字列です。
②描いた後は、painter.end()を呼び出すことがよくあるのですが、このコードではそういうことは行われていないようです。
「QPainterの主要な機能は描くことですが、①そのクラスはQPainterの構成と表示の質をカスタマイズすることもできるいくつかの関数も提供します。そして②クリッピングをすることができるものもあります。加えて、③ペインターのコンポジションモードを特定することによって、どんな異なる形のものでも、共に合成して管理することができます。」
①RenderHintという列挙型でまとめられています。
②setClipRegionやsetClipRectで、指定範囲外の部分を切り取ることができます。
③コンポジションモードは後で扱います。
「isActive関数はペインターがアクティブであるかどうかを示します。あるペインターはbegin関数によってアクティブ化され、そして、QPaintDeviceの引数を取ります。end関数は、デストラクタで、ペインターを非アクティブ化します。」
「QPaintDeviceとQPaintEngineクラスと共に、QPainterはQtのペイントシステムの基盤になります。QPainterは描画命令を行うために使われるクラスです。QPaintDeviceはQPainterを使って描かれるデバイスを表現します。QPaintEngineはペインターが異なるタイプのデバイス上に描くために使うインターフェースを提供します。もしペインターがアクティブであれば、device()メソッドはペインターがペイントしているペイントデバイスを返しますし、paintEngine()メソッドは、ペインターが現在実行しているペイントエンジンを返します。①詳細は、Paint System - Qt for Pythonを見てください。」
①PaintSystem
「たまにそれは他の誰かに独特のQPaintDeviceでペイントさせることが望ましいです。QPainter は、これを行うための静的関数 setRedirected() をサポートしています。」
よくわからなかったので翻訳機で翻訳しました。ここは一旦置いておきます。
「警告
ペイントデバイスがとあるウィジェットであるとき、QPainterは、paintEvent関数内でのみ、あるいは、paintEventによってコールされた関数内でのみ利用できます。」
「QPainterにはあなたの好みで描くようカスタマイズできるいくつかの設定があります。」
「fontメソッドはテキストを描くために使われるフォントです。もしペインターがアクティブであるならば、現在時点でセットされたフォントについて、そしてメトリクスの情報を、fontInfoメソッドとfontMetricsメソッドによって、それぞれ取得することができます。」
「ブラシは図形を塗りつぶすために使われる色やパターンを定義します。」
「ペンは線や境界線を描くために使われる色や点刻を定義します。」
「backgroundMode関数はbackground()メソッドがあるかどうかを定義します。それはOpaqueModeか、TransparentModeのどちらかです。」
「バックグラウンドメソッドはbackgroundMode関数がOpaqueModeであり、pen()が点刻であるときにのみ適用します。その場合、点刻内の背景ピクセルの色を示します。」
「brushOrigin()メソッドは、タイル化したブラシの原点を定義します。普通はウィジェットの背景の原点です。」
「viewport(), window(), worldTransform()はペインターの座標変換システムです。詳細は、座標変換セクションと座標システムのドキュメントを見てください。」
「hasClippingメソッドはペインターがクリップするかどうかを教えます。ペイントデバイスクリップも。もしペインターがクリップするのであれば、clipRegionメソッドの値にクリップします。」
クリップとは、指定された領域の外部を全部切り取ることを言います。第一章で、さりげなくsetClipRegionを使った例をお示ししたかと思いますが、指定したQRegionの部分以外のところは、切り取られていましたね。
layoutDirection()メソッドはテキストを描画するときペインターによって使われるレイアウトの方向を定義します。
worldMatrixEnabledメソッドはワールド変換が可能であるかどうかを教えます。
viewTransformEnabledメソッドはビュー変換が可能であるかどうかを教えます。
これらの設定のいくつかは、いくつかのペイントデバイス内のセッティングを反映していることに注意してください。例えば、fontです。begin関数は(あるいはQPainterのコンストラクタも同じく)これらのアトリビュートを、ペイントデバイスからコピーします。
save関数を使うことによって、いつでもQPainterの状態をセーブすることができます。内部のスタック上に利用可能なセッティング全てを保存します。restore関数はそれらを取り出して返します。
Drawing
(https://doc.qt.io/qtforpython-6/PySide6/QtGui/QPainter.html#drawing)
「QPainterは最もプリミティブなものを描くための関数を提供する。drawPoint() , drawPoints() , drawLine() , drawRect() , drawRoundedRect() , drawEllipse() , drawArc() , drawPie() , drawChord() , drawPolyline() , drawPolygon() , drawConvexPolygon() and drawCubicBezier()です。」
プリミティブとは基本要素のことです。コンピュータグラフィックスの図形を構成する点や線を指します。
「drawRectsとdrawLinesという二つの便利な関数があります。QRectあるいはQLineの配列内の与えられた数の矩形とラインを描きます。」
「QPainterクラスはfillRect関数も提供し、与えられたQRectを、与えられたQBrushで塗りつぶします。そしてeraseRect関数は、与えられた矩形の中にある領域を消去します。」
「これらの関数は、整数型と浮動小数点数型の関数がそれぞれ用意されています。」
Basic Drawing Example
基本描画サンプル
基本描画サンプルはQPainterクラスを使ってバラエティに富んだスタイルで基本的なグラフィックプリミティブの表示の仕方を示します。
このサンプルコードは、PySideのエグザンプルにも存在しています。
「もし複雑な形を描く必要がある、特に繰り返して行う必要がある場合、QPainterPathとdrawPathメソッドを使って作ることも考慮に入れてください。」
Painter Paths example
ペインターパスサンプル
「QPainterPathクラスはペイントオペレーションのコンテナを提供します。グラフィカルな図形を構築することと再利用を可能にします。」
ペインターパスサンプルは表示する複雑な図形を組みたてるのにどのようなペインターパスが利用されうるかを示します。
残念ながら、このコードはPySideのエグザンプルとしては存在しませんでした。なので、これは私が作って置いておきます。
from PySide6.QtWidgets import (QApplication, QWidget, QLabel,
QComboBox, QSpinBox, QGridLayout,
QSizePolicy)
from PySide6.QtGui import QPainterPath, QLinearGradient, QColor,QPen, QFont, QPalette, QPainter
from PySide6.QtCore import Qt, QSize
import math, sys
class Window(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.renderAreas = []
rectPath = QPainterPath()
rectPath.moveTo(20.0, 30.0)
rectPath.lineTo(80.0, 30.0)
rectPath.lineTo(80.0, 70.0)
rectPath.lineTo(20.0, 70.0)
rectPath.closeSubpath()
roundRectPath = QPainterPath()
roundRectPath.moveTo(80.0, 35.0)
roundRectPath.arcTo(70.0, 35.0, 10.0, 10.0, 0.0, 90.0)
roundRectPath.lineTo(25.0, 30.0)
roundRectPath.arcTo(20.0, 30.0, 10.0, 10.0, 90.0, 90.0)
roundRectPath.lineTo(20.0, 65.0)
roundRectPath.arcTo(20.0, 60.0, 10.0, 10.0, 180.0, 90.0)
roundRectPath.lineTo(75.0, 70.0)
roundRectPath.arcTo(70.0, 60.0, 10.0, 10.0, 270.0, 90.0)
roundRectPath.closeSubpath()
ellipsePath = QPainterPath()
ellipsePath.moveTo(80.0, 50.0)
ellipsePath.arcTo(20.0, 30.0, 60.0, 40.0, 0.0, 360.0)
piePath = QPainterPath()
piePath.moveTo(50.0, 50.0)
piePath.arcTo(20.0, 30.0, 60.0, 40.0, 60.0, 240.0)
piePath.closeSubpath()
polygonPath = QPainterPath()
polygonPath.moveTo(10.0, 80.0)
polygonPath.lineTo(20.0, 10.0)
polygonPath.lineTo(80.0, 30.0)
polygonPath.lineTo(90.0, 70.0)
polygonPath.closeSubpath()
groupPath = QPainterPath()
groupPath.moveTo(60.0, 40.0)
groupPath.arcTo(20.0, 20.0, 40.0, 40.0, 0.0, 360.0)
groupPath.moveTo(40.0, 40.0)
groupPath.lineTo(40.0, 80.0)
groupPath.lineTo(80.0, 80.0)
groupPath.lineTo(80.0, 40.0)
groupPath.closeSubpath()
textPath = QPainterPath()
timesFont = QFont("Times", 50)
timesFont.setStyleStrategy(QFont.ForceOutline)
textPath.addText(10, 70, timesFont, self.tr("Qt"))
bezierPath = QPainterPath()
bezierPath.moveTo(20, 30)
bezierPath.cubicTo(80, 0, 50, 50, 80, 80)
starPath = QPainterPath()
starPath.moveTo(90, 50)
for i in range(5):
starPath.lineTo(50 + 40*math.cos(0.8*i*math.pi),
50 + 40*math.sin(0.8*i*math.pi))
starPath.closeSubpath()
self.renderAreas.append(RenderArea(rectPath))
self.renderAreas.append(RenderArea(roundRectPath))
self.renderAreas.append(RenderArea(ellipsePath))
self.renderAreas.append(RenderArea(piePath))
self.renderAreas.append(RenderArea(polygonPath))
self.renderAreas.append(RenderArea(groupPath))
self.renderAreas.append(RenderArea(textPath))
self.renderAreas.append(RenderArea(bezierPath))
self.renderAreas.append(RenderArea(starPath))
self.fillRuleComboBox = QComboBox()
self.fillRuleComboBox.addItem(self.tr("Odd Even"))
self.fillRuleComboBox.addItem(self.tr("Winding"))
self.fillRuleLabel = QLabel(self.tr("Fill &Rule:"))
self.fillRuleLabel.setBuddy(self.fillRuleComboBox)
self.fillColor1ComboBox = QComboBox()
self.populateWithColors(self.fillColor1ComboBox)
self.fillColor1ComboBox.setCurrentIndex(self.fillColor1ComboBox.findText("mediumslateblue"))
self.fillColor2ComboBox = QComboBox()
self.populateWithColors(self.fillColor2ComboBox)
self.fillColor2ComboBox.setCurrentIndex(self.fillColor2ComboBox.findText("cornsilk"))
self.fillGradientLabel = QLabel(self.tr("&Fill Gradient"))
self.fillGradientLabel.setBuddy(self.fillColor1ComboBox)
self.fillToLabel = QLabel(self.tr("to"))
self.fillToLabel.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self.penWidthSpinBox = QSpinBox(minimum=0, maximum=20)
self.penWidthLabel = QLabel(self.tr("&Pen"))
self.penWidthLabel.setBuddy(self.penWidthSpinBox)
self.penColorComboBox = QComboBox()
self.populateWithColors(self.penColorComboBox)
self.penColorComboBox.setCurrentIndex(self.penColorComboBox.findText("darkslateblue"))
self.penColorLabel = QLabel()
self.penColorLabel.setBuddy(self.penColorComboBox)
self.fillRuleComboBox.activated.connect(self.fillRuleChanged)
self.fillColor1ComboBox.activated.connect(self.fillGradientChanged)
self.fillColor2ComboBox.activated.connect(self.fillGradientChanged)
self.penColorComboBox.activated.connect(self.penColorChanged)
topLayout = QGridLayout()
self.rotationAngleSpinBox = QSpinBox(maximum=359)
self.rotationAngleSpinBox.setWrapping(True)
self.rotationAngleSpinBox.setSuffix("\xB0")
self.rotationAngleLabel = QLabel(self.tr("&Rotation Angle"))
self.rotationAngleLabel.setBuddy(self.rotationAngleSpinBox)
for area in self.renderAreas:
self.penWidthSpinBox.valueChanged.connect(area.setPenWidth)
self.rotationAngleSpinBox.valueChanged.connect(area.setRotationAngle)
for num, area in enumerate(self.renderAreas):
topLayout.addWidget(area, int(num/3), num%3)
mainLayout = QGridLayout()
mainLayout.addLayout(topLayout, 0, 0, 1, 4)
mainLayout.addWidget(self.fillRuleLabel, 1, 0)
mainLayout.addWidget(self.fillRuleComboBox, 1, 1, 1, 3)
mainLayout.addWidget(self.fillGradientLabel, 2, 0)
mainLayout.addWidget(self.fillColor1ComboBox, 2, 1)
mainLayout.addWidget(self.fillToLabel, 2, 2)
mainLayout.addWidget(self.fillColor2ComboBox, 2, 3)
mainLayout.addWidget(self.penWidthLabel, 3, 0)
mainLayout.addWidget(self.penWidthSpinBox, 3, 1, 1, 3)
mainLayout.addWidget(self.penColorLabel, 4, 0)
mainLayout.addWidget(self.penColorComboBox, 4, 1, 1, 3)
mainLayout.addWidget(self.rotationAngleLabel, 5, 0)
mainLayout.addWidget(self.rotationAngleSpinBox, 5, 1, 1, 3)
self.setLayout(mainLayout)
self.fillRuleChanged()
self.fillGradientChanged()
self.penColorChanged()
self.penWidthSpinBox.setValue(2)
self.setWindowTitle("Painter Paths")
def fillRuleChanged(self):
rule = self.currentItemData(self.fillRuleComboBox)
for area in self.renderAreas:
area.setFillRule(rule)
def fillGradientChanged(self):
color1 = self.currentItemData(self.fillColor1ComboBox)
color2 = self.currentItemData(self.fillColor2ComboBox)
for area in self.renderAreas:
area.setFillGradient(color1, color2)
def penColorChanged(self):
color = self.currentItemData(self.penColorComboBox)
for area in self.renderAreas:
area.setPenColor(color)
def populateWithColors(self, comboBox):
colorNames = QColor.colorNames()
for name in colorNames:
comboBox.addItem(name, QColor(name))
def currentItemData(self, comboBox):
from PySide6.QtCore import QModelIndex
model = comboBox.model()
index = model.index(comboBox.currentIndex(), 0)
data = comboBox.model().data(index)
if comboBox is self.fillRuleComboBox:
data = Qt.FillRule(comboBox.currentIndex())
return data
class RenderArea(QWidget):
def __init__(self, path, parent=None):
super().__init__(parent)
self.path = path
self.fillColor1 = QColor()
self.fillColor2 = QColor()
self.penWidth = 1
self.penColor = QColor()
self.rotationAngle = 0
self.setBackgroundRole(QPalette.Base)
def minimumSizeHint(self):
return QSize(50, 50)
def sizeHint(self):
return QSize(100, 100)
def setFillRule(self, rule):
self.path.setFillRule(rule)
self.update()
def setFillGradient(self, color1, color2):
self.fillColor1 = color1
self.fillColor2 = color2
self.update()
def setPenWidth(self, width):
self.penWidth = width
self.update()
def setPenColor(self, color):
self.penColor = color
self.update()
def setRotationAngle(self, degrees):
self.rotationAngle = degrees
self.update()
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
painter.scale(self.width()/100.0, self.height()/100.0)
painter.translate(50.0, 50.0)
painter.rotate(-self.rotationAngle)
painter.translate(-50.0, -50.0)
pen = painter.pen()
pen.setColor(self.penColor)
pen.setWidth(self.penWidth)
pen.setStyle(Qt.SolidLine)
pen.setCapStyle(Qt.RoundCap)
pen.setJoinStyle(Qt.RoundJoin)
painter.setPen(pen)
gradient = QLinearGradient(0, 0, 0, 100)
gradient.setColorAt(0.0, self.fillColor1)
gradient.setColorAt(1.0, self.fillColor2)
painter.setBrush(gradient)
painter.drawPath(self.path)
def main():
app = QApplication()
window = Window()
window.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()
「QPainterも、与えられたQBrushそして、与えられたパスの輪郭を描くためのstrokePath関数で、塗りつぶしを行うfillPath関数を提供しています。」
「ベクターによるゆがみサンプルを見てください。QPainterPathを使って、テキストを描くためのより進んだベクターのテクニックの使い方を表示します。グラディエントサンプルは、Qt内で利用可能な異なる種類のグラディエントを表示します。パスストローキングサンプルは、利用可能なパターンの範囲を拡張するために、どのようにカスタムパターンが利用されうるのかを表示します。」
Vector Deformation
Gradients
Path Stroking
「テキストの描画は、drawText関数を使って行われます。粒度の高い位置取りを必要とする時、boudingRectメソッドが与えられたdrawTextメソッドコマンドがどこに描くのかを教えてくれます。」
これら3つのサンプルコードは、残念ながらPySide6には含まれていません。そこで、私なりに作ってみたのですが、C++とPythonではうまくいかないところがあります。ArthurStyleというスタイルを用いているのですが、Pythonの場合Sliderが表示されません。後、OpenGLの欄は無視しています。
まず、vector Deform Exampleです
このコードの前に、ArthurStyleとArthurFrameのインポートが必要になりますので、そちらも載せておきます。
ここから先は
¥ 300
Amazonギフトカード5,000円分が当たる
この記事が気に入ったらチップで応援してみませんか?