Using checkable QStandardItems in a ListView.
-
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. -
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.
-
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 ;)
-
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.
-
Can you post a complete sample code that reproduces this ?
-
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. -
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.