放置点示例

该示例展示了如何在拖放操作中区分可用的各种MIME格式。

Screenshot of the Drop Site example

放置点示例能够接受来自其他应用的拖放,并显示拖放对象提供的MIME格式。

本例中有两个类,DropAreaDropSiteWindow,以及一个 main() 函数。在 DropSiteWindow 中实例化一个 DropArea 对象;然后,在 main() 函数中调用 DropSiteWindow 对象。

DropArea 类定义

DropArea 类是 QLabel 的子类,具有一个公有槽,clear(),和一个 changed() 信号。

class DropArea : public QLabel
{
    Q_OBJECT

public:
    explicit DropArea(QWidget *parent = nullptr);

public slots:
    void clear();

signals:
    void changed(const QMimeData *mimeData = nullptr);

此外,DropArea 包含了四个 QWidget 事件处理的重新实现:

  1. dragEnterEvent()
  2. dragMoveEvent()
  3. dragLeaveEvent()
  4. dropEvent()

这些事件处理程序在其 DropArea 类的实现中进一步解释。

protected:
    void dragEnterEvent(QDragEnterEvent *event) override;
    void dragMoveEvent(QDragMoveEvent *event) override;
    void dragLeaveEvent(QDragLeaveEvent *event) override;
    void dropEvent(QDropEvent *event) override;
};

DropArea 类实现

DropArea 构造函数中,我们将最小尺寸设置为 200x200 像素,框架样式设置为 QFrame::SunkenQFrame::StyledPanel,并将内容对齐到中心。

DropArea::DropArea(QWidget *parent)
    : QLabel(parent)
{
    setMinimumSize(200, 200);
    setFrameStyle(QFrame::Sunken | QFrame::StyledPanel);
    setAlignment(Qt::AlignCenter);
    setAcceptDrops(true);
    setAutoFillBackground(true);
    clear();
}

同时,我们通过将 acceptDrops 属性设置为 true来在 DropArea 中启用拖放事件。然后,我们启用 autoFillBackground 属性并调用 clear() 函数。

当拖动正在进行且鼠标进入 DropArea 对象时,将调用 dragEnterEvent() 事件处理程序。对于 DropSite 示例,当鼠标进入 DropArea 时,我们将其文本设置为 "<drop content>" 并突出其背景。

void DropArea::dragEnterEvent(QDragEnterEvent *event)
{
    setText(tr("<drop content>"));
    setBackgroundRole(QPalette::Highlight);

    event->acceptProposedAction();
    emit changed(event->mimeData());
}

然后,我们在事件event上调用 acceptProposedAction(),将拖放操作设置为提议的操作。最后,我们通过数据参数(所丢弃数据和它们的MIME类型信息)发射 changed() 信号。

对于 dragMoveEvent,我们只通过调用 acceptProposedAction() 接受提议的 QDragMoveEvent 对象event

void DropArea::dragMoveEvent(QDragMoveEvent *event)
{
    event->acceptProposedAction();
}

DropArea 类对 dropEvent() 的实现提取事件的 MIME 数据并根据情况显示。

void DropArea::dropEvent(QDropEvent *event)
{
    const QMimeData *mimeData = event->mimeData();

mimeData 对象可以包含以下对象之一:图像、HTML 文本、Markdown 文本、纯文本或 URL 列表。

    if (mimeData->hasImage()) {
        setPixmap(qvariant_cast<QPixmap>(mimeData->imageData()));
    } else if (mimeData->hasFormat(u"text/markdown"_s)) {
        setText(QString::fromUtf8(mimeData->data(u"text/markdown"_s)));
        setTextFormat(Qt::MarkdownText);
    } else if (mimeData->hasHtml()) {
        setText(mimeData->html());
        setTextFormat(Qt::RichText);
    } else if (mimeData->hasText()) {
        setText(mimeData->text());
        setTextFormat(Qt::PlainText);
    } else if (mimeData->hasUrls()) {
        QList<QUrl> urlList = mimeData->urls();
        QString text;
        for (qsizetype i = 0, count = qMin(urlList.size(), qsizetype(32)); i < count; ++i)
            text += urlList.at(i).path() + u'\n';
        setText(text);
    } else {
        setText(tr("Cannot display data"));
    }
  • 如果 mimeData 包含图像,我们使用 setPixmap() 在 DropArea 中显示它。
  • 如果mimeData包含HTML,我们使用setText()显示它,并将DropArea的文本格式设置为Qt::RichText.
  • 如果mimeData包含Markdown,我们使用setText()显示它,并将DropArea的文本格式设置为Qt::MarkdownText.
  • 如果mimeData包含纯文本,我们使用setText()显示它,并将DropArea的文本格式设置为Qt::PlainText。如果mimeData包含URL,我们将遍历URL列表,在单独的行中显示它们。
  • 如果mimeData包含其他类型的对象,我们使用setText()将DropArea的文本设置为"无法显示数据",以通知用户。

然后我们将DropAreabackgroundRole设置为QPalette::Dark,并接受event建议的操作。

    setBackgroundRole(QPalette::Dark);
    event->acceptProposedAction();
}

当拖动正在进行且鼠标离开小部件时,会调用dragLeaveEvent()事件处理器。

void DropArea::dragLeaveEvent(QDragLeaveEvent *event)
{
    clear();
    event->accept();
}

对于DropArea的实现,我们首先调用clear(),然后接受建议的事件。

clear()函数将DropArea中的文本设置为"<drop content>",并将backgroundRole设置为QPalette::Dark。最后,它发出changed()信号。

void DropArea::clear()
{
    setText(tr("<drop content>"));
    setBackgroundRole(QPalette::Dark);

    emit changed();
}

DropSiteWindow类定义

DropSiteWindow类包含一个构造函数和一个公开槽,updateFormatsTable().

class DropSiteWindow : public QWidget
{
    Q_OBJECT

public:
    DropSiteWindow();

public slots:
    void updateFormatsTable(const QMimeData *mimeData);
    void copy();

private:
    DropArea *dropArea;
    QLabel *abstractLabel;
    QTableWidget *formatsTable;

    QPushButton *clearButton;
    QPushButton *copyButton;
    QPushButton *quitButton;
    QDialogButtonBox *buttonBox;
};

该类还包含一个私有实例DropAreadropAreaQLabelabstractLabelQTableWidgetformatsTableQDialogButtonBoxbuttonBox,以及两个QPushButton对象,clearButtonquitButton

DropSiteWindow类实现

DropSiteWindow的构造函数中,我们实例化abstractLabel并将其wordWrap属性设置为true。我们还调用adjustSize()函数调整abstractLabel的大小,以便其内容。

DropSiteWindow::DropSiteWindow()
{
    abstractLabel = new QLabel(tr("This example accepts drags from other "
                                  "applications and displays the MIME types "
                                  "provided by the drag object."));
    abstractLabel->setWordWrap(true);
    abstractLabel->adjustSize();

然后我们实例化dropArea并将它的changed()信号连接到DropSiteWindowupdateFormatsTable()槽。

    dropArea = new DropArea;
    connect(dropArea, &DropArea::changed,
            this, &DropSiteWindow::updateFormatsTable);

我们现在设置了QTableWidget对象,formatsTable。它的水平标题使用QStringList对象,labels设置。列数设置为两列,该表不可编辑。同时,formatTable的水平标题已格式化,以确保第二列扩展以占据额外空间。

    formatsTable = new QTableWidget;
    formatsTable->setColumnCount(2);
    formatsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
    formatsTable->setHorizontalHeaderLabels({tr("Format"),  tr("Content")});
    formatsTable->horizontalHeader()->setStretchLastSection(true);

实例化了三个QPushButton对象,clearButtoncopyButtonquitButton,并将它们添加到buttonBox——一个QDialogButtonBox对象中。我们在这里使用QDialogButtonBox以确保按钮以符合平台样式的布局呈现在布局中。

    clearButton = new QPushButton(tr("Clear"));
    copyButton = new QPushButton(tr("Copy"));
    quitButton = new QPushButton(tr("Quit"));

    buttonBox = new QDialogButtonBox;
    buttonBox->addButton(clearButton, QDialogButtonBox::ActionRole);
    buttonBox->addButton(copyButton, QDialogButtonBox::ActionRole);
#if !QT_CONFIG(clipboard)
    copyButton->setVisible(false);
#endif

    buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);

    connect(quitButton, &QAbstractButton::clicked, this, &QWidget::close);
    connect(clearButton, &QAbstractButton::clicked, dropArea, &DropArea::clear);
    connect(copyButton, &QAbstractButton::clicked, this, &DropSiteWindow::copy);

clicked() 信号用于 copyButtonclearButtonquitButton,分别连接到 copy()clear()close()。

在布局上,我们使用 QVBoxLayoutmainLayout 来垂直排列窗口部件。同时,我们将窗口标题设置为 "Drop Site",并将最小尺寸设置为 350x500 像素。

    QVBoxLayout *mainLayout = new QVBoxLayout(this);
    mainLayout->addWidget(abstractLabel);
    mainLayout->addWidget(dropArea);
    mainLayout->addWidget(formatsTable);
    mainLayout->addWidget(buttonBox);

    setWindowTitle(tr("Drop Site"));
    resize(700, 500);
}

接下来是 updateFormatsTable() 函数。这个函数更新 formatsTable,显示对象被拖放到 DropArea 对象上时的 MIME 格式。首先,我们将 QTableWidgetrowCount 属性设置为 0。然后,我们进行验证以确保传入的 QMimeData 对象是有效的。

void DropSiteWindow::updateFormatsTable(const QMimeData *mimeData)
{
    formatsTable->setRowCount(0);
    copyButton->setEnabled(false);
    if (!mimeData)
        return;

一旦我们确定 mimeData 是有效的,我们遍历其支持的格式。

注意: formats() 函数返回一个 QStringList 对象,包含 mimeData 支持的所有格式。

    const QStringList formats = mimeData->formats();
    for (const QString &format : formats) {
        QTableWidgetItem *formatItem = new QTableWidgetItem(format);
        formatItem->setFlags(Qt::ItemIsEnabled);
        formatItem->setTextAlignment(Qt::AlignTop | Qt::AlignLeft);

在每次迭代中,我们创建一个 QTableWidgetItemformatItem,并将其 flags 设置为 Qt::ItemIsEnabled,以及其 text alignment 设置为 Qt::AlignTopQt::AlignLeft

一个 QString 对象,text,根据 format 的内容进行了定制。我们在 text 上调用 QStringsimplified() 函数,以获取一个前后和字间没有额外空格的字符串。

        QString text;
        if (format == u"text/plain") {
            text = mimeData->text().simplified();
        } else if (format == u"text/markdown") {
            text = QString::fromUtf8(mimeData->data(u"text/markdown"_s));
        } else if (format == u"text/html") {
            text = mimeData->html().simplified();
        } else if (format == u"text/uri-list") {
            QList<QUrl> urlList = mimeData->urls();
            for (qsizetype i = 0, count = qMin(urlList.size(), qsizetype(32)); i < count; ++i)
                text.append(urlList.at(i).toString() + u' ');
        } else {
            QByteArray data = mimeData->data(format);
            if (data.size() > 32)
                data.truncate(32);
            text = QString::fromLatin1(data.toHex(' ')).toUpper();
        }

如果 format 包含一个 URL 列表,我们使用空格进行迭代。另一方面,如果 format 包含一个图像,我们通过将文本转换为十六进制来显示数据。

        int row = formatsTable->rowCount();
        formatsTable->insertRow(row);
        formatsTable->setItem(row, 0, new QTableWidgetItem(format));
        formatsTable->setItem(row, 1, new QTableWidgetItem(text));
    }

    formatsTable->resizeColumnToContents(0);
#if QT_CONFIG(clipboard)
    copyButton->setEnabled(formatsTable->rowCount() > 0);
#endif
}

一旦将 text 定制为包含适当的数据,我们使用 setItem() 将 formattext 都插入到 formatsTable 中。最后,在 formatsTable 的第一列上调用 resizeColumnToContents()。

主() 函数

main() 函数中,我们实例化 DropSiteWindow 并调用它的 show() 函数。

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    DropSiteWindow window;
    window.show();
    return app.exec();
}

示例项目 @ code.qt.io

© 2024 The Qt Company Ltd. 本文件包含的文档贡献归相应所有者所有。本文件提供的文档在自由软件基金会的 GNU 自由文档许可版本 1.3 条件下提供。Qt及其相关标志是 The Qt Company Ltd. 在芬兰以及全球其他国家的商标。所有其他商标属于其各自所有者。