拖放网站示例
该示例展示了如何区分拖放操作中可用的各种 MIME 格式。
Drop Site 示例接受来自其他应用程序的拖放操作,并显示拖放对象提供的 MIME 格式。
本例中有两个类DropArea
和DropSiteWindow
以及一个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 事件处理程序的重新实现:
DropArea
类的实现将进一步解释这些事件处理程序。
protected: void dragEnterEvent(QDragEnterEvent *event) override; void dragMoveEvent(QDragMoveEvent *event) override; void dragLeaveEvent(QDragLeaveEvent *event) override; void dropEvent(QDropEvent *event) override; };
DropArea 类的实现
在DropArea
构造函数中,我们将minimum size 设置为 200x200 像素,将frame style 设置为QFrame::Sunken 和QFrame::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() ,将下拉操作设置为建议的操作。最后,我们发出changed()
信号,并将被删除的数据及其 MIME 类型信息作为参数。
对于dragMoveEvent() ,我们只接受提议的QDragMoveEvent 对象,即event ,并使用acceptProposedAction() 。
void DropArea::dragMoveEvent(QDragMoveEvent *event) { event->acceptProposedAction(); }
DropArea
类的dropEvent() 实现会提取event 的 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->hasColor()) { const auto color = mimeData->colorData().value<QColor>(); setText(tr("Color: %1").arg(color.name())); setPalette(QPalette(color)); setBackgroundRole(QPalette::Button); } 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
的文本设置为 "无法显示数据",以通知用户。
然后,我们将DropArea
的backgroundRole 设置为QPalette::Dark ,并接受event
提议的操作。
if (!mimeData->hasColor()) { setPalette({}); 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; };
该类还包含DropArea
,dropArea
,QLabel,abstractLabel
,QTableWidget,formatsTable
,QDialogButtonBox,buttonBox
的私有实例,以及两个QPushButton 对象clearButton
和quitButton
。
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()
信号连接到DropSiteWindow
的updateFormatsTable()
插槽。
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 对象(clearButton
、copyButton
和quitButton
)被实例化,并添加到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);
copyButton
、clearButton
和quitButton
的clicked() 信号分别连接到copy()
、clear()
和close() 上。
在布局方面,我们使用QVBoxLayout,mainLayout
来垂直排列部件。我们还将窗口标题设为 "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 格式。首先,我们将QTableWidget 的rowCount 属性设置为 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);
在每次迭代中,我们都会创建一个QTableWidgetItem,formatItem
,并将其flags 设为Qt::ItemIsEnabled ,将其text alignment 设为Qt::AlignTop 和Qt::AlignLeft 。
QString 对象text
是根据format
的内容定制的,用于显示数据。我们在text
上调用QString 的simplified() 函数,以获得一个字前、字后或字与字之间都没有额外空格的字符串。
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 列表,我们将遍历这些 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() 将format
和text
插入formatsTable
。最后,我们在formatsTable
的第一列调用resizeColumnToContents() 。
main() 函数
在main()
函数中,我们实例化DropSiteWindow
并调用其show() 函数。
int main(int argc, char *argv[]) { QApplication app(argc, argv); DropSiteWindow window; window.show(); return app.exec(); }
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.