Obsah
1. Tvorba GUI v×Pythonu s využitím frameworku PySide: rastrové operace, vykreslování textů
2. Určení oblasti (regionu), která se může překreslit
3. První demonstrační příklad: vykreslování do oblasti ohraničené obdélníkem
4. Maskování vykreslování s využitím bitmapy (stencil)
5. Druhý demonstrační příklad: vykreslování do pixelů specifikovaných bitovou maskou
6. Množinové operace prováděné nad regiony
7. Třetí demonstrační příklad: vytvoření nového regionu s využitím operace nonekvivalence
8. Rastrové operace vykonávané při vykreslování dvourozměrných entit
9. Čtvrtý demonstrační příklad: vzorník základních rastrových operací
10. Pokročilejší rastrové operace a role alfa kanálu při provádění těchto operací
11. Pátý demonstrační příklad: vzorník vybraných pokročilejších rastrových operací
13. Osmý demonstrační příklad: vykreslení vycentrovaného textu
14. Devátý demonstrační příklad: nastavení stylu a velikosti fontu
15. Desátý demonstrační příklad: načtení fontu ze specifikované rodiny
16. Obsah následující části seriálu
17. Repositář s demonstračními příklady
1. Tvorba GUI v Pythonu s využitím frameworku PySide: rastrové operace, vykreslování textů
Dnes dokončíme popis možností frameworku PySide v oblasti vykreslování dvourozměrné grafiky. Ukážeme si zejména způsob konfigurace vykreslování jednotlivých dvourozměrných entit, a to na úrovni pixelů tvořících hranici popř. i vnitřek entity. Při vykreslování se totiž postupuje zhruba následujícím způsobem:
- Vrcholy popř. řídicí body dvourozměrných entit jsou podrobeny vybrané lineární transformaci (zvětšení, zmenšení, otočení, zkosení, posun atd.).
- Zjistí se, zda je entita vůbec viditelná, tj. zda transformované vrcholy leží ve viditelné oblasti.
- Dále se provede takzvaná rasterizace, tj. výpočet barev pixelů ležících na hranici entity (pero) a popř. i v ploše, kterou entita tvoří (štětec).
- V průběhu rasterizace se s každým vypočteným pixelem provádí další operace, což je téma dnešního článku.
Mezi prováděné rastrové operace patří:
- Test vůči masce, zda se má pixel vykreslit. Maska může být představována bitmapou, tj. její funkce odpovídá jedné konfigurovatelné vlastnostistencil bufferu v OpenGL.
- Dále se zkombinuje původní barva pixelu na plátně s barvou vypočtenou. Kombinací těchto dvou barev vznikne barva třetí, která je na plátno zapsána. Výchozí operací je pouhé přepsání staré barvy, ovšem je možné zvolit i jiné metody.
2. Určení oblasti (regionu), která se může překreslit
První operací, kterou si dnes popíšeme, je určení oblasti (neboli regionu), která se může překreslit. Všechny pixely, které by se měly vykreslit mimo stanovenou oblast, budou ignorovány, tj. nijak neovlivní již nakreslenou scénu. Region je možné vytvořit například z obyčejného obdélníku, což je pravděpodobně nejčastěji používaná varianta. Nejprve se vytvoří instance objektu typu QRegion a posléze se tento objekt (resp. přesněji řečeno reference na něj) předá metodě QPainter.setClipRegion():
# vytvoření objektu typu QPainter s předáním # reference na "pokreslovaný" objekt qPainter = QtGui.QPainter(self.image) # nastavení oblasti omezující vykreslování region = QtGui.QRegion(QtCore.QRect(x, y, šířka, výška)) qPainter.setClipRegion(region)
Ve skutečnosti je samozřejmě možné, aby měl region i jiné tvary. V takovém případě namísto QRect použijte instanci třídy typu QPolygon či dále popsanou bitmapu (QBitmap).
3. První demonstrační příklad: vykreslování do oblasti ohraničené obdélníkem
Dnešní první demonstrační příklad je založen na příkladu, s nímž jsme se již seznámili v předchozích částech tohoto seriálu. Po jeho spuštění se vykreslí vzorník štětců, které jsou součástí frameworku PySide a které tedy můžeme použít bez nutnosti jejich explicitní deklarace:
# 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) ... ... ...
Ovšem ve skutečnosti se vykreslí pouze vybraná oblast kreslicí plochy, protože před zahájením vlastního vykreslování nastavíme oblast, do které se má vykreslování provést. Okolní pixely, které do oblasti nezasahují, nebudou nijak modifikovány:
# nastavení oblasti omezující vykreslování region = QtGui.QRegion(QtCore.QRect(20, 20, MainWindow.IMAGE_WIDTH-40, MainWindow.IMAGE_HEIGHT-50)) qp.setClipRegion(region)
Výsledek můžeme vidět na následujícím screenshotu:
Obrázek 1: Screenshot okna prvního demonstračního příkladu.
Následuje výpis zdrojového kódu:
#!/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) # nastavení oblasti omezující vykreslování region = QtGui.QRegion(QtCore.QRect(20, 20, MainWindow.IMAGE_WIDTH-40, MainWindow.IMAGE_HEIGHT-50)) qp.setClipRegion(region) # 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()
4. Maskování vykreslování s využitím bitmapy (stencil)
Mnohem zajímavější je použití běžné bitmapy pro určení těch pixelů, které je možné vykreslit, a těch, které jsou naopak maskovány. Jen pro připomínku – termínem bitmapa se v PySide označují rastrové obrázky, v nichž je každý pixel reprezentován jediným bitem, takže vidíme, že se skutečně jedná o bitovou masku. Již v úvodní kapitole jsme si řekli, že tato bitmapa bude pracovat jako takzvaný stencil buffer známý například z knihovny OpenGL (ve skutečnosti má stencil buffer větší možnosti konfigurace, ovšem toto téma již nezapadá do kontextu tohoto článku).
Obrázek 2: Bitmapa, která bude sloužit jako maska ve druhém příkladu.
Nastavení bitmapy ve funkci masky je poměrně snadné, pouze stačí bitmapu načíst a použít ji jako argument do konstruktoru třídy QRegion:
# vytvoření objektu typu QPainter s předáním # reference na "pokreslovaný" objekt qPainter = QtGui.QPainter(self.image) # bitmapa tvořící masku regionu bitmap = QtGui.QBitmap(cesta_k_bitmapě) region = QtGui.QRegion(bitmap) # nastavení oblasti vykreslování qPainter.setClipRegion(region)
Obrázek 3: Toto není text, ale obdélník vyplněný gradientním přechodem. Při vykreslování byla použita bitová maska ze druhého obrázku.
5. Druhý demonstrační příklad: vykreslování do pixelů specifikovaných bitovou maskou
Ve druhém demonstračním příkladu vykreslíme do okna (prakticky přes celou jeho plochu) obdélník vyplněný opakujícím se gradientním přechodem od žluté barvy do barvy černé:
# výplň obdélníku brush = createBrushFromGradient(YELLOW, BLACK) # vykreslení obdélníku drawRectangleUsingCustomBrush(qp, BLACK, 0, 0, 340, 240, brush)
Ovšem před vlastním vykreslením vyplněného obdélníku nastavíme bitovou masku, která vykreslení omezí pouze na vybrané pixely:
# bitmapa tvořící masku regionu bitmap = QtGui.QBitmap("bitmaps/clip.xbm") region = QtGui.QRegion(bitmap) # nastavení oblasti vykreslování qp.setClipRegion(region)
Výsledek jste mohli vidět na obrázcích uvedených v předchozí kapitole. Opět následuje výpis zdrojového kódu tohoto 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 gradientního přechodu def createBrushFromGradient(color1, color2): gradient = QtGui.QLinearGradient(100, 100, 100, 160) 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 = 340 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) # bitmapa tvořící masku regionu bitmap = QtGui.QBitmap("bitmaps/clip.xbm") region = QtGui.QRegion(bitmap) # nastavení oblasti vykreslování qp.setClipRegion(region) # výplň obdélníku brush = createBrushFromGradient(YELLOW, BLACK) # vykreslení obdélníku drawRectangleUsingCustomBrush(qp, BLACK, 0, 0, 340, 240, 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()
6. Množinové operace prováděné nad regiony
V případě, že máme dvě instance třídy QRegion, tj. dvě oblasti vykreslování, můžeme z nich vytvořit třetí oblast s využitím vybrané množinové operace. Tyto operace se podobají množinovým operacím, s nimiž jsme se seznámili při popisování cest:
Operace | Metoda | Přetížený operátor | Alternativní operátor |
---|---|---|---|
sjednocení | united | |, |= | +, += |
průnik | intersected | &, &= | |
rozdíl | subtracted | -, -= | |
nonekvivalence | xored | ^, ^= |
Vidíme, že je k dispozici ještě čtvrtá operace, kterou si ukážeme hned v dalším příkladu popsaném v navazující kapitole.
Obrázek 4: Tento obrázek vznikl jednoduše – zkombinovali jsme dva regiony, první ve tvaru obdélníku a druhý reprezentovaný nám již známou bitovou maskou. Kombinace byla provedena operací XOR, která vedla k inverzi bitové masky v levé třetině obrázku.
7. Třetí demonstrační příklad: vytvoření nového regionu s využitím operace nonekvivalence
Ve třetím příkladu si ukážeme použití množinových operací provedených nad dvěma regiony. První region je tvořen pečlivě vybranou obdélníkovou oblastí, která zasahuje zhruba do levé třetiny plochy:
region1 = QtGui.QRegion(QtCore.QRect(0, 0, 110, MainWindow.IMAGE_HEIGHT))
Druhý region je tvořen bitmapou, resp. přesněji řečeno bitovou maskou:
# bitmapa tvořící masku regionu bitmap = QtGui.QBitmap("bitmaps/clip.xbm") region2 = QtGui.QRegion(bitmap)
Oblast vykreslování je určena třetím regionem, který vznikne z předchozích dvou regionů jejich zkombinováním operací nonekvivalence (XOR):
# nastavení oblasti vykreslování s využitím operace XOR qp.setClipRegion(region1 ^ region2)
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 # vytvoření štětce z gradientního přechodu def createBrushFromGradient(color1, color2): gradient = QtGui.QLinearGradient(100, 100, 100, 160) 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 = 340 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) # bitmapa tvořící masku regionu bitmap = QtGui.QBitmap("bitmaps/clip.xbm") # vytvoření dvou regionů region1 = QtGui.QRegion(QtCore.QRect(0, 0, 110, MainWindow.IMAGE_HEIGHT)) region2 = QtGui.QRegion(bitmap) # nastavení oblasti vykreslování s využitím operace XOR qp.setClipRegion(region1 ^ region2) # výplň obdélníku brush = createBrushFromGradient(YELLOW, BLACK) # vykreslení obdélníku drawRectangleUsingCustomBrush(qp, BLACK, 0, 0, MainWindow.IMAGE_WIDTH, MainWindow.IMAGE_HEIGHT, 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. Rastrové operace vykonávané při vykreslování dvourozměrných entit
Již v úvodní kapitole jsme si řekli, že při vykreslování dvourozměrných entit jsou v průběhu rasterizace aplikovány rastrové operace, které kombinují barvu již uloženého (vykresleného) pixelu a pixelu vykreslovaného. Podporovány jsou následující operace:
Konstanta pro metodu setCompositionMode | Význam |
---|---|
QPainter.RasterOp_SourceOrDestination | bitová operace OR |
QPainter.RasterOp_SourceAndDestination | bitová operace AND |
QPainter.RasterOp_SourceXorDestination | bitová operace XOR |
QPainter.RasterOp_NotSourceAndNotDestination | bitová operace NOR |
QPainter.RasterOp_NotSourceOrNotDestination | bitová operace NAND |
QPainter.RasterOp_NotSourceXorDestination | (NOT src) XOR dst |
QPainter.RasterOp_NotSource | negace hodnoty původního pixelu |
QPainter.RasterOp_NotSourceAndDestination | (NOT src) AND dst |
QPainter.RasterOp_SourceAndNotDestination | src AND (NOT dst) |
9. Čtvrtý demonstrační příklad: vzorník základních rastrových operací
Dnešní čtvrtý demonstrační příklad je poněkud delší, než příklady předchozí. Po jeho spuštění se na obrazovku vykreslí vzorník základních rastrových operací. Samotné vykreslování je založeno na uživatelské funkci nazvané twoOverlappingSquares, která skutečně vykreslí dva vyplněné čtverce, ovšem navíc před vykreslením druhého čtverce nastaví vybranou ROPs:
def twoOverlappingSquares(qPainter, color1, color2, x, y, compositionMode): # nastavení výchozího režimu míchání barev qPainter.setCompositionMode(QtGui.QPainter.CompositionMode_SourceOver) # první čtverec drawRectangleUsingBrush(qPainter, color1, x, y, 100, 100, QtCore.Qt.SolidPattern) # nastavení režimu míchání barev qPainter.setCompositionMode(compositionMode) # druhý čtverec drawRectangleUsingBrush(qPainter, color2, x+50, y+50, 100, 100, QtCore.Qt.SolidPattern)
Tato funkce je volána pro různé rastrové operace, takže na obrazovce skutečně uvidíme různé varianty kombinací barev pixelů dvou obdélníků:
# vykreslení sady překrývajících se čtverců twoOverlappingSquares(qPainter, GREEN, BLUE, COLUMN_1, ROW_1, QtGui.QPainter.CompositionMode_SourceOver) twoOverlappingSquares(qPainter, GREEN, BLUE, COLUMN_1, ROW_2, QtGui.QPainter.RasterOp_SourceOrDestination) twoOverlappingSquares(qPainter, GREEN, BLUE, COLUMN_2, ROW_2, QtGui.QPainter.RasterOp_SourceAndDestination) twoOverlappingSquares(qPainter, GREEN, BLUE, COLUMN_3, ROW_2, QtGui.QPainter.RasterOp_SourceXorDestination) twoOverlappingSquares(qPainter, GREEN, BLUE, COLUMN_1, ROW_3, QtGui.QPainter.RasterOp_NotSourceAndNotDestination) twoOverlappingSquares(qPainter, GREEN, BLUE, COLUMN_2, ROW_3, QtGui.QPainter.RasterOp_NotSourceOrNotDestination) twoOverlappingSquares(qPainter, GREEN, BLUE, COLUMN_3, ROW_3, QtGui.QPainter.RasterOp_NotSourceXorDestination) twoOverlappingSquares(qPainter, GREEN, BLUE, COLUMN_1, ROW_4, QtGui.QPainter.RasterOp_NotSource) twoOverlappingSquares(qPainter, GREEN, BLUE, COLUMN_2, ROW_4, QtGui.QPainter.RasterOp_NotSourceAndDestination) twoOverlappingSquares(qPainter, GREEN, BLUE, COLUMN_3, ROW_4, QtGui.QPainter.RasterOp_SourceAndNotDestination)
Obrázek 5: Vzorník základních rastrových operací.
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 "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) def twoOverlappingSquares(qPainter, color1, color2, x, y, compositionMode): # nastavení výchozího režimu míchání barev qPainter.setCompositionMode(QtGui.QPainter.CompositionMode_SourceOver) # první čtverec drawRectangleUsingBrush(qPainter, color1, x, y, 100, 100, QtCore.Qt.SolidPattern) # nastavení režimu míchání barev qPainter.setCompositionMode(compositionMode) # druhý čtverec drawRectangleUsingBrush(qPainter, color2, x+50, y+50, 100, 100, QtCore.Qt.SolidPattern) # nový widget bude odvozen od obecného hlavního okna class MainWindow(QtGui.QMainWindow): # rozměry rastrového obrázku IMAGE_WIDTH = 600 IMAGE_HEIGHT = 800 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) # vykreslení sady překrývajících se čtverců self.drawOverlappingSquares(qp) # vytvoření instance třídy QPixmap z objektu QImage self.pixmap = QtGui.QPixmap.fromImage(self.image) def drawOverlappingSquares(self, qPainter): # 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) # umístění čtverců na kreslicí ploše HORIZONTAL_DISTANCE = 200 VERTICAL_DISTANCE = 200 COLUMN_1 = 10 COLUMN_2 = COLUMN_1 + HORIZONTAL_DISTANCE COLUMN_3 = COLUMN_2 + HORIZONTAL_DISTANCE ROW_1 = 10 ROW_2 = ROW_1 + HORIZONTAL_DISTANCE ROW_3 = ROW_2 + HORIZONTAL_DISTANCE ROW_4 = ROW_3 + HORIZONTAL_DISTANCE # vykreslení sady překrývajících se čtverců twoOverlappingSquares(qPainter, GREEN, BLUE, COLUMN_1, ROW_1, QtGui.QPainter.CompositionMode_SourceOver) twoOverlappingSquares(qPainter, GREEN, BLUE, COLUMN_1, ROW_2, QtGui.QPainter.RasterOp_SourceOrDestination) twoOverlappingSquares(qPainter, GREEN, BLUE, COLUMN_2, ROW_2, QtGui.QPainter.RasterOp_SourceAndDestination) twoOverlappingSquares(qPainter, GREEN, BLUE, COLUMN_3, ROW_2, QtGui.QPainter.RasterOp_SourceXorDestination) twoOverlappingSquares(qPainter, GREEN, BLUE, COLUMN_1, ROW_3, QtGui.QPainter.RasterOp_NotSourceAndNotDestination) twoOverlappingSquares(qPainter, GREEN, BLUE, COLUMN_2, ROW_3, QtGui.QPainter.RasterOp_NotSourceOrNotDestination) twoOverlappingSquares(qPainter, GREEN, BLUE, COLUMN_3, ROW_3, QtGui.QPainter.RasterOp_NotSourceXorDestination) twoOverlappingSquares(qPainter, GREEN, BLUE, COLUMN_1, ROW_4, QtGui.QPainter.RasterOp_NotSource) twoOverlappingSquares(qPainter, GREEN, BLUE, COLUMN_2, ROW_4, QtGui.QPainter.RasterOp_NotSourceAndDestination) twoOverlappingSquares(qPainter, GREEN, BLUE, COLUMN_3, ROW_4, QtGui.QPainter.RasterOp_SourceAndNotDestination) 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. Pokročilejší rastrové operace a role alfa kanálu při provádění těchto operací
Při rasterizaci se kromě bitových operací zmíněných v osmé kapitole mohou použít i pokročilejí operace podporované frameworkem PySide. Jedná se o operace, které většinou používají alfa kanál zdrojových a/nebo cílových pixelů. Tento kanál typicky slouží pro určení míry smíchání barev zdrojového pixelu (tj. pixelu, který se bude vykreslovat) a pixelu cílového. Kódy těchto operací jsou reprezentovány konstantami QPainter.CompositionMode_* a význam některých z nich uvidíme v dnešním pátém demonstračním příkladu popsaném v navazující kapitole.
Obrázek 6: Vzorník rastrových operací vykreslený pátým demonstračním příkladem.
Ve skutečnosti je však možné alfa kanál použít i u klasických bitových ROPs, což je ukázáno na následujících dvou screenshotech, které vznikly nepatrnou úpravou čtvrtého příkladu:
Obrázek 7: Modré čtverce mají nastavenou poloviční průhlednost (vykresleno variantou čtvrtého příkladu).
Obrázek 8: Modré i zelené čtverce mají nastavenou poloviční průhlednost (vykresleno variantou čtvrtého příkladu).
11. Pátý demonstrační příklad: vzorník vybraných pokročilejších rastrových operací
V tomto příkladu jsou ukázány některé pokročilejší rastrové operace. Ze všech nabízených operací jsem vybral ty, které dávají zajímavé výsledky:
#!/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) def twoOverlappingSquares(qPainter, color1, color2, x, y, compositionMode): # nastavení výchozího režimu míchání barev qPainter.setCompositionMode(QtGui.QPainter.CompositionMode_SourceOver) # první čtverec drawRectangleUsingBrush(qPainter, color1, x, y, 100, 100, QtCore.Qt.SolidPattern) # nastavení režimu míchání barev qPainter.setCompositionMode(compositionMode) # druhý čtverec drawRectangleUsingBrush(qPainter, color2, x+50, y+50, 100, 100, QtCore.Qt.SolidPattern) # nový widget bude odvozen od obecného hlavního okna class MainWindow(QtGui.QMainWindow): # rozměry rastrového obrázku IMAGE_WIDTH = 600 IMAGE_HEIGHT = 800 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) # vykreslení sady překrývajících se čtverců self.drawOverlappingSquares(qp) # vytvoření instance třídy QPixmap z objektu QImage self.pixmap = QtGui.QPixmap.fromImage(self.image) def drawOverlappingSquares(self, qPainter): # 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) BLUE_50_ALPHA = (0, 0, 255, 128) GREEN_50_ALPHA = (0, 255, 0, 128) # umístění čtverců na kreslicí ploše HORIZONTAL_DISTANCE = 200 VERTICAL_DISTANCE = 200 COLUMN_1 = 10 COLUMN_2 = COLUMN_1 + HORIZONTAL_DISTANCE COLUMN_3 = COLUMN_2 + HORIZONTAL_DISTANCE ROW_1 = 10 ROW_2 = ROW_1 + HORIZONTAL_DISTANCE ROW_3 = ROW_2 + HORIZONTAL_DISTANCE ROW_4 = ROW_3 + HORIZONTAL_DISTANCE # vykreslení sady překrývajících se čtverců twoOverlappingSquares(qPainter, GREEN_50_ALPHA, BLUE_50_ALPHA, COLUMN_1, ROW_1, QtGui.QPainter.CompositionMode_SourceOver) twoOverlappingSquares(qPainter, GREEN_50_ALPHA, BLUE_50_ALPHA, COLUMN_1, ROW_2, QtGui.QPainter.CompositionMode_Source) twoOverlappingSquares(qPainter, GREEN_50_ALPHA, BLUE_50_ALPHA, COLUMN_2, ROW_2, QtGui.QPainter.CompositionMode_DestinationIn) twoOverlappingSquares(qPainter, GREEN_50_ALPHA, BLUE_50_ALPHA, COLUMN_3, ROW_2, QtGui.QPainter.CompositionMode_SourceOut) twoOverlappingSquares(qPainter, GREEN_50_ALPHA, BLUE_50_ALPHA, COLUMN_1, ROW_3, QtGui.QPainter.CompositionMode_Xor) twoOverlappingSquares(qPainter, GREEN_50_ALPHA, BLUE_50_ALPHA, COLUMN_2, ROW_3, QtGui.QPainter.CompositionMode_Plus) twoOverlappingSquares(qPainter, GREEN_50_ALPHA, BLUE_50_ALPHA, COLUMN_3, ROW_3, QtGui.QPainter.CompositionMode_Screen) twoOverlappingSquares(qPainter, GREEN_50_ALPHA, BLUE_50_ALPHA, COLUMN_1, ROW_4, QtGui.QPainter.CompositionMode_HardLight) twoOverlappingSquares(qPainter, GREEN_50_ALPHA, BLUE_50_ALPHA, COLUMN_2, ROW_4, QtGui.QPainter.CompositionMode_SoftLight) twoOverlappingSquares(qPainter, WHITE, BLUE_50_ALPHA, COLUMN_3, ROW_4, QtGui.QPainter.CompositionMode_ColorBurn) 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()
12. Vykreslení textů
Vykreslení textu, přesněji řečeno textového řetězce, na plochu spravovanou třídou QPainter, je v tom nejjednodušším případě snadné. Stačí totiž nastavit barvu vykreslování (vlastnost kreslicího pera), získat instanci třídy QFont a následně text vykreslit metodou QPainter.drawText(x, y, text).
Většinou ale potřebujeme přesněji určit oblast, do které se text vypíše. V tomto případě použijeme odlišnou variantu metody QPainter.drawText(). Tentokrát budou při volání této metody použity tři parametry:
- obdélník (QtCore.QRect), do něhož se má text vykreslit.
- celočíselný parametr se specifikací horizontálního i vertikálního zarovnání. Jedná se o kombinaci celočíselných konstantQtCore.Qt.Align*.
- posledním parametrem je pochopitelně vlastní text.
Podívejme se na příklad:
# vytvoření pera a nastavení barvy kreslení pen = QtGui.QPen(QtGui.QColor(255, 128, 128)) # kreslit se bude právě vytvořeným perem qp.setPen(pen) # obdélník, do kterého se text vypíše rect = QtCore.QRect(0, 0, MainWindow.IMAGE_WIDTH, MainWindow.IMAGE_HEIGHT) # zarovnání textu # - horizontální i vertikální vycentrování v rámci obdélníku align = QtCore.Qt.AlignHCenter | QtCore.Qt.AlignCenter # vykreslení textu qp.drawText(rect, align, "PySide")
Nejdůležitější vlastností textu je použitý font. Ten je možné získat různými způsoby, například přečtením reference na výchozí font přiřazený QPainteru:
# nastavení fontu font = qPainter.font() font.setBold(True) font.setPointSize(54) qPainter.setFont(font)
Užitečnější bývá explicitní určení rodiny fontů, velikosti textu (v typografických bodech), váhy (0 až 100%, určují se jí šířky tahů, tedy laicky „tučnost“) a příznaku, zda chceme použít kurzívu nebo skloněný text (pokud daná rodina kurzívu nemá):
# nastavení fontu font = QtGui.QFont("monospace", pointSize=54, weight=90, italic=True) qp.setFont(font)
13. Osmý demonstrační příklad: vykreslení vycentrovaného textu
V šestém příkladu je ukázán postup použitý pro vykreslení vycentrovaného textu, resp. přesněji řečeno textu vycentrovaného v zadaném obdélníku jak vertikálně, tak i horizontálně. Postup již známe, takže jen velmi stručně (povšimněte si, že nenastavujeme styl ani velikost fontu; budou použity výchozí hodnoty):
# obdélník, do kterého se text vypíše rect = QtCore.QRect(0, 0, MainWindow.IMAGE_WIDTH, MainWindow.IMAGE_HEIGHT) # zarovnání textu # - horizontální i vertikální vycentrování v rámci obdélníku align = QtCore.Qt.AlignHCenter | QtCore.Qt.AlignCenter # vykreslení textu qp.drawText(rect, align, "PySide")
Obrázek 9: Vycentrovaný text vykreslený do okna osmého příkladu.
Opět podle očekávání 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 # 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) # 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) # vykreslení textu # vytvoření pera a nastavení barvy kreslení pen = QtGui.QPen(QtGui.QColor(255, 128, 128)) # změna šířky pera pen.setWidth(1) # kreslit se bude právě vytvořeným perem qp.setPen(pen) # obdélník, do kterého se text vypíše rect = QtCore.QRect(0, 0, MainWindow.IMAGE_WIDTH, MainWindow.IMAGE_HEIGHT) # zarovnání textu # - horizontální i vertikální vycentrování v rámci obdélníku align = QtCore.Qt.AlignHCenter | QtCore.Qt.AlignCenter # vykreslení textu qp.drawText(rect, align, "PySide") # 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()
14. Devátý demonstrační příklad: nastavení stylu a velikosti fontu
V předposledním příkladu je ukázán způsob nastavení stylu a velikosti fontu. Není to nic těžkého, pouze potřebujeme získat výchozí font a následně změnit jeho vlastnosti. Konkrétně budeme požadovat tučný text a font o velikosti 54 bodů (nikoli pixelů):
# nastavení fontu font = qp.font() font.setBold(True) font.setPointSize(54) qp.setFont(font)
Obrázek 10: Vycentrovaný tučný text o velikosti 54 bodů vykreslený do okna devátého příkladu.
Podívejme se na 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 # 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) # 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) # vykreslení textu # vytvoření pera a nastavení barvy kreslení pen = QtGui.QPen(QtGui.QColor(255, 128, 128)) # změna šířky pera pen.setWidth(1) # kreslit se bude právě vytvořeným perem qp.setPen(pen) # nastavení fontu font = qp.font() font.setBold(True) font.setPointSize(54) qp.setFont(font) # obdélník, do kterého se text vypíše rect = QtCore.QRect(0, 0, MainWindow.IMAGE_WIDTH, MainWindow.IMAGE_HEIGHT) # zarovnání textu # - horizontální i vertikální vycentrování v rámci obdélníku align = QtCore.Qt.AlignHCenter | QtCore.Qt.AlignCenter # vykreslení textu qp.drawText(rect, align, "PySide") # 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. Desátý demonstrační příklad: načtení fontu ze specifikované rodiny
V posledním příkladu, který se dnes ukážeme, vytvoříme novou instanci třídy QFont, přičemž v konstruktoru specifikujeme jak rodinu fontu (zde „monospace“), tak i požadovanou velikost, váhu (90%) a taktéž to, že budeme chtít použít kurzívu (pokud ji rodina fontů nabízí) nebo skloněný text:
# nastavení fontu font = QtGui.QFont("monospace", pointSize=54, weight=90, italic=True) qp.setFont(font)
Obrázek 11: Příklad použití fontu vybraného výše zmíněným konstruktorem.
Opět se podívejme na zdrojový kód 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 # 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) # 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) # vykreslení textu # vytvoření pera a nastavení barvy kreslení pen = QtGui.QPen(QtGui.QColor(255, 128, 128)) # změna šířky pera pen.setWidth(1) # kreslit se bude právě vytvořeným perem qp.setPen(pen) # nastavení fontu font = QtGui.QFont("monospace", pointSize=54, weight=90, italic=True) qp.setFont(font) # obdélník, do kterého se text vypíše rect = QtCore.QRect(0, 0, MainWindow.IMAGE_WIDTH, MainWindow.IMAGE_HEIGHT) # zarovnání textu # - horizontální i vertikální vycentrování v rámci obdélníku align = QtCore.Qt.AlignHCenter | QtCore.Qt.AlignCenter # vykreslení textu qp.drawText(rect, align, "PySide") # 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()
16. Obsah následující části seriálu
V navazující části seriálu o tvorbě grafického uživatelského rozhraní v Pythonu již opustíme téma 2D grafiky. Popíšeme si totiž další widgety, jejichž chování je složitější, než chování základní sady widgetů (tlačítko, seznam, textové pole, položka menu atd.). Framework PySide totiž programátorům nabízí celou sadu komplikovanějších ovládacích prvků využitelných především v klasických desktopových aplikacích.
17. Repositář s demonstračními příklady
Zdrojové kódy všech deseti 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/presentations. 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
- PySide 1.2.1 documentation
https://pyside.github.io/docs/pyside/index.html - QImage
https://pyside.github.io/docs/pyside/PySide/QtGui/QImage.html - QPixmap
https://pyside.github.io/docs/pyside/PySide/QtGui/QPixmap.html - QBitmap
https://pyside.github.io/docs/pyside/PySide/QtGui/QBitmap.html - QPaintDevice
https://pyside.github.io/docs/pyside/PySide/QtGui/QPaintDevice.html - QPicture
https://pyside.github.io/docs/pyside/PySide/QtGui/QPicture.html - QPainter
https://pyside.github.io/docs/pyside/PySide/QtGui/QPainter.html - QPainterPath
https://pyside.github.io/docs/pyside/PySide/QtGui/QPainterPath.html - QGradient
https://pyside.github.io/docs/pyside/PySide/QtGui/QGradient.html - QLinearGradient
https://pyside.github.io/docs/pyside/PySide/QtGui/QLinearGradient.html - QRadialGradient
https://pyside.github.io/docs/pyside/PySide/QtGui/QRadialGradient.html - Afinní zobrazení
https://cs.wikipedia.org/wiki/Afinn%C3%AD_zobrazen%C3%AD - Differences Between PySide and PyQt
https://wiki.qt.io/Differences_Between_PySide_and_PyQt - PySide 1.2.1 tutorials
https://pyside.github.io/docs/pyside/tutorials/index.html - PySide tutorial
http://zetcode.com/gui/pysidetutorial/ - Drawing in PySide
http://zetcode.com/gui/pysidetutorial/drawing/ - Qt Core
https://pyside.github.io/docs/pyside/PySide/QtCore/Qt.html - QLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QLayout.html - QStackedLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QStackedLayout.html - QFormLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QFormLayout.html - QBoxLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QBoxLayout.html - QHBoxLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QHBoxLayout.html - QVBoxLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QVBoxLayout.html - QGridLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QGridLayout.html - QAction
https://pyside.github.io/docs/pyside/PySide/QtGui/QAction.html - QMessageBox
https://pyside.github.io/docs/pyside/PySide/QtGui/QMessageBox.html - QListWidget
https://pyside.github.io/docs/pyside/PySide/QtGui/QListWidget.html - Signals & Slots
http://doc.qt.io/qt-4.8/signalsandslots.html - Signals and Slots in PySide
http://wiki.qt.io/Signals_and_Slots_in_PySide - Intro to PySide/PyQt: Basic Widgets and Hello, World!
http://www.pythoncentral.io/intro-to-pysidepyqt-basic-widgets-and-hello-world/ - QWidget
https://pyside.github.io/docs/pyside/PySide/QtGui/QWidget.html - QMainWindow
https://pyside.github.io/docs/pyside/PySide/QtGui/QMainWindow.html - QLabel
https://pyside.github.io/docs/pyside/PySide/QtGui/QLabel.html - QAbstractButton
https://pyside.github.io/docs/pyside/PySide/QtGui/QAbstractButton.html - QCheckBox
https://pyside.github.io/docs/pyside/PySide/QtGui/QCheckBox.html - QRadioButton
https://pyside.github.io/docs/pyside/PySide/QtGui/QRadioButton.html - QButtonGroup
https://pyside.github.io/docs/pyside/PySide/QtGui/QButtonGroup.html - QFrame
https://pyside.github.io/docs/pyside/PySide/QtGui/QFrame.html#PySide.QtGui.PySide.QtGui.QFrame - QFrame.frameStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QFrame.html#PySide.QtGui.PySide.QtGui.QFrame.frameStyle - Leo editor
http://leoeditor.com/ - IPython Qt Console aneb vylepšený pseudoterminál
https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-ipython-a-ipython-notebook/#k06 - Vývojová prostředí ve Fedoře (4. díl)
https://mojefedora.cz/vyvojova-prostredi-ve-fedore-4-dil/ - Seriál Letní škola programovacího jazyka Logo
http://www.root.cz/serialy/letni-skola-programovaciho-jazyka-logo/ - Educational programming language
http://en.wikipedia.org/wiki/Educational_programming_language - Logo Tree Project:
http://www.elica.net/download/papers/LogoTreeProject.pdf - Hra Breakout napísaná v Tkinteri
https://www.root.cz/clanky/hra-breakout-napisana-v-tkinteri/ - Hra Snake naprogramovaná v Pythone s pomocou Tkinter
https://www.root.cz/clanky/hra-snake-naprogramovana-v-pythone-s-pomocou-tkinter/ - 24.1. turtle — Turtle graphics
https://docs.python.org/3.5/library/turtle.html#module-turtle - TkDND
http://freecode.com/projects/tkdnd - Python Tkinter Fonts
https://www.tutorialspoint.com/python/tk_fonts.htm - The Tkinter Canvas Widget
http://effbot.org/tkinterbook/canvas.htm - Ovládací prvek (Wikipedia)
https://cs.wikipedia.org/wiki/Ovl%C3%A1dac%C3%AD_prvek_%28po%C4%8D%C3%ADta%C4%8D%29 - Rezervovaná klíčová slova v Pythonu
https://docs.python.org/3/reference/lexical_analysis.html#keywords - TkDocs: Styles and Themes
http://www.tkdocs.com/tutorial/styles.html - Drawing in Tkinter
http://zetcode.com/gui/tkinter/drawing/ - Changing ttk widget text color (StackOverflow)
https://stackoverflow.com/questions/16240477/changing-ttk-widget-text-color - The Hitchhiker's Guide to Pyhton: GUI Applications
http://docs.python-guide.org/en/latest/scenarios/gui/ - 7 Top Python GUI Frameworks for 2017
http://insights.dice.com/2014/11/26/5-top-python-guis-for-2015/ - GUI Programming in Python
https://wiki.python.org/moin/GuiProgramming - Cameron Laird's personal notes on Python GUIs
http://phaseit.net/claird/comp.lang.python/python_GUI.html - Python GUI development
http://pythoncentral.io/introduction-python-gui-development/ - Graphic User Interface FAQ
https://docs.python.org/2/faq/gui.html#graphic-user-interface-faq - TkInter
https://wiki.python.org/moin/TkInter - Tkinter 8.5 reference: a GUI for Python
http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/index.html - TkInter (Wikipedia)
https://en.wikipedia.org/wiki/Tkinter - appJar
http://appjar.info/ - appJar (Wikipedia)
https://en.wikipedia.org/wiki/AppJar - appJar na Pythonhosted
http://pythonhosted.org/appJar/ - appJar widgets
http://appjar.info/pythonWidgets/ - Stránky projektu PyGTK
http://www.pygtk.org/ - PyGTK (Wikipedia)
https://cs.wikipedia.org/wiki/PyGTK - Stránky projektu PyGObject
https://wiki.gnome.org/Projects/PyGObject - Stránky projektu Kivy
https://kivy.org/#home - Stránky projektu PyQt
https://riverbankcomputing.com/software/pyqt/intro - PyQt (Wikipedia)
https://cs.wikipedia.org/wiki/PyGTK - Stránky projektu PySide
https://wiki.qt.io/PySide - PySide (Wikipedia)
https://en.wikipedia.org/wiki/PySide - Stránky projektu Kivy
https://kivy.org/#home - Kivy (framework, Wikipedia)
https://en.wikipedia.org/wiki/Kivy_(framework) - QML Applications
http://doc.qt.io/qt-5/qmlapplications.html - KDE
https://www.kde.org/ - Qt
https://www.qt.io/ - GNOME
https://en.wikipedia.org/wiki/GNOME - Category:Software that uses PyGTK
https://en.wikipedia.org/wiki/Category:Software_that_uses_PyGTK - Category:Software that uses PyGObject
https://en.wikipedia.org/wiki/Category:Software_that_uses_PyGObject - Category:Software that uses wxWidgets
https://en.wikipedia.org/wiki/Category:Software_that_uses_wxWidgets - GIO
https://developer.gnome.org/gio/stable/ - GStreamer
https://gstreamer.freedesktop.org/ - GStreamer (Wikipedia)
https://en.wikipedia.org/wiki/GStreamer - Wax Gui Toolkit
https://wiki.python.org/moin/Wax - Python Imaging Library (PIL)
http://infohost.nmt.edu/tcc/help/pubs/pil/ - 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/