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.4k 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.
  • Chris KawaC Offline
    Chris KawaC Offline
    Chris Kawa
    Lifetime Qt Champion
    wrote on last edited by Chris Kawa
    #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.

    mrjjM 1 Reply Last reply
    1
    • Chris KawaC Chris Kawa

      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.

      mrjjM Offline
      mrjjM Offline
      mrjj
      Lifetime Qt Champion
      wrote on 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
      • Chris KawaC Offline
        Chris KawaC Offline
        Chris Kawa
        Lifetime Qt Champion
        wrote on 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
        • mrjjM Offline
          mrjjM Offline
          mrjj
          Lifetime Qt Champion
          wrote on 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
          • Chris KawaC Offline
            Chris KawaC Offline
            Chris Kawa
            Lifetime Qt Champion
            wrote on 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.

            kshegunovK 1 Reply Last reply
            1
            • mrjjM Offline
              mrjjM Offline
              mrjj
              Lifetime Qt Champion
              wrote on 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
              • Chris KawaC Chris Kawa

                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.

                kshegunovK Offline
                kshegunovK Offline
                kshegunov
                Moderators
                wrote on last edited by kshegunov
                #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

                mrjjM 1 Reply Last reply
                0
                • kshegunovK kshegunov

                  @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.

                  mrjjM Offline
                  mrjjM Offline
                  mrjj
                  Lifetime Qt Champion
                  wrote on 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
                  • Chris KawaC Offline
                    Chris KawaC Offline
                    Chris Kawa
                    Lifetime Qt Champion
                    wrote on 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.

                    kshegunovK 1 Reply Last reply
                    0
                    • Chris KawaC Chris Kawa

                      @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.

                      kshegunovK Offline
                      kshegunovK Offline
                      kshegunov
                      Moderators
                      wrote on 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 last edited by haris123
                        #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

                        • Login

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