Реклама

Qt/C++ - Урок 061. Добавление изображений в приложение методом Drag And Drop из файлового менеджера

РуководствоQtQDropEvent, QStyledDelegate, QStandardItemModel, QListView2419

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

Проект приложения с Drag and Drop

Реклама

Комментарии

Комментарии

Только авторизованные пользователи могут оставлять комментарии.
Пожалуйста, Авторизуйтесь или Зарегистрируйтесь
Последние пройденные тестирования
  • namat
  • 22 июня 2017 г. 5:27

C++ - Тест 002. Константы

Результат - 91 баллов

  • namat
  • 22 июня 2017 г. 5:22

C++ - Тест 001. Первая программа и типы данных

Результат - 100 баллов

C++ - Тест 001. Первая программа и типы данных

Результат - 80 баллов

Последние комментарии
  • EVILEG
  • 16 июня 2017 г. 12:15

Qt/C++ - Урок 023. Перетаскивание QGraphicsItem на QGraphicsScene мышью

Не, не совсем так. Здесь нужно будет учитывать сдвиг позиции курсора относительно координаты (0, 0) в координатной системе MoveItem. Получается тогда так: Координаты сдвиг...

  • damix
  • 15 июня 2017 г. 21:39

Qt/C++ - Урок 023. Перетаскивание QGraphicsItem на QGraphicsScene мышью

Лучше где-то в классе MoveItem объявить QPointF mouseCoords; А потом void MoveItem::mousePressEvent(QGraphicsSceneMouseEvent *event){ mouseCoords = event->pos();}void...

  • EVILEG
  • 15 июня 2017 г. 13:27

QML - Урок 007. ListView Qml. Динамическое создание и удаление элементов

Не хотелось бы повторяться. В статье по сигналам и слотам в QML есть вариант использования C++ объекта. Там используется тип Connections , который можно настроить на сигн...

Сейчас обсуждают на форуме

QEvent. Переопределение метода event().

EVILEG, благодарю, что все разъяснили. ;)

  • EVILEG
  • 17 июня 2017 г. 13:13

QWizard

Думаю, что это возможно. У QWizard есть метод setButtonLayout , который устанавливает, какие именно кнопки могут быть видны на страницах. Так вот, у QWizardPa...

  • DrGen
  • 15 июня 2017 г. 17:10

Ключи QMultiMap в роли кнопок

Большое спасибо! даже не думал, что вопрос окажется интересным.

Вопрос по QTableWidget ?

Спасибо )