Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Load multiple pages (pixmaps) into a QScrollArea: how to gain efficiency?

Load multiple pages (pixmaps) into a QScrollArea: how to gain efficiency?

Scheduled Pinned Locked Moved General and Desktop
12 Posts 3 Posters 7.2k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • J Offline
    J Offline
    jesuisbenjamin
    wrote on last edited by
    #1

    Hello,

    I am using python-poppler-qt4 to create a PDF viewer. I've looked at the "QT Quarterly":http://doc.qt.nokia.com/qq/qq27-poppler.html's example, though it seems the viewer shows one page at a time (I can't follow C).

    I am currently loading an entire document, each pixmap rendered from pages onto QLabel, all labels stacked vertically on a QFrame. It works OK, but when I implement scaling, I need to regenerate the entire document's pixmaps, which takes more than a second for about 16 pages. That's, efficiently speaking, not acceptable.

    So I am now assuming the right thing to do is to load the pixmaps I need only, i.e. the current page's, the pervious page's and the next page's. It looks good in my head, but I have no idea as to how I should proceed programmatically. How to determine when the next page + 1 should be loaded and the previous page - 1 should be erased? Could you provide some guidance? Thanks.

    1 Reply Last reply
    0
    • M Offline
      M Offline
      mkuettler
      wrote on last edited by
      #2

      I assume you have a QScrollBar somewhere; you could use it to find out which part of the document you are currently viewing. Based on that you could decide which pages you want to have loaded.

      But I think using QFrame and QLabel for this is not optimal. I recommend using a QGraphicsScene, with a QGraphicsPixmapItem for each page. This approach should be much more flexible, though probably a bit more tricky to set up. It is also easier to implement zooming, as you can use QGraphicsView::scale to see the whole scene larger. You'd still have to re-render the pages that are actually visible, but the QGraphicsView makes it easy to grab all the items that are currently visible. And you would not have to worry about enlarging the pixmaps that you do not want to re-render (if you did not enlarge these in your QLabels, then the scrollbar space would not correspond to pages in a linear fashion anymore).

      1 Reply Last reply
      0
      • J Offline
        J Offline
        jesuisbenjamin
        wrote on last edited by
        #3

        Right. So if I understand the QGraphicsScene replaces my QFrame and the QGraphicsView the QScrollArea? In that case, how do I set the layout of my pages (i.e. aligned vertically). And what is the qreal I am supposed to pass into the QGraphicsView.Scale() (can't make out from the doc)?

        About your last note on the scollbar space: I currently resize the blank QLabels so as to keep up with the scaled size. So the scrollbar space not an issue.

        1 Reply Last reply
        0
        • M Offline
          M Offline
          mkuettler
          wrote on last edited by
          #4

          I'm sorry to answer so late.
          The QGraphicsScene is not a widget, it is just a management class for the items (and it can have more than one QGraphicsView associated to it). So maybe it replaces your current QFrame in some regards, but in terms of actual widgets the QGraphcisView is all you would have.

          The argument to QGraphicsView::scale is the scaling factor: 1 means no change at all, a value greater than one means zooming in, and a value smaller than one means zooming out.

          For the layout of the pages you have two options: you could do it manualy, using QGraphicsItem::setPos. That is rather strait forward, but you have to re-position the items when they change their size, and it is your responsibility that they don't overlap. The second option is to use a root item with a QGraphicsLinearLayout (see http://doc.qt.nokia.com/4.7-snapshot/qgraphicslinearlayout.html#details for a simple example). The problem with this solution is that the QGraphicsPixmapItem does not inherit QGraphicsLayoutItem, which it has to do in order to be used in a layout. So for that to work you would have to inherit from both of these classes, and implement functions like sizeHint and setGeometry (http://doc.qt.nokia.com/4.7-snapshot/qgraphicslayoutitem.html gives a few hints what you should do).

          I hope this helps a little. If you have another question I will try to answer more quickly next time.

          1 Reply Last reply
          0
          • J Offline
            J Offline
            jesuisbenjamin
            wrote on last edited by
            #5

            Thanks mkuettler. You have no obligation, as far as I am concerned, to answer promptly. Moreover your lagging made me work harder on my own solution. For now I stuck to the QFrame in a QScrollArea and I can find out on each moveEvent() or resizeEvent() whether a child page of the QFrame has become visible but checking their visibleRegion().isEmpty(). If those happen to be visible, I load the pixmaps into them.

            It works pretty well, it's not super smooth, but that's probably because I load Pixmaps at a 400dpi and allow them to stretch on scaling pages so I don't have to worry about them once they're loaded. I should probably be able to make the process a lot smoother by passing the rendering into a thread.

            The QGraphicsView remains a interesting alternative though.

            Thanks.

            1 Reply Last reply
            0
            • M Offline
              M Offline
              mkuettler
              wrote on last edited by
              #6

              It's good to hear that you have a working solution.
              I think it might be a good idea if you would not only render the images that are visible, but also the next and previous. That way you could avoid lags when you scroll through the document slowly. But that would only make any sense if rendering was done in a separate thread. Otherwise scrolling to a new page always makes the user wait for a new page to be rendered, and there would be no gain by having the actually visible page rendered already.

              1 Reply Last reply
              0
              • J Offline
                J Offline
                jesuisbenjamin
                wrote on last edited by
                #7

                Hi mkuettler,

                I think I've reached the end point of my let's-do-it-with-qscrollarea-anyway option. Loading images still is somehow delaying scrolling and zomming is really ugly.

                [quote author="mkuettler" date="1337351188"]
                The argument to QGraphicsView::scale is the scaling factor: 1 means no change at all, a value greater than one means zooming in, and a value smaller than one means zooming out.[/quote]

                That sounds appealing now, since there seems to be little to do about zooming in QScrollArea.

                [quote author="mkuettler" date="1337351188"]
                For the layout of the pages you have two options ... the second option is to use a root item with a QGraphicsLinearLayout (see http://doc.qt.nokia.com/4.7-snapshot/qgraphicslinearlayout.html#details for a simple example). The problem with this solution is that the QGraphicsPixmapItem does not inherit QGraphicsLayoutItem, which it has to do in order to be used in a layout. So for that to work you would have to inherit from both of these classes, and implement functions like sizeHint and setGeometry (http://doc.qt.nokia.com/4.7-snapshot/qgraphicslayoutitem.html gives a few hints what you should do).[/quote]

                Could I also put my QPixmap into a QLabel? QLabel would have the QLayoutItem's functions right?

                Also you mentioned
                [quote author="mkuettler" date="1337419605"] ... you could avoid lags when you scroll through the document slowly ... if rendering was done in a separate thread. Otherwise scrolling to a new page always makes the user wait for a new page to be rendered...
                [/quote]

                Indeed. However I tried to implement the loading of the PDF image into the Document widget through a thread, but that worked very poorly. First Qt does not seem to like pixmap handling outside of the main thread. Second I've manage to limit the thread's activity to QImage = Poppler.Page.renderToImage() which is the most time-consumming step of the image rendering, but that didn't go too well either: pages were rendered erroneously, with characters overlapping one another, returning error messages as if the method could not read the source image, or simply crashing the application.

                So I am going to look into the QGraphicsScene and QGraphicsView, and I hope rendering a zoom effect will look smooth and proper this time. And if you have any suggestion on the thread part, they'll be more than welcome. Thanks.

                1 Reply Last reply
                0
                • M Offline
                  M Offline
                  mkuettler
                  wrote on last edited by
                  #8

                  Hi jesuisbenjamin.

                  [quote author="jesuisbenjamin" date="1337928915"]
                  Could I also put my QPixmap into a QLabel? QLabel would have the QLayoutItem's functions right?
                  [/quote]

                  In order to use normal QWidgets in the GraphicsScene you have to use a QGraphicsProxyWidget (because every item in the scene must be a QGraphicsItem). Using that you can insert QLabels, and they should be correctly managed by layouts.

                  But using QGraphicsPixmapItems with manual placement would be very easy as well, because in your application you would not have to reposition them at all. The images do not have to change their size (only their resolution), because zooming can be done independently from the scene.

                  I'm sorry to hear that the thread did not worked properly. From what you wrote don't know what might have been the problem, but I would gladly look at the code. I'm not very experienced with threads, but this should be a simple usage, and I would very much like to get it working (if only to prove myself that I do know something about threads).

                  1 Reply Last reply
                  0
                  • J Offline
                    J Offline
                    jesuisbenjamin
                    wrote on last edited by
                    #9

                    Thanks! Implementing the QGraphicsView.scale() worked perfectly! I need to readjust the code and then look into loading images with a thread. I'll definitely get back to you on this.

                    1 Reply Last reply
                    0
                    • J Offline
                      J Offline
                      jesuisbenjamin
                      wrote on last edited by
                      #10

                      [quote author="mkuettler" date="1337937262"]
                      In order to use normal QWidgets in the GraphicsScene you have to use a QGraphicsProxyWidget (because every item in the scene must be a QGraphicsItem). Using that you can insert QLabels, and they should be correctly managed by layouts.
                      [/quote]

                      Hi there mkuettler,

                      I've followed your advice, using the QGraphicsProxyWidgets in pair with QLabels, as containers for the QPixmaps. The display works well and the zoom is smooth.

                      I encounter a problem though as I want to load images progressively. In QScrollArea I looked for visible pages by checking for each page whether its visbileRegion().isEmpty() or not. In the case of QGraphicsView I do not seem to be able to use this. All QLabels' visibleRegion() have the same QRect and are visible according to what the function returns. That is, however, not true, since only page 0 is in fact visible in the View. So what does it say about how the QGraphicsProxyWidget works?

                      Also in your first reply, you said:

                      [quote]
                      ... QGraphicsView makes it easy to grab all the items that are currently visible.
                      [/quote]

                      I trust you know what you were saying :) Yet I could not find any relevant method in the QGraphicsView's member list. How to see if the QGraphicsProxyWidget or its content, QLabel or QPixmap are indeed visible in the viewer?

                      Thanks,
                      Benjamin

                      1 Reply Last reply
                      0
                      • M Offline
                        M Offline
                        mkuettler
                        wrote on last edited by
                        #11

                        Hi Banjamin,

                        [quote author="jesuisbenjamin" date="1338110804"]
                        All QLabels' visibleRegion() have the same QRect and are visible according to what the function returns. [/quote]

                        I am not sure what causes this, but I could imagine that the QGraphicsProxyWidget considers everything visible, because it can not know (or does not check) whether there is a QGraphicsView seeing it. The QLabels just see the space provided to them by the QGraphicsProxyWidget, so they all return the same rect, because every rect is relative to its own QGraphicsProxyWidget.

                        I did not try this, but I thought about something like the following (with view denoting your QGraphicsView*):
                        @
                        view->items( QRect( QPoint(0,0), view->viewport()->size() ), Qt::IntersectsItemBoundingRect );
                        @

                        This returns a QList<QGraphicsItem*> containing all the items that intersect with the viewport rect of the QGraphicsView. The last argument is probably not necessary (the default is Qt::IntersectsItemShape), but it might improve speed (and all your items are rectangular anyway).

                        Please let me know whether this works,
                        Martin

                        1 Reply Last reply
                        0
                        • F Offline
                          F Offline
                          fredrossperry
                          wrote on last edited by
                          #12

                          I hAve a related question. When you zoom, are you leaving the scroll position alone? I am working on a similar app. When zoom happens, I scale up the visible pixmaps and then regenerate them at higher resolution. This works fine.

                          I'm now attempting to adjust the scroll position to be approximately the same as it was before the zoom, relatively speaking. But I still get a moment where I see the zoomed image at its not-final position, after I've done the scaling but before I've moved the scroll position.

                          1 Reply Last reply
                          0

                          • Login

                          • Login or register to search.
                          • First post
                            Last post
                          0
                          • Categories
                          • Recent
                          • Tags
                          • Popular
                          • Users
                          • Groups
                          • Search
                          • Get Qt Extensions
                          • Unsolved