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. Qt Display Table View with large number image
Forum Updated to NodeBB v4.3 + New Features

Qt Display Table View with large number image

Scheduled Pinned Locked Moved Unsolved General and Desktop
12 Posts 4 Posters 3.3k Views 3 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.
  • H Offline
    H Offline
    haris123
    wrote on 5 Mar 2016, 08:34 last edited by
    #1

    Hi,

    I need to display large amount of images in table View sometimes more than 10000, and loading altogether in to the model causes my application to use lot of RAM, and I found that I need to implement a custom model and display images only when the user scroll to that area, like unload top images when scroll down and vise-versa.

    Is it possible to do with QTableView and custom model?
    I already read the Qt model/view documentation, but couldn't understand much from there to implement it.

    1 Reply Last reply
    0
    • C Offline
      C Offline
      Chris Kawa
      Lifetime Qt Champion
      wrote on 5 Mar 2016, 08:51 last edited by Chris Kawa 3 May 2016, 08:51
      #2

      Yeah, holding 10000 images in ram is definitely not a good idea. It's basically a caching problem.
      There are couple of strategies you could take. Out of top of my head two come to mind (but there are many others I'm sure):

      • Use a QCache. The cost of the item can be for example image size. You can set a maximum size allowed. Then in your model override data() member and return your image loading it from the cache, and if it's not already there load it from disk into the cache first.

      • Create your own cache, for example a QMap<QPoint, QImage>, where QPoint part is the cell index in the table . Connect to the valueChanged signal of the scrollbars. In the handler load images that are within the visible area into the map and unload any that are not. You can keep in the map a little around the visible area to prevent images popping on the borders.

      In both cases you can load the images in a separate thread and emit dataChanged signal from the model when they are in, to avoid gui freezes when scrolling.

      M 1 Reply Last reply 5 Mar 2016, 09:04
      1
      • C Chris Kawa
        5 Mar 2016, 08:51

        Yeah, holding 10000 images in ram is definitely not a good idea. It's basically a caching problem.
        There are couple of strategies you could take. Out of top of my head two come to mind (but there are many others I'm sure):

        • Use a QCache. The cost of the item can be for example image size. You can set a maximum size allowed. Then in your model override data() member and return your image loading it from the cache, and if it's not already there load it from disk into the cache first.

        • Create your own cache, for example a QMap<QPoint, QImage>, where QPoint part is the cell index in the table . Connect to the valueChanged signal of the scrollbars. In the handler load images that are within the visible area into the map and unload any that are not. You can keep in the map a little around the visible area to prevent images popping on the borders.

        In both cases you can load the images in a separate thread and emit dataChanged signal from the model when they are in, to avoid gui freezes when scrolling.

        M Offline
        M Offline
        mrjj
        Lifetime Qt Champion
        wrote on 5 Mar 2016, 09:04 last edited by
        #3

        @Chris-Kawa
        hi
        Say he makes a custom model like
        http://www.informit.com/articles/article.aspx?p=1405547&seqNum=3

        Will it then automatically make only the visible items loaded?

        I understand it will ask via data() but Im wondering how
        I can know when to unload the images
        that has been scrolled past. ?

        1 Reply Last reply
        0
        • C Offline
          C Offline
          Chris Kawa
          Lifetime Qt Champion
          wrote on 5 Mar 2016, 09:13 last edited by
          #4

          If the image is loaded in the data() member then yes, it will be loaded only when needed (becomes visible). It does not handle unloading in any way as there is no model method called for cells that go out of view.

          You could of course subclass the view and implement something like this. On scroll see which cells went out of view and call data() with some custom role like UnloadRole to let model know it should unload the data. It's a slight variation of the second method I proposed.

          1 Reply Last reply
          0
          • M Offline
            M Offline
            mrjj
            Lifetime Qt Champion
            wrote on 5 Mar 2016, 09:23 last edited by
            #5

            Ok. so a custom model will help

            but if u scroll all the way down to the end, it will still
            have loaded all images ?

            So for a perfect "sliding frame" of loaded images, he will need a custom view too ? (using your UnloadRole method)

            Sorry for asking so much but I find this task interesting.

            1 Reply Last reply
            0
            • C Offline
              C Offline
              Chris Kawa
              Lifetime Qt Champion
              wrote on 5 Mar 2016, 09:31 last edited by
              #6

              but if u scroll all the way down to the end, it will still have loaded all images ?

              Yes, but at the same time it will unload the ones you scrolled past so there's only gonna be a small amount of images loaded at any given time.
              Of course, if you scroll really fast you will kill this model and your ui will be choppy as hell. That's why I suggested to offload the main thread for this. Also, another optimization could be to suspend any loading while the scrollbar is being dragged, but that's further down the road.

              Sorry for asking so much but I find this task interesting.

              Sure, no problem. It's one of those topics that seem simple and obvious at first glance but are getting really complicated really fast when you start to inspect the details.

              K 1 Reply Last reply 5 Mar 2016, 10:30
              1
              • M Offline
                M Offline
                mrjj
                Lifetime Qt Champion
                wrote on 5 Mar 2016, 09:37 last edited by
                #7

                ok so custom model is the way to go.
                Depending on his scrolling requirements, it might just work.

                It is indeed easy sounding but many details.

                Also as far as I know, he want to scale the images on load so your idea with offload the main thread soon
                might be needed. Not so much to allow for very fast scrolling but to allow for just normal smooth scrolling :)

                Thank you

                1 Reply Last reply
                0
                • C Chris Kawa
                  5 Mar 2016, 09:31

                  but if u scroll all the way down to the end, it will still have loaded all images ?

                  Yes, but at the same time it will unload the ones you scrolled past so there's only gonna be a small amount of images loaded at any given time.
                  Of course, if you scroll really fast you will kill this model and your ui will be choppy as hell. That's why I suggested to offload the main thread for this. Also, another optimization could be to suspend any loading while the scrollbar is being dragged, but that's further down the road.

                  Sorry for asking so much but I find this task interesting.

                  Sure, no problem. It's one of those topics that seem simple and obvious at first glance but are getting really complicated really fast when you start to inspect the details.

                  K Offline
                  K Offline
                  kshegunov
                  Moderators
                  wrote on 5 Mar 2016, 10:30 last edited by kshegunov 3 May 2016, 10:31
                  #8

                  @Chris-Kawa, @mrjj, @haris123
                  Hello guys,
                  Your discussion piqued my interest as well, so maybe I could ask some questions as well ...?
                  While I do support handing the loading to a separate thread, it might not be able to keep up when scrolling. What about showing a generic image from the model and when/if the image is loaded then show the correct one? As I imagine it, when scrolling down there could a lot of requests to load an image, but most of them quickly become moot, as the user would have scrolled past beyond the visible field. So possibly one could schedule the loading requests to the worker thread and if the user has scrolled past the item just mark them as invalid, and then the thread would have wasted some time, but the UI should stay responsive. In the end when the user pauses scrolling the images would be loaded and the view updated when the correct images are loaded (this doesn't exclude using caching though). How about this?

                  Kind regards.

                  Read and abide by the Qt Code of Conduct

                  M 1 Reply Last reply 5 Mar 2016, 10:36
                  0
                  • K kshegunov
                    5 Mar 2016, 10:30

                    @Chris-Kawa, @mrjj, @haris123
                    Hello guys,
                    Your discussion piqued my interest as well, so maybe I could ask some questions as well ...?
                    While I do support handing the loading to a separate thread, it might not be able to keep up when scrolling. What about showing a generic image from the model and when/if the image is loaded then show the correct one? As I imagine it, when scrolling down there could a lot of requests to load an image, but most of them quickly become moot, as the user would have scrolled past beyond the visible field. So possibly one could schedule the loading requests to the worker thread and if the user has scrolled past the item just mark them as invalid, and then the thread would have wasted some time, but the UI should stay responsive. In the end when the user pauses scrolling the images would be loaded and the view updated when the correct images are loaded (this doesn't exclude using caching though). How about this?

                    Kind regards.

                    M Offline
                    M Offline
                    mrjj
                    Lifetime Qt Champion
                    wrote on 5 Mar 2016, 10:36 last edited by
                    #9

                    @kshegunov
                    That would probably works pretty well and also
                    address the cost for scaling
                    the image since only the true visible would be scaled or at least
                    only some of them as other would be "invalid" as scrolled by.

                    1 Reply Last reply
                    0
                    • C Offline
                      C Offline
                      Chris Kawa
                      Lifetime Qt Champion
                      wrote on 5 Mar 2016, 10:36 last edited by
                      #10

                      @kshegunov Yeah, hanks for pointing that out. The thread should indeed be sort of a queue, that you can add request to and remove before they get executed if needed. Usually it has the nice effect that when scrolling at a reasonable pace you see items loading every few rows apart, so you don't have a choppy ui but you also don't scroll a wall of empty items.

                      K 1 Reply Last reply 5 Mar 2016, 10:43
                      0
                      • C Chris Kawa
                        5 Mar 2016, 10:36

                        @kshegunov Yeah, hanks for pointing that out. The thread should indeed be sort of a queue, that you can add request to and remove before they get executed if needed. Usually it has the nice effect that when scrolling at a reasonable pace you see items loading every few rows apart, so you don't have a choppy ui but you also don't scroll a wall of empty items.

                        K Offline
                        K Offline
                        kshegunov
                        Moderators
                        wrote on 5 Mar 2016, 10:43 last edited by
                        #11

                        @mrjj

                        the image since only the true visible would be scaled or at least
                        only some of them as other would be "invalid" as scrolled by.

                        It'd be a good idea to pre-scale the images in the worker, so the main thread doesn't waste time for doing that, but only for painting them. :)

                        @Chris-Kawa
                        It's basically what you suggested for caching, but with a somewhat custom tuned caching operation. So you were of course right in the first place, I'm just throwing in my 2 cents. :)

                        Read and abide by the Qt Code of Conduct

                        1 Reply Last reply
                        0
                        • H Offline
                          H Offline
                          haris123
                          wrote on 5 Mar 2016, 12:11 last edited by haris123 3 May 2016, 12:12
                          #12

                          Hi guys,

                          I read all above discussion, and I have started working to implement it,

                          So as a first step created a custom Delegate for table view,

                          tableviewdelegate.h

                          class TableViewDelegate : public QStyledItemDelegate
                          {
                          public:
                              enum datarole {ImagePathRole};
                          
                              TableViewDelegate();
                              ~TableViewDelegate();
                          
                              void paint(QPainter *painter,
                                         const QStyleOptionViewItem &option,
                                         const QModelIndex &index) const;
                          
                              QSize sizeHint(const QStyleOptionViewItem &option,
                                             const QModelIndex &index ) const;
                          
                              static QSize iconSize;
                          };
                          
                          

                          tableviewdelegate.cpp

                          #include "tableviewdelegate.h"
                          
                          QSize TableViewDelegate::iconSize = QSize(20, 20);
                          TableViewDelegate::TableViewDelegate()
                          {
                          }
                          
                          TableViewDelegate::~TableViewDelegate()
                          {
                          }
                          
                          QSize TableViewDelegate::sizeHint(const QStyleOptionViewItem &  option ,
                                                                  const QModelIndex & index) const
                          {
                              if(!index.isValid())
                                  return QSize();
                          
                              QSize size(option.rect.width(),option.rect.height());
                          
                              /* Keep the minimum height needed in mind. */
                              if(size.height()<iconSize.height())
                                  size.setHeight(iconSize.height());
                          
                              return size;
                          }
                          
                          void TableViewDelegate::paint(QPainter *painter,
                                                              const QStyleOptionViewItem &option,
                                                              const QModelIndex &index) const
                          {
                          
                              if(!index.isValid())
                                  return;
                          
                              painter->save();
                          
                              if (option.state & QStyle::State_Selected)
                                  painter->fillRect(option.rect, option.palette.highlight());
                          
                              QString imagePath = index.data(ImagePathRole).toString();
                              QPixmap pix(imagePath);
                              painter->drawPixmap(option.rect,pix);
                              painter->restore();
                          }
                          
                          

                          And the mainWindow.cpp

                          MainWindow::MainWindow(QWidget *parent) :
                              QMainWindow(parent),
                              ui(new Ui::MainWindow)
                          {
                              ui->setupUi(this);
                               QFuture<void> f2 = QtConcurrent::run(this,&MainWindow::loadTable);
                          }
                          
                          void MainWindow::loadTable(){
                          
                              QStandardItemModel *model;
                              model = new QStandardItemModel();
                          
                              ui->tableView->horizontalHeader()->setDefaultSectionSize(128);
                              ui->tableView->verticalHeader()->setDefaultSectionSize(128);
                              ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
                              ui->tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
                          
                              model=new QStandardItemModel();
                              for(int i=0;i<15;i++){ //15 column for now
                                   model->setHorizontalHeaderItem( i, new QStandardItem( QString::number(i+1) ) );
                              }
                          
                          
                              TableViewDelegate *tabledelegate;
                              tabledelegate = new TableViewDelegate();
                          
                              ui->tableView->setItemDelegate(tabledelegate);
                              ui->tableView->setModel(model);
                          
                              QString inpath ="C:/Users/user/Documents/allBio/";
                              QStringList ImagePathList=getImageList(inpath);
                          
                              int row=0,col=0;
                              for(int i=0;i<ImagePathList.size();i++){
                          
                                  QStandardItem *item = new QStandardItem();
                                  QString imagePath = inpath +"/"+ ImagePathList.at(i);
                                  item->setData(imagePath,TableViewDelegate::ImagePathRole);
                                  model->setItem(row, col, item);
                          
                                  col++;
                                  if(col==15){
                                      row++;
                                      col=0;
                                  }
                          
                              }
                          
                          }
                          
                          QStringList MainWindow::getImageList(QString inpath){
                          
                              QStringList ImagePathList;
                              QStringList filters;
                              filters << "*.jpeg" << "*.jpg";
                              QDir dir(inpath);
                              dir.setNameFilters(filters);
                              dir.setSorting(QDir::Time);
                              ImagePathList = dir.entryList();
                              return ImagePathList;
                          
                          }
                          
                          

                          Note: in the above code, anything that discussed above is not implemented, I just created custom delegate and applied to table view. I just want to make sure I am on right direction. Please let me know your valuable feedback

                          Also using the above code, the paint event get called every time on some user interaction on mainwindow , even after all the images loaded, and this cause the UI to freeze sometimes while scrolling.

                          1 Reply Last reply
                          0

                          8/12

                          5 Mar 2016, 10:30

                          • Login

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