====== 10. Qt - kreslení ====== Cílem cvičení je ukázat, jak je možné v QT kreslit grafická primitiva. Využijeme k tomu zejména třídu [[https://doc.qt.io/qt-5/qpainter.html|QPainter]], která umožňuje kreslení do jednotlivých GUI komponent. ==== Podklady ke cvičení ==== {{ :courses:b2b99ppc:tutorials:kresleni_-_template.zip |Template pro Kresleni}} Při práci s třídou [[https://doc.qt.io/qt-5/qpainter.html|QPainter]] využijeme skutečnosti, že při vykreslování komponent a takových změnách v aplikaci za běhu, které mohou ovlivňovat grafickou podobu (např. změna velikosti okna), je automaticky volána metoda [[https://doc.qt.io/qt-5/qwidget.html#paintEvent|paintEvent]], kterou lze v derivované třídě přepsat ((Mimochodem to také znamená, že nelze kreslit přímo v konstruktoru, protože vykreslování komponenty začne až po vytvoření objektu.)). Kromě implicitního volání lze metodu [[https://doc.qt.io/qt-5/qwidget.html#paintEvent|paintEvent]] explicitně volat pomocí metod [[https://doc.qt.io/qt-5/qwidget.html#repaint|repaint]] a [[https://doc.qt.io/qt-5/qwidget.html#update|update]]. class Canvas : public QWidget { Q_OBJECT public: Canvas (QWidget *parent=0); void paintEvent (QPaintEvent *event); }; Canvas::Canvas (QWidget *parent) : QWidget (parent) { resize(200, 200); } Aby mohl painter kreslit, musí vědět kam: k tomu slouží parametr konstruktor, tzv. ''painting device'' - může to být instance třídy [[https://doc.qt.io/qt-5/qwidget.html|QWidget]], [[https://doc.qt.io/qt-5/qpixmap.html|QPixmap]] nebo [[https://doc.qt.io/qt-5/qimage.html|QImage]]. Zde pracujeme s třídou derivovanou z [[https://doc.qt.io/qt-5/qwidget.html|QWidget]], předáme tedy ukazatel ''this''. POkud by bylo potřeba mít z libovolného důvodu painter sdílený v rámci třídy, je třeba ho před začátkem vykreslování aktivovat metodou [[https://doc.qt.io/qt-5/qpainter.html#begin|QPainter::begin()]] a ukončit metodou [[https://doc.qt.io/qt-5/qpainter.html#end|QPainter::end()]]. void Canvas::paintEvent(QPaintEvent *event) { QPainter p(this); p.fillRect(20, 20, 160, 160, QColor(220,0,0)); } Třída [[https://doc.qt.io/qt-5/qpainter.html|QPainter]] obsahuje řadu funkcí, pomocí který se do kreslit do ''painting device''. Některé z nich vyžadují, aby bylo zvoleno pero, tj. vytvořena instance třídy [[https://doc.qt.io/qt-5/qpen.html|QPen]]. QPen pen(Qt::black, 2); p.setPen(pen); p.drawRect(40, 40, 30, 30); Barvy lze používat buď předdefinované (statické atributy třídy [[https://doc.qt.io/qt-5/qcolor.html|QColor]]), nebo lze definovat libovolnou barvu pomocí souřadnic v některém z barevných modelů (např. defaultním ''RGB'' modelu). Součástí specifikace barvy může být i průhlednost, definovaná jako parametr instance [[https://doc.qt.io/qt-5/qcolor.html|QColor]] nebo pomocí metody [[https://doc.qt.io/qt-5/qpainter.html#setOpacity|QColor::setOpacity()]]. for (int i=0; i < 10; i++) { p.setOpacity((i+1)*0.1); p.fillRect(i*40, 0, 40, 40, Qt::darkGray); } {{ :courses:b2b99ppc:solutions:solutions_painting_02.png?300 |}} Barevný prostor ''RGB'', který je mainstreamem ve zpracování barevné informace, má celou řadu nevýhod. Díky tomu, že intenzita jednotlivých komponent modelu barvy ovlivňuje jas i barevnost, není možné např. jednoduše provádět porovnávání barev ve smyslu ''světlejší'', ''tmavší'' a podobně. Výhodnější proto někdy mohou být barevné modely, ve kterých je jasová složka oddělena od barevné informace, např. barevný prostor ''HSV'' - ''Hue'' je barva (0-359), ''Saturation'' je sytost (0-255) a ''Value'' je jas (0-255). for (int i=0; i < 10; i++) { QColor c; p.fillRect(i*40, 0, 40, 40, c.fromHsv(200, i*25, 255)); } {{ :courses:b2b99ppc:solutions:solutions_painting_03.png?300 |}} Pomocí třídy [[https://doc.qt.io/qt-5/qpainter.html|QPainter]] je možné i vytvářet texty. V následujícím příkladu je ukázáno, jak napsat text pomocí metody [[https://doc.qt.io/qt-5/qpainter.html#drawText|QPainter::drawText()]] a nastavit font instancí třídy [[https://doc.qt.io/qt-5/qfont.html|QFont]]. Příklad je zajímavý tím, že ukazuje možnost rotace a posunu kreslící plochy (painteru) pomocí metod [[https://doc.qt.io/qt-5/qpainter.html#rotate|QPainter::rotate()]] a [[https://doc.qt.io/qt-5/qpainter.html#translate|QPainter::translate()]]. Třešničkou je použití lineárního gradientu [[https://doc.qt.io/archives/qt-4.8/qlineargradient.html|QLinearGradient]] při vykreslení písma. {{:courses:b2b99ppc:solutions:solutions_painting_04.png?300 |}} QPainter q(this); QLinearGradient gradient (0, 20, 200, 20); gradient.setColorAt(1.0, Qt::red); gradient.setColorAt(0.0, Qt::yellow); q.setPen(QPen(gradient, 4)); q.setFont(QFont("Arial", 24, QFont::Bold)); q.rotate(90); q.translate(200, -200); for (int i = 0; i < 4; i++) { q.drawText(10, 10, tr("Ahoj PPC")); q.rotate(90); } Změna kreslící plochy (rotace, translace, transformace) je jednou dalších možností, jak kreslit pomocí [[https://doc.qt.io/qt-5/qpainter.html]]. Pro vytváření transformovaných objektů je vhodné vytvářet další instance této třídy, nebo uložit stav pomocí [[https://doc.qt.io/qt-5/qpainter.html#save|QPainter::save()]] do zásobníku, odkud může být vyzvednut po provedení transformace metodou [[https://doc.qt.io/qt-5/qpainter.html#restore|QPainter::restore()]].