페이지

2016년 3월 30일 수요일

pvbrowser 설치

1. pvbrowser 설치


pvbrowser는 리눅스, 윈도우, OS-X, OpenVMS, Maemo,... 등 여러가지 배포판을 지원해 줍니다. 소스코드를 다운받아 컴파일하여 사용할 수 도 있고(많이 번거롭죠...), 실행파일을 다운받아 설치할 수 도 있습니다.


윈도우용 배포판의 경우 두 가지가 제공되고 있습니다.
  • install-pvbrowser-client.exe
    pvbrowser 클라이언트 프로그램 만 설치할 경우

  • install-pvbrowser.exe
    pvbrowser, pvdeveloper,...를 설치할 경우
pvbrowser 서버 프로그램을 개발할 경우에는 install-pvbrowser.exe를 홈페이지에서 다운받아야 합니다.

저는 윈도우 운영체제에서 서버 프로그램을 만들고 싶기 때문에 install-pvbrowser.exe를 다운 받았습니다.(홈페이지에서 다운받는 속도는 조금 느립니다.)

윈도우용은 여기서 다운 받으시기 바랍니다.

pvbrowser는 GPL, LPGL 라이센스 규약을 따릅니다. 자세한 라이센스 내용은 확인해 보시기 바랍니다.
pvdeveloper를 이용하여 서버 프로그램을 만든 경우, pvbrowser, pvdevelop 프로그램 자체를 돈 받고 팔 수는 없지만(기본 제공 하여야 합니다.), 프로그래밍 비용은 청구할 수 있습니다. (자세한 내용은 라이센스 규약에서 확인하시기 바랍니다.)

여하튼 서버 프로그램을 설치해 보죠.

그림 1. install-pvbrowser 실행화면

설치가 끝나면 메모장이 열리면서 다음과 같은 메시지가 나타납니다.



간단히 살펴볼까요.
1. 레지스트리 설정이 필요없기 때문에 바로 실행할 수 있다.
2. start_pvdevlop.bat 를 실행하기 위해 환경변수 설정이 필요하다.
3. 독립모드(standalone)로 서버를 실행하기 위해 환경변수 "PATH"에 \PVDIR\win-mingw\bin 이 있어야 한다.
4. pvdevelop를 위해 Qt, MinGW 소프트웨어가 설치되야 한다.

추가적으로
소스코드에서 pvbrowser를 컴파일하는 방법이 설명되어 있습니다.


먼저 환경 변수 PATH를 확인해 보았습니다.
c:\pvb\win-mingw\bin
이 경로가 추가되어 있군요. 자 2번째 항목인 "start_pvdevlop.bat" 관련 내용은 자동으로 추가 되는 군요.  3번째 항목도 이 값으로 해결.

시스템변수를 살펴보면
PVBDIR c:\pvb
저는 pvbrowser를 설치할때 기본경로는 이용하였기 때문에, 생성된 PVBDIR 시스템변수의 경로가 기본 경로로 설정되어 있군요.

그러니까, 설치프로그램을 실행하면 1, 2, 3은 신경쓸 필요가 없습니다.

단지 C++, python을 이용하여 서버 프로그램을 작성하기 위한 4번째 항목만 설치하면 됩니다.

우선을 테스트를 위해 실행해 보죠
Desktop 바탕화면에 pvbrowser, start_pvdevelop 두 프로그램의 바로가기가 만들어져 있네요. start_pvdevelp를 실행하겠습니다.


그림 2. start_pvdevelop 도스창

그림3.pvdevelop 창


start_pvdevelop는 확장자가 "bat" 입니다. 즉, 배치파일이기 떄문에 그림 2와 같은 화면이 나타난 후 그림 3이 나타납니다.
도스창에서 아무 키보드 키를 누르면 창이 사라집니다.



배치프로그램을 이용하여 pvdevelop 환경 설정을 한 후 pvdevelop 프로그램을 실행합니다.


설치 테스트를 위해 기본 프로젝트를 만들어 보죠


그림4. 프로젝트 생성창

그림 4와 같은 화면이 나타납니다. 프로젝트를 저장할 폴더 지정, 프로젝트 이름을 지정한후사용할 프로그래밍 언어를 선택하면 됩니다. pvdevelop에서 지원하는 프로그래밍 언어는  C/C++, Lua, Python 3가지 입니다. 이중 C/C++이 기본입니다.

저는 C/C++ 기본값으로 프로젝트를 만들었습니다. OK 버튼을 누르니 다음과 같은 화면이 나타나네요.




그림 5. 디자이너 모드

그림 5와 같이 기본적으로 디자이너 모드( UI용 위젯을 삽입할 수 있는 모드)에서 시작하네요.

그림 6. 에디터 모드

툴박스에 위치한 Editor 아이콘을 누르면 그림6 과 같은 화면이 나타납니다. 그림 3 비교해 보시면, 빈 공간에 Qt pro 파일과 유사한 내용이 들어가 있는 것을 알 수 있습니다.

우선은 그냥 컴파일 해보죠.(Action->Make)


그림 6.Make 실행 도스창

 




한마디로 Make 파일을 실해할 수 없으니 mingw32-make 파일을 설치해 달라는 내용이네요.

이것은 C/C++을 컴파일하기 위한 MinGW이 설치되어 있지 않기 떄문입니다. MinGW을 설치하는 방법은 MinGW를 설치하거나 Qt-SDK를 설치하면 해결 됩니다.

윂페이지에보면 MinGW 만 설치하여도 "fake-qmake" Qt Development package 설치없이 사용할 수 있다고 되어 있는데...(방법을 몰라 그런지 몰라서 겠죠.. 저는 에러가 발생하네요.)

여하튼 Qt-SDK 하나만 설치하면 됩니다. (MinGW 단독으로 설치할 필요 없습니다.),



2. Qt 설치

Qt Download 센터에서 원하는 버전의 Qt 설치파일을 다운로드하신 후 실행하십시오.
저는, 좀 소심한 편이라, pvbrowser Window 버전 설치 화면에 나타나 있는 버전을 다운로드 하였습니다. 여하튼 다른 버전이라도 특이사항은 없는 것 같습니다.


그림 7. Qt 설치화면

필요하신 분은 설정하시면 됩니다. 여하튼 저는 Next->SKIP 하겠습니다.

그림 8.Qt 설치화면


그림 9. Qt 설치-설정화면


MinGW Toolkit이 필요하기 때문에 Tools 에 있는 MinGW 4.9.2 를 체크하여 주시기 바랍니다.

저는 가끔가다. Qt 를 이용하기 때문에, 나머지는 기본 설정으로 설치하였습니다.


그림 10. Qt 설치완료 화면

설치도 되고 해서 한번 실행해 보았습니다.

그림 11. Qt 실행 화면

잘 설치 된 것 같네요..

그림 12.Qt 폴더 내용

설치 완료 후 Qt 폴더 용량을 확인해 보니 3.45G... 엄청나네요...
C/C++ 연습용으로 쓰기에는 너무 큰가?
여하튼 그림 12에 있는  "mingw32-make.exe" 파일을 위해, 이 큰 프로그램을 설치하였습니다..(물론 다른 이유도 있습니다...)



3. start_pvdevelop 편집

start_pvdevelop.bat 파일은 pvdevelop.exe 파일을 실행하기 위한 선처리 구문을 정의한 파일입니다.
이곳에서 C/C++ 컴파일러 위차, Pathon 위치와 같은 값들을 변경할 수 있습니다.
내용을 편집할 것이 떄문에 만일의 사태에 대비하기 위해 저는 복사본을 하나 만들었습니다.

그림 13. start_pvdevelop.bat

C/C++을 사용할 경우, 파일 내용중 ":environment_not_set" 밑에 있는 QTDIR, MINGWDIR 두 값만 수정하면 됩니다. 만약 Python을 이용하시기 경우에는 PYDIR값을 변경하셔야 합니다.
% ":start_pvdevelop" 밑에 있는 변수들은 그대로 사용하십시오.

기본 설정 내용은 다음과 같습니다.



변경한 설정 내용입니다. Qt, MinGW 관련 경로 같을 수정하였습니다.



자, 그럼 start_pvdevelop를 실행하여 pvdevelop를 실행 시킨 후, 처음에 만들어 준 프로젝트를 열어 Make 해보죠,


그림 14.Make 실행파일
그림 14와 같이 실행되면 정상입니다.

그림 15. Start Server

Action->Start server를 눌러 실행하니 그림 15와 같은 화면이 나오는 군요. 더불어 다음과 같은 화면도 나타납니다.

그림 16. 보안 경고 창


방화벽에서 차단하겠다고 해서 액세스 허용하라고 했습니다. 허용하지 않으면 pvbrowser에서 서버 화면이 나타날까요? (확인해 보세요...)


그림 17.pvbrowser 실행화면


pvbrowser 클라이언트 프로그램을 실행하여, 현재 localhost에서 실행되고 있는 서버에 접속한 화면입니다. 정상적으로 동작하네요...

자,  pvdevelop 관련 설정이 모두 끝났습니다. 이제 부터 원하는 SCADA, HMI 를 직접 만들어 보시기 바랍니다.

그냥, 단순하게 C/C++ (표준) 연습용으로 사용하여도 무방합니다.

2016년 3월 27일 일요일

pvbrowser를 이용한 Arduino 통신 예제 (Modbus 모드)

MODBUS 통신

MODBUS 읽기, 쓰기 관련 함수들은 rlModbus 에 정의되어있습니다.
작성하는 mask에서 이 함수들을 사용하기 위해서는 slot 에 주석처리되어 있는 다음 구문의 주석처리를 제거하시기 바랍니다.

//extern rlModbusClient modbus; //Change if applicable

modbus 객체를 만들어 주는 구문입니다.  

읽기 함수
  • readShort()
    Word Data 읽기
  • readByte()
    Byte Data 읽기
  • readBit()
    Bit Data 읽기

먼저, 워드형(Word type) 데이터를 읽는 방법에 대해 알아보죠. readShort()함수는 다음과 같이 정의되어 있습니다.




offset, number 인자를 이용하여 정수형 데이터를 얻고 있습니다. 이 함수의 소스 코드를 확인해 보죠.


 Modbus 데몬을 만들때, 정의한 rlsharedMemory 에서 offset 과 number를 이용하여 데이터를 읽어 들입니다.

rlsharedMemory에서 원하는 데이터를 읽기 위한 함수의 정의입니다.


이 함수를 내부를 대강 둘러보면 rlsharedMemory (Modbus 통신데이터 저장 파일)에 있는 데이터를 mutex 방식을 이용하여 데이터를 읽어내는 코드를 포함하고 있습니다.

너무 깊숙이 들어온 것 같은데, 여하튼 rlsharedmemory에서 원하는 데이터를 가지고 오는 군요.


readShort() 함수 인자 중 offset은 베이스 주소 라고 생각해도 무방합니다. 실제 rlSharedMemory에서 읽어 오는 값의 위치는 offset + (number*2) 이며, 이 위치의 값을 정수형으로 읽어들이죠.
rlSharedMemory에 저장되어 있는 내용들은 기본적으로 Byte입니다.


예를 들어 50 번지의 내용을 읽고 싶다면

modbus.readShort(50, 0 );
modbus.readShort(45, 5 );
modbus.readShort(40, 10 );

이런 식으로 표현할 수 있습니다. 전체적으로 동일한 50번째 위치에서(정확하게는 x2) 두개의 byte를 읽어와 정수형 데이터를 반환해 줍니다.

써넣고 보니, 무슨 말을 하려고 하는 건지 잘 모르겠다.



잠깐, modbus 데몬 정의에 대해 알아보죠.

modbus를 정의할 때 cycle 구문을 한개만 사용한다면 별의미가 없지만, 두 개인 경우는 어떤식으로 구성해야 할까요. 또, 워드형만 읽지않고, 코일 형(bit)도 읽어들이고 싶다면.
한번 살펴보죠.

사이클 1개로 워드형 데이터만 읽어들이면 될 경우 아래와 같은 형태로 쓰면 됩니다.


Cycle1, 한개로 Address 0 ~ 28 까지 29개의 데이터를 읽어들이는 구문입니다.
이것을 두개의 Cycle로 읽어 들인다면.



Cycle1에서 0 ~9 까지 10개의데이터를 읽어들이고, cycle2에서 10 ~ 28 까지 19개의 데이터를 읽어들이고 있습니다.
이때 주의할 점은 cycle2의 시작 주소(start_adr)입니다. 이 값을 기준으로 shm파일(rlSharedMemory)의 저장 위치가 결정되는 것 같습니다. 연속으로 저장하기 위해 이 값을 cycle1 기준으로 맞춰주는 것이 중요합니다.

그림 1. Modbus Daemon 동작상태
%컴파일된 modbus daemon 실행파일은 프로젝트 폴더에 만들어지며, 개별 실행하시면 됩니다.

그림 1과 같은 경우가 modbus 데몬이 통신기기와 정상 연결된 상태입니다. 만약 정상 연결이 되지 않았다면 ret 값이 -1 로 나타납니다. 즉 읽어들인 데이터가 없다

그림 1은 stat_adr=0, num_register=1 즉 , 1 word 데이터를 읽어 들이는 경우 입니다. 1 word는 2byte 이므로 ret값에 2가 나타납니다.

그림2. Modbus Daemon 동작상태 (Cycle=1)

그림 2는 stat_ad=0, num_register=29로 설정한 경우 입니다. 29x2=58byte 따라서 ret 값이 58입니다.

그림 3. Modbus Daemon 동작상태(Cycle =2)
그림1, 2는 cycle을 한개만 사용한 경우지만, 그림3은 cycle을 2개 사용한 경우 입니다. ret 값이 58, 40이 반복적으로 계속 나타나는 군요....

자, 그럼 간단한 GUI 화면을 작성하여 Arduino에서 넘어온 데이터를 확인해 보았습니다.

그림 4. pvbrowser 실행 화면

지금까지는 cycle에서 function 3을 이용하여 word 데이터 만 읽어 들였습니다. 이 경우 만약 word 데이터를 이용하여 bit 처리(INPUT/OUTPUT)을 하려고 하면 많이 번거롭죠. 어떻게 해야 할까요. Single Coil 형태로 읽어 들이면 간단히 해결할 수 있습니다.


아두이노에서 32개의 INPUT Coil (32/16 = 2 Word), 32개의 OUTPUT Coil (32/16 = 2 Word)값을 입력받는다고 생각해보죠,
우선, 아두이노 modbus 통신용 데이터를 다음과 같이 정의하였습니다.

uint16_t au16data[29];


아두이노 Modbus 통신 데이터 au16data[0], au16data[1]은 Input Coil, au16data[2], au16data[3]은 Output Coil, 22 개는  측정데이터 혹은 내부 설정데이터, 나머지 3개는 mobus 상태로 구성하겠습니다.

au16data[0]    <--Input coil  (Bit 16)
au16data[1]    <--Input coil  (Bit 16)
au16data[2]    <--Output coil  (Bit 16)
au16data[3]    <--Output coil  (Bit 16)
au16data[4]    <--Data  (Word)
     |
au16data[26]    <--Data  (Word)
au16data[27]    <--Modbus Status  (Word)
au16data[28]    <--Modbus Status  (Word)
au16data[29]    <--Modbus Status  (Word)

이 경우 Cycle은 어떻게 설정해야 할 까요.



저는 이런 식으로 정의했습니다.

그림 5. Modbus daemon 실행 화면

Modbus daemon 작동상태를 확인해 보니 ret 값이 4, 4, 50 반복되고 있습니다. 정확히 동작하는 것 같군요.
자, 그럼 이것을 이용해 다음 GUI를 조금 더 수정해 보았습니다.



QImage 위젯을 이용하여 LED 형태를, 대강 만든 후, 비트 값을 검사하여 1인 경우 파란색, 0인경우 흰색으로 나타나게 해 보았습니다. 잘 작동하는 군요.


GUI 제어용 slot 코드입니다.



Arduino 소스 코드 입니다.



추가적인 것은 직접 해보시기 바랍니다.

2016년 3월 26일 토요일

pvdevelop 프로젝트 -UI 편집 방법 - Qwt 위젯

기타 Qwt 위젯들....

Qwt 위젯에 대해 한번, 스윽 보고 가죠. 상세한 설정에 대해서는 설명하지 않습니다.
사용할때 꼭 필요한 연관 함수들의 정의만 살펴보도록 하죠, 위젯 설명 중 기타 함수들이라고 되어 있는 부분은 막상 적용할려고 해보면 반응하지 않거나, 별로 의미가 없다고 본인이 판단한 함수들입니다. 지극히 본인의 판단...(다른 분들은 꼭 필요한 기능일 수 도 있지만)


그림 1. Qwt 위젯


Qwt 위젯의 경우 속성창에서 배경색을 변경하여도 변경되지 않는다.

1. QwtKnob

그림 2. QwtKnob 위젯

knob 값 설정



knob 설정값을 집어 넣습니다.

knob 기호 설정



개인적으로 knobDot가 더 보기 좋네요. 기본은 knoLine입니다.


knob 범위 설정



knob 읽기 전용 설정



기타 knob 함수
  • qwtKnobSetOrientation(p, id, orientation);
    의미 없음

  • qwtKnobSetBorderWidth(p, id, width);
  • qwtKnobSetTotalAngle(p, id, angle);
  • qwtKnobSetKnobWidth(p, id, width);
  • qwtKnobSetMass(p, id, mass);



2. QwtSlider

그림 3. QwtSlider 위젯

 Slider 범위 설정



%뭐가 잘못 됬나 범위 설정이 되지않고 무조건 0, 100의 값이 튀어나온다. 아마 작동하지 않는 기능같다.

 

Slider 방향설정



Slider 스케일 위치




Slider 배경 모양



개인적으로 SliderBgBoth가 가장 마음에 든다.



Slider 값 설정




Slider 읽기 전용 설정





기타 Slider 함수들
  • qwtSliderSetMass(p, id, mass);
  • qwtSliderSetThumbLength(p,id,length);
  • qwtSliderSetThumbWidth(p,id,width);
  • qwtSliderSetBorderWidth(p,id,width);
  • qwtSliderSetMargins(p,id,x,y);




3.QwtCounter

그림 4. QwtCounter 위젯

Counter 최소값 설정 (초기값 : 0)



Counter 최대값 설정 (초기값 : 1)



Counter 증가값 설정 (초기값 : 0.001)



Counter 초기값 설정 (초기값 : 0)




Counter 버튼 증분 설정 (우측, 좌측 화살표 1 버튼)




Counter 버튼 증분 설정 (우측, 좌측 화살표 2 버튼)




기타 QwtCouter 함수
  • qwtCounterSetStepButton3(p,id,n);
  • qwtCounterSetNumButtons(p,id,n);
  • qwtCounterSetIncSteps(p,id,button,n);




4. QwtWheel

그림 5.QwtWheel 위젯

Wheel 방향 설정



Wheel 읽기 전용 설정



Wheel 관성 설정



Wheel 회전 각도 설정 (360도(초기값) 최대)



Wheel 눈금 마크 설정



Wheel 내부 간격 지정(기본값이 좋다)




Wheel 초기값 지정 (0 ~ 100 사이)




기타 QwtWheel 함수
  • qwtWheelSetViewAngle(p,id,angle);
  • qwtWheelSetWheelWidth(p,id,width);



5. QwtThermo

그림 6. QwtThermo 위젯

QwtThermo 위젯 표시 범위 설정



QwtThemo 범위 설정


이 값은 qwtThermoSetScale()함수에서 설정한 min, max 값과 동일하거나 작은 값이어야한다. 실제 qwtThermoSetValue() 함수에서 설정한 값이 이 값 영역을 벗어나면, (작으면 맨 밑바닥에 조금, 크면 전체적으로 색상이 나타난다.)

QwtThemo 값 설정



QwtThemo 위젯, 라벨이 나타나는 방향 설정



QwtThemo 색상 설정



QwtThemo 알람 색상 설정



QwtThemo 알람 Level 설정



QwtThemo 알람 Enable 설정



QwtThemo 파이프 폭 설정




기타 qwtThermo 함수
  • qwtThermoSetBorderWidth(p,id,width);
  • qwtThermoSetMargin(p,id,margin);



6. QwtAnalogClock

그림 7. QwtAnalogClock 위젯

AnalogClock 시간 설정






Value는 초 개념입니다. (5분 30초 인 경우 330)


AnalogClock 모드 설정


기계 지침뿐 아니라 Scale (숫자)도 회전 가능합니다.

AnalogClock 테두리 두께 설정




기타 AnalogClock 다음과 같은 함수들이 있습니다. 사용방법은 잘 모르겠네요.

qwtAnalogClockSetMass(p,id,mass);
qwtAnalogClockSetReadOnly(p,id,rdonly);
qwtAnalogClockSetFrameShadow(p,id,shadow);
qwtAnalogClockShowBackground(p,id,show);
qwtAnalogClockSetWrapping(p,id,wrap);
qwtAnalogClockSetScale(p,id,maxMajIntv,maxMinIntv,step);
qwtAnalogClockSetScaleArc(p,id,min,max);
qwtAnalogClockSetOrigin(p,id,orig);
qwtAnalogClockSetNeedle(p,id,which,r1,g1,b1,r2,g2,b2,r3,g3,b3);





7. QwtDial

그림 8. QwtDial 위젯

Dial 값 설정



Dial 범위 설정



최소, 최대, interval
interval은 값 증가분인 것 같다. 1씩 증가하는 경우 1을 입력하자.

Dial 스케일 설정



maxMajIntv, maxMinIntv, step 세가지 인자가 있지만 테스트 결과 maxMajIntv 값에는 반응하지 않는 것같다.
내가 원하는 것 기준으로 한다면, maxMinintv, step 이 두가지만 사용하여도 된다.
  • maxMinIntv (부 눈금 제어)
    주 눈금 사이에 나타나는 눈금  제어
    예) 주 눈금 간격이 25일때 만약 5개의 눈금을 나타내고 싶으면 5, 10개를 나타내고 싶으면 10
    (주의)  주 눈금 값을 눈금 간격으로 나눈 값이 정수와 같이 떨어져야( 25/10 = 2.5) 제대로된 눈금이 나타난다. 떨어지지 않으면 자동으로 설정값보다 큰 수량으로 나타난다.
  • Step 주 눈금 제어
    예) 만약 qwtDialSetRange()에서 설정한 범위가 0 ~ 100, 25 간격으로 주 눈금을 나타내고 싶으면 25 를 step 값에 넣어주면된다.


Dial ReadOnly



GUI 화면에서 마우스로 값을 변경하고 싶으면 0, 기본적으로 Readonly =1 로 설정되어GUI 마우스 조작으로 변경할 수 없다.


Dial Scale Arc 설정



시계방향으로 값이 증가하며 시작 위치는 하부, 즉 하부가 0입니다. 거의 사용하지 않을 것 같다.


기타 QwtDial 관련 함수들입니다.
  • qwtDialSetMass(p,id,mass);
  • qwtDialSetFrameShadow(p,id,shadow);
  • qwtDialShowBackground(p,id,show);
  • qwtDialSetLineWidth(p,id,width);
  • qwtDialSetMode(p,id,mode);
  • qwtDialSetWrapping(p,id,wrap);
  • qwtDialSetOrigin(p,id,orig);
  • qwtDialSetNeedle(p,id,which,r1,g1,b1,r2,g2,b2,r3,g3,b3);




8. QwtCompass

그림 9. QwtCompass 위젯



QwtCompass 관련 함수들입니다.
  • qwtCompassSetSimpleCompassRose(p,id,numThorns,numThornLevels,width);
  • qwtCompassSetMass(p,id,mass);
  • qwtCompassSetReadOnly(p,id,rdonly);
  • qwtCompassSetFrameShadow(p,id,shadow);
  • qwtCompassShowBackground(p,id,show);
  • qwtCompassSetLineWidth(p,id,width);
  • qwtCompassSetMode(p,id,mode);
  • qwtCompassSetWrapping(p,id,wrap);
  • qwtCompassSetScale(p,id,maxMajIntv,maxMinIntv,step);
  • qwtCompassSetScaleArc(p,id,min,max);
  • qwtCompassSetOrigin(p,id,orig);
  • qwtCompassSetNeedle(p,id,which,r1,g1,b1,r2,g2,b2,r3,g3,b3);
  • qwtCompassSetValue(p,id,value);


이상으로 Qwt 관련 위젯에 대해 알아보았습니다.
위젯 자체에는 함수가 정의되어 있지만, 막상 적용해 보면 동작하지 않는 것 처럼 보이는 함수들이 많이 있었습니다. 하지만 기본적인 기능은 충분히 수행할 수 있습니다.

2016년 3월 24일 목요일

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

오늘은 QwtPlot 2탄으로 마커 기능에 대해 알아보죠.
우선 지난 시간에 그냥 넘어간 함수들을 살펴보죠.

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);

9개의 함수가 남아있네요... 한번 알아보겠습니다.
순서대로 만드시면 됩니다.

Marker Label 만드는 법

1. Marker 를 만듭니다.



사용 예)
  qpwInsertMarker(p, qwtplot_01, 100);

 마커도 다른 커브 개념으로 생각하면 되는 것 같습니다. 마커용으로 100을 지정하였습니다.


2. MarkerSymbol 을 지정합니다.




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

사용 예)
  qpwSetMarkerSymbol(p, id, 100, MarkerNone, RED,RED,0,0);


3. Marker를 표시할 위치를 지정합니다.



사용 예)
  qpwSetMarkerPos(p, id, 100, 25,150);


4. Maker 라벨을 정렬합니다.



align
AlignAuto
AlignLeft
AlignRight
AlignHCenter
AlignJustify
AlignHorizontal_Mask
AlignTop
AlignBottom
AlignVCenter
AlignVertical_Mask
AlignCenter

사용 예)
   qpwSetMarkerLabelAlign(p, id, 100, AlignRight);


5. Marker 라벨의 내용를 넣습니다.



사용 예)
   qpwSetMarkerLabel(p, id, 100, "Test What is");

컴파일 후 확인해 보겠습니다. 저는 100, 101 마크 두개를 넣어 주었습니다.

그림 1. 마커삽입 화면
두개 모두 잘 나왔습니다.




Marker Line 만드는 법

이건 메뉴얼에 설명이 제대로 되어 있지 않아 고생 많이 했습니다. (정확한 것이 아닐 수 도 있습니다.)

1. MakerLine을 만들자.



사용 예)
   qpwInsertLineMarker(p, id, 110, "L-Marker Test", yLeft);

% pos 가 하는 일이 무엇인지 모르겠습니다....


2. MakerLine Pen 지정



사용 예)
   qpwSetMarkerPen(p, id, 110, RED, 0);

% style 부분에 대한 자료를확인하지 못했음.


3. MarkerLine 스타일 지정



사용 예)
   qpwSetMarkerLineStyle(p, id, 110, 2);

style enum이 먹지 않습니다. 숫자로 넣어 주시기 바랍니다.
NoLine = 0; HLine =1; VLine =2; Cross=3


4. MarkerLine 위치 지정



사용 예)
  qpwSetMarkerPos(p, id, 110, 60,120);



그림 2. marker Line- VLine
그림 2와 같이 현재 지정한 위치 기준으로 , 수직 라인(적색)이 만들어 졌습니다. 수직라인의 위치는 qpwSetMarkerPos() 에서 x 위치로 지정한 60입니다.

그림 3. marker Line - HLine
qwpSetMarkerLineStyle() 함수의 스타일을 수평라인(HLine) 으로 변경하였습니다. 수평라인의 위치는 qpwSetMarkerPos()에서 y 위치로 지정한 120 입니다.

그림 4.marker Line - Cross Line

qwpSetMarkerLineStyle() 함수의 스타일을 Cross라인으로 변경하였습니다. 수평, 수직라인이 그려지는 위치는 qpwSetMarkerPos()에서 y 위치로 지정한 60, 120 입니다.



한가지 더, qpwSetMarkerLabelAlign() 함수를 이용한 지정한 위치 기준 mark 텍스트를 정렬할 수 있습니다.



사용 예)
   qpwSetMarkerLabelAlign(p, id, 110, AlignRight);

그림 5. 라벨 문자열 정렬

"L-Maker Test" 문자열의 위치를 그림5와 비교하여 보십시오. 기본값이 중앙정렬에서 오른쪽 정렬으로 변경되었습니다.


최정적인 qwtPlotInit() 함수 입니다. 필요하신 분 참조하시기 바랍니다.





그림 6.QwtPlot 위젯 영역 마우스 이벤트

캔버스 영역에서 왼쪽 마우스 버튼을 누르면 PLOT_MOUSE_PRESSED, PLOT_MOUSE_RELEASED 이벤트가 발생하면서 x, y 좌표가 나오네요. 이 이벤트를 이용하면 마커위치를 옮길 수 있을 것 같습니다.

한번 해 보겠습니다.

그림 7. 수직 마커 라인

QwtPlot 위젯 영역에서 마우스 왼쪽버튼을 눌렀을 때 발생하는 PLOT_MOUSE_PRESSED 이벤트를 처리하는 slot은 slotMousePressedEvent() 에서 처리됩니다. 이 함수로 넘어오는 실수형 x, y 값을 이용하여 Marker 라인을 움직이고, GUI 화면 좌측, 하단에 있는 label 위젯에 d->y1 배열에 저당된 값을 표시하도록 하였습니다.
마우스 버튼을 눌렀을 때 Markerline이 그려지게 하기 위해 qpwReplot()함수를 MousePressedEvent 함수에 같이 넣어주었습니다. nullEvent에서 처리하면 반응이 조금 느리네요.
입력된 코드는 다음과 같습니다.




 특이한 내용이 없으니,... 아, 주의해야 할 점이 있습니다.
이벤트로 넘어오는 실수형 x, y값의 QwtPlot 위젯 전체 영역에서 발생합니다. 즉 그래프가 그려지는 캔버스 영역이외에서도 발생합니다. 그렇게 큰 문제는 아니지만 캔버스 밖을 마우스로 누르면 Marker라인이 사라지는 군요..
x, y의 기준점, 즉 0,0 은 캔버스 좌측, 하단입니다. 만약 캔버스 영역 안에서 만 markerline이 반으하도록 하려면 코드를 수정하여야 합니다. 즉접 해 보시기 바랍니다.


마커에 표시되는 문자의 폰트 특성을 변경해주는 함수가 있네요... 하지만, 이 함수는 어떻게 사용해야 하나.... 짱구가 아프네... 뭐가 잘못 되었는지 모르겠지만 제대로 반응하지 않네요.

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(), 너는 뭐하는 놈이냐????