Tvorba GUI v PySide: grafické operace používající třídu QPainter

9. 1. 2018
Doba čtení: 33 minut

Sdílet

V sedmém článku věnovaném tvorbě grafického uživatelského rozhraní v Pythonu s využitím frameworku PySide si ukážeme některé možnosti nabízené třídou QPainter při vytváření 2D grafiky použité v GUI.

Obsah

1. Tvorba GUI v Pythonu s využitím frameworku PySide: grafické operace používající třídu QPainter

2. Základní operace podporované třídou QPainter

3. Praktický postup při vykreslování

4. První demonstrační příklad: použití metod QPainter.begin a QPainter.end

5. Použití „chytrého“ konstruktoru objektu typu QPainter

6. Druhý demonstrační příklad: použití „chytrého“ konstruktoru objektu typu QPainter

7. Modifikace barvy a šířky pera použitého při vykreslování

8. Názvy barev s paletě knihovny PySide

9. Třetí demonstrační příklad: modifikace barvy a šířky pera

10. Čtvrtý demonstrační příklad: použití antialiasingu při vykreslování

11. Čárkované a čerchované úsečky

12. Pátý demonstrační příklad: kresba čárkovaných a čerchovaných úseček

13. Použití štětců při kreslení vyplněných grafických objektů

14. Šestý demonstrační příklad: vzorník standardních štětců

15. Barva pozadí oblasti nevyplněné štětcem

16. Sedmý demonstrační příklad: specifikace barvy pozadí a použití standardních štětců

17. Repositář s demonstračními příklady

18. Odkazy na Internetu

1. Tvorba GUI v Pythonu s využitím frameworku PySide: grafické operace používající třídu QPainter

Na předchozí část seriálu o tvorbě grafického uživatelského rozhraní v Pythonu, v níž jsme si popsali základní vlastnosti tříd QImage, QPixmap a QBitmap, dnes navážeme. Ukážeme si totiž, jakým způsobem je možné vykreslovat základní geometrické (2D) tvary s využitím metod třídy QPainter. Tato třída totiž programátorům nabízí možnost vykreslení následujících tvarů a objektů:

Tvar/objekt Metoda
bod drawPoint()
sada bodů drawPoints()
úsečka drawLine()
sada úseček drawLines()
obdélník drawRect(), fillRect()
sada obdélníků drawRects()
obdélník se zakulacenými rohy drawRoundRect(), drawRoundedRect()
polyčára (lomená úsečka) drawPolyline()
polygon drawPolygon(), drawConvexPolygon()
oblouk drawArc()
kruhová úseč drawChord()
kruhová výseč drawPie()
kružnice nebo elipsa drawEllipse()
obecná cesta drawPath(), fillPath()
text drawText(), drawTextItem()
rastrový obrázek drawImage(), drawPicture(), drawPixmap(), …

Podobně, jako je tomu například ve vektorovém formátu SVG, PostScriptu či v knihovně OpenVG, se i při použití QPainteru setkáme se třemi důležitými pojmy:

  1. Pero (pen) je nástroj použitý při vykreslování obrysu tvarů a samozřejmě i všech liniových tvarů (úsečky, oblouky, …).
  2. Štětec (brush) je nástroj použitý pro vyplnění uzavřených tvarů (obdélník, polygon, elipsa, cesta).
  3. Cesta (path) se používá k popisu složitějších tvarů složených z úseček a oblouků. Nad cestami je definováno větší množství operací, například test, zda bod leží uvnitř objektu popsaného cestou atd.

Dnes se seznámíme především s použitím per a štětců.

2. Základní operace podporované třídou QPainter: kreslení do rastrového obrázku

Samotný objekt QPainter provádí vykreslení na nějaké „plátno“, což může být buď přímo hardwarové zařízení (grafický subsystém), nebo rastrový obrázek. Podívejme se nyní na způsob vykreslení velmi jednoduché grafiky na rastrový obrázek s využitím možností nabízených třídou QPainter. Jeden z možných postupů je následující:

  1. Vytvoření instance třídy QImage, která bude tvořit „plátno“ pro QPainter. Konstruktoru QImage se předává rozlišení (počet sloupců a řádků) rastrového obrázku i formát pixelů. Nejjednodušší (i když ne vždy nejrychlejší) je použít formát QtGui.QImage.Format_RGB32 pro plnobarevné obrázky.
  2. Vytvoření objektu typu QPainter konstruktorem QPainter() (tento konstruktor nemá v nejjednodušším případě žádné parametry).
  3. Informace instance třídy QPainter o začátku vykreslování do instance třídy QImage. To se provede zavoláním metody QPainter.begin().
  4. Provedení vlastního vykreslení (například barevné úsečky). Mezi příkazovými závorkami begin a end můžete zavolat libovolné množství vykreslovacích operací.
  5. Informace QPainteru o ukončení vykreslování. To se provede zavoláním metody QPainter.end().
  6. Konverze objektu typu QImage na QPixmap (nebo QBitmap). Této problematice jsme se věnovali ve druhé kapitole předchozího článku.
  7. QPixmap či QBitmap lze již přímo vykreslit, například umístěním na návěští. I touto problematikou jsme se zabývali minule.

3. Praktický postup při vykreslování

V praxi se jednotlivé body výše uvedeného postupu budou implementovat následujícím způsobem:

Nejprve vytvoříme rastrový obrázek, který bude představovat „plátno“, na které bude třída QPainter vykreslovat obrazce:

image = QtGui.QImage(MainWindow.IMAGE_WIDTH,
                     MainWindow.IMAGE_HEIGHT,
                     QtGui.QImage.Format_RGB32)

Dále vytvoříme objekt typu QPainter:

qp = QtGui.QPainter()

Informujeme tento objekt o začátku změn (vykreslování) do instance třídy QImage:

qp.begin(self.image)

Provedeme vlastní vykreslování – nastavíme barvu pera a vykreslíme tímto perem diagonální úsečku (povšimněte si, že se nemusíme nijak starat o to, kdy zanikne platnost objektu typu QColor – některé jiné toolkity totiž vyžadují explicitní uvolnění prostředků):

qp.setPen(QtGui.QColor(64, 255, 64))
qp.drawLine(10, 10, MainWindow.IMAGE_WIDTH-10, MainWindow.IMAGE_HEIGHT-10)

Informujeme objekt o konci změn (vykreslování):

qp.end()

V předposledním kroku se provede konverze objektu typu QImage na QPixmap:

pixmap = QtGui.QPixmap.fromImage(image)

Někde v další části kódu již můžeme pixmapu bez problémů vložit na navěští a tím ji vykreslit (resp. přesněji řečeno zobrazit uživateli):

label = QtGui.QLabel("test")
label.setPixmap(pixmap)
self.setCentralWidget(label)

Obrázek 1: Úsečka vykreslená příkazy popsanými v této kapitole.

4. První demonstrační příklad: použití metod QPainter.begin a QPainter.end

Výše uvedený postup pro vykreslení grafiky používající třídy QPainter a QImage je použit v dnešním prvním demonstračním příkladu, jehož úplný zdrojový kód vypadá následovně:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
import sys
 
# import "jádra" frameworku Qt i modulu pro GUI
from PySide import QtCore
from PySide import QtGui
 
 
# nový widget bude odvozen od obecného hlavního okna
class MainWindow(QtGui.QMainWindow):
 
    # rozměry rastrového obrázku
    IMAGE_WIDTH = 256
    IMAGE_HEIGHT = 256
 
    def __init__(self):
        # zavoláme konstruktor předka
        super(MainWindow, self).__init__()
 
        self.prepareImage()
        # konfigurace GUI + přidání widgetu do okna
        self.prepareGUI()
 
    def prepareImage(self):
        # vytvoření instance třídy QImage
        self.image = QtGui.QImage(MainWindow.IMAGE_WIDTH,
                                  MainWindow.IMAGE_HEIGHT,
                                  QtGui.QImage.Format_RGB32)
 
        # vytvoření objektu typu QPainter
        qp = QtGui.QPainter()
        # začátek změn
        qp.begin(self.image)
        # nastavení barvy kreslení
        qp.setPen(QtGui.QColor(64, 255, 64))
        # vykreslení úsečky
        qp.drawLine(10, 10,
                    MainWindow.IMAGE_WIDTH-10, MainWindow.IMAGE_HEIGHT-10)
        # konec změn
        qp.end()
 
        # vytvoření instance třídy QPixmap z objektu QImage
        self.pixmap = QtGui.QPixmap.fromImage(self.image)
 
    def prepareGUI(self):
        # velikost okna nezadávejte ručně - špatně se počítá kvůli toolbaru
        # self.resize(256, 300)
        self.setWindowTitle('QPainter')
 
        # tlačítko Quit
        quitAction = QtGui.QAction(QtGui.QIcon('icons/application-exit.png'),
                                   '&Quit', self)
        quitAction.triggered.connect(self.close)
        quitAction.setStatusTip('Quit the application')
        quitAction.setShortcut('Ctrl+Q')
 
        # nástrojový pruh
        self.toolbar = self.addToolBar('title')
        self.toolbar.setMovable(False)
 
        # přidání tlačítka na nástrojový pruh
        self.toolbar.addAction(quitAction)
 
        # doprostřed okna přidáme návěští s rastrovým obrázkem
        self.addLabelWithPixmap()
 
        # zobrazení hlavního okna
        self.show()
 
    def addLabelWithPixmap(self):
        # vytvoření návěští
        label = QtGui.QLabel("test")
        # přiřazení rastrového obrázku k návěští
        label.setPixmap(self.pixmap)
        # vložení návěští do hlavního okna
        self.setCentralWidget(label)
 
    def run(self, app):
        # zobrazení okna na obrazovce
        self.show()
        # vstup do smyčky událostí (event loop)
        app.exec_()
 
 
def main():
    app = QtGui.QApplication(sys.argv)
    MainWindow().run(app)
 
 
if __name__ == '__main__':
    main()

5. Použití „chytrého“ konstruktoru objektu typu QPainter

Třídu QPainter jsme prozatím používali takovým způsobem, že jsme nejprve vytvořili (zkonstruovali) instanci této třídy, následně jsme zavolali metodu QPainter.begin() s předáním obrázku, na který se bude kreslit, provedli jsme vlastní vykreslení a posléze jsme vykreslení explicitně ukončili metodou QPainter.end():

# vytvoření objektu typu QPainter
qp = QtGui.QPainter()
# začátek změn
qp.begin(self.image)
# nastavení barvy kreslení
qp.setPen(QtGui.QColor(64, 255, 64))
# vykreslení úsečky
qp.drawLine(10, 10,
            MainWindow.IMAGE_WIDTH-10, MainWindow.IMAGE_HEIGHT-10)
# konec změn
qp.end()

Tento postup měl nevýhodu v tom, že si programátor musí sám ohlídat použití metod QPainter.begin() a QPainter.end(). Tyto metody se vždy musí vyskytovat v páru a nesmí se překrývat, tj. nelze například dvakrát zavolat QPainter.begin() pro různé obrázky bez předchozího zavolání QPainter.end(). Navíc by se měla hlídat i návratová hodnota z první uvedené metody (True/False), což je test, který jsme pro jednoduchost vynechali. Tento poměrně nízkoúrovňový přístup můžeme obejít tak, že už při konstrukci objektu QPainter předáme konstruktoru rastrový obrázek, na který se má provést vlastní vykreslení:

# vytvoření objektu typu QPainter s předáním
# reference na "pokreslovaný" objekt
qp = QtGui.QPainter(self.image)
# nastavení barvy kreslení
qp.setPen(QtGui.QColor(64, 255, 64))
# vykreslení úsečky
qp.drawLine(10, 10,
            MainWindow.IMAGE_WIDTH-10, MainWindow.IMAGE_HEIGHT-10)

V tomto případě nemusíme explicitně volat metodu QPainter.begin(), protože tato operace se provede automaticky. Mnohem zajímavější je, že i metoda QPainter.end() se zavolá automaticky, a to konkrétně v destruktoru objektu typu QPainter (to mj. znamená, že by tento objekt měl mít pouze lokální viditelnost resp. oblast platnosti – pro jednoduchost by se celé vykreslení mělo provést v jediné metodě).

Obrázek 2: Výsledkem je naprosto stejné okno, jako v prvním příkladu.

6. Druhý demonstrační příklad: použití „chytrého“ konstruktoru objektu typu QPainter

Jakým způsobem je možné využít „chytrý“ konstruktor objektu typu QPainter je ukázáno v dnešním druhém demonstračním příkladu, jehož úplný zdrojový kód je vypsán pod tímto odstavcem:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
import sys
 
# import "jádra" frameworku Qt i modulu pro GUI
from PySide import QtCore
from PySide import QtGui
 
 
# nový widget bude odvozen od obecného hlavního okna
class MainWindow(QtGui.QMainWindow):
 
    # rozměry rastrového obrázku
    IMAGE_WIDTH = 256
    IMAGE_HEIGHT = 256
 
    def __init__(self):
        # zavoláme konstruktor předka
        super(MainWindow, self).__init__()
 
        self.prepareImage()
        # konfigurace GUI + přidání widgetu do okna
        self.prepareGUI()
 
    def prepareImage(self):
        # vytvoření instance třídy QImage
        self.image = QtGui.QImage(MainWindow.IMAGE_WIDTH,
                                  MainWindow.IMAGE_HEIGHT,
                                  QtGui.QImage.Format_RGB32)
 
        # vytvoření objektu typu QPainter s předáním
        # reference na "pokreslovaný" objekt
        qp = QtGui.QPainter(self.image)
        # nastavení barvy kreslení
        qp.setPen(QtGui.QColor(64, 255, 64))
        # vykreslení úsečky
        qp.drawLine(10, 10,
                    MainWindow.IMAGE_WIDTH-10, MainWindow.IMAGE_HEIGHT-10)
 
        # vytvoření instance třídy QPixmap z objektu QImage
        self.pixmap = QtGui.QPixmap.fromImage(self.image)
 
    def prepareGUI(self):
        # velikost okna nezadávejte ručně - špatně se počítá kvůli toolbaru
        # self.resize(256, 300)
        self.setWindowTitle('QPainter')
 
        # tlačítko Quit
        quitAction = QtGui.QAction(QtGui.QIcon('icons/application-exit.png'),
                                   '&Quit', self)
        quitAction.triggered.connect(self.close)
        quitAction.setStatusTip('Quit the application')
        quitAction.setShortcut('Ctrl+Q')
 
        # nástrojový pruh
        self.toolbar = self.addToolBar('title')
        self.toolbar.setMovable(False)
 
        # přidání tlačítka na nástrojový pruh
        self.toolbar.addAction(quitAction)
 
        # doprostřed okna přidáme návěští s rastrovým obrázkem
        self.addLabelWithPixmap()
 
        # zobrazení hlavního okna
        self.show()
 
    def addLabelWithPixmap(self):
        # vytvoření návěští
        label = QtGui.QLabel("test")
        # přiřazení rastrového obrázku k návěští
        label.setPixmap(self.pixmap)
        # vložení návěští do hlavního okna
        self.setCentralWidget(label)
 
    def run(self, app):
        # zobrazení okna na obrazovce
        self.show()
        # vstup do smyčky událostí (event loop)
        app.exec_()
 
 
def main():
    app = QtGui.QApplication(sys.argv)
    MainWindow().run(app)
 
 
if __name__ == '__main__':
    main()

7. Modifikace barvy a šířky pera použitého při vykreslování

Podívejme se nyní na modifikaci stylu vykreslovaných objektů. Již v úvodní kapitole jsme si řekli, že při kreslení obrysů uzavřených objektů i všech liniových objektů se používá nástroj nazvaný pero (pen) představovaný instancí třídy QPen. Při konstrukci objektu typu QPen můžeme specifikovat mj. i barvu kreslení. Následně se pero přiřadí instanci třídy QPainter. Změna barvy kreslení tedy může v tom nejjednodušším případě vypadat následovně:

pen = QtGui.QPen(barva)
# kreslit se bude právě vytvořeným perem
qPainter.setPen(pen)

Barva je představována instancí třídy QColor a lze ji specifikovat v konstruktoru této třídy následujícími způsoby:

Parametry Význam
rgb jediná celočíselná hodnota (long) s kódem RGB
red, green, blue tři barvové složky: červená, zelená, modrá
red, green, blue, alpha tři barvové složky doplněné o průhlednost
name jméno barvy (viz následující kapitolu)
PySide.QtCore.Qt.GlobalColor jedna z dvaceti globálních barev

Pozor: pokud se barva explicitně nespecifikuje, je pro kreslení použita černá barva, takže na implicitně černém obrázku nebude kresba viditelná!

Šířku kreslení (stopy) je možné zadat buď v konstruktoru třídy QPen (ovšem potom nelze specifikovat barvu), nebo zavoláním metody QPen.setWidth():

pen = QtGui.QPen(barva)
# změna šířky pera
pen.setWidth(width)
# kreslit se bude právě vytvořeným perem
qPainter.setPen(pen)

Implicitně je šířka stopy nastavena na nulu; to odpovídá nejmenší možné stopě kreslení (jeden pixel bez použití antialiasingu).

8. Názvy barev s paletě knihovny PySide

Názvy všech barev uložených do standardní palety knihovny PySide získáme velmi snadno zavoláním statické metody QtGui.QColor.colorNames(), která vrátí seznam názvů známých barev. Tento seznam si samozřejmě můžeme velmi snadno vypsat, například následujícím jednořádkovým příkazem:

print("\n".join(QtGui.QColor.colorNames()))

Výstup by měl vypadat následovně (PySide 1.2.x):

aliceblue
antiquewhite
aqua
aquamarine
azure
beige
bisque
black
blanchedalmond
blue
blueviolet
brown
burlywood
cadetblue
chartreuse
chocolate
coral
cornflowerblue
cornsilk
crimson
cyan
darkblue
darkcyan
darkgoldenrod
darkgray
darkgreen
darkgrey
darkkhaki
darkmagenta
darkolivegreen
darkorange
darkorchid
darkred
darksalmon
darkseagreen
darkslateblue
darkslategray
darkslategrey
darkturquoise
darkviolet
deeppink
deepskyblue
dimgray
dimgrey
dodgerblue
firebrick
floralwhite
forestgreen
fuchsia
gainsboro
ghostwhite
gold
goldenrod
gray
green
greenyellow
grey
honeydew
hotpink
indianred
indigo
ivory
khaki
lavender
lavenderblush
lawngreen
lemonchiffon
lightblue
lightcoral
lightcyan
lightgoldenrodyellow
lightgray
lightgreen
lightgrey
lightpink
lightsalmon
lightseagreen
lightskyblue
lightslategray
lightslategrey
lightsteelblue
lightyellow
lime
limegreen
linen
magenta
maroon
mediumaquamarine
mediumblue
mediumorchid
mediumpurple
mediumseagreen
mediumslateblue
mediumspringgreen
mediumturquoise
mediumvioletred
midnightblue
mintcream
mistyrose
moccasin
navajowhite
navy
oldlace
olive
olivedrab
orange
orangered
orchid
palegoldenrod
palegreen
paleturquoise
palevioletred
papayawhip
peachpuff
peru
pink
plum
powderblue
purple
red
rosybrown
royalblue
saddlebrown
salmon
sandybrown
seagreen
seashell
sienna
silver
skyblue
slateblue
slategray
slategrey
snow
springgreen
steelblue
tan
teal
thistle
tomato
transparent
turquoise
violet
wheat
white
whitesmoke
yellow
yellowgreen

Příklad použití jména barvy:

# vytvoření pera a nastavení barvy kreslení
pen = QtGui.QPen("gold")
 
# kreslit se bude právě vytvořeným perem
qPainter.setPen(pen)
 

9. Třetí demonstrační příklad: modifikace barvy a šířky pera

V dnešním třetím příkladu je ukázáno, jak je možné kreslit různobarevné úsečky s volitelnou šířkou stopy. O samotné kreslení úseček se stará uživatelská funkce drawLine():

# funkce pro vykreslení úsečky zadanou barvou
def drawLine(qPainter, color, x1, y1, x2, y2, width=0):
    # vytvoření pera a nastavení barvy kreslení
    pen = QtGui.QPen(QtGui.QColor(*color))
 
    # změna šířky pera
    pen.setWidth(width)
 
    # kreslit se bude právě vytvořeným perem
    qPainter.setPen(pen)
 
    # vykreslení úsečky
    qPainter.drawLine(x1, y1, x2, y2)

Tato funkce v příkladu několikrát zavolána, takže vznikne následující kresba:

Obrázek 3: Okno vykreslené třetím příkladem.

Následuje výpis úplného zdrojového kódu tohoto příkladu:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
import sys
import math
 
# import "jádra" frameworku Qt i modulu pro GUI
from PySide import QtCore
from PySide import QtGui
 
 
# funkce pro vykreslení úsečky zadanou barvou
def drawLine(qPainter, color, x1, y1, x2, y2, width=0):
    # vytvoření pera a nastavení barvy kreslení
    pen = QtGui.QPen(QtGui.QColor(*color))
 
    # změna šířky pera
    pen.setWidth(width)
 
    # kreslit se bude právě vytvořeným perem
    qPainter.setPen(pen)
 
    # vykreslení úsečky
    qPainter.drawLine(x1, y1, x2, y2)
 
 
# nový widget bude odvozen od obecného hlavního okna
class MainWindow(QtGui.QMainWindow):
 
    # rozměry rastrového obrázku
    IMAGE_WIDTH = 320
    IMAGE_HEIGHT = 240
 
    def __init__(self):
        # zavoláme konstruktor předka
        super(MainWindow, self).__init__()
 
        self.prepareImage()
        # konfigurace GUI + přidání widgetu do okna
        self.prepareGUI()
 
    def prepareImage(self):
        # vytvoření instance třídy QImage
        self.image = QtGui.QImage(MainWindow.IMAGE_WIDTH,
                                  MainWindow.IMAGE_HEIGHT,
                                  QtGui.QImage.Format_RGB32)
 
        # vytvoření objektu typu QPainter s předáním
        # reference na "pokreslovaný" objekt
        qp = QtGui.QPainter(self.image)
 
        # konstanty s n-ticemi představujícími základní barvy
        BLACK = (0, 0, 0)
        BLUE = (0, 0, 255)
        CYAN = (0, 255, 255)
        GREEN = (0, 255, 0)
        YELLOW = (255, 255, 0)
        RED = (255, 0, 0)
        MAGENTA = (255, 0, 255)
        WHITE = (255, 255, 255)
 
        # Vykreslení čar různou barvou
        drawLine(qp, BLUE,    10, 10, 160, 20)
        drawLine(qp, CYAN,    10, 20, 160, 30)
        drawLine(qp, GREEN,   10, 30, 160, 40)
        drawLine(qp, YELLOW,  10, 40, 160, 50)
        drawLine(qp, RED,     10, 50, 160, 60)
        drawLine(qp, MAGENTA, 10, 60, 160, 70)
 
        # Vykreslení čar s různým sklonem
        for i in range(1, 90, 5):
            # převod ze stupňů na radiány
            angle = math.radians(i)
            radius = 150
            # výpočet koncových bodů úseček
            x = radius * math.sin(math.radians(i))
            y = radius * math.cos(math.radians(i))
            # vykreslení jedné úsečky
            drawLine(qp, WHITE, MainWindow.IMAGE_WIDTH-1, 0,
                     MainWindow.IMAGE_WIDTH-x, y)
 
        # vykreslení čar různou šířkou
        for i in range(1, 10):
            drawLine(qp, WHITE, 10 + i*15, 90, 20 + i*15, 230, i)
 
        # vytvoření instance třídy QPixmap z objektu QImage
        self.pixmap = QtGui.QPixmap.fromImage(self.image)
 
    def prepareGUI(self):
        # velikost okna nezadávejte ručně - špatně se počítá kvůli toolbaru
        # self.resize(256, 300)
        self.setWindowTitle('QPainter')
 
        # tlačítko Quit
        quitAction = QtGui.QAction(QtGui.QIcon('icons/application-exit.png'),
                                   '&Quit', self)
        quitAction.triggered.connect(self.close)
        quitAction.setStatusTip('Quit the application')
        quitAction.setShortcut('Ctrl+Q')
 
        # nástrojový pruh
        self.toolbar = self.addToolBar('title')
        self.toolbar.setMovable(False)
 
        # přidání tlačítka na nástrojový pruh
        self.toolbar.addAction(quitAction)
 
        # doprostřed okna přidáme návěští s rastrovým obrázkem
        self.addLabelWithPixmap()
 
        # zobrazení hlavního okna
        self.show()
 
    def addLabelWithPixmap(self):
        # vytvoření návěští
        label = QtGui.QLabel("test")
        # přiřazení rastrového obrázku k návěští
        label.setPixmap(self.pixmap)
        # vložení návěští do hlavního okna
        self.setCentralWidget(label)
 
    def run(self, app):
        # zobrazení okna na obrazovce
        self.show()
        # vstup do smyčky událostí (event loop)
        app.exec_()
 
 
def main():
    app = QtGui.QApplication(sys.argv)
    MainWindow().run(app)
 
 
if __name__ == '__main__':
    main()

10. Čtvrtý demonstrační příklad: použití antialiasingu při vykreslování

Při kreslení objektů do rastrového obrázku je možné kdykoli zapnout či naopak vypnout antialiasing (aa). To se provádí metodou QPainter.setRenderHint(), které se předá konstanta QPainter.Antialiasing, popř. kombinace konstant QPainter.Antialiasing, QPainter.TextAntialiasing a QPainter.HighQualityAntialiasing:

# vytvoření objektu typu QPainter s předáním
# reference na "pokreslovaný" objekt
qp = QtGui.QPainter(self.image)
 
qp.setRenderHint(QtGui.QPainter.Antialiasing)

V případě, že potřebujeme antialiasing vypnout (kresba různých schémat atd.), předejte metodě QPainter.setRenderHint() pojmenovaný parametr on=False (implicitně má totiž hodnotu True):

# vypneme antialiasing
qp.setRenderHint(QtGui.QPainter.Antialiasing, on=False)

Obrázek 4: Stejná scéna jako v předchozím příkladu, ovšem nyní vykreslená s využitím antialiasingu.

Pro porovnání rozdílů mezi nepoužitím a použitím antialiasingu si porovnejte následující dva obrázky:

Obrázek 5: Detail šikmých úseček vykreslených bez použití antialiasingu (zvětšeno).

Obrázek 6: Detail šikmých úseček vykreslených s použitím antialiasingu (zvětšeno).

Dnešní čtvrtý demonstrační příklad obsahuje ukázku zapnutí antialiasingu při kreslení úseček:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
import sys
import math
 
# import "jádra" frameworku Qt i modulu pro GUI
from PySide import QtCore
from PySide import QtGui
 
 
# funkce pro vykreslení úsečky zadanou barvou
def drawLine(qPainter, color, x1, y1, x2, y2, width=0):
    # vytvoření pera a nastavení barvy kreslení
    pen = QtGui.QPen(QtGui.QColor(*color))
 
    # změna šířky pera
    pen.setWidth(width)
 
    # kreslit se bude právě vytvořeným perem
    qPainter.setPen(pen)
 
    # vykreslení úsečky
    qPainter.drawLine(x1, y1, x2, y2)
 
 
# nový widget bude odvozen od obecného hlavního okna
class MainWindow(QtGui.QMainWindow):
 
    # rozměry rastrového obrázku
    IMAGE_WIDTH = 320
    IMAGE_HEIGHT = 240
 
    def __init__(self):
        # zavoláme konstruktor předka
        super(MainWindow, self).__init__()
 
        self.prepareImage()
        # konfigurace GUI + přidání widgetu do okna
        self.prepareGUI()
 
    def prepareImage(self):
        # vytvoření instance třídy QImage
        self.image = QtGui.QImage(MainWindow.IMAGE_WIDTH,
                                  MainWindow.IMAGE_HEIGHT,
                                  QtGui.QImage.Format_RGB32)
 
        # vytvoření objektu typu QPainter s předáním
        # reference na "pokreslovaný" objekt
        qp = QtGui.QPainter(self.image)
 
        qp.setRenderHint(QtGui.QPainter.Antialiasing)
 
        # konstanty s n-ticemi představujícími základní barvy
        BLACK = (0, 0, 0)
        BLUE = (0, 0, 255)
        CYAN = (0, 255, 255)
        GREEN = (0, 255, 0)
        YELLOW = (255, 255, 0)
        RED = (255, 0, 0)
        MAGENTA = (255, 0, 255)
        WHITE = (255, 255, 255)
 
        # Vykreslení čar různou barvou
        drawLine(qp, BLUE,    10, 10, 160, 20)
        drawLine(qp, CYAN,    10, 20, 160, 30)
        drawLine(qp, GREEN,   10, 30, 160, 40)
        drawLine(qp, YELLOW,  10, 40, 160, 50)
        drawLine(qp, RED,     10, 50, 160, 60)
        drawLine(qp, MAGENTA, 10, 60, 160, 70)
 
        # Vykreslení čar s různým sklonem
        for i in range(1, 90, 5):
            # převod ze stupňů na radiány
            angle = math.radians(i)
            radius = 150
            # výpočet koncových bodů úseček
            x = radius * math.sin(math.radians(i))
            y = radius * math.cos(math.radians(i))
            # vykreslení jedné úsečky
            drawLine(qp, WHITE, MainWindow.IMAGE_WIDTH-1, 0,
                     MainWindow.IMAGE_WIDTH-x, y)
 
        # vykreslení čar různou šířkou
        for i in range(1, 10):
            drawLine(qp, WHITE, 10 + i*15, 90, 20 + i*15, 230, i)
 
        # vytvoření instance třídy QPixmap z objektu QImage
        self.pixmap = QtGui.QPixmap.fromImage(self.image)
 
    def prepareGUI(self):
        # velikost okna nezadávejte ručně - špatně se počítá kvůli toolbaru
        # self.resize(256, 300)
        self.setWindowTitle('QPainter')
 
        # tlačítko Quit
        quitAction = QtGui.QAction(QtGui.QIcon('icons/application-exit.png'),
                                   '&Quit', self)
        quitAction.triggered.connect(self.close)
        quitAction.setStatusTip('Quit the application')
        quitAction.setShortcut('Ctrl+Q')
 
        # nástrojový pruh
        self.toolbar = self.addToolBar('title')
        self.toolbar.setMovable(False)
 
        # přidání tlačítka na nástrojový pruh
        self.toolbar.addAction(quitAction)
 
        # doprostřed okna přidáme návěští s rastrovým obrázkem
        self.addLabelWithPixmap()
 
        # zobrazení hlavního okna
        self.show()
 
    def addLabelWithPixmap(self):
        # vytvoření návěští
        label = QtGui.QLabel("test")
        # přiřazení rastrového obrázku k návěští
        label.setPixmap(self.pixmap)
        # vložení návěští do hlavního okna
        self.setCentralWidget(label)
 
    def run(self, app):
        # zobrazení okna na obrazovce
        self.show()
        # vstup do smyčky událostí (event loop)
        app.exec_()
 
 
def main():
    app = QtGui.QApplication(sys.argv)
    MainWindow().run(app)
 
 
if __name__ == '__main__':
    main()

11. Čárkované a čerchované úsečky

Při kresbě liniových obrazců nebo okrajů plošných obrazců je možné zvolit vzorek (styl) úseček – plná (výchozí nastavení), čárkovaná, čerchovaná, střídavá apod. K nastavení vzorku vykreslení úsečky slouží metoda QPen.setDashPattern(). Parametrem této metody je seznam délek vykreslených segmentů prokládaných délkami segmentů nevykreslených. Podívejme se na jednoduchý příklad. Nejprve si vytvoříme pomocnou funkci pro vykreslení úsečky s volitelným vzorkem:

# funkce pro vykreslení úsečky zadanou barvou
def drawDashedLine(qPainter, color, x1, y1, x2, y2, pattern):
    # vytvoření pera a nastavení barvy kreslení
    pen = QtGui.QPen(QtGui.QColor(*color))
 
    # změna typu čáry
    pen.setStyle(QtCore.Qt.CustomDashLine)
    pen.setDashPattern(pattern)
 
    # kreslit se bude právě vytvořeným perem
    qPainter.setPen(pen)
 
    # vykreslení úsečky
    qPainter.drawLine(x1, y1, x2, y2)

Tuto funkci použijeme pro vykreslení čar s různým vzorkem:

# Vykreslení čar různým stylem
drawDashedLine(qp, YELLOW, 10, 10, 160, 10, [1, 1])
drawDashedLine(qp, YELLOW, 10, 20, 160, 20, [1, 10])
drawDashedLine(qp, YELLOW, 10, 30, 160, 30, [10, 1])
drawDashedLine(qp, YELLOW, 10, 40, 160, 40, [10, 10])
drawDashedLine(qp, YELLOW, 10, 50, 160, 50, [10, 1, 10, 5])
drawDashedLine(qp, YELLOW, 10, 60, 160, 60, [5, 5])

Podívejme se nyní na výsledek:

Obrázek 7: Různé vzorky čar (zvětšeno 2×).

Při kreslení šikmých úseček je situace složitější, protože délky nebudou přesně odpovídat (ty platí jen pro vodorovné či svislé tvary):

# Vykreslení čar s různým sklonem
for i in range(1, 90, 5):
    # převod ze stupňů na radiány
    angle = math.radians(i)
    radius = 150
    # výpočet koncových bodů úseček
    x = radius * math.sin(math.radians(i))
    y = radius * math.cos(math.radians(i))
    # vykreslení jedné úsečky
    drawDashedLine(qp, WHITE, MainWindow.IMAGE_WIDTH-1, 0,
                   MainWindow.IMAGE_WIDTH-x, y,
                   [5, 5])

Obrázek 8: Šikmé úsečky (zvětšeno 2×, používá se antialiasing).

Zajímavé bude také sledovat, co se stane při změně šířky pera, tj. šířky kreslené stopy. Výše uvedenou funkci si nepatrně upravíme tak, aby akceptovala další nepovinný parametr s šířkou:

# funkce pro vykreslení úsečky zadanou barvou
def drawDashedLine(qPainter, color, x1, y1, x2, y2, pattern, width=0):
    # vytvoření pera a nastavení barvy kreslení
    pen = QtGui.QPen(QtGui.QColor(*color))
 
    # změna šířky pera
    pen.setWidth(width)
 
    # změna typu čáry
    pen.setStyle(QtCore.Qt.CustomDashLine)
    pen.setDashPattern(pattern)
 
    # kreslit se bude právě vytvořeným perem
    qPainter.setPen(pen)
 
    # vykreslení úsečky
    qPainter.drawLine(x1, y1, x2, y2)

Zkusíme si vykreslit několik úseček s různou tloušťkou:

# vykreslení čar různou šířkou
for i in range(1, 10):
    drawDashedLine(qp, WHITE, 10 + i*15, 90, 10 + i*15, 230, [5, 5], i)

Výsledek je možná překvapivý:

Obrázek 9: Šířka pera ovlivňuje i délku vzorků (zvětšeno 2×).

12. Pátý demonstrační příklad: kresba čárkovaných a čerchovaných úseček

Postup při kreslení úseček s různým stylem je ukázán v dalším – dnes již pátém – demonstračním příkladu, po jehož spuštění by se měl vykreslit tento obrázek:

Obrázek 10: Okno vykreslené pátým demonstračním příkladem.

Následuje výpis zdrojového kódu tohoto příkladu:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
import sys
import math
 
# import "jádra" frameworku Qt i modulu pro GUI
from PySide import QtCore
from PySide import QtGui
 
 
# funkce pro vykreslení úsečky zadanou barvou
def drawDashedLine(qPainter, color, x1, y1, x2, y2, pattern, width=0):
    # vytvoření pera a nastavení barvy kreslení
    pen = QtGui.QPen(QtGui.QColor(*color))
 
    # změna šířky pera
    pen.setWidth(width)
 
    # změna typu čáry
    pen.setStyle(QtCore.Qt.CustomDashLine)
    pen.setDashPattern(pattern)
 
    # kreslit se bude právě vytvořeným perem
    qPainter.setPen(pen)
 
    # vykreslení úsečky
    qPainter.drawLine(x1, y1, x2, y2)
 
 
# nový widget bude odvozen od obecného hlavního okna
class MainWindow(QtGui.QMainWindow):
 
    # rozměry rastrového obrázku
    IMAGE_WIDTH = 320
    IMAGE_HEIGHT = 240
 
    def __init__(self):
        # zavoláme konstruktor předka
        super(MainWindow, self).__init__()
 
        self.prepareImage()
        # konfigurace GUI + přidání widgetu do okna
        self.prepareGUI()
 
    def prepareImage(self):
        # vytvoření instance třídy QImage
        self.image = QtGui.QImage(MainWindow.IMAGE_WIDTH,
                                  MainWindow.IMAGE_HEIGHT,
                                  QtGui.QImage.Format_RGB32)
 
        # vytvoření objektu typu QPainter s předáním
        # reference na "pokreslovaný" objekt
        qp = QtGui.QPainter(self.image)
 
        # konstanty s n-ticemi představujícími základní barvy
        BLACK = (0, 0, 0)
        BLUE = (0, 0, 255)
        CYAN = (0, 255, 255)
        GREEN = (0, 255, 0)
        YELLOW = (255, 255, 0)
        RED = (255, 0, 0)
        MAGENTA = (255, 0, 255)
        WHITE = (255, 255, 255)
 
        # Vykreslení čar různým stylem
        drawDashedLine(qp, YELLOW, 10, 10, 160, 10, [1, 1])
        drawDashedLine(qp, YELLOW, 10, 20, 160, 20, [1, 10])
        drawDashedLine(qp, YELLOW, 10, 30, 160, 30, [10, 1])
        drawDashedLine(qp, YELLOW, 10, 40, 160, 40, [10, 10])
        drawDashedLine(qp, YELLOW, 10, 50, 160, 50, [10, 1, 10, 5])
        drawDashedLine(qp, YELLOW, 10, 60, 160, 60, [5, 5])
 
        qp.setRenderHint(QtGui.QPainter.Antialiasing)
 
        # Vykreslení čar s různým sklonem
        for i in range(1, 90, 5):
            # převod ze stupňů na radiány
            angle = math.radians(i)
            radius = 150
            # výpočet koncových bodů úseček
            x = radius * math.sin(math.radians(i))
            y = radius * math.cos(math.radians(i))
            # vykreslení jedné úsečky
            drawDashedLine(qp, WHITE, MainWindow.IMAGE_WIDTH-1, 0,
                           MainWindow.IMAGE_WIDTH-x, y,
                           [5, 5])
 
        # vykreslení čar různou šířkou
        for i in range(1, 10):
            drawDashedLine(qp, WHITE, 10 + i*15, 90, 10 + i*15, 230, [5, 5], i)
 
        # vytvoření instance třídy QPixmap z objektu QImage
        self.pixmap = QtGui.QPixmap.fromImage(self.image)
 
    def prepareGUI(self):
        # velikost okna nezadávejte ručně - špatně se počítá kvůli toolbaru
        # self.resize(256, 300)
        self.setWindowTitle('QPainter')
 
        # tlačítko Quit
        quitAction = QtGui.QAction(QtGui.QIcon('icons/application-exit.png'),
                                   '&Quit', self)
        quitAction.triggered.connect(self.close)
        quitAction.setStatusTip('Quit the application')
        quitAction.setShortcut('Ctrl+Q')
 
        # nástrojový pruh
        self.toolbar = self.addToolBar('title')
        self.toolbar.setMovable(False)
 
        # přidání tlačítka na nástrojový pruh
        self.toolbar.addAction(quitAction)
 
        # doprostřed okna přidáme návěští s rastrovým obrázkem
        self.addLabelWithPixmap()
 
        # zobrazení hlavního okna
        self.show()
 
    def addLabelWithPixmap(self):
        # vytvoření návěští
        label = QtGui.QLabel("test")
        # přiřazení rastrového obrázku k návěští
        label.setPixmap(self.pixmap)
        # vložení návěští do hlavního okna
        self.setCentralWidget(label)
 
    def run(self, app):
        # zobrazení okna na obrazovce
        self.show()
        # vstup do smyčky událostí (event loop)
        app.exec_()
 
 
def main():
    app = QtGui.QApplication(sys.argv)
    MainWindow().run(app)
 
 
if __name__ == '__main__':
    main()

13. Použití štětců při kreslení vyplněných grafických objektů

V úvodní kapitole jsme si řekli, že při kreslení uzavřených tvarů (obdélník, polygon, elipsa, cesta) je možné takové tvary vyplnit, přičemž pro vyplnění se používá nástroj nazvaný jednoduše štětec (brush. Štětec může být představován bitmapou (je to jedna z několika praktických možností použití bitmap ve frameworku PySide), popř. je možné štětec vybrat z připraveného vzorníku. Dnes se pro jednoduchost budeme zabývat právě existujícími vzorky štětců. Každý vzorek je představován jednou z konstant:

QtCore.Qt.SolidPattern
QtCore.Qt.HorPattern
QtCore.Qt.VerPattern
QtCore.Qt.CrossPattern
QtCore.Qt.BDiagPattern
QtCore.Qt.FDiagPattern
QtCore.Qt.DiagCrossPattern
QtCore.Qt.Dense1Pattern
QtCore.Qt.Dense2Pattern
QtCore.Qt.Dense3Pattern
QtCore.Qt.Dense4Pattern
QtCore.Qt.Dense5Pattern
QtCore.Qt.Dense6Pattern
QtCore.Qt.Dense7Pattern

Obrázek 11: Vzorník výše vypsaných štětců (v daném pořadí).

Vzorky štětců se nastavují takto (podtržená část kódu):

# funkce pro vykreslení obdélníku zadanou barvou a se specifikovaným štětcem
def drawRectangleUsingBrush(qPainter, color, x, y, width, height, brush_style, pen_width=0):
    # vytvoření pera a nastavení barvy kreslení
    pen = QtGui.QPen(QtGui.QColor(*color))
 
    # změna šířky pera
    pen.setWidth(pen_width)
    qPainter.setPen(pen)
 
    # změna tvaru štětce
    brush = QtGui.QBrush(QtGui.QColor(*color))
    brush.setStyle(brush_style)
    qPainter.setBrush(brush)
 
    # vykreslení obdélníku
    qPainter.drawRect(x, y, width, height)

Povšimněte si, že musíme specifikovat i barvu štětce, která se může lišit od barvy pera.

14. Šestý demonstrační příklad: vzorník standardních štětců

V šestém příkladu jsou ukázány základní vzorky štětců, které jsme si popsali v předchozí kapitole, takže se jen podívejme, jak může vypadat kód, který vzorník po svém spuštění vytiskne:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
import sys
import math
 
# import "jádra" frameworku Qt i modulu pro GUI
from PySide import QtCore
from PySide import QtGui
 
 
# funkce pro vykreslení obdélníku zadanou barvou a se specifikovaným štětcem
def drawRectangleUsingBrush(qPainter, color, x, y, width, height, brush_style, pen_width=0):
    # vytvoření pera a nastavení barvy kreslení
    pen = QtGui.QPen(QtGui.QColor(*color))
 
    # změna šířky pera
    pen.setWidth(pen_width)
    qPainter.setPen(pen)
 
    # změna tvaru štětce
    brush = QtGui.QBrush(QtGui.QColor(*color))
    brush.setStyle(brush_style)
    qPainter.setBrush(brush)
 
    # vykreslení obdélníku
    qPainter.drawRect(x, y, width, height)
 
 
# nový widget bude odvozen od obecného hlavního okna
class MainWindow(QtGui.QMainWindow):
 
    # rozměry rastrového obrázku
    IMAGE_WIDTH = 440
    IMAGE_HEIGHT = 140
 
    def __init__(self):
        # zavoláme konstruktor předka
        super(MainWindow, self).__init__()
 
        self.prepareImage()
        # konfigurace GUI + přidání widgetu do okna
        self.prepareGUI()
 
    def prepareImage(self):
        # vytvoření instance třídy QImage
        self.image = QtGui.QImage(MainWindow.IMAGE_WIDTH,
                                  MainWindow.IMAGE_HEIGHT,
                                  QtGui.QImage.Format_RGB32)
 
        # vytvoření objektu typu QPainter s předáním
        # reference na "pokreslovaný" objekt
        qp = QtGui.QPainter(self.image)
 
        # konstanty s n-ticemi představujícími základní barvy
        BLACK = (0, 0, 0)
        BLUE = (0, 0, 255)
        CYAN = (0, 255, 255)
        GREEN = (0, 255, 0)
        YELLOW = (255, 255, 0)
        RED = (255, 0, 0)
        MAGENTA = (255, 0, 255)
        WHITE = (255, 255, 255)
 
        # Vykreslení obdélníků různým stylem
        drawRectangleUsingBrush(qp, YELLOW, 10, 10, 50, 50, QtCore.Qt.SolidPattern)
        drawRectangleUsingBrush(qp, YELLOW, 70, 10, 50, 50, QtCore.Qt.HorPattern)
        drawRectangleUsingBrush(qp, YELLOW, 130, 10, 50, 50, QtCore.Qt.VerPattern)
        drawRectangleUsingBrush(qp, YELLOW, 190, 10, 50, 50, QtCore.Qt.CrossPattern)
        drawRectangleUsingBrush(qp, YELLOW, 250, 10, 50, 50, QtCore.Qt.BDiagPattern)
        drawRectangleUsingBrush(qp, YELLOW, 310, 10, 50, 50, QtCore.Qt.FDiagPattern)
        drawRectangleUsingBrush(qp, YELLOW, 370, 10, 50, 50, QtCore.Qt.DiagCrossPattern)
 
        drawRectangleUsingBrush(qp, WHITE, 10, 70, 50, 50, QtCore.Qt.Dense1Pattern)
        drawRectangleUsingBrush(qp, WHITE, 70, 70, 50, 50, QtCore.Qt.Dense2Pattern)
        drawRectangleUsingBrush(qp, WHITE, 130, 70, 50, 50, QtCore.Qt.Dense3Pattern)
        drawRectangleUsingBrush(qp, WHITE, 190, 70, 50, 50, QtCore.Qt.Dense4Pattern)
        drawRectangleUsingBrush(qp, WHITE, 250, 70, 50, 50, QtCore.Qt.Dense5Pattern)
        drawRectangleUsingBrush(qp, WHITE, 310, 70, 50, 50, QtCore.Qt.Dense6Pattern)
        drawRectangleUsingBrush(qp, WHITE, 370, 70, 50, 50, QtCore.Qt.Dense7Pattern)
 
        # vytvoření instance třídy QPixmap z objektu QImage
        self.pixmap = QtGui.QPixmap.fromImage(self.image)
 
    def prepareGUI(self):
        # velikost okna nezadávejte ručně - špatně se počítá kvůli toolbaru
        # self.resize(256, 300)
        self.setWindowTitle('QPainter')
 
        # tlačítko Quit
        quitAction = QtGui.QAction(QtGui.QIcon('icons/application-exit.png'),
                                   '&Quit', self)
        quitAction.triggered.connect(self.close)
        quitAction.setStatusTip('Quit the application')
        quitAction.setShortcut('Ctrl+Q')
 
        # nástrojový pruh
        self.toolbar = self.addToolBar('title')
        self.toolbar.setMovable(False)
 
        # přidání tlačítka na nástrojový pruh
        self.toolbar.addAction(quitAction)
 
        # doprostřed okna přidáme návěští s rastrovým obrázkem
        self.addLabelWithPixmap()
 
        # zobrazení hlavního okna
        self.show()
 
    def addLabelWithPixmap(self):
        # vytvoření návěští
        label = QtGui.QLabel("test")
        # přiřazení rastrového obrázku k návěští
        label.setPixmap(self.pixmap)
        # vložení návěští do hlavního okna
        self.setCentralWidget(label)
 
    def run(self, app):
        # zobrazení okna na obrazovce
        self.show()
        # vstup do smyčky událostí (event loop)
        app.exec_()
 
 
def main():
    app = QtGui.QApplication(sys.argv)
    MainWindow().run(app)
 
 
if __name__ == '__main__':
    main()

15. Barva pozadí oblasti nevyplněné štětcem

Při vyplňování uzavřených tvarů štětcem je možné si zvolit i barvu pozadí, přičemž pozadím jsou v tomto kontextu myšleny ty pixely, které sice leží uvnitř tvaru, ale štětec je při kreslení vynechá (nejedná se tedy o pozadí celé kreslicí plochy – plátna). Barva pozadí je stavová veličina a je ji možné kdykoli změnit (to ovlivní další vykreslování, nikoli však již vykreslené objekty). Navíc nezapomeňte, že kromě barvy pozadí je nutné nastavit i režim vykreslování pozadí na hodnotu QtCore.Qt.BGMode.OpaqueMode, protože implicitně se používá průhledné pozadí (přesněji řečeno nejsou pixely pozadí vůbec modifikovány). Podívejme se na příklad:

# vytvoření objektu typu QPainter s předáním
# reference na "pokreslovaný" objekt
qp = QtGui.QPainter(self.image)
 
# barva pozadi a povoleni vykreslení pozadi (vyplne)
qp.setBackgroundMode(QtCore.Qt.BGMode.OpaqueMode)
qp.setBackground(QtGui.QColor(red, green, blue))

16. Sedmý demonstrační příklad: specifikace barvy pozadí a použití standardních štětců

Sedmý a současně i dnešní poslední demonstrační příklad se od příkladu šestého odlišuje jen tím, že je v něm explicitně specifikována barva pozadí při použití standardních štětců. Vykreslený obrázek se tedy změní z:

Obrázek 12: Rozdíl mezi použitím a nepoužitím pozadí při vykreslování plochy tvarů štětci.

bitcoin školení listopad 24

Samozřejmě opět následuje výpis zdrojového kódu tohoto příkladu:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
import sys
import math
 
# import "jádra" frameworku Qt i modulu pro GUI
from PySide import QtCore
from PySide import QtGui
 
 
# funkce pro vykreslení obdélníku zadanou barvou a se specifikovaným štětcem
def drawRectangleUsingBrush(qPainter, color, x, y, width, height, brush_style, pen_width=0):
    # vytvoření pera a nastavení barvy kreslení
    pen = QtGui.QPen(QtGui.QColor(*color))
 
    # změna šířky pera
    pen.setWidth(pen_width)
    qPainter.setPen(pen)
 
    # změna tvaru štětce
    brush = QtGui.QBrush(QtGui.QColor(*color))
    brush.setStyle(brush_style)
    qPainter.setBrush(brush)
 
    # vykreslení obdélníku
    qPainter.drawRect(x, y, width, height)
 
 
# nový widget bude odvozen od obecného hlavního okna
class MainWindow(QtGui.QMainWindow):
 
    # rozměry rastrového obrázku
    IMAGE_WIDTH = 440
    IMAGE_HEIGHT = 140
 
    def __init__(self):
        # zavoláme konstruktor předka
        super(MainWindow, self).__init__()
 
        self.prepareImage()
        # konfigurace GUI + přidání widgetu do okna
        self.prepareGUI()
 
    def prepareImage(self):
        # vytvoření instance třídy QImage
        self.image = QtGui.QImage(MainWindow.IMAGE_WIDTH,
                                  MainWindow.IMAGE_HEIGHT,
                                  QtGui.QImage.Format_RGB32)
 
        # vytvoření objektu typu QPainter s předáním
        # reference na "pokreslovaný" objekt
        qp = QtGui.QPainter(self.image)
 
        # konstanty s n-ticemi představujícími základní barvy
        BLACK = (0, 0, 0)
        BLUE = (0, 0, 255)
        CYAN = (0, 255, 255)
        GREEN = (0, 255, 0)
        YELLOW = (255, 255, 0)
        RED = (255, 0, 0)
        MAGENTA = (255, 0, 255)
        WHITE = (255, 255, 255)
 
        # barva pozadi a povoleni vykreslení pozadi (vyplne)
        qp.setBackgroundMode(QtCore.Qt.BGMode.OpaqueMode)
        qp.setBackground(QtGui.QColor(*BLUE))
 
        # Vykreslení obdélníků různým stylem
        drawRectangleUsingBrush(qp, YELLOW, 10, 10, 50, 50, QtCore.Qt.SolidPattern)
        drawRectangleUsingBrush(qp, YELLOW, 70, 10, 50, 50, QtCore.Qt.HorPattern)
        drawRectangleUsingBrush(qp, YELLOW, 130, 10, 50, 50, QtCore.Qt.VerPattern)
        drawRectangleUsingBrush(qp, YELLOW, 190, 10, 50, 50, QtCore.Qt.CrossPattern)
        drawRectangleUsingBrush(qp, YELLOW, 250, 10, 50, 50, QtCore.Qt.BDiagPattern)
        drawRectangleUsingBrush(qp, YELLOW, 310, 10, 50, 50, QtCore.Qt.FDiagPattern)
        drawRectangleUsingBrush(qp, YELLOW, 370, 10, 50, 50, QtCore.Qt.DiagCrossPattern)
 
        drawRectangleUsingBrush(qp, WHITE, 10, 70, 50, 50, QtCore.Qt.Dense1Pattern)
        drawRectangleUsingBrush(qp, WHITE, 70, 70, 50, 50, QtCore.Qt.Dense2Pattern)
        drawRectangleUsingBrush(qp, WHITE, 130, 70, 50, 50, QtCore.Qt.Dense3Pattern)
        drawRectangleUsingBrush(qp, WHITE, 190, 70, 50, 50, QtCore.Qt.Dense4Pattern)
        drawRectangleUsingBrush(qp, WHITE, 250, 70, 50, 50, QtCore.Qt.Dense5Pattern)
        drawRectangleUsingBrush(qp, WHITE, 310, 70, 50, 50, QtCore.Qt.Dense6Pattern)
        drawRectangleUsingBrush(qp, WHITE, 370, 70, 50, 50, QtCore.Qt.Dense7Pattern)
 
        # vytvoření instance třídy QPixmap z objektu QImage
        self.pixmap = QtGui.QPixmap.fromImage(self.image)
 
    def prepareGUI(self):
        # velikost okna nezadávejte ručně - špatně se počítá kvůli toolbaru
        # self.resize(256, 300)
        self.setWindowTitle('QPainter')
 
        # tlačítko Quit
        quitAction = QtGui.QAction(QtGui.QIcon('icons/application-exit.png'),
                                   '&Quit', self)
        quitAction.triggered.connect(self.close)
        quitAction.setStatusTip('Quit the application')
        quitAction.setShortcut('Ctrl+Q')
 
        # nástrojový pruh
        self.toolbar = self.addToolBar('title')
        self.toolbar.setMovable(False)
 
        # přidání tlačítka na nástrojový pruh
        self.toolbar.addAction(quitAction)
 
        # doprostřed okna přidáme návěští s rastrovým obrázkem
        self.addLabelWithPixmap()
 
        # zobrazení hlavního okna
        self.show()
 
    def addLabelWithPixmap(self):
        # vytvoření návěští
        label = QtGui.QLabel("test")
        # přiřazení rastrového obrázku k návěští
        label.setPixmap(self.pixmap)
        # vložení návěští do hlavního okna
        self.setCentralWidget(label)
 
    def run(self, app):
        # zobrazení okna na obrazovce
        self.show()
        # vstup do smyčky událostí (event loop)
        app.exec_()
 
 
def main():
    app = QtGui.QApplication(sys.argv)
    MainWindow().run(app)
 
 
if __name__ == '__main__':
    main()

17. Repositář s demonstračními příklady

Zdrojové kódy všech sedmi dnes popsaných demonstračních příkladů byly opět, podobně jako tomu bylo i v předchozích článcích, uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/pre­sentations. Pokud nechcete klonovat celý repositář, můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:

18. Odkazy na Internetu

  1. PySide 1.2.1 documentation
    https://pyside.github.io/doc­s/pyside/index.html
  2. QImage
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QIma­ge.html
  3. QPixmap
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QPix­map.html
  4. QBitmap
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QBit­map.html
  5. QPaintDevice
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QPa­intDevice.html
  6. QPicture
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QPic­ture.html
  7. QPainter
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QPa­inter.html
  8. QPainterPath
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QPa­interPath.html
  9. Afinní zobrazení
    https://cs.wikipedia.org/wi­ki/Afinn%C3%AD_zobrazen%C3%AD
  10. Differences Between PySide and PyQt
    https://wiki.qt.io/Differen­ces_Between_PySide_and_PyQt
  11. PySide 1.2.1 tutorials
    https://pyside.github.io/doc­s/pyside/tutorials/index.html
  12. PySide tutorial
    http://zetcode.com/gui/py­sidetutorial/
  13. Drawing in PySide
    http://zetcode.com/gui/py­sidetutorial/drawing/
  14. Qt Core
    https://pyside.github.io/doc­s/pyside/PySide/QtCore/Qt­.html
  15. QLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QLa­yout.html
  16. QStackedLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QStac­kedLayout.html
  17. QFormLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QFor­mLayout.html
  18. QBoxLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QBox­Layout.html
  19. QHBoxLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QHBox­Layout.html
  20. QVBoxLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QVBox­Layout.html
  21. QGridLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QGrid­Layout.html
  22. QAction
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QAc­tion.html
  23. QMessageBox
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QMes­sageBox.html
  24. QListWidget
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QLis­tWidget.html
  25. Signals & Slots
    http://doc.qt.io/qt-4.8/signalsandslots.html
  26. Signals and Slots in PySide
    http://wiki.qt.io/Signals_an­d_Slots_in_PySide
  27. Intro to PySide/PyQt: Basic Widgets and Hello, World!
    http://www.pythoncentral.io/intro-to-pysidepyqt-basic-widgets-and-hello-world/
  28. QWidget
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QWid­get.html
  29. QMainWindow
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QMa­inWindow.html
  30. QLabel
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QLa­bel.html
  31. QAbstractButton
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QAb­stractButton.html
  32. QCheckBox
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QChec­kBox.html
  33. QRadioButton
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QRa­dioButton.html
  34. QButtonGroup
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QBut­tonGroup.html
  35. QFrame
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QFra­me.html#PySide.QtGui.PySi­de.QtGui.QFrame
  36. QFrame.frameStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QFra­me.html#PySide.QtGui.PySi­de.QtGui.QFrame.frameStyle
  37. Leo editor
    http://leoeditor.com/
  38. IPython Qt Console aneb vylepšený pseudoterminál
    https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-ipython-a-ipython-notebook/#k06
  39. Vývojová prostředí ve Fedoře (4. díl)
    https://mojefedora.cz/vyvojova-prostredi-ve-fedore-4-dil/
  40. Seriál Letní škola programovacího jazyka Logo
    http://www.root.cz/serialy/letni-skola-programovaciho-jazyka-logo/
  41. Educational programming language
    http://en.wikipedia.org/wi­ki/Educational_programmin­g_language
  42. Logo Tree Project:
    http://www.elica.net/downlo­ad/papers/LogoTreeProject­.pdf
  43. Hra Breakout napísaná v Tkinteri
    https://www.root.cz/clanky/hra-breakout-napisana-v-tkinteri/
  44. Hra Snake naprogramovaná v Pythone s pomocou Tkinter
    https://www.root.cz/clanky/hra-snake-naprogramovana-v-pythone-s-pomocou-tkinter/
  45. 24.1. turtle — Turtle graphics
    https://docs.python.org/3­.5/library/turtle.html#mo­dule-turtle
  46. TkDND
    http://freecode.com/projects/tkdnd
  47. Python Tkinter Fonts
    https://www.tutorialspoin­t.com/python/tk_fonts.htm
  48. The Tkinter Canvas Widget
    http://effbot.org/tkinter­book/canvas.htm
  49. Ovládací prvek (Wikipedia)
    https://cs.wikipedia.org/wi­ki/Ovl%C3%A1dac%C3%AD_prvek_­%28po%C4%8D%C3%ADta%C4%8D%29
  50. Rezervovaná klíčová slova v Pythonu
    https://docs.python.org/3/re­ference/lexical_analysis.html#ke­ywords
  51. TkDocs: Styles and Themes
    http://www.tkdocs.com/tuto­rial/styles.html
  52. Drawing in Tkinter
    http://zetcode.com/gui/tkin­ter/drawing/
  53. Changing ttk widget text color (StackOverflow)
    https://stackoverflow.com/qu­estions/16240477/changing-ttk-widget-text-color
  54. The Hitchhiker's Guide to Pyhton: GUI Applications
    http://docs.python-guide.org/en/latest/scenarios/gui/
  55. 7 Top Python GUI Frameworks for 2017
    http://insights.dice.com/2014/11/26/5-top-python-guis-for-2015/
  56. GUI Programming in Python
    https://wiki.python.org/mo­in/GuiProgramming
  57. Cameron Laird's personal notes on Python GUIs
    http://phaseit.net/claird/com­p.lang.python/python_GUI.html
  58. Python GUI development
    http://pythoncentral.io/introduction-python-gui-development/
  59. Graphic User Interface FAQ
    https://docs.python.org/2/faq/gu­i.html#graphic-user-interface-faq
  60. TkInter
    https://wiki.python.org/moin/TkInter
  61. Tkinter 8.5 reference: a GUI for Python
    http://infohost.nmt.edu/tcc/hel­p/pubs/tkinter/web/index.html
  62. TkInter (Wikipedia)
    https://en.wikipedia.org/wiki/Tkinter
  63. appJar
    http://appjar.info/
  64. appJar (Wikipedia)
    https://en.wikipedia.org/wiki/AppJar
  65. appJar na Pythonhosted
    http://pythonhosted.org/appJar/
  66. appJar widgets
    http://appjar.info/pythonWidgets/
  67. Stránky projektu PyGTK
    http://www.pygtk.org/
  68. PyGTK (Wikipedia)
    https://cs.wikipedia.org/wiki/PyGTK
  69. Stránky projektu PyGObject
    https://wiki.gnome.org/Pro­jects/PyGObject
  70. Stránky projektu Kivy
    https://kivy.org/#home
  71. Stránky projektu PyQt
    https://riverbankcomputin­g.com/software/pyqt/intro
  72. PyQt (Wikipedia)
    https://cs.wikipedia.org/wiki/PyGTK
  73. Stránky projektu PySide
    https://wiki.qt.io/PySide
  74. PySide (Wikipedia)
    https://en.wikipedia.org/wiki/PySide
  75. Stránky projektu Kivy
    https://kivy.org/#home
  76. Kivy (framework, Wikipedia)
    https://en.wikipedia.org/wi­ki/Kivy_(framework)
  77. QML Applications
    http://doc.qt.io/qt-5/qmlapplications.html
  78. KDE
    https://www.kde.org/
  79. Qt
    https://www.qt.io/
  80. GNOME
    https://en.wikipedia.org/wiki/GNOME
  81. Category:Software that uses PyGTK
    https://en.wikipedia.org/wi­ki/Category:Software_that_u­ses_PyGTK
  82. Category:Software that uses PyGObject
    https://en.wikipedia.org/wi­ki/Category:Software_that_u­ses_PyGObject
  83. Category:Software that uses wxWidgets
    https://en.wikipedia.org/wi­ki/Category:Software_that_u­ses_wxWidgets
  84. GIO
    https://developer.gnome.or­g/gio/stable/
  85. GStreamer
    https://gstreamer.freedesktop.org/
  86. GStreamer (Wikipedia)
    https://en.wikipedia.org/wi­ki/GStreamer
  87. Wax Gui Toolkit
    https://wiki.python.org/moin/Wax
  88. Python Imaging Library (PIL)
    http://infohost.nmt.edu/tcc/hel­p/pubs/pil/
  89. Why Pyjamas Isn’t a Good Framework for Web Apps (blogpost z roku 2012)
    http://blog.pyjeon.com/2012/07/29/why-pyjamas-isnt-a-good-framework-for-web-apps/

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.