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
AD

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

  • Результат:50бали,
  • Рейтинг балів-4
m
  • molni99
  • 26 жовтня 2024 р. 01:37

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

  • Результат:80бали,
  • Рейтинг балів4
m
  • molni99
  • 26 жовтня 2024 р. 01:29

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

  • Результат:20бали,
  • Рейтинг балів-10
Останні коментарі
ИМ
Игорь Максимов22 листопада 2024 р. 11:51
Django - Підручник 017. Налаштуйте сторінку входу до Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi31 жовтня 2024 р. 14:37
Django - Урок 064. Як написати розширення для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 жовтня 2024 р. 08:19
Читалка файлів fb3 на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов05 жовтня 2024 р. 07:51
Django - Урок 064. Як написати розширення для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas505 липня 2024 р. 11:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Тепер обговоріть на форумі
Evgenii Legotckoi
Evgenii Legotckoi24 червня 2024 р. 15:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 листопада 2024 р. 06:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject04 червня 2022 р. 03:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 жовтня 2024 р. 09:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

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