1#!/usr/bin/env python
2
3
4#############################################################################
5##
6## Copyright (C) 2013 Riverbank Computing Limited.
7## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
8## All rights reserved.
9##
10## This file is part of the examples of PyQt.
11##
12## $QT_BEGIN_LICENSE:BSD$
13## You may use this file under the terms of the BSD license as follows:
14##
15## "Redistribution and use in source and binary forms, with or without
16## modification, are permitted provided that the following conditions are
17## met:
18##   * Redistributions of source code must retain the above copyright
19##     notice, this list of conditions and the following disclaimer.
20##   * Redistributions in binary form must reproduce the above copyright
21##     notice, this list of conditions and the following disclaimer in
22##     the documentation and/or other materials provided with the
23##     distribution.
24##   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
25##     the names of its contributors may be used to endorse or promote
26##     products derived from this software without specific prior written
27##     permission.
28##
29## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
40## $QT_END_LICENSE$
41##
42#############################################################################
43
44
45from math import cos, pi, sin
46
47from PyQt5.QtCore import QSize, Qt
48from PyQt5.QtGui import (QBrush, QColor, QFont, QLinearGradient, QPainter,
49        QPainterPath, QPalette, QPen)
50from PyQt5.QtWidgets import (QApplication, QComboBox, QGridLayout, QLabel,
51        QSizePolicy, QSpinBox, QWidget)
52
53
54class RenderArea(QWidget):
55    def __init__(self, path, parent=None):
56        super(RenderArea, self).__init__(parent)
57
58        self.path = path
59
60        self.penWidth = 1
61        self.rotationAngle = 0
62        self.setBackgroundRole(QPalette.Base)
63
64    def minimumSizeHint(self):
65        return QSize(50, 50)
66
67    def sizeHint(self):
68        return QSize(100, 100)
69
70    def setFillRule(self, rule):
71        self.path.setFillRule(rule)
72        self.update()
73
74    def setFillGradient(self, color1, color2):
75        self.fillColor1 = color1
76        self.fillColor2 = color2
77        self.update()
78
79    def setPenWidth(self, width):
80        self.penWidth = width
81        self.update()
82
83    def setPenColor(self, color):
84        self.penColor = color
85        self.update()
86
87    def setRotationAngle(self, degrees):
88        self.rotationAngle = degrees
89        self.update()
90
91    def paintEvent(self, event):
92        painter = QPainter(self)
93        painter.setRenderHint(QPainter.Antialiasing)
94        painter.scale(self.width() / 100.0, self.height() / 100.0)
95        painter.translate(50.0, 50.0)
96        painter.rotate(-self.rotationAngle)
97        painter.translate(-50.0, -50.0)
98
99        painter.setPen(
100                QPen(self.penColor, self.penWidth, Qt.SolidLine, Qt.RoundCap,
101                        Qt.RoundJoin))
102        gradient = QLinearGradient(0, 0, 0, 100)
103        gradient.setColorAt(0.0, self.fillColor1)
104        gradient.setColorAt(1.0, self.fillColor2)
105        painter.setBrush(QBrush(gradient))
106        painter.drawPath(self.path)
107
108
109class Window(QWidget):
110    NumRenderAreas = 9
111
112    def __init__(self):
113        super(Window, self).__init__()
114
115        rectPath = QPainterPath()
116        rectPath.moveTo(20.0, 30.0)
117        rectPath.lineTo(80.0, 30.0)
118        rectPath.lineTo(80.0, 70.0)
119        rectPath.lineTo(20.0, 70.0)
120        rectPath.closeSubpath()
121
122        roundRectPath = QPainterPath()
123        roundRectPath.moveTo(80.0, 35.0)
124        roundRectPath.arcTo(70.0, 30.0, 10.0, 10.0, 0.0, 90.0)
125        roundRectPath.lineTo(25.0, 30.0)
126        roundRectPath.arcTo(20.0, 30.0, 10.0, 10.0, 90.0, 90.0)
127        roundRectPath.lineTo(20.0, 65.0)
128        roundRectPath.arcTo(20.0, 60.0, 10.0, 10.0, 180.0, 90.0)
129        roundRectPath.lineTo(75.0, 70.0)
130        roundRectPath.arcTo(70.0, 60.0, 10.0, 10.0, 270.0, 90.0)
131        roundRectPath.closeSubpath()
132
133        ellipsePath = QPainterPath()
134        ellipsePath.moveTo(80.0, 50.0)
135        ellipsePath.arcTo(20.0, 30.0, 60.0, 40.0, 0.0, 360.0)
136
137        piePath = QPainterPath()
138        piePath.moveTo(50.0, 50.0)
139        piePath.lineTo(65.0, 32.6795)
140        piePath.arcTo(20.0, 30.0, 60.0, 40.0, 60.0, 240.0)
141        piePath.closeSubpath()
142
143        polygonPath = QPainterPath()
144        polygonPath.moveTo(10.0, 80.0)
145        polygonPath.lineTo(20.0, 10.0)
146        polygonPath.lineTo(80.0, 30.0)
147        polygonPath.lineTo(90.0, 70.0)
148        polygonPath.closeSubpath()
149
150        groupPath = QPainterPath()
151        groupPath.moveTo(60.0, 40.0)
152        groupPath.arcTo(20.0, 20.0, 40.0, 40.0, 0.0, 360.0)
153        groupPath.moveTo(40.0, 40.0)
154        groupPath.lineTo(40.0, 80.0)
155        groupPath.lineTo(80.0, 80.0)
156        groupPath.lineTo(80.0, 40.0)
157        groupPath.closeSubpath()
158
159        textPath = QPainterPath()
160        timesFont = QFont('Times', 50)
161        timesFont.setStyleStrategy(QFont.ForceOutline)
162        textPath.addText(10, 70, timesFont, "Qt")
163
164        bezierPath = QPainterPath()
165        bezierPath.moveTo(20, 30)
166        bezierPath.cubicTo(80, 0, 50, 50, 80, 80)
167
168        starPath = QPainterPath()
169        starPath.moveTo(90, 50)
170        for i in range(1, 5):
171            starPath.lineTo(50 + 40 * cos(0.8 * i * pi),
172                    50 + 40 * sin(0.8 * i * pi))
173        starPath.closeSubpath()
174
175        self.renderAreas = [RenderArea(rectPath), RenderArea(roundRectPath),
176                RenderArea(ellipsePath), RenderArea(piePath),
177                RenderArea(polygonPath), RenderArea(groupPath),
178                RenderArea(textPath), RenderArea(bezierPath),
179                RenderArea(starPath)]
180        assert len(self.renderAreas) == 9
181
182        self.fillRuleComboBox = QComboBox()
183        self.fillRuleComboBox.addItem("Odd Even", Qt.OddEvenFill)
184        self.fillRuleComboBox.addItem("Winding", Qt.WindingFill)
185
186        fillRuleLabel = QLabel("Fill &Rule:")
187        fillRuleLabel.setBuddy(self.fillRuleComboBox)
188
189        self.fillColor1ComboBox = QComboBox()
190        self.populateWithColors(self.fillColor1ComboBox)
191        self.fillColor1ComboBox.setCurrentIndex(
192                self.fillColor1ComboBox.findText("mediumslateblue"))
193
194        self.fillColor2ComboBox = QComboBox()
195        self.populateWithColors(self.fillColor2ComboBox)
196        self.fillColor2ComboBox.setCurrentIndex(
197                self.fillColor2ComboBox.findText("cornsilk"))
198
199        fillGradientLabel = QLabel("&Fill Gradient:")
200        fillGradientLabel.setBuddy(self.fillColor1ComboBox)
201
202        fillToLabel = QLabel("to")
203        fillToLabel.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
204
205        self.penWidthSpinBox = QSpinBox()
206        self.penWidthSpinBox.setRange(0, 20)
207
208        penWidthLabel = QLabel("&Pen Width:")
209        penWidthLabel.setBuddy(self.penWidthSpinBox)
210
211        self.penColorComboBox = QComboBox()
212        self.populateWithColors(self.penColorComboBox)
213        self.penColorComboBox.setCurrentIndex(
214                self.penColorComboBox.findText('darkslateblue'))
215
216        penColorLabel = QLabel("Pen &Color:")
217        penColorLabel.setBuddy(self.penColorComboBox)
218
219        self.rotationAngleSpinBox = QSpinBox()
220        self.rotationAngleSpinBox.setRange(0, 359)
221        self.rotationAngleSpinBox.setWrapping(True)
222        self.rotationAngleSpinBox.setSuffix(u'\N{DEGREE SIGN}')
223
224        rotationAngleLabel = QLabel("&Rotation Angle:")
225        rotationAngleLabel.setBuddy(self.rotationAngleSpinBox)
226
227        self.fillRuleComboBox.activated.connect(self.fillRuleChanged)
228        self.fillColor1ComboBox.activated.connect(self.fillGradientChanged)
229        self.fillColor2ComboBox.activated.connect(self.fillGradientChanged)
230        self.penColorComboBox.activated.connect(self.penColorChanged)
231
232        for i in range(Window.NumRenderAreas):
233            self.penWidthSpinBox.valueChanged.connect(self.renderAreas[i].setPenWidth)
234            self.rotationAngleSpinBox.valueChanged.connect(self.renderAreas[i].setRotationAngle)
235
236        topLayout = QGridLayout()
237        for i in range(Window.NumRenderAreas):
238            topLayout.addWidget(self.renderAreas[i], i / 3, i % 3)
239
240        mainLayout = QGridLayout()
241        mainLayout.addLayout(topLayout, 0, 0, 1, 4)
242        mainLayout.addWidget(fillRuleLabel, 1, 0)
243        mainLayout.addWidget(self.fillRuleComboBox, 1, 1, 1, 3)
244        mainLayout.addWidget(fillGradientLabel, 2, 0)
245        mainLayout.addWidget(self.fillColor1ComboBox, 2, 1)
246        mainLayout.addWidget(fillToLabel, 2, 2)
247        mainLayout.addWidget(self.fillColor2ComboBox, 2, 3)
248        mainLayout.addWidget(penWidthLabel, 3, 0)
249        mainLayout.addWidget(self.penWidthSpinBox, 3, 1, 1, 3)
250        mainLayout.addWidget(penColorLabel, 4, 0)
251        mainLayout.addWidget(self.penColorComboBox, 4, 1, 1, 3)
252        mainLayout.addWidget(rotationAngleLabel, 5, 0)
253        mainLayout.addWidget(self.rotationAngleSpinBox, 5, 1, 1, 3)
254        self.setLayout(mainLayout)
255
256        self.fillRuleChanged()
257        self.fillGradientChanged()
258        self.penColorChanged()
259        self.penWidthSpinBox.setValue(2)
260
261        self.setWindowTitle("Painter Paths")
262
263    def fillRuleChanged(self):
264        rule = Qt.FillRule(self.currentItemData(self.fillRuleComboBox))
265
266        for i in range(Window.NumRenderAreas):
267            self.renderAreas[i].setFillRule(rule)
268
269    def fillGradientChanged(self):
270        color1 = QColor(self.currentItemData(self.fillColor1ComboBox))
271        color2 = QColor(self.currentItemData(self.fillColor2ComboBox))
272
273        for i in range(Window.NumRenderAreas):
274            self.renderAreas[i].setFillGradient(color1, color2)
275
276    def penColorChanged(self):
277        color = QColor(self.currentItemData(self.penColorComboBox))
278
279        for i in range(Window.NumRenderAreas):
280            self.renderAreas[i].setPenColor(color)
281
282    def populateWithColors(self, comboBox):
283        colorNames = QColor.colorNames()
284        for name in colorNames:
285            comboBox.addItem(name, name)
286
287    def currentItemData(self, comboBox):
288        return comboBox.itemData(comboBox.currentIndex())
289
290
291if __name__ == '__main__':
292
293    import sys
294
295    app = QApplication(sys.argv)
296    window = Window()
297    window.show()
298    sys.exit(app.exec_())
299