2011-05-08 4 views
7

Qt에서 큰 이미지 (2GB +)를 여는 이미지 비주얼라이저를 만들고 있습니다. 큰 이미지를 512X512의 여러 타일로 나누면됩니다. 그런 다음 원본 이미지 크기의 QGraphicsScene을로드하고 addPixmap을 사용하여 각 타일을 QGraphic Scene에 추가합니다. 궁극적으로 최종 사용자에게 거대한 이미지처럼 보입니다. 실제로 작은 이미지가 연속적으로 배열되어있어 장면에 함께 붙어 있습니다. 우선이 방법이 좋은 방법입니까?QGraphicsScene 및 QGraphicsView로 타일링

모든 타일을 장면에로드하려고하면 많은 양의 메모리가 사용됩니다. 그래서 볼 수있는 타일 만로드 할 생각입니다. 나는 이미 QGraphicsScene을 서브 클래스 화하고 그것의 드래그 이벤트를 오버라이드 (override)함으로써, 어떤 타일이 움직임에 기반하여 다음에로드 될 필요가 있는지를 알 수있게했다. 내 문제는 스크롤바에서 움직임을 추적하는 것입니다. 스크롤바가 움직일 때마다 호출되는 이벤트를 만들 수있는 방법이 있습니까? 옵션이 아닌 QGraphicsView 서브 클래 싱.

대신로드 및 추가 픽스맵의

, 픽스맵을 포장 클래스를 추가 한 경우에만로드 :

답변

7

QGraphicsScene이 보이지 않는 것을 렌더링 않을만큼 똑똑하다, 그래서 여기에 당신이해야 할 무엇 그들은 먼저 렌더링됩니다. (컴퓨터 과학자들은이를 "프록시 패턴"이라고 부른다.) 그런 다음 타이머를 기반으로 픽스맵을 언로드 할 수 있습니다. 너무 빨리 언로드되면 투명하게 다시로드됩니다. 현재 렌더링 레벨의 프록시 경로를 알 수 있으므로 더 작은 해상도의 이미지가로드 될 때로드됩니다.


편집 : 시작하려면 몇 가지 코드가 있습니다.

(I도이 컴파일, 그렇게하지 않은 "주의를 : 그건 당신이 하위 클래스로 원하는, 그래서 QGraphicsScene가 (당신이 ::addPixmap를 호출하는 경우,이 장면 뒤에 ...GraphicsItem로 변환 것)하는 QGraphicsItem이다립니다 그 모든 것을 참고 강사 "하지만 옳은 일을하고있어, 당신은 절대적으로 QGraphicsView로보기를 필요로하지 않는

class MyPixmap: public QGraphicsItem{ 
    public: 
     // make sure to set `item` to NULL in the constructor 
     MyPixmap() 
      : QGraphicsItem(...), item(NULL){ 
     } 

     // you will need to add a destructor 
     // (and probably a copy constructor and assignment operator) 

     QRectF boundingRect() const{ 
      // return the size 
      return QRectF(...); 
     } 

     void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, 
        QWidget *widget){ 
      if(NULL == item){ 
       // load item: 
       item = new QGraphicsPixmapItem(...); 
      } 
      item->paint(painter, option, widget); 
     } 

    private: 
     // you'll probably want to store information about where you're 
     // going to load the pixmap from, too 

     QGraphicsPixmapItem *item; 
}; 

는 당신은 QGraphicsScene::addItem(...)

+0

아주 좋습니다. 지옥에서 많은 일을 구할 수 있습니다. 나는 이것이 스크롤하는 동안 천천히 또는 말더듬을 것이지만 궁금해하고 있었다. 원래 계획했던 것은 마우스와 스크롤바 이벤트를 사용하여 사용자가보고있는 것을 추적하고 주변 타일을로드하여 별도의 스레드를 사용하여 미리 장면에 배치하는 것이 었습니다. 사용자가 장면을 따라 이동함에 따라 배경 스레드는 단순히 타일을로드하고 언로드합니다. QGraphicsItem 페인트가 이미 백그라운드에서이를 수행합니까? –

+1

별도의 스레드에서로드하려는 경우 페인트 이벤트에서 이미지를로드하는 대신 작업자 스레드로 신호를 보내야하며 페인트가 호출 될 때마다 스레드에서로드 할 때까지 어떤 종류의 빈 이미지를 그립니다 . 타일이 실제로 보이기 전에'boundingRect()'가 페인트 이벤트를 트리거하기 위해 실제 크기보다 큰 값을 반환 할 수 있습니다. 그러나 조심하지 않으면 다른 부작용이있을 수 있습니다. – James

+0

큰 도움을 주셔서 감사합니다. 거의 완료된 타일로드가 있습니다. 이제 타일을 내리는 가장 좋은 방법은 무엇입니까? 이전 게시물에서 이해 한 것부터, 장면의 모든 PixmapItem을 파괴하는 타이머를 실행하고 몇 초마다 NULL로 설정하는 것이 좋습니다. 그리고 페인트 메소드가 모든 업데이트에 대해 호출되기 때문에 현재보고있는 타일을 자동으로 복원합니다. 내가 맞습니까? –

3

를 사용하여 QGraphicsScene에 픽스맵을 추가 할 수 있습니다) (예를 들면 다른 물건을 올려 때문에 큰 배경 픽스맵 위에) QAbstractScrollArea을 서브 클래 싱하고 scrollContentsBy()paintEvent()을 다시 구현하는 것이 좋습니다.

pixmaps의 LRU 캐시 (영감을 얻기 위해 QPixmapCache 참조)를 추가하고 paintEvent() 풀을 사용하여 픽스맵을 앞쪽으로 당기고 설정합니다. 이것은 QGraphicsItem보다 더 많은 일 같은 소리가 나는 경우에

이, 날 믿어, 그것은 아니에요 :) 답변이 이미 선택되어

+0

미래의 개발 관점에서, 나는 어떤 시점에서 래스터 데이터 위에 벡터 오버레이를 추가해야 할 것이다. 그래서 선과 다각형을 그릴 때 모양 그리기 기능이 많이 필요할 것입니다. QGraphicsView는 많은 자료를 사용할 수있는 것 같습니다. 그래서 저는 지금 QGraphicsView를 고수하고 있습니다 –

4

있지만, 나는 내 의견을 표현하고 싶습니다.

특히 타이머 사용으로 인해 선택한 대답이 마음에 들지 않습니다. 픽스맵을 언로드하는 타이머? 사용자가 실제로 이미지를 잘보고 싶다고 말하면 몇 초 후에 이미지가 언로드되고 이미지가 다시 표시 될 수 있도록 무언가를해야합니다. 아니면 또 다른 몇 초 후에 픽스맵을로드하는 또 다른 타이머를 넣을 수 있습니까? 또는 수천 개의 항목이 표시되어 있는지 확인합니다. 이것은 매우 자극적이며 잘못 될뿐만 아니라 프로그램이 항상 리소스를 사용하게 될 것입니다.사용자가 프로그램을 최소화하고 영화를 재생한다고 가정하면, 그는 몇 초마다 내 영화가 얼어 붙는 이유를 궁금해 할 것입니다 ...

글쎄, 타이머를 사용하는 제안 된 아이디어를 오해 한 경우.

실제로 mmutz가 제안한 아이디어가 더 좋습니다. 그것은 Mandelbrot example을 생각 나게했습니다. 그것을 한번보세요. 그릴 내용을 계산하는 대신이 부분을 다시 표시해야하는 부분을 이미지에로드 할 수 있습니다.

내가 훨씬 간단한 방법으로를 QGraphicsView를 사용하여 다른 솔루션을 제시 할 것이다 결론적으로

:

1) (사용 이미지를로드하지 않고 QImageReader를 이미지의 크기를 확인)

2) 장면의 크기가 동일하기를

3) Pixmap 항목을 사용하는 대신 DrawBackground() 함수를 다시 구현하십시오. 매개 변수 중 하나가 새로운 노출 된 사각형을 제공합니다. 즉, 사용자가 조금만 스크롤하면이 새 부분 만로드하고 그립니다 (이미지의 일부만로드하려면 setClipRect() 및 read() 메서드 사용) QImageReader 클래스). 일부 변환이있는 경우 다른 매개 변수 (QPainter)에서 가져 와서 이미지를 그리기 전에 이미지에 적용 할 수 있습니다.

제 생각에는 최상의 솔루션은 내 솔루션을 Mandelbrot example에 표시된 스레드와 결합하는 것입니다.

내가 지금 생각할 수있는 유일한 문제는 사용자가 큰 스케일 요소로 축소하는 경우입니다. 그런 다음 거대한 이미지를로드하고 크기를 조정하려면 얼마 동안 많은 리소스가 필요합니다. 이제 QImageReader의 일부 기능이 아직 있음을 알았습니다. setScaledSize()는 아마도 우리가 필요로하는 것만 할 수 있습니다 - 크기를 설정하고 이미지를로드하면 처음에는로드되지 않습니다. 전체 이미지 - 시도해보십시오. 또 다른 방법은 축척 계수를 제한하는 것입니다. 픽스맵 항목을 사용하는 방법을 고수하면 어쨌든해야 할 일입니다.

희망이 도움이됩니다.

+0

QGraphicsItem을 서브 클래 싱하고 페인트 오버로딩을 실제로 제안한 Autopulated의 제안은 정말 잘되었습니다. 빈 QGraphicsItem을 붙여 넣을 수 있다는 의미입니다. 이것은 많은 기억을 차지해서는 안됩니다. QGraphicsItem의 paint 메서드는 뷰 내부에서 볼 때만 호출됩니다. 그래서 실제 이미지 데이터가 뷰에 표시 될 때 객체로만로드되도록 구현했습니다. 로딩 타일은 꽤 많은 도움을줍니다. 이제 유일한 문제는 사용되지 않은 타일을 언로드하는 것입니다. –

+0

몇 가지 접근법을 고려한 후에 타일에 큐 접근법을 사용할 것입니다. 새 타일을 가져 와서 오래된 타일 (FIFO)을 삭제하는 일정한 크기의 큐를 유지합니다. 이것은 타이머를 사용하는 것보다 훨씬 낫습니다. 어떻게 생각해? –

+0

그리고 저는 조기에 확대/축소 문제를 고려했습니다. 내가 계획 한 것은 몇 가지 확대/축소 레벨 (타일 레벨 0 -> 100 % 레벨 1 -> 50 % 등)의 타일을 생성하는 것입니다. 사용자가 50-100 % 확대/축소하는 동안 QGraphicsView에서 setScale 함수를 사용합니다 (QAbstractScrollArea에서 QGraphicsView를 사용하는 또 다른 이점). 사용자가 25-50 % 확대/축소 수준에 들어가면 새로운 타일 세트가로드되고 (레벨 1 타일) 배율이 다시 1로 설정됩니다. 짧은 피라미드로 해결해야합니다. 또한 QGraphicsItem의 페인트를 오버라이드하면 피라미드 모양의 확대/축소를 완벽하게 통합 할 수 있습니다. –

관련 문제