본문 바로가기
SWE/Qt

Qt Example] Painting Path

by S나라라2 2019. 8. 30.
반응형

 

Qt는 예제를 실행해볼 수 있는 Demo 를 제공해준다.

다음 위치에 실행 프로그램이 있다. Qt > 4.7.2(버전) > bin > qtdemo.exe

 

Qt Demo

많은 예제들이 있는데 그중 Painter Paths 를 따라해보며 그림 그리는 것을 익혀보려고 한다.

 

아래에 있는 Launch 버튼을 누르면 실제로 실행하며 테스트해볼 수 있다.

Qt demo 실행 결과

 

나는 가장 상단 왼쪽에 있는 두 개의 도형(사각형, 둥근 모서리 사각형)과 Qt글자 도형을 테스트해보려고 한다.

 

Demo 실행 프로그램 하단 우측에 Documentation 버튼을 누르면 예제코드를 볼 수 있다.

 

 

<Qt로 도형 그리기>

 

실행 결과
예제결과

예제에서는 콤보박스에서 각종 설정이 가능하다. (테두리 색, 도형 채워지는 색, 그라데이션, 펜의 두께..)

나는 콤보박스나 버튼, 이벤트 처리 부분은 생략하고 그림 그리는 것만 실행해보았다.

 

 

QPainterPath 클래스

 

#include <QPainterPath>

 

해당 오브젝트는 그래픽 빌딩 블럭들로 구성되어있다. ( 사각형, 타원, 선, 곡선 등..)

또한 filling, outlining, clipping 옵션이 있다.

보통의 drawing operations 보다 painter paths의 주요 장점은 복잡한 모양을 한번만 생성하면 된다는 것이다.

처음에 한 번 생성하고 QPainter::drawPath() 함수를 호출하여 여러번 그릴 수 있다. 

 

 

예제 코드

 

구조

RenderArea Class : custom widget displaying a single painter path

Window Class : the application main window displaying several RenderArea widgets, and allowing the user to manipulate the painter paths' filling, pen, color and rotation angle

 

//window.h

#include <QWidget>

#include "renderArea.h"

class Window : public QWidget
{
	Q_OBJECT

public :
	Window();

private :
	enum { NumRenderAreas = 3 };

	RenderArea *renderAreas[NumRenderAreas];
};

window 클래스는 QWidget을 상속받았다. 

 

우리가 filling, color를 업데이트하기 위해서는 slots를 실행해야만 한다.

그 이유는 QComboBox는 argument로 전달된 값을 signal로 전달하지 않는다. 

 

 

//window.cpp

#include <QtGui>
#include <math.h>

#include "window.h"

Window::Window()
{
	setFixedSize(400,200);

	QPainterPath rectPath;
	rectPath.moveTo(20.0 , 30.0);
	rectPath.lineTo(80.0, 30.0);
	rectPath.lineTo(80.0, 70.0);
	rectPath.lineTo(20.0, 70.0);
	rectPath.closeSubpath();
	
	QPainterPath roundRectPath;
    roundRectPath.moveTo(80.0, 35.0);
    roundRectPath.arcTo(70.0, 30.0, 10.0, 10.0, 0.0, 90.0);
    roundRectPath.lineTo(25.0, 30.0);
    roundRectPath.arcTo(20.0, 30.0, 10.0, 10.0, 90.0, 90.0);
    roundRectPath.lineTo(20.0, 65.0);
    roundRectPath.arcTo(20.0, 60.0, 10.0, 10.0, 180.0, 90.0);
    roundRectPath.lineTo(75.0, 70.0);
    roundRectPath.arcTo(70.0, 60.0, 10.0, 10.0, 270.0, 90.0);
    roundRectPath.closeSubpath();

	QPainterPath textPath;
	QFont timesFont("Times", 40);
	timesFont.setStyleStrategy(QFont::ForceOutline);
	textPath.addText(10, 70, timesFont, tr("hy"));

	renderAreas[0] = new RenderArea(rectPath);
	renderAreas[1] = new RenderArea(roundRectPath);
	renderAreas[2] = new RenderArea(textPath);
	Q_ASSERT(NumRenderAreas == 3 );

	QGridLayout *topLayout = new QGridLayout;
	for(int i=0; i<NumRenderAreas; ++i)
		topLayout->addWidget(renderAreas[i], i/3, i%3);

	QGridLayout *mainLayout = new QGridLayout;
	mainLayout->addLayout(topLayout, 0, 0, 1, 4);

	setLayout(mainLayout);
	setWindowTitle(tr("Painter Paths"));

}

void Q_ASSERT (bool test)

: 만약 test가 false일 경우, 소스 코드파일 이름과 line number를 포함한 경고 메시지를 출력한다.

예제 )

// File : div.cpp

#include <QtGlobal>

 

int divide(int a, int b)

{

Q_ASSERT( b != 0 )

return a / b;

}

 

If b is Zero, the Q_ASSERT statement 는 qFatal()함수를 사용해서 다음과 같은 메시지를 출력할 것이다.

ASSERT : "b==0" in file div.cpp, lin 7

 

//renderArea.h

#include <QWidget>
#include <QSize>
#include <QPainter>

class RenderArea : public QWidget
{
	Q_OBJECT
		
public :
	RenderArea(const QPainterPath &path, QWidget *parent = 0);

	QSize minimumSizeHint() const;
	QSize sizeHint() const;

	public slots :
	void setFillRule(Qt::FillRule rule);
	void setFillGradient(const QColor &color1, const QColor &color2);
	void setPenWidth(int width);
	void setPenColor(const QColor &color);
	void setRotationAngle(int degrees);

protected :
	void paintEvent(QPaintEvent *event);

private :
	QPainterPath path;
	QColor fillColor1;
	QColor fillColor2;
	int penWidth;
	QColor penColor;
	int rotationAngle;
};

 

//renderArea.cpp

#include "renderArea.h"

RenderArea::RenderArea(const QPainterPath &path, QWidget *parent)
: QWidget(parent), path(path)
{
	penWidth = 2;
	rotationAngle = 0;
	setBackgroundRole(QPalette::Base);

	penColor = QColor::QColor(255,0,0,255); // red // rgb
	fillColor1 = QColor::QColor(255,255,255,255); // 
	fillColor2 = QColor::QColor(255,255,255,255);

	// QColor::fromCmykF(iny cyan, megenta, yellow, black, int alpha-channel)


	update();
}


QSize RenderArea::minimumSizeHint() const
{
	return QSize(50,50);
}

QSize RenderArea::sizeHint() const
{
	return QSize(100,100);
}

void RenderArea::setFillRule(Qt::FillRule rule)
{
	path.setFillRule(rule);
	update();
}

void RenderArea::setFillGradient(const QColor &color1, const QColor &color2)
{
	fillColor1 = color1;
	fillColor2 = color2;
	update();
}

void RenderArea::setPenWidth(int width)
{
	penWidth = width;
	update();
}

void RenderArea::setPenColor(const QColor &color)
{
	penColor = color;
	update();
}

void RenderArea::setRotationAngle(int degrees)
{
	rotationAngle = degrees;
	update();
}


void RenderArea::paintEvent(QPaintEvent *)
{
	QPainter painter(this);
	painter.setRenderHint(QPainter::Antialiasing);

	painter.scale(width() / 100.0, height()/100.0 );
	painter.translate(50.0, 50.0);
	painter.rotate(-rotationAngle);
	painter.translate(-50.0, -50.0);

	painter.setPen(QPen(penColor, penWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
	QLinearGradient gradient(0,0,0,100);
	gradient.setColorAt(0.0, fillColor1);
	gradient.setColorAt(1.0, fillColor2);
	painter.setBrush(gradient);
	painter.drawPath(path);
	
	update();

}

 

//main.cpp

#include <QApplication>
#include <QStyleFactory>

#include "window.h"

int main(int argc, char *argv[])
{
	QApplication app(argc, argv);

	Window window;
	window.show();
	return app.exec();
}

 

 

내가 작성한 코드(위의 코드)에는 없지만 예제 코드에서 흥미로웠던 점은

사용자가  QComboBox에서 menu 옵션 선택을 변경하였을 때

signal을 slot에게 주는 코드 구현 부분이다.

connect(penColorComboBox, SIGNAL(activated(int)), this, SLOT(penColorChanged()));

void Window::penColorChanged()
{
	QColor color = qvariant_cast<QColor>(currentItemData(penColorComboBox));
    for(int i=0; i < NumRenderAreas; ++i)
    	renderArea[i]->setPenColor(color);
}

 

반응형