ドロップサイトの例

この例では、ドラッグ&ドロップ操作で利用可能なさまざまなMIMEフォーマットを区別する方法を示します。

Screenshot of the Drop Site example

ドロップ・サイトの例では、他のアプリケーションからのドロップを受け付け、ドラッグ・オブジェクトが提供するMIMEフォーマットを表示します。

この例では、DropAreaDropSiteWindow の2つのクラスと、main() 関数があります。DropArea オブジェクトがDropSiteWindow でインスタンス化され、DropSiteWindow オブジェクトがmain() 関数で呼び出されます。

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 には、4つの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 コンストラクタでは、minimum size を 200x200 ピクセルに設定し、frame styleQFrame::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() 関数を呼び出します。

dragEnterEvent() イベント・ハンドラは、ドラッグが進行中で、マウスがDropArea オブジェクトに入ると呼び出されます。DropSite の例では、マウスがDropArea に入ると、そのテキストを "<drop content>" に設定し、その背景をハイライトします。

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

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

そして、eventacceptProposedAction() を呼び出し、ドロップ・アクションを提案されたものに設定します。最後に、ドロップされたデータとそのMIMEタイプ情報をパラメータとして、changed() シグナルを発信する。

dragMoveEvent()については、提案されたQDragMoveEvent オブジェクト、eventacceptProposedAction()で受け入れるだけである。

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->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 に他の種類のオブジェクトが含まれている場合、DropArea のテキストを、setText() を "Cannot display data" に設定して、ユーザーに通知する。

次に、DropArea'のbackgroundRoleQPalette::Dark に設定し、event'のアクション案を受け入れる。

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

dragLeaveEvent() イベント・ハンドラは、ドラッグ中にマウスがウィジェットから離れると呼び出される。

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

DropArea'の実装では、clear() をクリアしてから、提案されたイベントを受け入れます。

clear() 関数は、DropArea のテキストを "<drop content>" に設定し、backgroundRoleQPalette::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 、2つの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 を使って設定します。列の数は2つに設定され、表は編集できない。また、formatTable の水平ヘッダーは、2列目が伸びるようにフォーマットされ、追加のスペースが確保されます。

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

3つの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);

copyButtonclearButtonquitButtonclicked() シグナルは、それぞれ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 ()関数は、mimeData がサポートするすべてのフォーマットを含むQStringList オブジェクトを返します。

    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 を作成し、flagsQt::ItemIsEnabled に、text alignmentQt::AlignTopQt::AlignLeft に設定する。

QString オブジェクト、text は、format の内容に従ってデータを表示するようにカスタマイズされる。QString'のsimplified ()関数をtext に呼び出して、単語の前後や間にスペースが追加されていない文字列を取得する。

        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 に画像が含まれている場合は、テキストを16進数に変換してデータを表示する。

        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()関数

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. 本書に含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。