Напишем небольшое приложение, которое позволит методом Drag And Drop перетаскивать изображения из файлового менеджера в само наше приложение. При этом в приложении будет область просмотра изображения и список всех изображений, которые мы поместили в наше приложение. При этом при клике по изображению в списке в область основного просмотра будет помещаться изображение, по которому мы кликнули. В данном списке будет формироваться у каждого элемента превью изображения без текста. Такое превью будет формироваться с помощью делегата, наследованного от QStyledDelegate.
Приложение будет выглядеть следующим образом:
Структура проекта
- DropEvent.pro - профайл проекта;
- main.cpp - файл с main функцией;
- widget.h - заголовочный файл окна приложения;
- widget.cpp - файл исходных кодов приложения;
- imagedelegate.h - заголовочный файл делегата элемента списка;
- Imagedelegate.cpp - файл исходных кодов делегата элемента списка.
Делегат в данном проекте требуется для того, чтобы удалить текст под изображениями. Дело в том, что для отображения превью картинок будет использоваться QListView и QStandardItemModel , которые не имеют функционала отображения иконок без текста. Но убрать текст возможно с помощью делегата, полностью переопределив отрисовку внешнего вида элемента списка.
Приводить исходный код файлов DropEvent.pro и main.cpp я не буду, поскольку там программный код создаваемый по умолчанию при создании проекта.
widget.h
В заголовочном файле окна приложения переопределим методы для событий Drag и Drop , а также добавим объекты для формирования интерфейса и модель данных для изображений.
- #ifndef WIDGET_H
- #define WIDGET_H
- #include <QWidget>
- #include <QPalette>
- #include <QDragEnterEvent>
- #include <QMimeData>
- #include <QDropEvent>
- #include <QScrollArea>
- #include <QLabel>
- #include <QListView>
- #include <QGridLayout>
- #include <QStandardItemModel>
- class Widget : public QWidget
- {
- Q_OBJECT
- public:
- explicit Widget(QWidget *parent = 0);
- ~Widget();
- // Метод события перетаскивания
- virtual void dragEnterEvent(QDragEnterEvent* event) override;
- // Метод события отпускания объекта с данными
- virtual void dropEvent(QDropEvent *event) override;
- private slots:
- // Слот для обработки кликов по элементам списка
- void onImagesListViewClicked(const QModelIndex& index);
- private:
- QScrollArea* m_scrollArea; // Область скроллинга изображения
- QLabel* m_imageLabel; // Лейбл для отображения картинок
- QListView* m_imagesListView; // Список с изображениями
- QGridLayout* m_gridLayout; // Сетка для интерфейса
- QStandardItemModel* m_imagesModel; // Модель данных с изображениями
- };
- #endif // WIDGET_H
widget.cpp
Для формирования списка с изображениями будет использоваться QStandardItemModel , а для отображения элементов модели будет использоваться QListView. Для кастомизированного отображения элементов в списке будет использоваться Делегат, поскольку только переопределив отображение внешнего вида можно будет убрать текст. Кстати, этот текст содержит в себе путь к файлу изображения, который и будет использоваться для создания QPixmap и отображения картинки как в основном просмотре, так и в превьюшках. Чтобы получить из модели данных путь к файлу, необходимо будет воспользоваться методом data(), и в качестве аргументов передать QModellndex и enum Qt::DisplayRole , который является аргументом по умолчанию.
- #include "widget.h"
- #include "ui_widget.h"
- #include <QStandardItem>
- #include "imagedelegate.h"
- Widget::Widget(QWidget *parent) :
- QWidget(parent)
- {
- setAcceptDrops(true); // разрешаем события отпускания объектов данных
- setMinimumWidth(640);
- setMinimumHeight(480);
- /// Настраиваем интерфейс
- m_gridLayout = new QGridLayout(this);
- m_imagesListView = new QListView(this);
- // Создадим модель данных для списка изображений
- m_imagesModel = new QStandardItemModel(m_imagesListView);
- m_imagesListView->setModel(m_imagesModel); // Установим модель во вьюшку для превью изображений
- m_imagesListView->setFixedWidth(200);
- // Без делегата не удастся избавиться от текста в элементе списка и настроить отображение превью
- m_imagesListView->setItemDelegate(new ImageDelegate(m_imagesModel, m_imagesListView));
- // Настраиваем область скроллинга для текущего изображения
- m_scrollArea = new QScrollArea(this);
- m_scrollArea->setBackgroundRole(QPalette::Dark);
- m_imageLabel = new QLabel(this);
- m_scrollArea->setWidget(m_imageLabel);
- m_gridLayout->addWidget(m_scrollArea, 0, 0);
- m_gridLayout->addWidget(m_imagesListView, 0, 1);
- connect(m_imagesListView, &QListView::clicked, this, &Widget::onImagesListViewClicked);
- }
- Widget::~Widget()
- {
- }
- void Widget::dragEnterEvent(QDragEnterEvent *event)
- {
- // Обязательно необходимо допустить событие переноса данных в область окна приложения
- event->accept();
- }
- void Widget::dropEvent(QDropEvent *event)
- {
- // Когда отпускаем файл в область приложения,
- // то забираем путь к файлу из MIME данных
- QString filePath = event->mimeData()->urls()[0].toLocalFile();
- // Создаём изображение
- QPixmap pixmap(filePath);
- // Помещаем его в область скроллинга через QLabel
- m_imageLabel->setPixmap(pixmap);
- m_imageLabel->resize(pixmap.size());
- // Добавляем элемент в список
- m_imagesModel->appendRow(new QStandardItem(QIcon(pixmap), filePath));
- }
- void Widget::onImagesListViewClicked(const QModelIndex &index)
- {
- // Когда кликаем по элементу в списке, то забираем путь к файлу
- QPixmap pixmap(m_imagesModel->data(index).toString());
- // И устанавливаем файл в область основного просмотра
- m_imageLabel->setPixmap(pixmap);
- m_imageLabel->resize(pixmap.size());
- }
imagedelegate.h
А вот и сам делегат, в чью задачу входит отображение элемента в списке. Для получения пути к файлу изображения я передал указатель на модель данных, а через QModelIndex в методе paint буду получать путь к изображению.
Ещё одним важным моментом является использование метода sizeHint(). Который корректирует размеры элемента в списке. Если в нём не сделать корректировку размера, то размер элемента будет равен по высоте обычно текстовой строке. Превью будет выглядеть совсем ужасно.
- #ifndef IMAGEDELEGATE_H
- #define IMAGEDELEGATE_H
- #include <QStyledItemDelegate>
- #include <QPainter>
- #include <QStyleOptionViewItem>
- #include <QModelIndex>
- #include <QStandardItemModel>
- #include <QPixmap>
- #include <QDebug>
- class ImageDelegate : public QStyledItemDelegate
- {
- public:
- explicit ImageDelegate(QStandardItemModel *model, QObject *parent = nullptr);
- virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
- virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
- QStandardItemModel* m_model;
- };
- #endif // IMAGEDELEGATE_H
Imagedelegate.cpp
Ещё одним важным моментом является использование QRect из QStyleOptionViewItem. Дело в том, что он содержит не только высоту и ширину элемента, но и его положение по x и y в списке. Если не учесть эти координаты, то можно будет увидеть, что все элементы будут отрисовываться в одном месте. Например, в левом верхнем углу списка, если вы зададите x = 0 и y = 0 при отрисовке.
- #include "imagedelegate.h"
- ImageDelegate::ImageDelegate(QStandardItemModel *model, QObject *parent) :
- QStyledItemDelegate(parent),
- m_model(model)
- {
- }
- void ImageDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
- {
- // Вместо отрисовки иконки и текста будем отрисовывать только одно изображение
- // с небольшими отступами в 5 пикселей
- QPixmap pix(m_model->data(index).toString());
- QRect optionRect = option.rect;
- painter->drawPixmap(optionRect.x() + 5,
- optionRect.y() + 5,
- optionRect.width() - 10,
- optionRect.height() - 10 ,
- pix);
- }
- QSize ImageDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
- {
- // Корректируем размеры области отображения объекта в списке
- QSize result = QStyledItemDelegate::sizeHint(option, index);
- result.setHeight(140);
- result.setWidth(140);
- return QSize(140, 140);
- }
А можно сразу передовать Qpixmap?
Нет, нужно сконвертировать информацию в удобоваримый mime type
Доброго времени суток.
А если нужно и изображение и текст?
Что-то потерялся немного....
// Вместо отрисовки иконки и текста будем отрисовывать только одно изображение
// с небольшими отступами в 5 пикселей
QPixmap pix(m_model->data(index).toString());
Добрый день. Посмотрите описание методов drawText у QPainter, он позволит и текст нарисовать