Напишем небольшое приложение, которое позволит методом 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, он позволит и текст нарисовать