2009. 11. 14. 22:41
보통 책에서 나오는 다이얼로그 하나 나오고 닫기 해야만 넘어가는 히스토그램에서 벋어나고 싶다면?

왜 저자들이 일부로 그리했을까?

이유는
1. 닫기를 않하고 넘어가려면 모달리스 다이얼 로그를 만들어야 한다.
2. 그림 마다 다른 히스토그램을 그려야 할껀데 이거 처리하기 귀찮다.

결국 모든건 귀차니즘 때문이다.

자세히 들어가도록 하자.

다이얼로그는 2가지가 있다.
모달
모달리스

한넘은 모든 자원을 다 차지하고 닫기를 눌러야 넘어가 지는 모달
다른 하나는 다른 MDI 윈도우와 공존하는 모달리스 다이얼로그이다.

모달 다이얼로그의 코드는 흔히 많이 봤을꺼다.

// 간단하긔
dlg.DoModal() == IDOK

값을 받아올일 있을 때, Doc쪽에 잘 연결 시켜서 위에 한줄이면 된다.

그런데 모달의 경우는 하나가 다 자원 차지하고 지혼자 살다가 죽으면 되니 문제가 없지만
공존하려면?
함수 하나에서 저넘 수명이 끝나면 안되것지? 뒷처리도 해야하고

그렇기에 수명이 꽤 (아마 프로그램만큼) 긴 곳에 넣어두고 다른 MDI가 뒤에서 하고 있을 일을
우리가 직접 해주어야 한다.

진접 MDI 흉내는 귀찮으므로 우선 1개만 있다고 하자.

우선 모달리스 다이얼로그에 대해 간략히 3가지만 알아가자

1. 어딘가 선언하고
CWinApphitogramDlg *histodlg; // 그냥 늘 만드는 다이얼로그

2. new로 생성후 Create를 하자
this->histodlg = new hitogramDlg;
histodlg->Create(IDD_Histogram); //IDD_Histogram 늘 만드는 히스토 그램

3. 어디선가 종료전 이넘을 지우자
delete histodlg;



본인은

App에
//class CmyCVApp - 아마 여기 말고 MainFrame에 넣는게 옳지 않았을까 한다.
CWinApphitogramDlg *histodlg; // 그냥 늘 만드는 다이얼로그

int CmyCVApp::ExitInstance() // 속성에서 이 함수를 찾아서 더블클릭하면 알아서 만들고 연결해주니 편하다
{
delete histodlg;
}

BOOL CmyCVApp::InitInstance() // 이넘은 보통 생성되어 잇다.
{
    this->histodlg = new hitogramDlg;
    histodlg->Create(IDD_Histogram);
}

아래 처럼 했다.

이제 실제 Hitogram의 동작을 살펴보자.

// 단순한 외부 호출용 public DO함수
int CmyCVApp::ReNewHistogram(void)
{
    histodlg->ReNewHistogram();
    return 0;
}

int CmyCVApp::ShowHistowindow(void)
{
// 모달리스 다이얼로그는 아래 명령 1줄로 그릴수 있다.
    histodlg->ShowWindow(SW_SHOW);
    return 0;
}

// class CmyCVDoc 의 적절한 함수에 (Histogram을 뛰우는 함수 안이면 적절하겠져?)
CmyCVApp* pwinapp = static_cast<CmyCVApp*>(AfxGetApp());
pwinapp->ShowHistowindow();    

라고 두어 Hitogram 메뉴를 딱 누르면 히스토 그램이 뜨도록 했다.

이제 Hitogram의 그림은 어케 그릴까?


ReNewHistogram()
MDI에서 그림 창이 바뀜에 따라 히스토그램을 갱신하도록 했다.
그러한 작업은 View에서 속성에서 OnActiveView에 아래처럼 하게 하면 된다.
void CmyCVView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
{
    // TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
    CmyCVApp* pwinapp = static_cast<CmyCVApp*>(AfxGetApp());
    pwinapp->ReNewHistogram(); // MDI가 클릭되면 histogram renew함수를 호출하세여
    CScrollView::OnActivateView(bActivate, pActivateView, pDeactiveView); // MS 코드
}
//좀더 좋게 만들려면 그림 변화를 검사하도록 하거나(얼마안되는 계산량 절감)
//Doc에 Histogram을 만들어 놓고 알아서 갱신 하도록 하면 되것지?

ReNewHistogram 함수는
ActiveFrame의 Doc에 이미지를 찾아야하므로 몇단계를 밝아야하고
Doc가 만들어 질때도 호출되므로, 그림이 불려와질땐 호출되면 안되니 Null 검사를 한다.


// 속성에서 WM_PAINT 를 더블클릭하면  이 함수를 만들어 준다.
// WM_PAINT 즉 그림 다시 그릴때 오면 항상 호출
OnPaint()
단순히 어디서나 나오는 히스토그램 그리는 방법을 구현하거다

이러니 모달리스로 구현하지 않는다.
귀찮거든?






int hitogramDlg::ReNewHistogram(void)
{   
    CMainFrame *pFrame = (CMainFrame *)AfxGetMainWnd();
    CChildFrame *pChild = (CChildFrame *)pFrame->GetActiveFrame();
    CmyCVDoc *pDoc = (CmyCVDoc *)pChild->GetActiveDocument();
    if (pDoc == NULL) return -1;
    const BYTE * const * pImg = pDoc->MyImg.getImg2DPointer();
    const unsigned int sizeX = pDoc->MyImg.getX();
    const unsigned int sizeY = pDoc->MyImg.getY();
    // Set histogram
    memset(&histoImg, 0, 256*sizeof(float));

    for(int y =0; y < sizeY; y++)
    {
        for(int x =0; x < sizeX; x++)
        {
            int idx = pImg[y][x];
            // add one number
            histoImg[idx]++;
        }
    }

    // normalize
    int maxvalue = 0;
    for( int x = 0; x < 256; x++)
        maxvalue  = (maxvalue > histoImg[x]) ? maxvalue : histoImg[x];
    for( int x = 0; x < 256; x++)
        histoImg[x] /= maxvalue;

    InvalidateRect(NULL);
    return 0;
}


void hitogramDlg::OnPaint()
{
    CPaintDC dc(this); // device context for painting
    // TODO: 여기에 메시지 처리기 코드를 추가합니다.
    // 그리기 메시지에 대해서는 CDialog::OnPaint()을(를) 호출하지 마십시오.
   
    int nWidth = 1;
    COLORREF  m_color = RGB(255,0,0);
    CPen myPen(PS_SOLID, nWidth, m_color);
    CPen *pOldPen = dc.SelectObject(&myPen);

    // Draw histogram on dialog
    CRect rect;
    GetClientRect(&rect);
   
    int height = rect.Height() - 5;
    int width = rect.Width() - 5;

    for(int x =0; x < 256; x++)
    {       
        dc.MoveTo( int(x* width /256)+2, height - 5);
        dc.LineTo( int(x* width /256)+2, height*(1-histoImg[x]) -5);
    }
}

Note:

onActivateFrame()
onActivateView()
View는 MDI를 클릭할때마다 호출된다
Frame은 MDI를 클릭해도 호출이 안된다.

Posted by newpolaris