Evgenii Legotckoi
Evgenii Legotckoi30 березня 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 я не буду, оскільки там програмний код, який створюється за замовчуванням при створенні проекту.

віджет.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);
}

Проект програми з перетягуванням

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

Вам це подобається? Поділіться в соціальних мережах!

Юрий
  • 20 січня 2021 р. 13:34

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

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

Evgenii Legotckoi
  • 02 липня 2021 р. 07:45

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

Ruslan Polupan
  • 11 серпня 2022 р. 03:37

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

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

Evgenii Legotckoi
  • 24 серпня 2022 р. 07:32

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

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
e
  • ehot
  • 31 березня 2024 р. 14:29

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

  • Результат:78бали,
  • Рейтинг балів2
B
  • Bogdannn
  • 27 березня 2024 р. 19:21

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

  • Результат:16бали,
  • Рейтинг балів-10
B
  • Bogdannn
  • 27 березня 2024 р. 19:15

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

  • Результат:46бали,
  • Рейтинг балів-6
Останні коментарі
k
kmssr08 лютого 2024 р. 18:43
Qt Linux - Урок 001. Автозапуск програми Qt під Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко05 лютого 2024 р. 01:50
Qt WinAPI - Урок 007. Робота з ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 грудня 2023 р. 10:30
Boost - статичне зв&#39;язування в проекті CMake під Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 грудня 2023 р. 08:38
Boost - статичне зв&#39;язування в проекті CMake під Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik18 грудня 2023 р. 21:01
Qt/C++ - Урок 056. Підключення бібліотеки Boost в Qt для компіляторів MinGW і MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Тепер обговоріть на форумі
a
a_vlasov14 квітня 2024 р. 06:41
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел Дорофеев14 квітня 2024 р. 02:35
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrex04 квітня 2024 р. 04:47
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…
P
Pisych27 лютого 2023 р. 04:04
Как получить в массив значения из связанной модели? Спасибо, разобрался:))
AC
Alexandru Codreanu19 січня 2024 р. 11:57
QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…

Слідкуйте за нами в соціальних мережах