페이지

2016년 3월 24일 목요일

pvdevelop 프로젝트 -UI 편집 방법 - 2D graph (QwtPlot) 1편


pvbrowser에서 2D 그래프를 만드는 방법은 두가지 입니다. 하는 QwtPlot을 이용하는 것, 또 하나는 직접 그려주는 것, 서로 장단점이 있는 것 같은데, 편한 것을 사용하면됩니다.

오늘은 QwtPlot 위젯에 대해 알아보도록 하겠습니다. 이 위젯은 Qwt 라이브러리를 이용한 것으로 자세한 내용은 링크를 참조하시기 바랍니다.

여담... Qt 에서 그래프 Plot용ㅇ도로 가장 많이 사용하는 외부 라이브러리가. Qwt , QCustomPlot입니다. 서로 장단점이 있어 어떤 것이 좋다라고 말하기는 좀 힘드네요. 사용자 기호라고 밖에는,... pvbrowser는 qwt 를 지원합니다.

자, 그럼 pvbrowser의 QwtPlot이 지원하는 함수들은

qpwSetCurveData(p, id, curve, count,x[],y[]);
//qpwSetBufferedCurveData(p, id, curve);
qpwReplot(p, id);
qpwSetTitle(p, id, "Text");
qpwSetCanvasBackground(p, id, r, g, b);
//qpwEnableOutline(p, id, ival);
//qpwSetOutlinePen(p, id, r, g, b);
qpwSetAutoLegend(p, id, ival);
qpwEnableLegend(p, id, ival);
qpwSetLegnedPos(p, id, pos);
qpwSetLegendFrameStyle(p,id,style);
qpwEnableGridXMin(p,id);
qpwSetGridMajPen(p, id, r, g, b, style);
qpwSetGridMinPen(p, id, r, g, b, style);
qpwEnableAxis(p, id, pos);
qpwSetAxisTitle(p, id, pos, "text");
qpwSetAxisOptions(p, id, pos, ival);
qpwSetAxisMaxMajor(p, id, pos, ival);
qpwSetAxisMaxMinor(p, id, pos, ival);
qpwInserCurve(p, id, index, "text");
qpwRemoveCurve(p, id, index);
qpwSetCurvePen(p, id, index, r, g, b, width, style);
qpwSetCurveSymbol(p, id, index, symbol, r1, g1, b1, r2, g2, b2, w, h);
qpwSetCurveYAxis(p, id, index, pos);
//qpwInsertMarker(p, id, index);
//qpwSetMarkerLineStyle(p, id, index, style);
//qpwSetMarkerPos(p, id, index, x, y);
//qpwSetMarkerLabelAlign(p, id, index, align);
//qpwSetMarkerPen(p, id, index, r, g, b, style);
//qpwSetMarkerLabel(p, id, number, "text");
//qpwSetMarkerFont(p, id, index, "family", size, style);
//qpwSetMarkerSymbol(p, id, index, symbol, r1, g1, b1, r2, g2, b2, w, h);
//qpwInsertLineMarker(p, id, index, "text", pos);
qpwSetAxisScaleDraw(p, id, pos, "text");
qpwSetAxisScale(p, id, pos, min, max, step);

36개.... 많다. 하나씩 하다보면 끝나겠지....

테스트하기 위한 GUI를 다음과 같이 만들었다.

그림 1.QwtPlot 위젯 테스트용 GUI
지금까지의 내용을 잘 이해했다면 충분히 혼자서 그릴 수 있는 화면이다.
 본인이 사용한 QwtPlot 위젯이름은 qwtplot_01을 사용하였다.
pvbrowser창 크기가 변할 때, 가변으로 변하게 하기 위해 layout을 이용하였다.

QwtPlot 위젯은 그래프를 그리기 위한 도화지, 도화지에 그림을 그릴 때 한가지 색만 칠하지 않고 여러가지 색으로 칠한다. 색을 칠하기 위한 색연필(크래파스)를 커브로 생각하면 이해하기 쉽다. 우선을 간단하게 생각하자. 물론 깊이 들어가면 많이 머리 아프다. 특히 멀티 x축 같은 것은 기대하지 말기 바란다...


그럼 QwtPlot 위젯에 data 커브를 나타나게 해보자.
크게 보면 아래의 순서로 그래프를 만든다.
  1. QwtPlot 위젯을 만든다.
  2. Graph로 나타낼 데이터 세트를 만든다.
  3. QwtPlot 위젯에 데이터 커브를 정의해 준다.
  4. 정의된 데이터 커브의 데이터 세트를 넣어준다.
  5. QwtPlot위젯을 다시 그린다.

 설명은 반대 순서로 하겠습니다. 이게 조금 이해하기 편한 것 같다...

QwtPlot 위젯을 다시 그려주는 함수, 커브가 변경되거면, 다시 그려줘야 한다. 이때 사용하는 함수가 qpwReplot()함수이다. 일반적으로 slitNullEvent() 함수에 넣어준다.



사용 예)
  qpwReplot(p, qwtplot_01);

그럼, 다시 그려줄 커브의 데이터 세트는 어떻게 집어 넣는가. qpwSetCurveData()함수를 이용하여 데이터 세트를 넣어준다.



사용 예)
  qpwSetCurveData(p, qwtplot_01, 0, 100,  d->x1, d->Y1);
  qpwSetCurveData(p, qwtplot_01, 1, 100,  d->x2, d->Y2);

  d->x1, d->x2는 double 형 배열이다, 배열의 크기는 100.
 qpwSetCurveData의 4번째 인자인 count를 배열의 크기오 동일하게 넣어주면, 100개의 데이터가 모두 나타나고, 만약 10을 집어넣으면 10개의 데이터만 그래프에 나타난다.

커브로 표시하고 싶은, 데이터 세트가 변경될 때 마다 이 함수를 계속 불러주어야 한다. 그래야, 변경된 커브가 qwtPlot 위젯에 반영된다.
편하게 사용할려면 qpwSetCurveData() 함수와 qpwReplot()함수는 함께, 사용하여야 한다. 즉 데이터 세트 변경 내용을 qpwSetCurveData()에 넣은 후 qpwReplot()을 시키면 변경된 내용이 QwtPlot 위젯에 나타난다.

자, QwtPlot은 도화지라고 했다. 어떤 색연필(크래파스)를 사용하여, 데이터 세트를 그릴 지 qwtPlot 위젯에 알려주자

QwtPlot 위젯에 커브를 넣어주기 위해서 사용하는 함수가 qpwInsertCurve() 이다.



사용 예)
  qpwInsertCurve(p, qwtplot_01, 0, "Test 0");
  qpwInsertCurve(p, qwtplot_01, 1, "Test 1");

  "Test 1" 을 커브 0으로 정의하여 QwtPlot 위젯에 넣어주었다.

어렵게 생각하지 말자,  넣어준 커브를 구분하기 위한 index. (index 값은 0 부터 시작한다.
편의상 0부터 시작하는 거지 아무 숫자나 넣어도 된다.)

text는 커브 설명(이름...)이라고 생각하자. Legend 창에 표시하고 싶은 내용을 넣으면 된다.

이 세 함수만 있어도 QwtPlot 위젯에 커브(그래프)를 나타낼 수 있다. 아니 이게 전부다. 나머지는 커브를 꾸미거나, X, Y 축 설정 변경, ...이런 것들이 거의 모든 내용이다.


QwtPlot위젯에 나타낼 데이터 세트를 만들어보자. 데이터가 없으면 기본 QwtPlot위젯만나타나며, 도화지(커브가 그려지는) 공간은 비어 있을 것이다.(grid가 기본 설정되어 완전히 빈 공간은 아니지마...) 여하튼 데이터 세트(X축, Y축) 두개를 만들어 주었다. 즉... 그래프 두개를 그려줄 것이다.



DATA struct 에 우선 그래프 두개를 만들기 위한 변수를 정의하였다. x1, y1은 커브 1, x2, y2는 커브 2에 사용할 것이다.

아래 코드를 이용하여 데이터를 초기화 시켰다. 초기화 코드는 어디에?
slotInit()함수에 넣어 주었다.



이제 거의 다 되었다. 아래와 같은 함수를 만든 후, slotInit() 함수에서 호출하자.



다들 잘 하시겠지만, 혹시나 하여,,, 최종적인 slotInit() 함수는 다음과 같 습니다.



자, 그럼 컴파일 후 pvbrowser에서 확인해 보죠

그림2. pvbrowser 실행 화면
그림2와 같은 화면이 나타나면 성공하신 겁니다.



이제부터 진행할 내용은 그래프를 꾸미는 개념이 강합니다.

qpwSetCanvasBackground()

그래프가 그려지는 영역을 캔버스라고 하였습니다. 캔버스의 색깔을 바꿔볼까요.
기본 캔버스의 색상은(239, 239, 239)입니다. 검은색으로 해볼까요.
qpwSetCanvasBackground() 함수를 이용합니다.




사용 예)
 qpwSetCanvasBackground(p, qwtplot_01, 0, 0. 0);

qpwSetTitle()

그래프에 이름(Title)을 넣어보죠
qpwSetTitle()이 함수를 이용합니다.



사용 예)
  qpwSetTitle(p, qwtplot_01, "Test QwtPlot");

그림 3. qwtplot 꾸미기 1
그래프 상단에 "Test QwtPlot"이 표시되었습니다. 도화지 새상은 검은색으로..., 기존에 만든 커브 1, 2의 색상이 검은색이어서 나타나지 않네요.

qpwEnableAxis()

커브의 x, y 축을 나타나게 하기 위해 사용하는 함수 입니다. 기본적으로 xBottom, yLeft는 Enable 되어 있습니다.



사용 예)
  qpwEnableAxis(p, qwtplot_01, yRight);

yRight 축을 나타나게 하였습니다.

그림 4. 오른쪽 y축 추가 화면

qpwSetAxisTitle()

축의 타이틀을 지정하는 함수 입니다.


사용 예)
   qpwSetAxisTitle(p, qwtplot_01, xBottom, "Alpha");

   qpwSetAxisTitle(p, qwtplot_01, yLeft, "f(Alpha)");

   qpwSetAxisTitle(p, qwtplot_01, yRight, "x(Alpha)");


그림 5.Title 나타내기


Legend 관련

Legend를 입력하기 위해 사용하는 함수입니다.
 커브 생성시 넣어준 text를 레전드로 사용합니다.



사용 예)
 qpwSetAutoLegne(p, qwtplot_01, 1)

??? 사용 목적이 불분명...qwt 에서 나중에 확인해 보자...
어쨌든, 기본적으로 설정하기시 바랍니다.



사용 예)
  qpwEnableLegend(p, qwtplot_01, 1);

Legend를 나타나도록 설정합니다. 이것만으로는 나타나지 않고 legend가 나타날 위치도 함께 지정해 주어야 나타납니다.




사용 예)
qpwSetLegendPos(p, qwtplot_01, RightLegend);

그럼 확인해 볼까요.

그림 6. Legend 나타내기

혹시 몰라. 지금까지의 추가한 qwtPlotInit() 함수의 코드입니다.



 Legend관련 마지막 함수를 살펴보죠.



ShapeShadow
NoFrame
Plain
Box
Raised
Panel
Sunken
WinPanel
Mshadow
HLine
VLine
StyledPanel
PopupPanel
MenuBarPanel
ToolBarPanel
LineEditPanel
TabWidgetPanel
GroupBoxPanel
MShape

Shape 과 Shadow에 정의된 enum이 모두 적용되는지는 테스트 해보지 않았습니다. 아마 일부만 적용될 것 같습니다. 테스트는 직접 해보시기 바랍니다. 저 같은 경우는 머리가 좋지않아 반복, 학습을 많이 해야 그나마 머리속에 조금 들어옵니다... 여하튼 반복이 최고 입니다.

사용예)
qpwSetLegendFrameStyle(p, qwtplot_01, Panel | Sunken);

그래프에 나타는 커브의 속성(색상, 라인 종류,...)을 변경하여, 변경내용이 legend에 어떻게 반영되는지 확인해 보죠.
사용할 함수는 qpwSetCurvePen() 함수입니다. 커브를 그리기 위한 Pen을 설정하는 겁니다.



PenStyle
NoPen
SolidLine
DashLine
DotLine
DashDotLine
DashDotDotLine
MPenStyle

펜의 기본값은 폭 1, solidLine으로 지정되어 있군요. 사용해 보겠습니다.
사용 예)

   qpwSetCurvePen(p, qwtplot_01, 0, BLUE, 2, DashDotLine);

   qpwSetCurvePen(p, qwtplot_01, 1, GREEN, 2, DashDotLine);
펜의 기본 폭은 2, 펜스타일은 DashDotLine으로 변경하였습니다.

그림 7.Curve 특성 변경 후

커브의 형태가 변경되었을 뿐 아니라, Legend도 같이 변했습니다.

그래프를  y축을 한번 살펴보죠, 왼쪽 y축의 최대값은 300, 오른쪽 y축의 최대값은 1000입니다. y축의 최대값이 300 인 것은  커브 1의 최대값이 99 x 3 = 297 이기 때문입니다. 커브 2는 99x2 = 198 이죠, 하고 싶은 말은...
커브가 생성되면 기본적으로 yLeft 가 기본값이라는 겁니다. 그럼 curve2를 yRight에 지정할려면 어떻게 해야 할까요. 이때 사용하는 함수가 qpwSetCurveYAxis()입니다.



사용 예)
  qpwSetCurveYAxis(p, id, 0, yLeft);

  qpwSetCurveYAxis(p, id, 1, yRight);

기본적으로 yLeft 로 지정되지만, 그래도 확실히 하기 위해 커브 0을 yLeft로 저정해 주었습니다.

그림 8. 커브 축 지정
왼쪽, 오른쪽 y축이 변경되었습니다. 커브 1이 사라진 것 같죠, 아닙니다. 커브 2와 동일한 위치이기 때문에 이런 현상이 발생하는 군요...

데이터 포인트 위치에 기호(Symbol)을 삽입해 보겠습니다. 사용할 함수는 qpwSetCurveSymbol()입니다.



MarkerSymbol
MarkerNone
MarkerEllipse
MarkerRect
MarkerDiamond
MarkerTriangle
MarkerDTriangle
MarkerUTriangle
MarkerLTriangle
MarkerRTriangle
MarkerCross
MakerXCross
MarkerStyleCnt

인자 마지막에 있는 w, h 는 폭과 높이 겠죠...

사용 예)
qpwSetCurveSymbol(p, id, 0, MarkerDiamond, RED, GREEN, 20,20);
qpwSetCurveSymbol(p, id, 1, MarkerEllipse, RED, GREEN, 20,20);
 

그림 9.  데이터 심벌 지정

재미있는 것은 Diamond, Ellipse를 사용했는데 나오는 모양은 틀리네요. 천천히 확인해 보아야 하겠습니다.
심벌을 자세히 들여다 보면, 외각은 녹색, 내부는 RED(?) 입니다.

5번째 색상인자는 내부색, 6번째 색상 인자는 외부 경계 색이군요...


점점... 스크롤 압박이 발생하는 군요... 그래도 끝까지 가보죠.


축 설정

축에 대해 조금 더 알아보죠
먼저 qpwSetAxisScale()... 뭐 하는 함수일까요?



 표시할 축 영역을  설정하기 위해 사용합니다. 그냥 해보십시오. 간단합니다.
사용 예)
qpwSetAxisScale(p, qwtplot_01, xBottom, 0, 50, 25);

유사한 함수가 있네요.



min, max, step 대신 text가 나타나있네요. text 포멧은 QDateTime.toString을 이용해 날짜와 시간을...
그냥 사용하십시오. 기본 형식은 "yyyy-MM-dd hh:mm:ss"입니다.
만약 시간만 표시하고 싶오면 "hh:mm:ss", 초 만 표시하겠다. "ss"

x 데이터 기준입니다. 현재 X 데이터가 0 부터 시작하기 때문에 00-00, 만약 X의 값이 60 이면 01-00 으로 나타납니다.

그림 10.x 축 시간 지정.

좋은 기능이지만, 만약 x축 데이터가 시간 형식으로 지정되어 있다면, 데이터 변경(정확한 명칭이 기억나지 않는 군요...)하여야 제대로 반영됩니다. 주의하여 사용하기시 바랍니다.

 기억할 점. 데이터가 표시될 영역은 qpwSetAxisScale() 함수를 이용하여 지정한 후 qpwSetAxisScaleDraw() 함수를 사용하십시오.


가끔, 혹은 축의 형태를 변경해야 할 경우가 있습니다. 대표적인 경우가 지수 표시인 경우가 있겠죠. qpwSetAxisOptions() 이 함수를 이용하면 되는 것 같은데,... 뭘 잘못한건지. 제대로 동작하지 않네요... 다음에 다시 확인해 보자.




QwtAutoscale
pvNone
IncludeRef
Symmetric
Floating
Logarithmic
Inverted

잘 모르겠다.. 적용이 않된다. ...OTL...


축의 Major 눈금 갯수를 지정합니다. 정확히 지정한 갯수로 나타나는 경우도 있지만, 나타나지 않는 경우도 많습니다. 지정한 값 근처에서 가장 보기 좋은 갯수를 자동으로 지정하는 것 같습니다.
 


사용 예)
  qpwSetAxisMaxMajor(p, qwtplot_01, yLeft, 10);


축의 Major 눈금 갯수를 지정합니다. 정확히 지정한 갯수로 나타나는 경우도 있지만, 나타나지 않는 경우도 많습니다. 지정한 값 근처에서 가장 보기 좋은 갯수를 자동으로 지정하는 것 같습니다.



사용 예)
  qpwSetAxisMaxMinor(p, qwtplot_01, yLeft, 10);

Minor Grid를 나타냅니다. 숨기는 기능은 없습니다. 아마, 처음부터 다시 그려야 할 듯.



사용 예)
  qpwEnableGridXMin(p, qwtplot_01);


그럼 Major Grid, Minor Grid의 특성를 지정해 봅시다. 두 함수 이름만 살짝 틀리고 나머지 인자들은 깉습니다.






사용 예)
  qpwSetGridMajPen(p, qwtplot_01, GREEN, DashLine);
  qpwSetGridMinPen(p, qwtplot_01, GREEN, DashLine);




자, 그럼 컴파일 후 확인해 보죠.

그림 11. 최종 화면
최종화면을 확인해 보시기 바랍니다.

이 밖에 커브를 빼고 싶으면 qpwRemoveCurve() 함수를 사용하시면 됩니다. 테스트는 직접 한번 해보시기 바랍니다.

이번에 설명하지 않은 함수 가운데 qpwEnableOutline(), qpwSetOutlinePen() 함수가 있습니다. 이 함수의 역활은 캔버스 가장자리를 모양을 제어하는 함수 같은데, 제대로 동작하지 않네요... 하지만 사용할 필요성을 별로 느끼지 않아... 통과...

qpwSetBufferedCurveData(), 너는 뭐하는 놈이냐????

댓글 없음:

댓글 쓰기