Qt - podstawy rysowania z wykorzystaniem klasy QPainter

Autor podstrony: Krzysztof Zajączkowski

Stronę tą wyświetlono już: 7093 razy

Klasa QPainter umożliwia rysowanie np. po oknie programu czy kontrolki, jak również rysowanie po bitmapie a także drukowanie. Klasa ta zawiera zestaw metod umożliwiających rysowanie różnego typu prymitywów, takich jak:

Rysowanie po oknie programu

Bardzo często do rysowania po oknie programu wykorzystuje się zdarzenie paintEvent. Zdarzenie to jest wywoływane gdy następuje konieczność odświeżenia (odrysowania) całego lub wyznaczonego obszaru okna programu (co wiąże się z optymalizacją). Program wywołuje to zdarzenie, gdy jakaś część okna wymaga odrysowania. Dzieje się tak np. przy zmianie rozmiaru okna, minimalizacji i maksymalizacji. Oto prosty przykład wykorzystania tego zdarzenia do narysowania okręgu:

void MainWindow::paintEvent(QPaintEvent *event){ QWidget::paintEvent(event); QPainter painter; painter.begin(this); // w tym momencie podpinam obiekt klasy painter pod okno programu, co oznacza rysowanie po nim painter.drawEllipse(QPoint(width() / 2, height() / 2), 100, 100); // rysowanie elipsy na środku okna painter.end(); // koniec rysowania po oknie }

Każda zmiana rozmiaru okna spowoduje jego odrysowanie co z kolei sprawi, że okrąg narysowany będzie zawsze rysował się na środku tegoż okna. Gdyby zrobić mały eksperyment i cały proces rysowania wykonać jednorazowo np. po kliknięciu w oknie programu lewym przyciskiem myszy to okazałoby się, że każde odświeżenie okna wymazywałoby tak narysowany element. Z tego względu proces rysowania powinien odbywać się pod zdarzeniem paintEvent, natomiast zapamiętywanie obiektów, które powinny być rysowane powinno być realizowane oddzielnie.

Rysowanie po obiekcie klasy QImage

Możliwe jest również rysowanie po utworzonym obiekcie klasy QImage. Oto przykład:

#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), brushLine(QColor(255,0,0)), // konstruktor obiektu klasy QBrush penLine(brushLine, 4) // konstruktor obiektu klasy QPen { ui->setupUi(this); image = new QImage(800, 300, QImage::Format_ARGB32); // tworzę bitmapę o rozmiarze 800 x 300 z 32 bitową głębią kolorów image->fill(QColor(255, 255, 255)); // wypełniam bitmapę kolorem białym paintOnImage = new QPainter; // tworzę obiekt klasy QPainter paintOnImage->begin(image); // podpinam grafikę pod obiekt klasy QPainter paintOnImage->setPen(penLine); // ustawiam pędzel rysowania } MainWindow::~MainWindow() { delete ui; paintOnImage->end(); // kończę rysowanko po bitmapie delete image; // usuwam bitmapę delete paintOnImage; // usuwam obiekt klasy rysującej po bitmapie } void MainWindow::paintEvent(QPaintEvent *event){ QWidget::paintEvent(event); QPainter painter; painter.begin(this); painter.drawImage(0, 0, *image); // rysowanie utworzonej wcześniej bitmapy painter.end(); } void MainWindow::mouseMoveEvent(QMouseEvent *event){ QWidget::mouseMoveEvent(event); if(event->buttons() & Qt::LeftButton){ // jeżeli lewy przycisk mychy został wciśnięty to paintOnImage->drawLine(mousePos.x() , mousePos.y(), event->x(), event->y()); // rysuję linię od ostatnio odnotowanego punktu do bieżącego // zapamiętuję punkt mousePos.setX(event->x()); mousePos.setY(event->y()); repaint(); // odrysowuję okno } } void MainWindow::mousePressEvent(QMouseEvent *event){ // trzeba zapamiętać punkt kliknięcia myszką mousePos.setX(event->x()); mousePos.setY(event->y()); }

Powyższy prosty przykład umożliwia rysowanie jednym pędzlem po bitmapie poprzez wciśnięcie i przytrzymanie lewego przycisku myszy wraz z przemieszczeniem kursora myszy. Jest to realizowane w zdarzeniu mouseMoveEvent. Sam proces rysowania bitmapy w oknie programu jest z kolei realizowany w zdarzeniu paintEvent. Warto dodać, że domyślnie każda kontrolka wywołuje zdarzenie mouseMoveEvent tylko wtedy, gdy któryś z przycisków myszy zostanie wciśnięty. Stan ten można zmodyfikować korzystając z metody setMouseTracking wchodzącej w skład każdej kontrolki czy też każdego okna programu.

Qt Creator - przykład rysowania po bitmapie
Rys. 1
Qt Creator - przykład rysowania po bitmapie

Metody rysujące klasy QPainter

Oto lista metod rysujących wchodzących w skład klasy QPainter:

Pędzle i desenie

Pędzle to obiekty klasy QPen, które opisują właściwości linii rysowanych przez obiekt klasy QPainter. W celu wykorzystania pędzla obiekt klasy QPainter musi wywołać metodę begin a następnie za pomocą metody setPen można ustawić bieżący pędzel rysowania. Każdy pędzel ma do dyspozycji podstawowy zestaw styli, które można wyświetlić za pomocą poniższego kodu:

QPen pen(Qt::black); // tworzę czarny pędzel pen.setWidth(2); // ustawiam jego szerokość na 2 px for(int i = 0; i < Qt::CustomDashLine; i++){ QLine line(10, 30 + 10 * i, 110, 30 + 10 * i); // tworzę obiekt QLine (linii) pen.setStyle(Qt::PenStyle(i)); // ustawiam styl pędzla painter.setPen(pen); // ustawiam pędzel painter.drawLine(line); // rysuję linię ustawionym stylem pędzla }

Wynik działania powyższego kodu pokazany został na poniższym rysunku.

Qt Creator - wyświetlone podstawowe style pędzli
Rys. 2
Qt Creator - wyświetlone podstawowe style pędzli:
  • Qt::NoPen - brak pędzla;
  • Qt::SolidLine - linia ciągła;
  • Qt::DashLine - przerywana długa;
  • Qt::DotLine - kropkowana;
  • Qt::DashDotLine - linia kropka;
  • Qt::DashDotDotLine - linia kropka kropka;

Istnieje jeszcze jeden styl Qt::CustomDashLine, który umożliwia ustawienie własnego stylu kreskowania w następujący sposób:

QPen pen(Qt::black); pen.setWidth(2); pen.setStyle(Qt::CustomDashLine); // ustawienie stylu zdefiniowanego przez użytkownika pen.setDashPattern( // ta metoda ustawia wzorzec rysowanej linii {10, 2, 2, 2, 2, 2, 2, 2, 2, 2} // QVector, którego kolejne wartości określają na przemian długość odcinka, odstęp, długość odcinka ... ); painter.setPen(pen); QLine line(10, 30, 210, 30); painter.drawLine(line);

Powyższy kod narysuje linię o odcinkach długości 10, 2, 2, 2, 2, 10, 2, 2... i odstępach równych 2.

Podobnie rzecz ma się z deseniami opisującymi sposób wypełnienia obiektów rysowanych. Desenie są obiektami klasy QBrush i tak jak w przypadku pędzli można dany deseń ustawić jako bieżący za pomocą metody setBrush obiektu klasy QPainter. Każde wypełnienie ma dostępną listę standardowych wzorców, oto przykładowy kod generujący podgląd takich deseni:

QBrush brush(Qt::black); // obiekt klasy QBrush z ustawionym kolorem czarnym QRect rect; rect.setCoords(10, 10, 110, 50); // nazwy styli deseni QStringList styleNames = { "Qt::NoBrush", "Qt::SolidPattern", "Qt::Dense1Pattern", "Qt::Dense2Pattern", "Qt::Dense3Pattern", "Qt::Dense4Pattern", "Qt::Dense5Pattern", "Qt::Dense6Pattern", "Qt::Dense7Pattern", "Qt::HorPattern", "Qt::VerPattern", "Qt::CrossPattern", "Qt::BDiagPattern", "Qt::FDiagPattern", "Qt::DiagCrossPattern", "Qt::LinearGradientPattern" }; for(int i = 0; i < Qt::LinearGradientPattern; i++){ brush.setStyle(Qt::BrushStyle(i)); // ustawienie patternu painter.setBrush(brush); // ustawienie wypełnienia rect.moveTo(10, i * 40 + 20); // przemieszczenie prostokąta painter.drawRect(rect); // rysowanie prostokąta painter.drawText(120, 40 + i * 40, styleNames[i]); // rysowanie opisu stylu wypełnienia }

Na poniższym rysunku pokazany został efekt działania powyższego kodu.

Qt Creator - podstawowe style tekstur wypełnienia
Rys. 3
Qt Creator - podstawowe style tekstur wypełnienia:
  • Qt::NoBrush - brak wypełnienia;
  • Qt::SolidPattern - jednolite wypełnienie;
  • Qt::Dense1Pattern;
  • Qt::Dense2Pattern;
  • Qt::Dense3Pattern;
  • Qt::Dense4Pattern;
  • Qt::Dense5Pattern;
  • Qt::Dense6Pattern;
  • Qt::Dense7Pattern;
  • Qt::HorPattern;
  • Qt::VerPattern;
  • Qt::CrossPattern;
  • Qt::BDiagPattern;
  • Qt::FDiagPattern;
  • Qt::DiagCrossPattern;
  • Qt::LinearGradientPattern;

Obiekt QBrush ma do dyspozycji style, umożliwiające tworzenie wypełnień gradientowych. Oto spis takich styli:

Istnieje też styl Qt::TexturePattern umożliwiający ustawienie jako wypełnienia swojej własnej grafiki za pomocą metody setTexture.

Operacje macierzowe

Za pomocą odpowiednich mtod klasy QPainter można:

Pierwsze trzy metody można sobie wyobrazić jak działają, natomiast ja pokażę jak można wykorzystując klasę QTransform obracać rysowany obiekt o kąt względem dowolnego punktu. Oto przykład:

for(int i = 0; i < 360; i++){ QTransform transform; // tworzę obiekt klasy QTransform transform.rotate(30 * i); // obracam go // mnożę macież przesunięcia razy macierz obrotu razy macierz powrotnego przesunięcia transform = QTransform::fromTranslate(-150, -125) * transform * QTransform::fromTranslate(150, 125); // to obraca rysowaną później elipsę o zadany kąt względem jej środka painter.setTransform(transform); // ustawienie obiektu przekształcenia liniowego painter.drawEllipse(100, 100, 100, 50); // rysowanie obróconej elipsy } painter.setTransform(QTransform()); // przywrócenie stanu początkowego

Wynik działania powyższego kodu widoczny jest na poniższym rysunku.

Qt Creator - efekt zastosowania transformacji
Rys. 4
Qt Creator - efekt zastosowania transformacji
Strony powiązane
strony powiązane
  1. doc.qt.io/qt-4.8/qpainter.html - opis klasy QPainter dostępny na stronie Qt
Propozycje książek