Using checkable QStandardItems in a ListView.



  • I have subclassed QListView that I use to display images as thumbnails using QStandardItem.

    So basically I have a number of items that for each one I do

    setModel(standardItemModel);
    thumbItem->setIcon(... The pixmap of an image.
    standardItemModel->appendRow(thumbIitem);
    

    I then decided to make each item checkable by doing

    thumbIitem->setCheckable(true);
    

    But the thing is that the checkbox does not appear at the side of the icon but depending on the icons' aspect ration sometimes it even gets covered by it.

    Is there a way to make the icon leave some space for the thumbnail to be visible?


  • Lifetime Qt Champion

    Hi,

    Are you using QListView in ListMode or IconMode ?



  • Hi,
    I have it on Icon mode. The only way I thought about somehow overcoming it was to force the aspect ratio of the thumbnails so as to make them not fall above the checkbox. Because when I tried ListMode it messed with my label, no longer showing it below the image but next to it. So I thought it would be easier to just push the images a little bit to the right.


  • Lifetime Qt Champion

    Can you provide a minimal compilable example that reproduces this behavior ?



  • Unfortunately no because the necessary code that reads and loads the images would be to big. Maybe if I manage to finish this project and upload it you could have a look then.

    But in any case I believe that it was not intended to have checkable items in a listView that is set in iconMode. I would recommend just setting it on listMode and removing the captions for each thumbnail.


  • Lifetime Qt Champion

    Right now I don't see why you couldn't have checkable items in icon mode. It might just be that you are the first to try that ;)



  • Ow you perfectly can. Its just that depending on the image it might sit right above the checkbox, so you have to manually play with the aspect ratio in order to not cover it.


  • Lifetime Qt Champion

    Is it only a size problem ? If so can you tell at which size it's looking nice and when it's starting to bug ?



  • if all your images are for example 640x480 then you will not see any checkboxes at all.

    if again you display alongside with those images another image that is 440x400 then they show up just fine.

    In case you only have images that are 640x480 then what you have to do (what I did) is change the aspect ratio until the look fine. That should be an aspect ratio of about 2.


  • Lifetime Qt Champion

    Can you post a complete sample code that reproduces this ?



  • The thing is that in order for it to be a complete example, the coed has to be quite big. Once I get back my mac (probably this Monday) I will try to reproduce it again and upload the project somewhere.


  • Lifetime Qt Champion

    Wouldn't the QListView + model setup be enough ? Fake images can be easily created with QImage



  • I tried making a minimal compilable example and failed.
    I abandoned the project some time ago, because I had no idea what more to do with it. In any case I am attaching an image showing the effect from the full app and the thumbnails class.

    #include "clsthumbview.h"
    
    #define NO_THUMB_SIZE	64
    
    clsThumbView::clsThumbView(QWidget *parent) : QListView(parent)
    {
        GImData::qcolorThumbsBackgroundColor = GImData::eqsetAppSettings->value("backgroundThumbColor").value<QColor>();
        GImData::qcolorThumbsTextColor = GImData::eqsetAppSettings->value("textThumbColor").value<QColor>();
        setThumbColors();
        GImData::euintThumbSpacing = GImData::eqsetAppSettings->value("thumbSpacing").toInt();
        GImData::euintThumbPagesReadahead = GImData::eqsetAppSettings->value("thumbPagesReadahead").toInt();
        GImData::euintThumbsLayout = GImData::eqsetAppSettings->value("thumbLayout").toInt();
        thumbSize = GImData::eqsetAppSettings->value("thumbsZoomVal").toInt();
        currentRow = 0;
    
        setViewMode(QListView::IconMode);
        setSelectionMode(QAbstractItemView::ExtendedSelection);
        setResizeMode(QListView::Adjust);
        setWordWrap(true);
        setDragEnabled(true);
        setEditTriggers(QAbstractItemView::NoEditTriggers);
        setItemDelegate(new QItemDelegate);
        setUniformItemSizes(false);
    
        thumbViewModel = new QStandardItemModel(this);
        thumbViewModel->setSortRole(SortRole);
        setModel(thumbViewModel);
    
        connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(loadVisibleThumbs(int)));
        connect(this->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
                    this, SLOT(handleSelectionChanged(QItemSelection)));
    
        thumbsDir = new QDir();
        fileFilters = new QStringList;
    
        emptyImg.load(":/images/no_image.png");
    
        QTime time = QTime::currentTime();
        qsrand((uint)time.msec());
    }
    
    void clsThumbView::setThumbColors()
    {
        QString bgColor = "background: rgb(%1, %2, %3); ";
        bgColor = bgColor.arg(GImData::qcolorThumbsBackgroundColor.red())
                            .arg(GImData::qcolorThumbsBackgroundColor.green())
                            .arg(GImData::qcolorThumbsBackgroundColor.blue());
    
        QString ss = "QListView { " + bgColor + "background-image: url("
                        + GImData::eqstringThumbsBackImage
                        + "); background-attachment: fixed; }";
        setStyleSheet(ss);
    
        QPalette scrollBarOrigPal = verticalScrollBar()->palette();
        QPalette thumbViewOrigPal = palette();
        thumbViewOrigPal.setColor(QPalette::Text, GImData::qcolorThumbsTextColor);
        setPalette(thumbViewOrigPal);
        verticalScrollBar()->setPalette(scrollBarOrigPal);
    }
    
    void clsThumbView::selectCurrentIndex()
    {
        if (currentIndex.isValid() && thumbViewModel->rowCount() > 0)
        {
            scrollTo(currentIndex);
            setCurrentIndex(currentIndex);
        }
    }
    
    QString clsThumbView::getSingleSelectionFilename()
    {
        if (selectionModel()->selectedIndexes().size() == 1)
            return thumbViewModel->item(selectionModel()->selectedIndexes().first().row())->data(FileNameRole).toString();
    
        return("");
    }
    
    int clsThumbView::getNextRow()
    {
        if (currentRow == thumbViewModel->rowCount() - 1)
            return -1;
    
        return currentRow + 1;
    }
    
    int clsThumbView::getPrevRow()
    {
        if (currentRow == 0)
            return -1;
    
        return currentRow - 1;
    }
    
    int clsThumbView::getLastRow()
    {
        return thumbViewModel->rowCount() - 1;
    }
    
    int clsThumbView::getCurrentRow()
    {
        return currentRow;
    }
    
    void clsThumbView::setCurrentRow(int row)
    {
        if (row >= 0)
            currentRow = row;
        else
            currentRow = 0;
    }
    
    bool clsThumbView::setCurrentIndexByName(QString &FileName)
    {
        QModelIndexList indexList = thumbViewModel->match(thumbViewModel->index(0, 0), FileNameRole, FileName);
        if (indexList.size())
        {
            currentIndex = indexList[0];
            setCurrentRow(currentIndex.row());
            setRowHidden(currentIndex.row(), false);
            return true;
        }
    
        return false;
    }
    
    bool clsThumbView::setCurrentIndexByRow(int row)
    {
        QModelIndex idx = thumbViewModel->indexFromItem(thumbViewModel->item(row));
        if (idx.isValid())
        {
            currentIndex = idx;
            setCurrentRow(idx.row());
            return true;
        }
    
        return false;
    }
    
    void clsThumbView::handleSelectionChanged(const QItemSelection&)
    {
        QModelIndexList indexesList = selectionModel()->selectedIndexes();
        int nSelected = indexesList.size();
    
        // Maybe set this as a title.
        if (nSelected == 1)
        {
            QString imageFullPath = thumbViewModel->item(indexesList.first().row())->data(FileNameRole).toString();
        }
        updateThumbsSelection();
    }
    
    void clsThumbView::startDrag(Qt::DropActions)
    {
        QModelIndexList indexesList = selectionModel()->selectedIndexes();
        if (indexesList.isEmpty())
        {
            return;
        }
    
        QDrag *drag = new QDrag(this);
        QMimeData *mimeData = new QMimeData;
        QList<QUrl> urls;
        for (QModelIndexList::const_iterator it = indexesList.constBegin(),
                                            end = indexesList.constEnd(); it != end; ++it)
        {
            urls << QUrl(thumbViewModel->item(it->row())->data(FileNameRole).toString());
        }
        mimeData->setUrls(urls);
        drag->setMimeData(mimeData);
        QPixmap pix;
        if (indexesList.count() > 1)
        {
            pix = QPixmap(128, 112);
            pix.fill(Qt::transparent);
            QPainter painter(&pix);
            painter.setBrush(Qt::NoBrush);
            painter.setPen(QPen(Qt::white, 2));
            int x = 0, y = 0, xMax = 0, yMax = 0;
            for (int i = 0; i < qMin(5, indexesList.count()); ++i)
            {
                QPixmap pix = thumbViewModel->item(indexesList.at(i).row())->icon().pixmap(72);
                if (i == 4) {
                    x = (xMax - pix.width()) / 2;
                    y = (yMax - pix.height()) / 2;
                }
                painter.drawPixmap(x, y, pix);
                xMax = qMax(xMax, qMin(128, x + pix.width()));
                yMax = qMax(yMax, qMin(112, y + pix.height()));
                painter.drawRect(x + 1, y + 1, qMin(126, pix.width() - 2), qMin(110, pix.height() - 2));
                x = !(x == y) * 56;
                y = !y * 40;
            }
            painter.end();
            pix = pix.copy(0, 0, xMax, yMax);
            drag->setPixmap(pix);
        } else
        {
            pix = thumbViewModel->item(indexesList.at(0).row())->icon().pixmap(128);
            drag->setPixmap(pix);
        }
        drag->setHotSpot(QPoint(pix.width() / 2, pix.height() / 2));
        drag->exec(Qt::CopyAction | Qt::MoveAction | Qt::LinkAction, Qt::IgnoreAction);
    }
    
    void clsThumbView::abort()
    {
        abortOp = true;
    }
    
    void clsThumbView::loadVisibleThumbs(int scrollBarValue)
    {
        static int lastScrollBarValue = 0;
    
        if (GImData::euintThumbsLayout == Compact)
        {
            scrolledForward = true;
        }
        else
        {
            scrolledForward = (scrollBarValue >= lastScrollBarValue);
        }
    
        lastScrollBarValue = scrollBarValue;
    
    Start:
        int first = getFirstVisibleThumb();
        int last = getLastVisibleThumb();
        if (abortOp || first < 0 || last < 0)
            return;
    
        if (scrolledForward)
        {
            last += ((last - first) * (GImData::euintThumbPagesReadahead + 1));
            if (last >= thumbViewModel->rowCount())
                last = thumbViewModel->rowCount() - 1;
        }
        else
        {
            first -= (last - first) * (GImData::euintThumbPagesReadahead + 1);
            if (first < 0)
                first = 0;
    
            last += 10;
            if (last >= thumbViewModel->rowCount())
                last = thumbViewModel->rowCount() - 1;
        }
    
        if (thumbsRangeFirst == first && thumbsRangeLast == last) {
            return;
        }
    
        thumbsRangeFirst = first;
        thumbsRangeLast = last;
    
        loadThumbsRange();
        if (!abortOp)
            goto Start;
    }
    
    int clsThumbView::getFirstVisibleThumb()
    {
        QModelIndex idx;
    
        for (int currThumb = 0; currThumb < thumbViewModel->rowCount(); ++currThumb)
        {
            idx = thumbViewModel->indexFromItem(thumbViewModel->item(currThumb));
            if (viewport()->rect().contains(QPoint(0, visualRect(idx).y() + visualRect(idx).height() + 1)))
            {
                return idx.row();
            }
        }
    
        return -1;
    }
    
    int clsThumbView::getLastVisibleThumb()
    {
        QModelIndex idx;
    
        for (int currThumb = thumbViewModel->rowCount() -1; currThumb >= 0 ; --currThumb)
        {
            idx = thumbViewModel->indexFromItem(thumbViewModel->item(currThumb));
            if (viewport()->rect().contains(QPoint(0, visualRect(idx).y() + visualRect(idx).height() + 1)))
            {
                return idx.row();
            }
        }
    
        return -1;
    }
    
    bool clsThumbView::isThumbVisible(QModelIndex idx)
    {
        if (viewport()->rect().contains(QPoint(0, visualRect(idx).y() + visualRect(idx).height() + 1)))
        {
            return true;
        }
    
        return false;
    }
    
    void clsThumbView::updateThumbsCount()
    {
        QString state ;
    
        if (thumbViewModel->rowCount() > 0)
        {
            state = tr("%n image(s)", "", thumbViewModel->rowCount());
        }
        else
        {
            state = tr("No images");
        }
    
        emit setStatus(state);
    }
    
    void clsThumbView::updateThumbsSelection()
    {
        QString state;
        int nSelected = selectionModel()->selectedIndexes().size();
    
        if (!nSelected)
        {
            updateThumbsCount();
            return;
        }
        else if (nSelected >= 1)
            state = tr("Selected %1 of %2")
                        .arg(QString::number(nSelected))
                        .arg(tr(" %n image(s)", "", thumbViewModel->rowCount()));
        emit setStatus(state);
    }
    
    void clsThumbView::loadPrepare()
    {
        float thumbAspect = 1.33;
        if (GImData::euintThumbsLayout == Compact)
            thumbAspect = 1.77;
        else if (GImData::euintThumbsLayout == Squares)
            thumbAspect = 2;
    
        thumbHeight = (GImData::euintThumbsLayout == Squares)? thumbSize * thumbAspect : thumbSize;
        thumbWidth = (GImData::euintThumbsLayout == Squares)? thumbSize * thumbAspect : thumbHeight * thumbAspect;
        setIconSize(QSize(thumbWidth, thumbHeight));
    
        fileFilters->clear();
        QString textFilter("*");
        textFilter+= filterStr;
        *fileFilters	<< textFilter + "*.BMP"
                        << textFilter + "*.GIF"
                        << textFilter + "*.ICO"
                        << textFilter + "*.JPEG"
                        << textFilter + "*.JPG"
                        << textFilter + "*.MNG"
                        << textFilter + "*.PBM"
                        << textFilter + "*.PGM"
                        << textFilter + "*.PNG"
                        << textFilter + "*.PPM"
                        << textFilter + "*.SVG"
                        << textFilter + "*.SVGZ"
                        << textFilter + "*.TGA"
                        << textFilter + "*.TIF"
                        << textFilter + "*.TIFF"
                        << textFilter + "*.WBMP"
                        << textFilter + "*.XBM"
                        << textFilter + "*.XPM"
                        << textFilter + "*.JPE";
        thumbsDir->setNameFilters(*fileFilters);
        thumbsDir->setFilter(QDir::Files);
        if (GImData::eboolShowHiddenFiles)
        {
            thumbsDir->setFilter(thumbsDir->filter() | QDir::Hidden);
        }
    
        thumbsDir->setPath(currentViewDir);
    
        QDir::SortFlags tempThumbsSortFlags = thumbsSortFlags;
        if (tempThumbsSortFlags & QDir::Size || tempThumbsSortFlags & QDir::Time)
        {
            tempThumbsSortFlags ^= QDir::Reversed;
        }
        thumbsDir->setSorting(tempThumbsSortFlags);
    
        thumbViewModel->clear();
        setSpacing(GImData::euintThumbSpacing);
    
        if (isNeedScroll)
        {
            scrollToTop();
        }
    
        abortOp = false;
        newIndex = 0;
    
        thumbsRangeFirst = -1;
        thumbsRangeLast = -1;
    }
    
    void clsThumbView::load()
    {
        loadPrepare();
        initThumbs();
        updateThumbsCount();
        loadVisibleThumbs();
    
        if (GImData::eboolIncludeSubFolders)
        {
            emit showBusy(true);
            QDirIterator iterator(currentViewDir, QDirIterator::Subdirectories);
            while (iterator.hasNext())
            {
                iterator.next();
                if (iterator.fileInfo().isDir() && iterator.fileName() != "." && iterator.fileName() != "..")
                {
                    thumbsDir->setPath(iterator.filePath());
    
                    initThumbs();
                    updateThumbsCount();
                    loadVisibleThumbs();
    
                    if (abortOp)
                    {
                        goto finish;
                    }
                }
                QApplication::processEvents();
            }
            updateThumbsSelection();
        }
    
    finish:
        busy = false;
        emit showBusy(false);
        return;
    }
    
    void clsThumbView::initThumbs()
    {
        thumbFileInfoList = thumbsDir->entryInfoList();
        static QStandardItem *thumbIitem;
        static int currThumb;
        static QPixmap emptyPixMap;
        static QSize hintSize;
    
        emptyPixMap = QPixmap::fromImage(emptyImg).scaled(thumbWidth, thumbHeight);
    
        if (GImData::euintThumbsLayout == Squares)
            hintSize = QSize(thumbWidth / 2, thumbWidth / 2);
        else if (GImData::euintThumbsLayout == Classic)
            hintSize = QSize(thumbWidth, thumbHeight +
                                (GImData::eboolShowLabels? QFontMetrics(font()).height() + 5 : 0));
    
        for (currThumb = 0; currThumb < thumbFileInfoList.size(); ++currThumb)
        {
            thumbFileInfo = thumbFileInfoList.at(currThumb);
            thumbIitem = new QStandardItem();
            thumbIitem->setData(false, LoadedRole);
            thumbIitem->setData(currThumb, SortRole);
            thumbIitem->setData(thumbFileInfo.filePath(), FileNameRole);
            if (GImData::euintThumbsLayout != Squares && GImData::eboolShowLabels)
                thumbIitem->setData(thumbFileInfo.fileName(), Qt::DisplayRole);
            if (GImData::euintThumbsLayout == Compact)
                thumbIitem->setIcon(emptyPixMap);
            thumbIitem->setTextAlignment(Qt::AlignTop | Qt::AlignHCenter);
            if (GImData::euintThumbsLayout != Compact)
                thumbIitem->setSizeHint(hintSize);
    
            // Make checkable.
            //thumbIitem->setCheckable(true);
    
            thumbViewModel->appendRow(thumbIitem);
        }
    }
    
    void clsThumbView::loadThumbsRange()
    {
        static QImageReader thumbReader;
        static QSize currThumbSize;
        static int currRowCount;
        static QString imageFileName;
        static QImage thumb;
        int currThumb;
    
        currRowCount = thumbViewModel->rowCount();
    
        for (scrolledForward? currThumb = thumbsRangeFirst : currThumb = thumbsRangeLast;
                (scrolledForward? currThumb <= thumbsRangeLast : currThumb >= thumbsRangeFirst);
                scrolledForward? ++currThumb : --currThumb)
        {
            if (abortOp || thumbViewModel->rowCount() != currRowCount)
                break;
    
            if (thumbViewModel->item(currThumb)->data(LoadedRole).toBool())
                continue;
    
            imageFileName = thumbViewModel->item(currThumb)->data(FileNameRole).toString();
            thumbReader.setFileName(imageFileName);
            currThumbSize = thumbReader.size();
    
            if (currThumbSize.isValid())
            {
                if (!GImData::eboolNoEnlargeSmallThumb
                    || (currThumbSize.width() > thumbWidth || currThumbSize.height() > thumbHeight))
                {
                    currThumbSize.scale(QSize(thumbWidth, thumbHeight), Qt::KeepAspectRatio);
                }
    
                thumbReader.setScaledSize(currThumbSize);
                thumb = thumbReader.read();
    
                thumbViewModel->item(currThumb)->setIcon(QPixmap::fromImage(thumb));
            }
            else
            {
                thumbViewModel->item(currThumb)->setIcon(QIcon::fromTheme("image-missing",
                                        QIcon(":/images/error_image.png")).pixmap(NO_THUMB_SIZE, NO_THUMB_SIZE));
                currThumbSize.setHeight(NO_THUMB_SIZE);
                currThumbSize.setWidth(NO_THUMB_SIZE);
            }
    
            if (GImData::euintThumbsLayout == Compact)
            {
                if (GImData::eboolShowLabels)
                    currThumbSize.setHeight(currThumbSize.height() + QFontMetrics(font()).height() + 5);
                thumbViewModel->item(currThumb)->setSizeHint(currThumbSize);
                if (isThumbVisible(thumbViewModel->item(currThumb)->index()))
                    setRowHidden(currThumb, false);
            }
    
            thumbViewModel->item(currThumb)->setData(true, LoadedRole);
    
            QApplication::processEvents();
        }
    
        if (GImData::euintThumbsLayout == Compact && thumbViewModel->rowCount() > 0)
        {
            setRowHidden(0 , false);
        }
    
        abortOp = false;
    }
    
    void clsThumbView::addThumb(QString &imageFullPath)
    {
        QStandardItem *thumbIitem = new QStandardItem();
        QImageReader thumbReader;
        QSize hintSize;
        QSize currThumbSize;
        static QImage thumb;
    
        if (GImData::euintThumbsLayout == Squares)
            hintSize = QSize(thumbWidth / 2, thumbWidth / 2);
        else if (GImData::euintThumbsLayout == Classic)
            hintSize = QSize(thumbWidth, thumbHeight +
                                (GImData::eboolShowLabels? QFontMetrics(font()).height() + 5 : 0));
    
        thumbFileInfo = QFileInfo(imageFullPath);
        thumbIitem->setData(true, LoadedRole);
        thumbIitem->setData(0, SortRole);
        thumbIitem->setData(thumbFileInfo.filePath(), FileNameRole);
        if (GImData::euintThumbsLayout != Squares && GImData::eboolShowLabels)
            thumbIitem->setData(thumbFileInfo.fileName(), Qt::DisplayRole);
    
        thumbReader.setFileName(imageFullPath);
        currThumbSize = thumbReader.size();
        if (currThumbSize.isValid())
        {
            if (!GImData::eboolNoEnlargeSmallThumb
                || (currThumbSize.width() > thumbWidth || currThumbSize.height() > thumbHeight))
            {
                currThumbSize.scale(QSize(thumbWidth, thumbHeight), Qt::KeepAspectRatio);
            }
    
            thumbReader.setScaledSize(currThumbSize);
            thumb = thumbReader.read();        
    
            thumbIitem->setIcon(QPixmap::fromImage(thumb));
        }
        else
        {
            thumbIitem->setIcon(QIcon::fromTheme("image-missing",
                                    QIcon(":/images/error_image.png")).pixmap(NO_THUMB_SIZE, NO_THUMB_SIZE));
            currThumbSize.setHeight(NO_THUMB_SIZE);
            currThumbSize.setWidth(NO_THUMB_SIZE);
        }
    
        if (GImData::euintThumbsLayout == Compact)
        {
            if (GImData::eboolShowLabels)
                currThumbSize.setHeight(currThumbSize.height() + QFontMetrics(font()).height() + 5);
            thumbIitem->setSizeHint(currThumbSize);
        }
        else
            thumbIitem->setSizeHint(hintSize);
    
        thumbViewModel->appendRow(thumbIitem);
    }
    
    void clsThumbView::wheelEvent(QWheelEvent *event_)
    {
        if (event_->delta() < 0)
            verticalScrollBar()->setValue(verticalScrollBar()->value() + thumbHeight);
        else
            verticalScrollBar()->setValue(verticalScrollBar()->value() - thumbHeight);
    }
    
    void clsThumbView::mousePressEvent(QMouseEvent *event_)
    {
        QListView::mousePressEvent(event_);
    
        if (GImData::eboolReverseMouseBehavior && event_->button() == Qt::MiddleButton)
        {
            if (selectionModel()->selectedIndexes().size() == 1)
                emit(doubleClicked(selectionModel()->selectedIndexes().first()));
        }
    }
    
    void clsThumbView::invertSelection()
    {
        QItemSelection toggleSelection;
        QModelIndex firstIndex = thumbViewModel->index(0, 0);
        QModelIndex lastIndex = thumbViewModel->index(thumbViewModel->rowCount() - 1, 0);
        toggleSelection.select(firstIndex, lastIndex);
        selectionModel()->select(toggleSelection, QItemSelectionModel::Toggle);
    }
    
    

    As you said if you use ListMode the issue disappears. In Icon mode though, if all icons are of the same size and with an aspect ratio of something like 16:9 then they will overlap with the checkbox.


  • Lifetime Qt Champion

    That's a bit big to analyze without the header, is the project available somewhere ?



  • No there is not at the moment. But as I said earlier this was based on some code from here. I tested it too, and if you try to add checkboxes just in the way I have done in the code above and compile, you can clearly see what I am talking about. Just make sure you browse to a folder that contains only images of the dimensions I specified.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.