Evgenii Legotckoi
30 марта 2017 г. 23:47

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

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

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3.  
  4. #include <QWidget>
  5. #include <QPalette>
  6. #include <QDragEnterEvent>
  7. #include <QMimeData>
  8. #include <QDropEvent>
  9. #include <QScrollArea>
  10. #include <QLabel>
  11. #include <QListView>
  12. #include <QGridLayout>
  13. #include <QStandardItemModel>
  14.  
  15. class Widget : public QWidget
  16. {
  17. Q_OBJECT
  18.  
  19. public:
  20. explicit Widget(QWidget *parent = 0);
  21. ~Widget();
  22.  
  23. // Метод события перетаскивания
  24. virtual void dragEnterEvent(QDragEnterEvent* event) override;
  25. // Метод события отпускания объекта с данными
  26. virtual void dropEvent(QDropEvent *event) override;
  27.  
  28. private slots:
  29. // Слот для обработки кликов по элементам списка
  30. void onImagesListViewClicked(const QModelIndex& index);
  31.  
  32. private:
  33. QScrollArea* m_scrollArea; // Область скроллинга изображения
  34. QLabel* m_imageLabel; // Лейбл для отображения картинок
  35. QListView* m_imagesListView; // Список с изображениями
  36. QGridLayout* m_gridLayout; // Сетка для интерфейса
  37. QStandardItemModel* m_imagesModel; // Модель данных с изображениями
  38. };
  39.  
  40. #endif // WIDGET_H

widget.cpp

Для формирования списка с изображениями будет использоваться QStandardItemModel , а для отображения элементов модели будет использоваться QListView. Для кастомизированного отображения элементов в списке будет использоваться Делегат, поскольку только переопределив отображение внешнего вида можно будет убрать текст. Кстати, этот текст содержит в себе путь к файлу изображения, который и будет использоваться для создания QPixmap и отображения картинки как в основном просмотре, так и в превьюшках. Чтобы получить из модели данных путь к файлу, необходимо будет воспользоваться методом data(), и в качестве аргументов передать QModellndex и enum Qt::DisplayRole , который является аргументом по умолчанию.

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3.  
  4. #include <QStandardItem>
  5. #include "imagedelegate.h"
  6.  
  7. Widget::Widget(QWidget *parent) :
  8. QWidget(parent)
  9. {
  10. setAcceptDrops(true); // разрешаем события отпускания объектов данных
  11. setMinimumWidth(640);
  12. setMinimumHeight(480);
  13.  
  14. /// Настраиваем интерфейс
  15. m_gridLayout = new QGridLayout(this);
  16. m_imagesListView = new QListView(this);
  17.  
  18. // Создадим модель данных для списка изображений
  19. m_imagesModel = new QStandardItemModel(m_imagesListView);
  20. m_imagesListView->setModel(m_imagesModel); // Установим модель во вьюшку для превью изображений
  21. m_imagesListView->setFixedWidth(200);
  22.  
  23. // Без делегата не удастся избавиться от текста в элементе списка и настроить отображение превью
  24. m_imagesListView->setItemDelegate(new ImageDelegate(m_imagesModel, m_imagesListView));
  25.  
  26. // Настраиваем область скроллинга для текущего изображения
  27. m_scrollArea = new QScrollArea(this);
  28. m_scrollArea->setBackgroundRole(QPalette::Dark);
  29. m_imageLabel = new QLabel(this);
  30. m_scrollArea->setWidget(m_imageLabel);
  31. m_gridLayout->addWidget(m_scrollArea, 0, 0);
  32. m_gridLayout->addWidget(m_imagesListView, 0, 1);
  33.  
  34. connect(m_imagesListView, &QListView::clicked, this, &Widget::onImagesListViewClicked);
  35. }
  36.  
  37. Widget::~Widget()
  38. {
  39.  
  40. }
  41.  
  42. void Widget::dragEnterEvent(QDragEnterEvent *event)
  43. {
  44. // Обязательно необходимо допустить событие переноса данных в область окна приложения
  45. event->accept();
  46. }
  47.  
  48. void Widget::dropEvent(QDropEvent *event)
  49. {
  50. // Когда отпускаем файл в область приложения,
  51. // то забираем путь к файлу из MIME данных
  52. QString filePath = event->mimeData()->urls()[0].toLocalFile();
  53. // Создаём изображение
  54. QPixmap pixmap(filePath);
  55. // Помещаем его в область скроллинга через QLabel
  56. m_imageLabel->setPixmap(pixmap);
  57. m_imageLabel->resize(pixmap.size());
  58.  
  59. // Добавляем элемент в список
  60. m_imagesModel->appendRow(new QStandardItem(QIcon(pixmap), filePath));
  61. }
  62.  
  63. void Widget::onImagesListViewClicked(const QModelIndex &index)
  64. {
  65. // Когда кликаем по элементу в списке, то забираем путь к файлу
  66. QPixmap pixmap(m_imagesModel->data(index).toString());
  67. // И устанавливаем файл в область основного просмотра
  68. m_imageLabel->setPixmap(pixmap);
  69. m_imageLabel->resize(pixmap.size());
  70. }

imagedelegate.h

А вот и сам делегат, в чью задачу входит отображение элемента в списке. Для получения пути к файлу изображения я передал указатель на модель данных, а через QModelIndex в методе paint буду получать путь к изображению.

Ещё одним важным моментом является использование метода sizeHint(). Который корректирует размеры элемента в списке. Если в нём не сделать корректировку размера, то размер элемента будет равен по высоте обычно текстовой строке. Превью будет выглядеть совсем ужасно.

  1. #ifndef IMAGEDELEGATE_H
  2. #define IMAGEDELEGATE_H
  3.  
  4. #include <QStyledItemDelegate>
  5. #include <QPainter>
  6. #include <QStyleOptionViewItem>
  7. #include <QModelIndex>
  8. #include <QStandardItemModel>
  9. #include <QPixmap>
  10. #include <QDebug>
  11.  
  12. class ImageDelegate : public QStyledItemDelegate
  13. {
  14. public:
  15. explicit ImageDelegate(QStandardItemModel *model, QObject *parent = nullptr);
  16.  
  17. virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
  18. virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
  19. QStandardItemModel* m_model;
  20. };
  21.  
  22.  
  23. #endif // IMAGEDELEGATE_H

Imagedelegate.cpp

Ещё одним важным моментом является использование QRect из QStyleOptionViewItem. Дело в том, что он содержит не только высоту и ширину элемента, но и его положение по x и y в списке. Если не учесть эти координаты, то можно будет увидеть, что все элементы будут отрисовываться в одном месте. Например, в левом верхнем углу списка, если вы зададите x = 0 и y =  0 при отрисовке.

  1. #include "imagedelegate.h"
  2.  
  3. ImageDelegate::ImageDelegate(QStandardItemModel *model, QObject *parent) :
  4. QStyledItemDelegate(parent),
  5. m_model(model)
  6. {
  7.  
  8. }
  9.  
  10. void ImageDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
  11. {
  12. // Вместо отрисовки иконки и текста будем отрисовывать только одно изображение
  13. // с небольшими отступами в 5 пикселей
  14. QPixmap pix(m_model->data(index).toString());
  15. QRect optionRect = option.rect;
  16. painter->drawPixmap(optionRect.x() + 5,
  17. optionRect.y() + 5,
  18. optionRect.width() - 10,
  19. optionRect.height() - 10 ,
  20. pix);
  21. }
  22.  
  23. QSize ImageDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
  24. {
  25. // Корректируем размеры области отображения объекта в списке
  26. QSize result = QStyledItemDelegate::sizeHint(option, index);
  27. result.setHeight(140);
  28. result.setWidth(140);
  29. return QSize(140, 140);
  30. }

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

Рекомендуемые статьи по этой тематике

По статье задано0вопрос(ов)

4

Вам это нравится? Поделитесь в социальных сетях!

Юрий
  • 21 января 2021 г. 0:34

// Вместо отрисовки иконки и текста будем отрисовывать только одно изображение
// с небольшими отступами в 5 пикселей
QPixmap pix(m_model->data(index).toString());

А можно сразу передовать Qpixmap?

Evgenii Legotckoi
  • 2 июля 2021 г. 17:45

Нет, нужно сконвертировать информацию в удобоваримый mime type

Ruslan Polupan
  • 11 августа 2022 г. 13:37

Доброго времени суток.
А если нужно и изображение и текст?
Что-то потерялся немного....

// Вместо отрисовки иконки и текста будем отрисовывать только одно изображение
// с небольшими отступами в 5 пикселей
QPixmap pix(m_model->data(index).toString());

Evgenii Legotckoi
  • 24 августа 2022 г. 17:32

Добрый день. Посмотрите описание методов drawText у QPainter, он позволит и текст нарисовать

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь