у меня есть QTreeView с QStandardItemModel. Моя модель может быть изменена для отображения ее элементов в 4 различных режимах:
Я сделал подкласс QStyledItemDelegate для отображения звезд (например, Пример звездного делегата)
В режиме 2 или 3 можно включить обложку альбома в настройках (и установить размер, например, 64 х 64 пикселя).
Чтобы уменьшить объем памяти, обложки лениво загружаются, когда элементы отображаются на экране. Ни один фоновый процесс не сканирует жесткий диск каждый раз, когда вы запускаете аудиоплеер.
Это работает нормально, но пользовательский опыт может быть улучшен. На самом деле, используя колесную мышку, чехлы загружаются без проблем. При использовании вертикальной полосы прокрутки в библиотеке 500 альбомов и ее перемещении вниз вы можете услышать царапины на жестком диске при загрузке * .jpeg или * .png. После того, как все обложки загружены, прокрутка становится совершенно плавной (мне нужно избавиться от них позже).
Я подкласс QScrollBar и обнаружил MousePressEvent а также MouseReleaseEvent временно отключить загрузку.
Я создал сигнал, когда кто-то нажимает на полосу прокрутки, и подключил его к моему QStyledItemDelegate. Тем не менее, обложки «появляются» на экране.
Я хотел бы отображать плавно, с QPropertyAnimation класс (и Анимационные рамки). К сожалению, QStyledItemDelegate, QIcon, QStandardItem не являются ни QObject, ни QWidget, поэтому я не могу использовать 2 или 3 строки кода для создания этого вида анимации.
Есть ли обходной путь, или какой-то (не такой уродливый) взлом?
Я бы предпочел не переопределять paintEvent, чтобы воссоздать все с нуля в моем QTreeView, потому что это кажется довольно сложным, но, возможно, я ошибаюсь.
Ну, я не нашел правильного способа с QPropertyAnimation, так что вот гораздо более сложное решение.
void LibraryScrollBar::mouseMoveEvent(QMouseEvent *e)
{
if (_hasNotEmittedYet) {
qDebug() << "hide covers when moving";
emit displayItemDelegate(false);
_hasNotEmittedYet = false;
}
QScrollBar::mouseMoveEvent(e);
}
void LibraryScrollBar::mouseReleaseEvent(QMouseEvent *e)
{
if (!_hasNotEmittedYet) {
qDebug() << "show covers when stopped moving";
emit displayItemDelegate(true);
_hasNotEmittedYet = true;
}
QScrollBar::mouseReleaseEvent(e);
}
void LibraryTreeView::init(LibrarySqlModel *sql)
{
/// some code before
LibraryScrollBar *vScrollBar = new LibraryScrollBar(this);
this->setVerticalScrollBar(vScrollBar);
connect(vScrollBar, &LibraryScrollBar::displayItemDelegate, [=](bool b) {
_itemDelegate->displayIcon(b);
b ? _timer->start() : _timer->stop();
});
connect(_timer, &QTimer::timeout, this, &LibraryTreeView::repaintIcons);
}
void LibraryTreeView::repaintIcons()
{
static qreal r = 0;
if (_timer->isActive()) {
r += 0.01;
_itemDelegate->setIconOpacity(r);
if (r >= 1) {
_timer->stop();
r = 0;
}
this->viewport()->repaint();
}
}
void LibraryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
painter->save();
painter->setFont(Settings::getInstance()->font(Settings::LIBRARY));
QStandardItem *item = _libraryModel.data()->itemFromIndex(_proxy.data()->mapToSource(index));
QStyleOptionViewItem o = option;
initStyleOption(&o, index);
// Removes the dotted rectangle to the focused item
o.state &= ~QStyle::State_HasFocus;
int type = item->data(LibraryTreeView::Type).toInt();
switch (type) {
case LibraryTreeView::Album:
this->drawAlbum(painter, o, item);
break;
/// etc
}
painter->restore();
}
/** Albums have covers usually. */
void LibraryItemDelegate::drawAlbum(QPainter *painter, QStyleOptionViewItem &option, QStandardItem *item) const
{
static QImageReader imageReader;
static int coverSize = Settings::getInstance()->coverSize();
QString file = item->data(LibraryTreeView::DataCoverPath).toString();
// Display a light selection rectangle when one is moving the cursor
if (option.state & QStyle::State_MouseOver && ~option.state & QStyle::State_Selected) {
painter->save();
painter->setPen(option.palette.highlight().color());
painter->setBrush(option.palette.highlight().color().lighter(175));
painter->drawRect(option.rect.adjusted(0, 0, -1, -1));
painter->restore();
} else if (option.state & QStyle::State_Selected) {
// Display a not so light rectangle when one has chosen an item. It's darker than the mouse over
painter->save();
painter->setPen(option.palette.highlight().color());
painter->setBrush(option.palette.highlight().color().lighter(160));
painter->drawRect(option.rect.adjusted(0, 0, -1, -1));
painter->restore();
}
if (_showCovers) {
/// XXX: extract this elsewhere
// Qt::UserRole + 20 == false => pixmap not loaded ; == true => pixmap loaded
if (item->data(Qt::UserRole + 20).toBool() == false && !file.isEmpty()) {
FileHelper fh(file);
QFileInfo f(file);
qDebug() << "loading cover from harddrive";
// If it's an inner cover, load it
if (FileHelper::suffixes().contains(f.suffix())) {
std::unique_ptr<Cover> cover(fh.extractCover());
if (cover) {
QPixmap p;
p.loadFromData(cover->byteArray(), cover->format());
p = p.scaled(coverSize, coverSize);
item->setIcon(p);
item->setData(true, Qt::UserRole + 20);
}
} else {
imageReader.setFileName(QDir::fromNativeSeparators(file));
imageReader.setScaledSize(QSize(coverSize, coverSize));
item->setIcon(QPixmap::fromImage(imageReader.read()));
item->setData(true, Qt::UserRole + 20);
}
}
}
bool b = item->data(Qt::UserRole + 20).toBool();
if (_showCovers && b) {
QPixmap p = option.icon.pixmap(QSize(coverSize, coverSize));
QRect cover(option.rect.x() + 1, option.rect.y() + 1, coverSize, coverSize);
if (_animateIcons) {
painter->save();
painter->setOpacity(_iconOpacity);
painter->drawPixmap(cover, p);
painter->restore();
} else {
painter->drawPixmap(cover, p);
}
}
// It's possible to have missing covers in your library, so we need to keep alignment.
QPoint topLeft(option.rect.x() + coverSize + 5, option.rect.y());
QFontMetrics fmf(Settings::getInstance()->font(Settings::LIBRARY));
QRect rectText(topLeft, option.rect.bottomRight());
option.textElideMode = Qt::ElideRight;
QString s = fmf.elidedText(option.text, Qt::ElideRight, rectText.width());
painter->drawText(rectText, Qt::AlignVCenter, s);
}
Других решений пока нет …