Atom

2010
24
lut

Kurs Qt – Część 6 – Rysowanie

Kategoria: Kurs Qt, Programowanie, Qt, TechblogMatthew @ 01:11

6. (z dużym opóźnieniem) część kursu Qt. Z racji, że ostatnio zostałem zmuszony do rysowania grafów itp. rzeczy (na szczęście można było wybrać technologię) to dziś przedstawię właśnie rysowanie (proste, nie grafów, za dużo nerwów mnie to kosztowało ;P) w Qt. Przy okazji mały pokaz jak zrobić menu kontekstowe

Myślę, że pliku main.cpp nie muszę już przedstawiać, więc od razu przejdę do tego co należy wsadzić do nagłówka i dlaczego:

widget.hpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#ifndef WIDGET_HPP
#define WIDGET_HPP

#include <QtGui>

enum CoRysowac
{
   kolo, kwadrat, napis
};

class Widget : public QWidget
{
   Q_OBJECT

public:
   Widget(QWidget *parent = 0);
   ~Widget();

protected:
   void paintEvent(QPaintEvent *);
   void mousePressEvent(QMouseEvent *);
   void mouseMoveEvent(QMouseEvent *);
   void contextMenuEvent(QContextMenuEvent *);

protected slots:
   void ustawKolo();
   void ustawKwadrat();
   void ustawNapis();
   void ustawDynamicznie();

private:
   CoRysowac coRysowac;
   bool narysuj;
   bool dynamicznie;
   int x;
   int y;
   int dx;
   int dy;

   QAction *qaKolo;
   QAction *qaKwadrat;
   QAction *qaNapis;
   QAction *qaDynamicznie;
};

#endif // WIDGET_HPP

Na początku zdefiniowaliśmy sobie enuma, dzięki któremu będziemy mogli w łatwy sposób definiować co będziemy rysować narysować. Dalej mamy zwykłą klasę dziedziczącą po QWidget. Dalej, deklarujemy przesłonięcie metod odpowiedzialnych za obsługę zdarzeń związanych kolejno z: rysowaniem, naciśnięciem myszy, przesunięciem myszy oraz menu kontekstowym. Dalej są sloty, których zadaniem będzie ustawianie co chcemy rysować. Trochę zmiennych pomocniczych. Zmienna narysuj powie nam czy w danej chwili chcemy rysować, dynamicznie czy będzie możliwa zmiana rozmiaru rysowanego obiektu, x i y opisują współrzędne początkowe obiektu a dx i dy różnicę od kliknięcia do przesunięcia w przypadku zmiany rozmiaru. Dalej są akcje które dodamy do menu abyśmy mogli wywoływać zmiany tego co i jak chcemy rysować.

Natomiast definicja metod klasy wygląda tak:

widget.hpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include "widget.hpp"

Widget::Widget(QWidget *parent) : QWidget(parent)
{
   QTextCodec::setCodecForTr (QTextCodec::codecForName ("UTF-8"));

   coRysowac = kolo;
   dx = 0;
   dy = 0;
   dynamicznie = false;

   qaKolo = new QAction(tr("Koło"), this);
   connect(qaKolo, SIGNAL(triggered()), this, SLOT(ustawKolo()));

   qaKwadrat = new QAction(tr("Kwadrat"), this);
   connect(qaKwadrat, SIGNAL(triggered()), this, SLOT(ustawKwadrat()));

   qaNapis = new QAction(tr("Napis"), this);
   connect(qaNapis, SIGNAL(triggered()), this, SLOT(ustawNapis()));

   qaDynamicznie = new QAction(tr("Zmienny rozmiar"), this);
   qaDynamicznie->setCheckable(true);
   connect(qaDynamicznie, SIGNAL(triggered()), this, SLOT(ustawDynamicznie()));
}

Widget::~Widget()
{
}

void Widget::paintEvent(QPaintEvent *e)
{
   if (narysuj)
   {
      QPainter painter(this);

      QColor kolor(255, 0, 255);

      painter.setPen(kolor);

      switch(coRysowac)
      {
      case kolo:
         painter.drawEllipse(x-50, y-50, 100+dx, 100+dy);
         break;

      case kwadrat:
         painter.drawRect(x-50, y-50, 100+dx, 100+dy);
         break;

      case napis:
         painter.drawText(x, y, "http://blog.matthew.org.pl/");
         break;

      default:
         break;
      }

      narysuj = false;
   }
}

void Widget::mousePressEvent(QMouseEvent *e)
{
   if (e->button() == Qt::LeftButton)
   {
      dx = 0;
      dy = 0;
      x = e->x();
      y = e->y();
      narysuj = true;
      update();
   }
}

void Widget::mouseMoveEvent(QMouseEvent *e)
{
   if(dynamicznie)
   {
      dx = e->x() - x;
      dy = e->y() - y;
      narysuj = true;
      update();
   }
}

void Widget::contextMenuEvent(QContextMenuEvent *e)
{
   QMenu menu(this);

   menu.addAction(qaKolo);
   menu.addAction(qaKwadrat);
   menu.addAction(qaNapis);
   menu.addSeparator();
   menu.addAction(qaDynamicznie);
   menu.exec(e->globalPos());
}

void Widget::ustawKolo()
{
   coRysowac = kolo;
}

void Widget::ustawKwadrat()
{
   coRysowac = kwadrat;
}

void Widget::ustawNapis()
{
   coRysowac = napis;
}

void Widget::ustawDynamicznie()
{
   dynamicznie = !dynamicznie;
}

Pierwszą metodą jest konstruktor klasy Widget. Wszystko co zostało tam użyte powinno być już wam znane z poprzednich części kursu. Jedyna nowość to ustawienie akcji możliwości bycia zaznaczaną (linia 22). Dalej mamy domyślny destruktor, żeby przejść do metody paintEvent(). W niej umieścimy kod odpowiedzialny za rysowanie. Najważniejszym komponentem tej metody jest obiekt klasy QPainter (linia 34.), to przy pomocy jego metod będziemy rysowali. A żeby nie było całkiem nudno ustawimy jakiś wesoły kolor. W tym celu tworzymy zmienną klasy QColor, która będzie przechowywała nasz wybrany kolor (linia 36.). Ustawiamy wybrany kolor naszemu obiektowi QPainter poprzez metodę setPen() (linia 38.), z nią, jak również z innymi metodami ustawiającymi różne właściwości QPaintera, polecam się zapoznać poprzez przejrzenie dokumentacji. W liniach 43, 47 oraz 51 mamy wywołania metod rysujących odpowiednio koło, kwadrat oraz napis na widgetcie. Metodom tym podajemy x i y miejsca od którego ma się zacząć rysowanie (lewy górny róg figury) oraz szerokość i wysokość jaką figura ma mieć. W przypadku napisu, zamiast szerokości i wysokości podajemy sam napis. Po resztę metod również odsyłam do dokumentacji. Metoda paintEvent() jest wywoływana przy każdym przerysowaniu (repaint()) lub aktualizacji stanu widgeta (update()).
Dalej mamy metody mousePressEvent() oraz mouseMoveEvent(). Są wywoływane odpowiednio, przy wciśnięciu myszy oraz przy jej ruchu. Pierwszej używamy do narysowania obiektu (w zasadzie to pobrania współrzędnych i powiedzenie metodzie paintEvent(), że chcemy coś narysować), a drugiej do zmiany rozmiarów tych obiektów. Aby możliwe było rysowanie tylko lewym przyciskiem myszy, sprawdzamy który został wciśnięty, poprzez wywołanie metody button() z obiektu klasy QMouseEvent i porównujemy go z enumem reprezentującym odpowiedni przycisk (linia 64.)
Metoda contextMenuEvent, służy do wyświetlania menu kontekstowego. Różnica między takim tworzeniem menu, w stosunku do poprzednich części kursu, jest taka, że za każdym wywołaniem tej metody tworzymy nowy obiekt QMenu, oraz musimy go ręcznie wywołać metodą exec() (linia 95.) podając jako parametr współrzędne w którym ma się pojawić menu.

Myślę, że pozostała część kodu nie wymaga wyjaśnienia, a jeżeli ktoś jej nie rozumie to będzie to dobre ćwiczenie dla niego. ;) Jeżeli jednak ktoś całkiem przy tym polegnie, to proszę śmiało pisać w komentarzach. Temat rysowania zostanie, jak znajdę chwilę czasu, poszerzony o transformacje narysowanych obiektów.

EDIT:

Ponieważ bigfun trochę marudził, że mu się nie chce kodu przeklepywać, a z chęcią by przetestował błędy jakie zrobiłem w kodzie więc od dzisiaj każda następna (a może i znajdę czas na poprzednie) porcja kodu będzie opakowywana w repozytorium gita: git://github.com/matthewpl/qt6.git.

2 komentarzy do “Kurs Qt – Część 6 – Rysowanie”

  1. maciek, dnia 8 października 2010 o 13:08 napisał(a):

    Nie mogę poradzić sobie ze śćiągnięciem całości kodu z git://github.com/pingwinek/qt6.git. Da się wrzucic na jakiś serwer?

  2. Matthew, dnia 8 października 2010 o 13:34 napisał(a):

    @maciek: ja sobie poradziłem bez problemu, więc nie widzę sensu wrzucania tego gdzieś indziej. I co znaczy „nie mogę poradzić sobie”? Może po prostu używasz nie tego co trzeba (w sensie próbujesz po http, zamiast gitem)?

Zostaw komentarz

Subscribe without commenting