Напишемо невелику програму, яка дозволить методом 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); }
А можно сразу передовать Qpixmap?
Нет, нужно сконвертировать информацию в удобоваримый mime type
Доброго времени суток.
А если нужно и изображение и текст?
Что-то потерялся немного....
// Вместо отрисовки иконки и текста будем отрисовывать только одно изображение
// с небольшими отступами в 5 пикселей
QPixmap pix(m_model->data(index).toString());
Добрый день. Посмотрите описание методов drawText у QPainter, он позволит и текст нарисовать