Hlavní navigace

Tvorba GUI v Pythonu s PySide: pokročilejší grafické operace používající třídu QPainter

16. 1. 2018
Doba čtení: 38 minut

Sdílet

V již osmém článku věnovaném tvorbě GUI v Pythonu s využitím PySide se zaměříme na další možnosti nabízené třídou QPainter při tvorbě 2D grafiky. Ukážeme si tvorbu štětců, všechny jednoduché 2D entity i práci s polygony.

Obsah

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

2. Vytvoření štětce (brush) z externího rastrového obrázku (bitmapy i pixmapy)

3. První demonstrační příklad – vytvoření štětce z externí pixmapy

4. Druhý demonstrační příklad – vytvoření štětce z externí bitmapy

5. Gradientní barevné přechody využitelné při kreslení vyplněných obrazců

6. Třetí demonstrační příklad – použití lineárního gradientu ve funkci štětce

7. Čtvrtý demonstrační příklad – opakující se lineární přechod (pruhy)

8. Pátý demonstrační příklad – radiální barevný přechod

9. 2D entity, které je možné vykreslit s využitím třídy QPainter

10. Body a úsečky

11. Obdélníky, kružnice a elipsy

12. Obdélníky se zaoblenými rohy

13. Oblouky, kruhové výseče a kruhové úseče

14. Šestý demonstrační příklad – vykreslení „jednoduchých“ 2D entit

15. Polyčáry (lomené čáry) a polygony (mnohoúhelníky)

16. Objekt typu QPolygon, přetížený operátor << pro konstrukci polygonu

17. Sedmý demonstrační příklad – vykreslení domku jednou lomenou čarou (polyčarou)

18. Osmý demonstrační příklad – vykreslení hvězdy s využitím polygonu

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

20. Odkazy na Internetu

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

V předchozím článku jsme se seznámili se způsobem vykreslování 2D entit s využitím třídy QPainter, která programátorům nabízí relativně vysokoúrovňový přístup k tvorbě grafiky, na rozdíl od třídy QImage, kde jsme byli omezeni na změnu barev jednotlivých pixelů, popř. na vyplnění oblasti konstantní barvou. Připomeňme si jen krátce, že QPainter je třída, která pouze realizuje vykreslovací algoritmy, ovšem vykreslování je nutné provádět na nějaké „plátno“. To může být představováno například instancí třídy QPixmap apod. QPainter navíc umožňuje změnu stylu vykreslování obrysů entit a stylu výplně vnitřku uzavřených entit (u liniových entit, tj. například úseček či oblouků, samozřejmě vnitřek neexistuje a není vyplňován). Styl vykreslování je řízen stavem dvou objektů, které mohou být přiřazeny instanci třídy QPainter:

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

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

Minule jsme se již s těmito nástroji setkali, takže již víme, jakým způsobem je možné změnit barvu, šířku i styl obrysů 2D entit (vlastnosti pera) a taktéž jsme se seznámili se základními styly výplně (vlastnosti štětce). Dnes si ukážeme, jak je možné styl výplně načíst z externí bitmapy (vzorek) či pixmapy (textura), popř. jak se používají gradientní (barevné) přechody pro vyplnění jednotlivých 2D entit. Následně se seznámíme se všemi 2D entitami nabízenými třídou QPainter, a to včetně entit s proměnným počtem vrcholů.

Obrázek 2: Vzorník standardních štětců, které již jsou ve frameworku PySide připraveny.

2. Vytvoření štětce (brush) z externího rastrového obrázku (bitmapy i pixmapy)

Kromě vzorků štětců, které jsou součástí frameworku PySide, je možné vytvořit nový vzorek s využitím bitmapy nebo pixmapy. Pokud se použije bitmapa, tj. rastrový obrázek, kde je každý pixel reprezentován jen jediným bitem, je nutné nastavit barvu vzorku, a to přímo při konstrukci štětce. U pixmap to samozřejmě není nutné, protože pixmapy jsou reprezentovány buď obrázky s barvovou paletou či obrázky plnobarevnými (truecolor).

Nový štětec se z bitmapy vytvoří takto:

brush = QtGui.QBrush(barva, bitmapa)

U štětce tvořeného z pixmapy se nemusí specifikovat barva:

brush = QtGui.QBrush(pixmapa)

Obrázek 3: Bitmapa, kterou v dalším příkladech použijeme jako podklad pro vzorek štětce. V praxi se sice setkáme spíše se skutečnými opakujícími se vzorky, my si však potřebujeme ukázat, jak se podklad posouvá či naopak neposouvá společně s vykreslovanými 2D entitami.

Bitmapu nebo pixmapu je možné načíst z externích souborů. To se provede jednoduše předáním cesty a jména souboru s rastrovým obrázkem do konstruktoru QBitmap(filename) popř. do konstruktoru QPixmap(filename). Podporovány jsou tyto formáty rastrových obrázků:

Koncovka souboru Formát
.bmp Windows Bitmap
.gif Graphic Interchange Format
.jpg, .jpeg Joint Photographic Experts Group
.png Portable Network Graphics
.pbm Portable Bitmap
.pgm Portable Graymap
.ppm Portable Pixmap
.xbm X11 Bitmap
.xpm X11 Pixmap

Obrázek 4: Pixmapa, kterou v dalších příkladech taktéž použijeme jako podklad pro vzorek štětce.

3. První demonstrační příklad – vytvoření štětce z externí pixmapy

V dnešním prvním demonstračním příkladu je ukázán jeden ze způsobů vytvoření stylu štětce z externí pixmapy (barevného obrázku). Samotné načtení vzorku/textury, která bude použita při vyplňování 2D entit, je realizováno ve funkci createBrushFromPixmap:

# vytvoření štětce z pixmapy
def createBrushFromPixmap(filename):
    pixmap = QtGui.QPixmap(filename)
    return QtGui.QBrush(pixmap)

Posléze je možné texturu štětce jednoduše načíst:

brush = createBrushFromPixmap("pixmaps/voronoi.png")

a následně použít při vykreslení uzavřeného obrazce:

# 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
qPainter.setBrush(brush)
 
# vykreslení obdélníku
qPainter.drawRect(x, y, width, height)

Povšimněte si toho, jak na sebe navazují vnitřní plochy jednotlivých 2D obrazců vyplněných stejnou texturou – pokud bychom animovali posun jednoho čtverce pouhou změnou jeho souřadnic, vnitřní vzorek by se neposouval:

Obrázek 5: Několik čtverců, jejichž vnitřní plocha používá totožnou texturu.

Úplný zdrojový kód prvního příkladu je umístěn pod tento odstavec:

#!/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
 
 
# vytvoření štětce z pixmapy
def createBrushFromPixmap(filename):
    pixmap = QtGui.QPixmap(filename)
    return QtGui.QBrush(pixmap)
 
 
# funkce pro vykreslení obdélníku zadanou barvou a se specifikovaným štětcem
def drawRectangleUsingCustomBrush(qPainter, color, x, y, width, height, brush, 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
    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 = 250
    IMAGE_HEIGHT = 170
 
    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)
 
        # vymazání obrázku
        self.image.fill(0)
 
        # 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)
 
        brush = createBrushFromPixmap("pixmaps/voronoi.png")
 
        # Vykreslení obdélníků různým stylem
        drawRectangleUsingCustomBrush(qp, YELLOW, 10, 10, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, RED, 90, 10, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, BLUE, 170, 10, 70, 70, brush)
 
        # barva pozadi a povoleni vykreslení pozadi (vyplne)
        qp.setBackgroundMode(QtCore.Qt.BGMode.OpaqueMode)
        qp.setBackground(QtGui.QColor(*BLUE))
 
        drawRectangleUsingCustomBrush(qp, WHITE, 10, 90, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, WHITE, 90, 90, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, WHITE, 170, 90, 70, 70, brush)
 
        # 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()

4. Druhý demonstrační příklad – vytvoření štětce z externí bitmapy

Ve druhém příkladu je ukázáno použití bitmapy pro vytvoření jednobarevného nebo dvoubarevného vzorku. Povšimněte si jedné změny oproti předchozímu příkladu – vzhledem k tomu, že některé formáty bitmap neobsahují informace o barvě, ale jen vlastní bitový vzorek, je nutné specifikovat barvu „zapnutých“ pixelů, a to ideálně přímo v konstruktoru třídy QBrush:

# vytvoření štětce z bitmapy
def createBrushFromBitmap(color, filename):
    bitmap = QtGui.QBitmap(filename)
    c = QtGui.QColor(*color)
    return QtGui.QBrush(c, bitmap)

Takto připravenou funkci použijeme pro načtení bitmapy uložené ve formátu XBM (X BitMap), což je dnes již sice dosti zastaralý formát, ale mnohdy se ještě setkáme s ikonami i výplňovými vzorky uloženými právě v tomto formátu:

WHITE = (255, 255, 255)
brush = createBrushFromBitmap(WHITE, "bitmaps/test.xbm")

Použití takto vytvořeného štětce se již nijak neliší od předchozího příkladu:

Obrázek 6: Několik čtverců, jejichž vnitřní plocha používá totožný bitový vzorek. Opět si povšimněte faktu, že se vzorek neposunuje společně s vlastní plochou čtverců.

Následuje výpis zdrojového kódu dnešního druhého demonstračního 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
 
 
# vytvoření štětce z bitmapy
def createBrushFromBitmap(color, filename):
    bitmap = QtGui.QBitmap(filename)
    c = QtGui.QColor(*color)
    return QtGui.QBrush(c, bitmap)
 
 
# funkce pro vykreslení obdélníku zadanou barvou a se specifikovaným štětcem
def drawRectangleUsingCustomBrush(qPainter, color, x, y, width, height, brush, 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
    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 = 330
    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)
 
        # vymazání obrázku
        self.image.fill(0)
 
        # 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)
 
        brush = createBrushFromBitmap(WHITE, "bitmaps/test.xbm")
 
        # Vykreslení obdélníků různým stylem
        drawRectangleUsingCustomBrush(qp, YELLOW, 10, 10, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, RED, 90, 10, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, BLUE, 170, 10, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, MAGENTA, 250, 10, 70, 70, brush)
 
        drawRectangleUsingCustomBrush(qp, WHITE, 10, 90, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, WHITE, 90, 90, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, WHITE, 170, 90, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, WHITE, 250, 90, 70, 70, brush)
 
        drawRectangleUsingCustomBrush(qp, BLACK, 10, 170, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, BLACK, 90, 170, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, BLACK, 170, 170, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, BLACK, 250, 170, 70, 70, brush)
 
        # 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. Gradientní barevné přechody využitelné při kreslení vyplněných obrazců

Další možnost vyplňování uzavřených tvarů spočívá v použití gradientích (barevných) přechodů. Existuje několik variant těchto přechodů, přičemž nejpoužívanější je lineární přechod (v libovolném směru) nebo radiální přechod. U lineárních přechodů je nutné specifikovat dva body a taktéž libovolné množství barev. Přechod je vypočten na úsečce ležící mezi těmito dvěma body – tím je určen jak směr přechodu, tak i nepřímo jeho šířka (čím jsou body blíže u sebe, tím je šířka přechodu menší). Barvy je možné specifikovat jak přímo v zadaných bodech (relativní souřadnice na úsečce budou v tomto případě rovny 0,0 a 1,0, nebo v libovolném místě zmíněné úsečky). Celkový počet zadaných barev není omezen na dvě, můžeme například vytvořit přechod mezi třemi barvami:

gradient = QtGui.QLinearGradient(x1, y1, x2, y2)
 
# barva přesně v prvním bodu
gradient.setColorAt(0.0, QtGui.QColor(barva1))
 
# barva v polovině přechodu
gradient.setColorAt(0.5, QtGui.QColor(barva2))
 
# barva přesně v posledním bodu
gradient.setColorAt(1.0, QtGui.QColor(barva3))
 
brush = QtGui.QBrush(gradient)

Obrázek 7: Lineární gradientní přechod se třemi barvami.

U gradientního přechodu je možné navíc nastavit režim opakování či zrcadlení, což je téma, které si ukážeme v demonstračním příkladu.

Obrázek 8: Lineární gradientní přechod se čtyřmi barvami.

U specifikace radiálního přechodu se zadává střed a poloměr kružnice, v níž k výpočtu změny barev dochází. Barvy se zadávají na úsečce ze středu směrem k zadané kružnici. Ani zde není počet barev omezen:

gradient = QtGui.QRadialGradient(cx, cy, radius)
gradient.setColorAt(0.2, QtGui.QColor(barva1))
gradient.setColorAt(0.5, QtGui.QColor(barva1))
gradient.setColorAt(1.0, QtGui.QColor(barva3))
brush QtGui.QBrush(gradient)

Obrázek 9: Radiální gradientní přechod se čtyřmi barvami.

6. Třetí demonstrační příklad – použití lineárního gradientu ve funkci štětce

V dnešním třetím demonstračním příkladu je ukázána tvorba lineárního gradientního přechodu mezi dvěma barvami. Výsledek by měl vypadat následovně:

Obrázek 10: Lineární gradientní přechod se dvěma barvami vykreslený demonstračním příkladem.

Podívejme se nyní na to, jak vypadá zdrojový kód 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
 
 
# vytvoření štětce z gradientního přechodu
def createBrushFromGradient(color1, color2):
    gradient = QtGui.QLinearGradient(100, 100, 200, 200)
    gradient.setColorAt(0.2, QtGui.QColor(*color1))
    gradient.setColorAt(0.8, QtGui.QColor(*color2))
    return QtGui.QBrush(gradient)
 
 
# funkce pro vykreslení obdélníku zadanou barvou a se specifikovaným štětcem
def drawRectangleUsingCustomBrush(qPainter, color, x, y, width, height, brush, 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
    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 = 330
    IMAGE_HEIGHT = 250
 
    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)
 
        # vymazání obrázku
        self.image.fill(0)
 
        # 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)
 
        brush = createBrushFromGradient(YELLOW, RED)
 
        # Vykreslení obdélníků různým stylem
        drawRectangleUsingCustomBrush(qp, YELLOW, 10, 10, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, RED, 90, 10, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, BLUE, 170, 10, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, MAGENTA, 250, 10, 70, 70, brush)
 
        drawRectangleUsingCustomBrush(qp, WHITE, 10, 90, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, WHITE, 90, 90, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, WHITE, 170, 90, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, WHITE, 250, 90, 70, 70, brush)
 
        drawRectangleUsingCustomBrush(qp, BLACK, 10, 170, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, BLACK, 90, 170, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, BLACK, 170, 170, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, BLACK, 250, 170, 70, 70, brush)
 
        # 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. Čtvrtý demonstrační příklad – opakující se lineární přechod (pruhy)

V dalším demonstračním příkladu je ukázán vliv volby:

gradient.setSpread(QtGui.QGradient.Spread.ReflectSpread)

Tato volba způsobí, že se lineární přechod bude opakovat, ovšem každé sudé opakování bude navíc zrcadleno. Výsledek můžeme vidět na následujícím screenshotu:

Obrázek 11: Opakující se lineární gradientní přechod se dvěma barvami.

Opět se podívejme na zdrojový kód příkladu. Ten se odlišuje od příkladu předchozího prakticky jen odlišným nastavením přechodu, volbou jiných barev a takovou úpravou obou bodů přechodu, aby začátek přechodu korespondoval s velikostí vykreslovaných čtverců:

#!/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
 
 
# vytvoření štětce z gradientního přechodu
def createBrushFromGradient(color1, color2):
    gradient = QtGui.QLinearGradient(100, 100, 100, 140)
    gradient.setColorAt(0.2, QtGui.QColor(*color1))
    gradient.setColorAt(1.0, QtGui.QColor(*color2))
    gradient.setSpread(QtGui.QGradient.Spread.ReflectSpread)
    return QtGui.QBrush(gradient)
 
 
# funkce pro vykreslení obdélníku zadanou barvou a se specifikovaným štětcem
def drawRectangleUsingCustomBrush(qPainter, color, x, y, width, height, brush, 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
    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 = 330
    IMAGE_HEIGHT = 250
 
    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)
 
        # vymazání obrázku
        self.image.fill(0)
 
        # 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)
 
        brush = createBrushFromGradient(WHITE, BLUE)
 
        # Vykreslení obdélníků různým stylem
        drawRectangleUsingCustomBrush(qp, YELLOW, 10, 10, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, RED, 90, 10, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, BLUE, 170, 10, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, MAGENTA, 250, 10, 70, 70, brush)
 
        drawRectangleUsingCustomBrush(qp, WHITE, 10, 90, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, WHITE, 90, 90, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, WHITE, 170, 90, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, WHITE, 250, 90, 70, 70, brush)
 
        drawRectangleUsingCustomBrush(qp, BLACK, 10, 170, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, BLACK, 90, 170, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, BLACK, 170, 170, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, BLACK, 250, 170, 70, 70, brush)
 
        # 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()

8. Pátý demonstrační příklad – radiální barevný přechod

V pátém příkladu je ukázán způsob použití radiálního barevného přechodu. Střed přechodu je umístěn na souřadnice [85, 85] a jeho poloměr je nastaven na 100 délkových jednotek. Navíc je povoleno opakování přechodu se zrcadlením (schválně si zkuste zakomentovat volání metody setSpread()):

gradient = QtGui.QRadialGradient(85, 85, 100)
gradient.setColorAt(0.2, QtGui.QColor(*color1))
gradient.setColorAt(1.0, QtGui.QColor(*color2))
gradient.setSpread(QtGui.QGradient.Spread.ReflectSpread)
brush = QtGui.QBrush(gradient)

Obrázek 12: Opakující se radiální barevný přechod se dvěma barvami vykreslený demonstračním příkladem.

Následuje výpis zdrojového kódu 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
 
 
# vytvoření štětce z gradientního přechodu
def createBrushFromGradient(color1, color2):
    gradient = QtGui.QRadialGradient(85, 85, 100)
    gradient.setColorAt(0.2, QtGui.QColor(*color1))
    gradient.setColorAt(1.0, QtGui.QColor(*color2))
    gradient.setSpread(QtGui.QGradient.Spread.ReflectSpread)
    return QtGui.QBrush(gradient)
 
 
# funkce pro vykreslení obdélníku zadanou barvou a se specifikovaným štětcem
def drawRectangleUsingCustomBrush(qPainter, color, x, y, width, height, brush, 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
    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 = 330
    IMAGE_HEIGHT = 250
 
    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)
 
        # vymazání obrázku
        self.image.fill(0)
 
        # 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)
 
        brush = createBrushFromGradient(YELLOW, BLACK)
 
        # Vykreslení obdélníků různým stylem
        drawRectangleUsingCustomBrush(qp, YELLOW, 10, 10, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, RED, 90, 10, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, BLUE, 170, 10, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, MAGENTA, 250, 10, 70, 70, brush)
 
        drawRectangleUsingCustomBrush(qp, WHITE, 10, 90, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, WHITE, 90, 90, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, WHITE, 170, 90, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, WHITE, 250, 90, 70, 70, brush)
 
        drawRectangleUsingCustomBrush(qp, BLACK, 10, 170, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, BLACK, 90, 170, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, BLACK, 170, 170, 70, 70, brush)
        drawRectangleUsingCustomBrush(qp, BLACK, 250, 170, 70, 70, brush)
 
        # 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()

9. 2D entity, které je možné vykreslit s využitím třídy QPainter

Třída QPainter 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(), …

V dalších kapitolách se postupně s jednotlivými entitami seznámíme (s výjimkou textu a cest; těm bude věnován samostatný článek).

10. Body a úsečky

Body se vykreslují buď pomocí metody drawPoint() nebo (pokud potřebujeme vykreslit větší množství bodů současně) s využitím metody drawPoints(). Na tomto místě je vhodné si uvědomit, že se body odlišují od pixelů, protože pixel je dále nedělitelná diskrétní jednotka, ale bod může být vykreslen s využitím stop různé velikosti a tvaru (stopa se nastavuje pomocí atributů pera). Následující kód vykreslí náhodné body v dané oblasti:

# funkce pro vykreslení bodu zadanou barvou
def drawPoint(qPainter, color, x, y):
    setColor(qPainter, color)
 
    # vykreslení jediného bodu
    qPainter.drawPoint(x, y)
 
 
for _ in range(250):
    x = random.uniform(90, 160)
    y = random.uniform(10, 80)
    drawPoint(qp, WHITE, x, y)

S úsečkami jsme se již setkali minule, takže si jen krátce uvedeme funkci pro vykreslení úsečky specifikovanou barvou:

# funkce pro vykreslení úsečky zadanou barvou
def drawLine(qPainter, color, x1, y1, x2, y2):
    setColor(qPainter, color)
 
    # vykreslení úsečky
    qPainter.drawLine(x1, y1, x2, y2)

Obrázek 13: Úsečka a náhodně vykreslené body.

11. Obdélníky, kružnice a elipsy

Obdélníky i elipsy jsou specifikovány stejným způsobem, konkrétně souřadnicí levého horního rohu, šířkou a výškou. Pokud je šířka a výška shodná, vykreslí se čtverec popř. kružnice. Podívejme se na příklad:

# funkce pro vykreslení obdélníku zadanou barvou
def drawRectangle(qPainter, color, x, y, width, height):
    setColor(qPainter, color)
 
    # vykreslení obdélníku
    qPainter.drawRect(x, y, width, height)
 
 
# funkce pro vykreslení elipsy zadanou barvou
def drawEllipse(qPainter, color, x, y, width, height):
    # vytvoření pera a nastavení barvy kreslení
    pen = QtGui.QPen(QtGui.QColor(*color))
    qPainter.setPen(pen)
 
    # vykreslení elipsy
    qPainter.drawEllipse(x, y, width, height)
 
 
# funkce pro vykreslení kružnice zadanou barvou
def drawCircle(qPainter, color, cx, cy, radius):
    setColor(qPainter, color)
 
    # vykreslení kružnice
    qPainter.drawEllipse(cx-radius, cy-radius, 2*radius, 2*radius)

Pro kreslení kružnic jsou zvoleny odlišné parametry, konkrétně souřadnice středu a poloměr. Podobně je samozřejmě možné upravit i funkci pro kreslení elipsy, pouze se musí specifikovat poloměr obou poloos.

Obrázek 14: Obdélník (resp. čtverec jako speciální typ obdélníku), kružnice a dvě elipsy.

12. Obdélníky se zaoblenými rohy

Obdélníky se zaoblenými rohy se vykreslují metodou drawRoundRect() a drawRoundedRect(). Kromě souřadnice jednoho vrcholu obdélníku a jeho rozměrů se těmto metodám předávají i poloměry rohů (v horizontálním i vertikálním směru). Z dalšího příkladu si povšimněte, že pokud je poloměr příliš velký, stává se ze čtverce kružnice:

# funkce pro vykreslení obdélníku zadanou barvou a se zaoblenými rohy
def drawRoundedRectangle(qPainter, color, x, y, width, height, r):
    setColor(qPainter, color)
 
    # vykreslení obdélníku
    qPainter.drawRoundedRect(x, y, width, height, r, r)
 
drawRoundedRectangle(qp, MAGENTA, 10, 170, 70, 70, 1)
drawRoundedRectangle(qp, MAGENTA, 90, 170, 70, 70, 10)
drawRoundedRectangle(qp, MAGENTA, 170, 170, 70, 70, 20)
drawRoundedRectangle(qp, MAGENTA, 250, 170, 70, 70, 1000)

Obrázek 15: Čtverec se zaoblenými rohy; poloměr zaoblení se zleva doprava postupně zvětšuje.

13. Oblouky, kruhové výseče a kruhové úseče

Pro kreslení oblouků, kruhových výsečí a kruhových úsečí se používají velmi podobné metody, které se od sebe odlišují pouze jménem. U všech tří metod se opět specifikuje souřadnice jednoho z rohů obalového obdélníku následovaná šířkou a výškou tohoto obdélníku. Navíc však ještě musíme specifikovat počáteční úhel oblouku a jeho délku. Obě tyto hodnoty se zadávají ve stupních vynásobených konstantou 16 (tento způsob specifikace úhlů odpovídá konceptům, na nichž je postavena knihovna Qt):

# funkce pro vykreslení oblouku zadanou barvou
def drawArc(qPainter, color, cx, cy, radius, angle, span):
    setColor(qPainter, color)
 
    # vykreslení kružnice
    qPainter.drawArc(cx-radius, cy-radius, 2*radius, 2*radius, 16*angle, 16*span)
 
 
# funkce pro vykreslení kruhové výseče zadanou barvou
def drawPie(qPainter, color, cx, cy, radius, angle, span):
    setColor(qPainter, color)
 
    # vykreslení kruhové výseče
    qPainter.drawPie(cx-radius, cy-radius, 2*radius, 2*radius, 16*angle, 16*span)
 
 
# funkce pro vykreslení kruhové úseče zadanou barvou
def drawChord(qPainter, color, cx, cy, radius, angle, span):
    setColor(qPainter, color)
 
    # vykreslení kruhové úseče
    qPainter.drawChord(cx-radius, cy-radius, 2*radius, 2*radius, 16*angle, 16*span)

Obrázek 16: Oblouky, kruhové výseče a kruhové úseče.

14. Šestý demonstrační příklad – vykreslení „jednoduchých“ 2D entit

Všechny typy entit popsaných v předchozích čtyřech kapitolách jsou vykresleny dnešním šestým příkladem, což je ostatně patrné z následujícího screenshotu:

Obrázek 17: Všechny „jednoduché“ 2D entity podporované třídou QPainter.

Opět se podívejme na úplný zdrojový kód tohoto příkladu:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
import sys
import math
import random
 
# import "jádra" frameworku Qt i modulu pro GUI
from PySide import QtCore
from PySide import QtGui
 
 
# nastavení barvy kreslení (pera) na zadanou barvu
def setColor(qPainter, color):
    # vytvoření pera a nastavení barvy kreslení
    pen = QtGui.QPen(QtGui.QColor(*color))
 
    # kreslit se bude právě vytvořeným perem
    qPainter.setPen(pen)
 
 
# funkce pro vykreslení bodu zadanou barvou
def drawPoint(qPainter, color, x, y):
    setColor(qPainter, color)
 
    # vykreslení jediného bodu
    qPainter.drawPoint(x, y)
 
 
# funkce pro vykreslení úsečky zadanou barvou
def drawLine(qPainter, color, x1, y1, x2, y2):
    setColor(qPainter, color)
 
    # vykreslení úsečky
    qPainter.drawLine(x1, y1, x2, y2)
 
 
# funkce pro vykreslení obdélníku zadanou barvou
def drawRectangle(qPainter, color, x, y, width, height):
    setColor(qPainter, color)
 
    # vykreslení obdélníku
    qPainter.drawRect(x, y, width, height)
 
 
# funkce pro vykreslení obdélníku zadanou barvou a se zaoblenými rohy
def drawRoundedRectangle(qPainter, color, x, y, width, height, r):
    setColor(qPainter, color)
 
    # vykreslení obdélníku
    qPainter.drawRoundedRect(x, y, width, height, r, r)
 
 
# funkce pro vykreslení elipsy zadanou barvou
def drawEllipse(qPainter, color, x, y, width, height):
    # vytvoření pera a nastavení barvy kreslení
    pen = QtGui.QPen(QtGui.QColor(*color))
    qPainter.setPen(pen)
 
    # vykreslení elipsy
    qPainter.drawEllipse(x, y, width, height)
 
 
# funkce pro vykreslení kružnice zadanou barvou
def drawCircle(qPainter, color, cx, cy, radius):
    setColor(qPainter, color)
 
    # vykreslení kružnice
    qPainter.drawEllipse(cx-radius, cy-radius, 2*radius, 2*radius)
 
 
# funkce pro vykreslení oblouku zadanou barvou
def drawArc(qPainter, color, cx, cy, radius, angle, span):
    setColor(qPainter, color)
 
    # vykreslení kružnice
    qPainter.drawArc(cx-radius, cy-radius, 2*radius, 2*radius, 16*angle, 16*span)
 
 
# funkce pro vykreslení kruhové výseče zadanou barvou
def drawPie(qPainter, color, cx, cy, radius, angle, span):
    setColor(qPainter, color)
 
    # vykreslení kruhové výseče
    qPainter.drawPie(cx-radius, cy-radius, 2*radius, 2*radius, 16*angle, 16*span)
 
 
# funkce pro vykreslení kruhové úseče zadanou barvou
def drawChord(qPainter, color, cx, cy, radius, angle, span):
    setColor(qPainter, color)
 
    # vykreslení kruhové úseče
    qPainter.drawChord(cx-radius, cy-radius, 2*radius, 2*radius, 16*angle, 16*span)
 
 
# nový widget bude odvozen od obecného hlavního okna
class MainWindow(QtGui.QMainWindow):
 
    # rozměry rastrového obrázku
    IMAGE_WIDTH = 330
    IMAGE_HEIGHT = 520
 
    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)
 
        # vymazání obrázku
        self.image.fill(0)
 
        # 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í různých 2D entit
        drawLine(qp, GREEN, 10, 10, 80, 80)
 
        for _ in range(250):
            x = random.uniform(90, 160)
            y = random.uniform(10, 80)
            drawPoint(qp, WHITE, x, y)
 
        drawRectangle(qp, YELLOW, 10, 90, 70, 70)
        drawCircle(qp, RED, 125, 125, 35)
        drawEllipse(qp, CYAN, 170, 30+80, 70, 35)
        drawEllipse(qp, BLUE, 268, 10+80, 35, 70)
 
        drawRoundedRectangle(qp, MAGENTA, 10, 170, 70, 70, 1)
        drawRoundedRectangle(qp, MAGENTA, 90, 170, 70, 70, 10)
        drawRoundedRectangle(qp, MAGENTA, 170, 170, 70, 70, 20)
        drawRoundedRectangle(qp, MAGENTA, 250, 170, 70, 70, 1000)
 
        drawArc(qp, CYAN, 10+35, 260+35, 35, 0, 90)
        drawArc(qp, CYAN, 90+35, 260+35, 35, 45, 90)
        drawArc(qp, CYAN, 170+35, 260+35, 35, 45, 180)
        drawArc(qp, CYAN, 250+35, 260+35, 35, 45, 270)
 
        drawPie(qp, YELLOW, 10+35, 350+35, 35, 0, 90)
        drawPie(qp, YELLOW, 90+35, 350+35, 35, 45, 90)
        drawPie(qp, YELLOW, 170+35, 350+35, 35, 45, 180)
        drawPie(qp, YELLOW, 250+35, 350+35, 35, 45, 270)
 
        drawChord(qp, GREEN, 10+35, 440+35, 35, 0, 90)
        drawChord(qp, GREEN, 90+35, 440+35, 35, 45, 90)
        drawChord(qp, GREEN, 170+35, 440+35, 35, 45, 180)
        drawChord(qp, GREEN, 250+35, 440+35, 35, 45, 270)
 
        # 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. Polyčáry (lomené čáry) a polygony (mnohoúhelníky)

Některé typy 2D entit, zejména lomené čáry a polygony (mnohoúhelníky), mohou být deklarovány pomocí takřka libovolného množství vrcholů. Z tohoto důvodu se při vykreslování těchto entit využívá datová struktura nazvaná QPolygon a vykreslení lomené čáry a mnohoúhelníku bude vypadat následovně:

# funkce pro vykreslení lomené čáry zadanou barvou
def drawPolyline(qPainter, color, polygon):
    setColor(qPainter, color)
 
    # vykreslení lomené čáry
    qPainter.drawPolyline(polygon)

a:

# funkce pro vykreslení polygonu zadanou barvou
def drawPolygon(qPainter, color, brush, polygon, fillrule):
    setColor(qPainter, color)
 
    # změna tvaru štětce
    qPainter.setBrush(brush)
 
    # vykreslení polygonu
    qPainter.drawPolygon(polygon, fillrule)

Povšimněte si, že u polygonu je možné nastavit režim vyplňování – buď se vyplní celá plocha polygonu nebo pouze ta část, která je označena jako vnitřní algoritmem sudý-lichý. Tento algoritmus pro libovolný bod X vytvoří paprsek z tohoto bodu do nekonečna a zjišťuje počet průsečíků paprsku s hranami objektu. Pokud je počet průsečíků lichý, leží bod X uvnitř objektu, jinak leží vně. Rozdíl uvidíme při kresbě hvězdy.

16. Objekt typu QPolygon, přetížený operátor << pro konstrukci polygonu

Objekt typu QPolygon obsahuje souřadnice vrcholů reprezentované datovou strukturou QPoint. To znamená, že pokud máme sekvenci dvojic [x,y], můžeme z této sekvence vytvořit objekt typu QPolygon relativně snadno, například takto:

# vytvoření polygonu ze sekvence koordinát [x,y]
def createPolygon(coordinatesSequence):
    polygon = QtGui.QPolygon()
    for coordinates in coordinatesSequence:
        p = QtCore.QPoint(coordinates[0], coordinates[1])
        polygon.append(p)
    return polygon

Existuje však ještě jednodušší způsob, protože pro objekt QPolygon byl přetížen operátor <<, který taktéž realizuje metodu append(). Funkci tedy můžeme přepsat i následujícím (pro někoho čitelnějším) způsobem:

# vytvoření polygonu ze sekvence koordinát [x,y]
def createPolygon(coordinatesSequence):
    polygon = QtGui.QPolygon()
    for coordinates in coordinatesSequence:
        p = QtCore.QPoint(coordinates[0], coordinates[1])
        polygon << p
    return polygon

17. Sedmý demonstrační příklad – vykreslení domku jednou lomenou čarou (polyčarou)

V dnešním sedmém demonstračním příkladu je ukázáno, jakým způsobem je možné vykreslit domek jednou lomenou čarou neboli polyčarou. Postup je následující:

Vytvoříme polygon představující jednotlivé body, které se budou spojovat tak, aby vznikl domek:

polygon = createPolygon([[100, 200],
                         [200, 200],
                         [100, 100],
                         [100, 200],
                         [200, 100],
                         [100, 100],
                         [150,  50],
                         [200, 100],
                         [200, 200]])

Vykreslíme polyčáru:

# vytvoření pera a nastavení barvy kreslení
pen = QtGui.QPen(QtGui.QColor(*color))
 
# kreslit se bude právě vytvořeným perem
qPainter.setPen(pen)
 
# vykreslení lomené čáry
qPainter.drawPolyline(polygon)

Výsledek by měl vypadat takto:

Obrázek 18: Domek vykreslený jedním tahem.

Následuje výpis celého zdrojového kódu demonstračního 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
 
 
# nastavení barvy kreslení (pera) na zadanou barvu
def setColor(qPainter, color):
    # vytvoření pera a nastavení barvy kreslení
    pen = QtGui.QPen(QtGui.QColor(*color))
 
    # kreslit se bude právě vytvořeným perem
    qPainter.setPen(pen)
 
 
# vytvoření polygonu ze sekvence koordinát [x,y]
def createPolygon(coordinatesSequence):
    polygon = QtGui.QPolygon()
    for coordinates in coordinatesSequence:
        p = QtCore.QPoint(coordinates[0], coordinates[1])
        polygon.append(p)
    return polygon
 
 
# funkce pro vykreslení lomené čáry zadanou barvou
def drawPolyline(qPainter, color, polygon):
    setColor(qPainter, color)
 
    # vykreslení lomené čáry
    qPainter.drawPolyline(polygon)
 
 
# nový widget bude odvozen od obecného hlavního okna
class MainWindow(QtGui.QMainWindow):
 
    # rozměry rastrového obrázku
    IMAGE_WIDTH = 300
    IMAGE_HEIGHT = 250
 
    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)
 
        # vymazání obrázku
        self.image.fill(0)
 
        # 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í různých 2D entit
        polygon = createPolygon([[100, 200],
                                 [200, 200],
                                 [100, 100],
                                 [100, 200],
                                 [200, 100],
                                 [100, 100],
                                 [150,  50],
                                 [200, 100],
                                 [200, 200]])
        drawPolyline(qp, YELLOW, polygon)
 
        # 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()

18. Osmý demonstrační příklad – vykreslení hvězdy s využitím polygonu

V osmém a současně i dnešním posledním demonstračním příkladu je ukázán způsob vykreslení hvězdy jedním tahem, tentokrát s využitím 2D entity typu polygon (mnohoúhelník). Nejprve opět vytvoříme polygon představující vrcholy hvězdy. Vypomůžeme si funkcí, která vypočítá souřadnice n-tého vrcholu hvězdy. Ty jsou navzájem otočeny o 360°/5=72°, ovšem jednotlivé vrcholy se propojují ob jeden, tedy o 144°:

# výpočet souřadnic n-tého vrcholu hvězdy
def starVertex(cx, cy, radius, n):
    angle = math.radians(n*144)
    return cx + radius * math.sin(angle), cy - radius * math.cos(angle)

Polygon s vrcholy hvězdy:

polygon = createPolygon([starVertex(120, 120, 100, 0),
                         starVertex(120, 120, 100, 1),
                         starVertex(120, 120, 100, 2),
                         starVertex(120, 120, 100, 3),
                         starVertex(120, 120, 100, 4)])

Následně hvězdu vykreslíme, a to s různým nastavením parametru fillrule:

qPainter.drawPolygon(polygon, QtCore.Qt.OddEvenFill)
qPainter.drawPolygon(polygon, QtCore.Qt.WindingFill)

Výsledek by měl vypadat následovně:

Obrázek 19: Hvězdy vykreslené s využitím různých parametrů fillrule.

root_podpora

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
 
 
# nastavení barvy kreslení (pera) na zadanou barvu
def setColor(qPainter, color):
    # vytvoření pera a nastavení barvy kreslení
    pen = QtGui.QPen(QtGui.QColor(*color))
 
    # kreslit se bude právě vytvořeným perem
    qPainter.setPen(pen)
 
 
# vytvoření polygonu ze sekvence koordinát [x,y]
def createPolygon(coordinatesSequence):
    polygon = QtGui.QPolygon()
    for coordinates in coordinatesSequence:
        p = QtCore.QPoint(coordinates[0], coordinates[1])
        polygon << p
    return polygon
 
 
# vytvoření štětce z pixmapy
def createBrushFromPixmap(filename):
    pixmap = QtGui.QPixmap(filename)
    return QtGui.QBrush(pixmap)
 
 
# funkce pro vykreslení polygonu zadanou barvou
def drawPolygon(qPainter, color, brush, polygon, fillrule):
    setColor(qPainter, color)
 
    # změna tvaru štětce
    qPainter.setBrush(brush)
 
    # vykreslení polygonu
    qPainter.drawPolygon(polygon, fillrule)
 
 
# výpočet souřadnic n-tého vrcholu hvězdy
def starVertex(cx, cy, radius, n):
    angle = math.radians(n*144)
    return cx + radius * math.sin(angle), cy - radius * math.cos(angle)
 
 
# nový widget bude odvozen od obecného hlavního okna
class MainWindow(QtGui.QMainWindow):
 
    # rozměry rastrového obrázku
    IMAGE_WIDTH = 460
    IMAGE_HEIGHT = 250
 
    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)
 
        # vymazání obrázku
        self.image.fill(0)
 
        # 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)
 
        brush = createBrushFromPixmap("pixmaps/voronoi.png")
 
        # Vykreslení polygonu
        polygon = createPolygon([starVertex(120, 120, 100, 0),
                                 starVertex(120, 120, 100, 1),
                                 starVertex(120, 120, 100, 2),
                                 starVertex(120, 120, 100, 3),
                                 starVertex(120, 120, 100, 4)])
 
        drawPolygon(qp, YELLOW, brush, polygon, QtCore.Qt.OddEvenFill)
 
        polygon = createPolygon([starVertex(330, 120, 100, 0),
                                 starVertex(330, 120, 100, 1),
                                 starVertex(330, 120, 100, 2),
                                 starVertex(330, 120, 100, 3),
                                 starVertex(330, 120, 100, 4)])
 
        drawPolygon(qp, WHITE, brush, polygon, QtCore.Qt.WindingFill)
 
        # 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()

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

Zdrojové kódy všech osmi dnes popsaných demonstračních příkladů (plus jeden příklad zmíněný jen částečně) 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:

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

Byl pro vás článek přínosný?