見出し画像

Qt for Python リファレンスガイド QPainter

QPainter

 では、Qtの基本でしばしば紹介されるペインターについてちょっと見ていきたいと思います。

QPainter provides highly optimized functions to do most of the drawing GUI programs require. It can draw everything from simple lines to complex shapes like pies and chords. It can also draw aligned text and pixmaps. Normally, it draws in a “natural” coordinate system, but it can also do view and world transformation. QPainter can operate on any object that inherits the QPaintDevice class.

QPainter - Qt for Python

「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)


pie
rectangle = QRectF(10.0, 20.0, 80.0, 60.0)
startAngle = 30 * 16
spanAngle = 120 * 16
painter = QPainter(self)
painter.drawChord(rect, startAngle, spanAngle)


chord

ビュー変換とワールド変換

 別記事で扱う予定です。

The common use of QPainter is inside a widget’s paint event: Construct and customize (e.g. set the pen or the brush) the painter. Then draw. Remember to destroy the QPainter object after drawing. For example:

QPainter - Qt for Python

 「①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()を呼び出すことがよくあるのですが、このコードではそういうことは行われていないようです。

The core functionality of QPainter is drawing, but the class also provide several functions that allows you to customize QPainter ‘s settings and its rendering quality, and others that enable clipping. In addition you can control how different shapes are merged together by specifying the painter’s composition mode.

QPainter - Qt for Python

 「QPainterの主要な機能は描くことですが、①そのクラスはQPainterの構成と表示の質をカスタマイズすることもできるいくつかの関数も提供します。そして②クリッピングをすることができるものもあります。加えて、③ペインターのコンポジションモードを特定することによって、どんな異なる形のものでも、共に合成して管理することができます。」

 The isActive() function indicates whether the painter is active. A painter is activated by the begin() function and the constructor that takes a QPaintDevice argument. The end() function, and the destructor, deactivates it.

QPainter - Qt for Python

①RenderHintという列挙型でまとめられています。
②setClipRegionやsetClipRectで、指定範囲外の部分を切り取ることができます。
③コンポジションモードは後で扱います。

 「isActive関数はペインターがアクティブであるかどうかを示します。あるペインターはbegin関数によってアクティブ化され、そして、QPaintDeviceの引数を取ります。end関数は、デストラクタで、ペインターを非アクティブ化します。」

 Together with the QPaintDevice and QPaintEngine classes, QPainter form the basis for Qt’s paint system. QPainter is the class used to perform drawing operations. QPaintDevice represents a device that can be painted on using a QPainter . QPaintEngine provides the interface that the painter uses to draw onto different types of devices. If the painter is active, device() returns the paint device on which the painter paints, and paintEngine() returns the paint engine that the painter is currently operating on. For more information, see the Paint System .

QPainter - Qt for Python

 「QPaintDeviceとQPaintEngineクラスと共に、QPainterはQtのペイントシステムの基盤になります。QPainterは描画命令を行うために使われるクラスです。QPaintDeviceはQPainterを使って描かれるデバイスを表現します。QPaintEngineはペインターが異なるタイプのデバイス上に描くために使うインターフェースを提供します。もしペインターがアクティブであれば、device()メソッドはペインターがペイントしているペイントデバイスを返しますし、paintEngine()メソッドは、ペインターが現在実行しているペイントエンジンを返します。①詳細は、Paint System - Qt for Pythonを見てください。」

①PaintSystem

 Sometimes it is desirable to make someone else paint on an unusual QPaintDevice . QPainter supports a static function to do this, setRedirected().

QPainter - Qt for Python

 「たまにそれは他の誰かに独特のQPaintDeviceでペイントさせることが望ましいです。QPainter は、これを行うための静的関数 setRedirected() をサポートしています。」

 よくわからなかったので翻訳機で翻訳しました。ここは一旦置いておきます。

 Warning
When the paintdevice is a widget, QPainter can only be used inside a paintEvent() function or in a function called by paintEvent().

QPainter - Qt for Python

 「警告

ペイントデバイスがとあるウィジェットであるとき、QPainterは、paintEvent関数内でのみ、あるいは、paintEventによってコールされた関数内でのみ利用できます。」

There are several settings that you can customize to make QPainter draw according to your preferences:

QPainter - Qt for Python

「QPainterにはあなたの好みで描くようカスタマイズできるいくつかの設定があります。」

font() is the font used for drawing text. If the painter isActive() , you can retrieve information about the currently set font, and its metrics, using the fontInfo() and fontMetrics() functions respectively.

QPainter - Qt for Python

 「fontメソッドはテキストを描くために使われるフォントです。もしペインターがアクティブであるならば、現在時点でセットされたフォントについて、そしてメトリクスの情報を、fontInfoメソッドとfontMetricsメソッドによって、それぞれ取得することができます。」

 brush() defines the color or pattern that is used for filling shapes.

QPainter - Qt for Python

 「ブラシは図形を塗りつぶすために使われる色やパターンを定義します。」

pen() defines the color or stipple that is used for drawing lines or boundaries.

QPainter - Qt for Python

「ペンは線や境界線を描くために使われる色や点刻を定義します。」

backgroundMode() defines whether there is a background() or not, i.e it is either OpaqueMode or TransparentMode .

QPainter - Qt for Python

 「backgroundMode関数はbackground()メソッドがあるかどうかを定義します。それはOpaqueModeか、TransparentModeのどちらかです。」

background() only applies when backgroundMode() is OpaqueMode and pen() is a stipple. In that case, it describes the color of the background pixels in the stipple.

QPainter - Qt for Python

 「バックグラウンドメソッドはbackgroundMode関数がOpaqueModeであり、pen()が点刻であるときにのみ適用します。その場合、点刻内の背景ピクセルの色を示します。」

 brushOrigin() defines the origin of the tiled brushes, normally the origin of widget’s background.

QPainter - Qt for Python

 「brushOrigin()メソッドは、タイル化したブラシの原点を定義します。普通はウィジェットの背景の原点です。」

 viewport() , window() , worldTransform() make up the painter’s coordinate transformation system. For more information, see the Coordinate Transformations section and the Coordinate System documentation.

QPainter - Qt for Python

 「viewport(), window(), worldTransform()はペインターの座標変換システムです。詳細は、座標変換セクションと座標システムのドキュメントを見てください。」

hasClipping() tells whether the painter clips at all. (The paint device clips, too.) If the painter clips, it clips to clipRegion() .

QPainter - Qt for Python

 「hasClippingメソッドはペインターがクリップするかどうかを教えます。ペイントデバイスクリップも。もしペインターがクリップするのであれば、clipRegionメソッドの値にクリップします。」

 クリップとは、指定された領域の外部を全部切り取ることを言います。第一章で、さりげなくsetClipRegionを使った例をお示ししたかと思いますが、指定したQRegionの部分以外のところは、切り取られていましたね。

 layoutDirection() defines the layout direction used by the painter when drawing text.

QPainter - Qt for Python

 layoutDirection()メソッドはテキストを描画するときペインターによって使われるレイアウトの方向を定義します。

 worldMatrixEnabled() tells whether world transformation is enabled.

QPainter - Qt for Python

 worldMatrixEnabledメソッドはワールド変換が可能であるかどうかを教えます。

 viewTransformEnabled() tells whether view transformation is enabled.

QPainter - Qt for Python

 viewTransformEnabledメソッドはビュー変換が可能であるかどうかを教えます。

Note that some of these settings mirror settings in some paint devices, e.g. font() . The begin() function (or equivalently the QPainter constructor) copies these attributes from the paint device.

QPainter - Qt for Python

これらの設定のいくつかは、いくつかのペイントデバイス内のセッティングを反映していることに注意してください。例えば、fontです。begin関数は(あるいはQPainterのコンストラクタも同じく)これらのアトリビュートを、ペイントデバイスからコピーします。

You can at any time save the QPainter ‘s state by calling the save() function which saves all the available settings on an internal stack. The restore() function pops them back.

QPainter - Qt for Python

save関数を使うことによって、いつでもQPainterの状態をセーブすることができます。内部のスタック上に利用可能なセッティング全てを保存します。restore関数はそれらを取り出して返します。 

Drawing

(https://doc.qt.io/qtforpython-6/PySide6/QtGui/QPainter.html#drawing)

QPainter - Qt for Python

QPainter provides functions to draw most primitives: drawPoint() , drawPoints() , drawLine() , drawRect() , drawRoundedRect() , drawEllipse() , drawArc() , drawPie() , drawChord() , drawPolyline() , drawPolygon() , drawConvexPolygon() and drawCubicBezier(). The two convenience functions, drawRects() and drawLines() , draw the given number of rectangles or lines in the given array of QRects or QLines using the current pen and brush.

QPainter - Qt for Python

「QPainterは最もプリミティブなものを描くための関数を提供する。drawPoint() , drawPoints() , drawLine() , drawRect() , drawRoundedRect() , drawEllipse() , drawArc() , drawPie() , drawChord() , drawPolyline() , drawPolygon() , drawConvexPolygon() and drawCubicBezier()です。」

プリミティブとは基本要素のことです。コンピュータグラフィックスの図形を構成する点や線を指します。

「drawRectsとdrawLinesという二つの便利な関数があります。QRectあるいはQLineの配列内の与えられた数の矩形とラインを描きます。」

The QPainter class also provides the fillRect() function which fills the given QRect , with the given QBrush , and the eraseRect() function that erases the area inside the given rectangle.

QPainter - Qt for Python

「QPainterクラスはfillRect関数も提供し、与えられたQRectを、与えられたQBrushで塗りつぶします。そしてeraseRect関数は、与えられた矩形の中にある領域を消去します。」

All of these functions have both integer and floating point versions.

QPainter - Qt for Python

「これらの関数は、整数型と浮動小数点数型の関数がそれぞれ用意されています。」

Basic Drawing Example

The Basic Drawing example shows how to display basic graphics primitives in a variety of styles using the QPainter class.

QPainter - Qt for Python

基本描画サンプル

基本描画サンプルはQPainterクラスを使ってバラエティに富んだスタイルで基本的なグラフィックプリミティブの表示の仕方を示します。


Basic Painter Example

このサンプルコードは、PySideのエグザンプルにも存在しています。 

If you need to draw a complex shape, especially if you need to do so repeatedly, consider creating a QPainterPath and drawing it using drawPath() .

QPainter - Qt for Python

 「もし複雑な形を描く必要がある、特に繰り返して行う必要がある場合、QPainterPathとdrawPathメソッドを使って作ることも考慮に入れてください。」 

Painter Paths example

The QPainterPath class provides a container for painting operations, enabling graphical shapes to be constructed and reused.

QPainter - Qt for Python

ペインターパスサンプル

「QPainterPathクラスはペイントオペレーションのコンテナを提供します。グラフィカルな図形を構築することと再利用を可能にします。」

The Painter Paths example shows how painter paths can be used to build complex shapes for rendering.

QPainter - Qt for Python

ペインターパスサンプルは表示する複雑な図形を組みたてるのにどのようなペインターパスが利用されうるかを示します。

残念ながら、このコードは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 also provides the fillPath() function which fills the given QPainterPath with the given QBrush , and the strokePath() function that draws the outline of the given path (i.e. strokes the path).

QPainter - Qt for Python

 「QPainterも、与えられたQBrushそして、与えられたパスの輪郭を描くためのstrokePath関数で、塗りつぶしを行うfillPath関数を提供しています。」

 See also the Vector Deformation example which shows how to use advanced vector techniques to draw text using a QPainterPath , the Gradients example which shows the different types of gradients that are available in Qt, and the Path Stroking example which shows Qt’s built-in dash patterns and shows how custom patterns can be used to extend the range of available patterns.

QPainter - Qt for Python

 「ベクターによるゆがみサンプルを見てください。QPainterPathを使って、テキストを描くためのより進んだベクターのテクニックの使い方を表示します。グラディエントサンプルは、Qt内で利用可能な異なる種類のグラディエントを表示します。パスストローキングサンプルは、利用可能なパターンの範囲を拡張するために、どのようにカスタムパターンが利用されうるのかを表示します。」

 

Vector Deformation


Vector Deformation

Gradients

 

Gradients Example


Path Stroking


Path Stroking

Text drawing is done using drawText() . When you need fine-grained positioning, boundingRect() tells you where a given drawText() command will draw.

QPainter - Qt for Python

「テキストの描画は、drawText関数を使って行われます。粒度の高い位置取りを必要とする時、boudingRectメソッドが与えられたdrawTextメソッドコマンドがどこに描くのかを教えてくれます。」

これら3つのサンプルコードは、残念ながらPySide6には含まれていません。そこで、私なりに作ってみたのですが、C++とPythonではうまくいかないところがあります。ArthurStyleというスタイルを用いているのですが、Pythonの場合Sliderが表示されません。後、OpenGLの欄は無視しています。

まず、vector Deform Exampleです

このコードの前に、ArthurStyleとArthurFrameのインポートが必要になりますので、そちらも載せておきます。


ここから先は

207,556字 / 13画像

¥ 300

この記事が気に入ったらサポートをしてみませんか?