Евгений Легоцкой30 марта 2017 г. 13: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 , а также добавим объекты для формирования интерфейса и модель данных для изображений.

#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

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.
Поддержать автора Donate

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

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

Комментарии

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

Позвольте мне порекомендовать вам отличный хостинг, на котором расположен EVILEG.

В течение многих лет Timeweb доказывает свою стабильность.

Для проектов на Django рекомендую VDS хостинг

Посмотреть Хостинг
НИ

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:90баллов,
  • Очки рейтинга8
НИ

C++ - Тест 003. Условия и циклы

  • Результат:92баллов,
  • Очки рейтинга8
НИ

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

  • Результат:80баллов,
  • Очки рейтинга4
Последние комментарии
D

Django - Урок 001. Развёртывание сайта на Django + PostgreSQL + Gunicorn + Nginx

А почему нельзя? Где можно об этом почитать? Киньте, пожалуйста, в меня ссылкой.
D

Django - Урок 049. Оптимизация производительности Django на примере боевого проекта

Огромное спасибо вам за статью! Для меня стали открытием select_related и prefetch_related
t
  • t1m4
  • 24 февраля 2021 г. 2:56

Django - Урок 052. Переопределение модели пользователя

В данном случае я заходил под superuser но все равно не появлялись эти поля

Django - Урок 001. Развёртывание сайта на Django + PostgreSQL + Gunicorn + Nginx

Поднял сервис с помощью systemd, вот по этому мануалу: https://habr.com/ru/post/501414/
t
  • t1m4
  • 23 февраля 2021 г. 7:11

Django - Урок 052. Переопределение модели пользователя

А как дать ему эти права?
Сейчас обсуждают на форуме
  • BlinCT
  • 6 марта 2021 г. 10:57

Работа с QJsonObject и получение данных из него

Вопрос решен. Оказалось что я не очень внимательно прочитал описание метода root.take("devices") Оказывается он удаляте ключ, а занчит и все данные по нему. И по этому после этого…

Как совместить таблицу и дерево в Qt

Добрый день. Имеется таблица QTableView, нужно у некоторых строк сделать возможность раскрытий, как в QTreeVidget и отоборажать в них аналогичные строки. Скажите пожалуйста, как это мо…
KM

не получаеться добавить списки в списокб ошибка в петле

решение: с line 99: listy = [] lista = [] for single_lp in max_list_from_all_plates: suma = 0 lists = [] for sign in single_lp: Highest_score=0 bigl…
KM

Не отдаётся статика на виртуальной машине

В итоге выставил 775 на все файлы проекта и всё заработало. Но я не уверен, что так правильно.
О нас
Услуги
© EVILEG 2015-2020
Рекомендует хостинг TIMEWEB