retireSakiの日記

たぶん引退した?ソフトウエアエンジニアのブログ

Qt でドラッグアイテムを受取る方法

 今回は前回作成した

retiresaki.hatenablog.com

をベースに、ちょっと便利な機能を追加します。

 アプリで扱うファイルは、通常ファイルダイアログなどを通じて取得します。
ファイルを開く場合、ファイルダイアログの他Nautilusなどのアプリからのドラッグ&ドロップ経由でも扱えると便利なケースが多々あります (^o^)v

そこで、今回はドラッグ&ドロップ経由でもXSPFファイルを開けるように改良します。
 実際の動作を動画にて...


組み込み方

(1)ドロップ許可を与えます。

  setAcceptDrops(true);
 を使います。

(2)ドラッグ通知時にドロップ可能な判断します。

 以下の関数でドラッグメッセージを受け取り、ドロップ可能などうか判断します。
  void dragEnterEvent(QDragEnterEvent *e);

 関数内で
  e->mimeData()
 が受取可能な mime か判断します。
  hasText() = text/plain
  hasHtml() = text/html
  hasUrls() = text/uri-list
  hasImage() = image/ *
  hasColor() = application/x-color

 の5種類です。

 受取可能なものであれば、
  acceptProposedAction();
 で許可を与えます。
 許可すると、ドラッグ中のマウスカーソルの形状も変化します。
 こんな感じのマウスカーソルです (^^♪

(3)ドロップ通知時に対応した処理を行います。

 以下の関数でドロップメッセージを受け取り、ドロップ時の処理を行います。
  void dropEvent(QDropEvent *e);

 時間がかかるような処理の場合、別のメッセージなどを発生させ、ドロップスレッドを開放させます。

 今回は、処理が短時間で済むので、そのままファイルのオープン処理を行っています。


今回もメインウィンドウのクラスソースとヘッダーだけ張り付けます。


mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QtWidgets>
#include <QStandardPaths>
#include <QFileDialog>

#include <QDomDocument>

#include "aboutdialog.h"
#include "appinitdata.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:

    // menus
    void on_action_Open_triggered();
    void on_action_Quit_triggered();
    void on_action_Save_triggered();
    void on_action_SaveAS_triggered();
    void on_action_New_triggered();

    void on_action_About_triggered();

private:
    Ui::MainWindow *ui;
    QLabel *label_MessageBar_normal = new QLabel;

    // application init-data
    appInitData appInit;

    // XML tree
    QDomDocument domDocument;
    QString editing_Filepath;

    int m_nTrack_cnt;

    void open_PlayLlist_file(QString filename);

    void XML_tree_open();
    bool XML_tree_read(QIODevice *device);

    void parsePlaylistElement(QTreeWidgetItem *ptree_item,const QDomElement &root);
    void parseTrackElement(QTreeWidgetItem *ptree_track, const QDomElement &element);

protected:
    void dragEnterEvent(QDragEnterEvent *e);
    void dropEvent(QDropEvent *e);
};

#endif // MAINWINDOW_H


mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // get init
    appInit.m_byteArray_name = "treeWidget_playlist";
    appInit.get_appInitDatas(this->window(), "MAIN");

    // setting status bar
    ui->statusBar->addWidget(label_MessageBar_normal);
    label_MessageBar_normal->setText(tr("File not selected"));

    // setting treeWidget_playlist
    QStringList list_label;
    list_label << tr("tag") << tr("value");
    ui->treeWidget_playlist->header()->setSectionResizeMode(QHeaderView::Interactive);
    ui->treeWidget_playlist->setHeaderLabels(list_label);
    if (appInit.m_bbyteArray)
        ui->treeWidget_playlist->header()->restoreState(appInit.m_byteArray);

    // etc init
    editing_Filepath = appInit.m_sLastopenPath;

    // accept drop
    setAcceptDrops(true);
}

MainWindow::~MainWindow()
{
    // set init
    appInit.m_byteArray_name = "treeWidget_playlist";
    appInit.m_byteArray = ui->treeWidget_playlist->header()->saveState();
    appInit.m_bbyteArray = true;

    if (editing_Filepath.length() == 0)
        appInit.m_sLastopenPath = "";
    else{
        QFileInfo fi = QFileInfo(editing_Filepath);
        if (fi.isFile())
            appInit.m_sLastopenPath = QFileInfo(editing_Filepath).path();
        else
            appInit.m_sLastopenPath = editing_Filepath;
    }

    appInit.set_appInitDatas(this->window(), "MAIN");

    delete ui;
}

/* Menu actions */
void MainWindow::on_action_Quit_triggered()
{
    this->close();
}

void MainWindow::on_action_Open_triggered()
{

    QString init_path;

    if (editing_Filepath.length() > 3){
        QFileInfo fi = QFileInfo(editing_Filepath);
        if (fi.isFile())
            init_path = QFileInfo(editing_Filepath).path();
        else
            init_path = editing_Filepath;
    }
    else
        init_path = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation)[0];

    QString filename = QFileDialog::getOpenFileName(
                this,
                tr("Open Playlist file"),
                init_path,
                tr("Playlist File (*.xspf);;ALL file (*.*)"));
    if (filename.length()==0){
        this->statusBar()->showMessage(tr("select : Canseled"),3000);
        return;
    }

}

void MainWindow::open_PlayLlist_file(QString filename)
{
    editing_Filepath = filename;
    label_MessageBar_normal->setText(editing_Filepath);
    this->statusBar()->showMessage(tr("select : ")+filename,3000);

    XML_tree_open();

    label_MessageBar_normal->setText(
                QString::number(m_nTrack_cnt)+" tracks << "+editing_Filepath);
}

void MainWindow::on_action_Save_triggered()
{

}

void MainWindow::on_action_SaveAS_triggered()
{

}

void MainWindow::on_action_New_triggered()
{

}

void MainWindow::on_action_About_triggered()
{
    aboutDialog *pdialog = new aboutDialog(this);
    pdialog->exec();
    delete pdialog;
}

/************************************************************/
/* functions */
void MainWindow::XML_tree_open()
{
    ui->treeWidget_playlist->clear();

    QFile file(editing_Filepath);
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
        QMessageBox::warning(this, tr("PLeditor"),
                             tr("Cannot read file %1:\n%2.")
                             .arg(editing_Filepath)
                             .arg(file.errorString()));
        this->statusBar()->showMessage(tr("Cannot open file : ")+editing_Filepath, 3000);
        return;
    }

    XML_tree_read(&file);
}

bool MainWindow::XML_tree_read(QIODevice *device)
{
    QString errorStr;
    int errorLine;
    int errorColumn;

    if (!domDocument.setContent(
                device, true, &errorStr, &errorLine,&errorColumn)) {
        QMessageBox::information(window(), tr("PLeditor"),
                                 tr("Parse error at line %1, column %2:\n%3")
                                 .arg(errorLine)
                                 .arg(errorColumn)
                                 .arg(errorStr));
        this->statusBar()->showMessage(
                    tr("Parse error in file : ")+editing_Filepath, 3000);
        return false;
    }

    QDomElement root = domDocument.documentElement();
    if (root.tagName() != "playlist") {
        QMessageBox::information(window(), tr("PLeditor"),
                                 tr("The file is not an playlist file."));
        this->statusBar()->showMessage(
                    tr("not an playlist : ")+editing_Filepath, 3000);
        return false;
    } else if (root.hasAttribute("version")
               && root.attribute("version") != "1") {
        QMessageBox::information(window(), tr("PLeditor"),
                                 tr("The file is not an playlist version 1 "
                                    "file."));
        this->statusBar()->showMessage(
                    tr("not an playlist version: ")+editing_Filepath, 3000);
        return false;
    }

    QStringList liststr;

    QDomNamedNodeMap attrMap = root.attributes();

    liststr << "playlist "+attrMap.item(0).toAttr().name() << attrMap.item(0).toAttr().value();
    QTreeWidgetItem *ptree_item = new QTreeWidgetItem(liststr, 0);
    ptree_item->setToolTip(0,tr("attibute in playlist"));
    ui->treeWidget_playlist->addTopLevelItem(ptree_item);

    // playlist attribute
    int attrCount = attrMap.length();
    for(int i = 0; i < attrCount; ++i){
        QDomAttr attr = attrMap.item(i).toAttr();
    }

    // in playlist-tag
    parsePlaylistElement(ptree_item,root);

    // in tracklist
    m_nTrack_cnt = 0;

    // Loops in a track list
    QDomElement child = root.firstChildElement("trackList");
    while (!child.isNull()) {
        liststr.clear();
        liststr << "<tracklist>" << "";
        QTreeWidgetItem *ptree_item_tracklist = new QTreeWidgetItem(liststr, 0);
        ptree_item_tracklist->setToolTip(0,tr("tracklist"));
        ptree_item->addChild(ptree_item_tracklist);

        // Loops in a track
        QDomElement child2 = child.firstChildElement("track");
        while (!child2.isNull()) {
            liststr.clear();
            liststr << "<track>" << "";
            QTreeWidgetItem *ptree_item_track = new QTreeWidgetItem(liststr, 0);
            ptree_item_track->setToolTip(0,tr("track"));
            ptree_item_tracklist->addChild(ptree_item_track);

            // in track
            parseTrackElement(ptree_item_track,child2);

            m_nTrack_cnt++;

            child2 = child2.nextSiblingElement("track");
        }

        child = child.nextSiblingElement("tracklist");
    }

    ui->treeWidget_playlist->expandAll();

    if (m_nTrack_cnt == 0){
        this->statusBar()->showMessage(
                    tr("Track was not found : ")+editing_Filepath, 10000);
    }
    else {
        this->statusBar()->showMessage(
                    QString::number(m_nTrack_cnt)+tr(" tracks found : ")+editing_Filepath, 10000);
    }

    return true;
}

// Elements other than track list
void MainWindow::parsePlaylistElement(QTreeWidgetItem *ptree_playlist,const QDomElement &root)
{
    QStringList liststr;

    QDomNode node = root.firstChild();
    while (!node.isNull()) {
        if (node.isElement()) {
            QDomElement element = node.toElement();
            if (element.tagName() != "trackList"){
                liststr.clear();
                liststr << element.tagName() << element.text();
                QTreeWidgetItem *ptree_playlist_element = new QTreeWidgetItem(liststr, 0);
                ptree_playlist_element->setFlags(Qt::ItemIsEditable|Qt::ItemIsEnabled);
                ptree_playlist->addChild(ptree_playlist_element);
            }
        }
        node = node.nextSibling();
    }

}

// Elements in a track
void MainWindow::parseTrackElement(QTreeWidgetItem *ptree_track,const QDomElement &element)
{
    QStringList liststr;

    QDomElement child = element.firstChildElement();
    while (!child.isNull()) {
        QString tag_name = child.tagName();
        QString value = child.text();

        QString str= tag_name+","+value;

        liststr.clear();
        liststr << tag_name << value;

        QTreeWidgetItem *ptree_track_item = new QTreeWidgetItem(liststr, 0);
        ptree_track_item->setFlags(Qt::ItemIsEditable|Qt::ItemIsEnabled);
        ptree_track->addChild(ptree_track_item);

        child = child.nextSiblingElement();
    }

}

/************************************************************
    drag & drop xspf-file
 ************************************************************/

void MainWindow::dragEnterEvent(QDragEnterEvent *e)
{
    if (e->mimeData()->hasUrls()) {
        e->acceptProposedAction();
    }
}
void MainWindow::dropEvent(QDropEvent *e)
{
    if (e->mimeData()->hasUrls()) {
        QString suffix = QFileInfo(e->mimeData()->text().trimmed()).suffix();
        if (suffix.toLower() == "xspf"){
            open_PlayLlist_file(QUrl(e->mimeData()->text().trimmed()).toLocalFile());
        }
    }
}

他のクラスファイルやリソースファイルなどはZIPファイルをダウンロードして確認してください。


ZIP-file (51KB) download from Google DRIVE

 空いた時間を見つけながら作成しているので、バグ等があってもご愛嬌でお願いします。(簡単なデバックと動作テストしかしていません)
 今回は表示だけですが、最終的にはエディターとなる予定です (^^♪
また、区切りの良いタイミングで公開しますので、気長にお待ちくださいw

「Qt Quick」アプリケーション開発 (I・O BOOKS)

「Qt Quick」アプリケーション開発 (I・O BOOKS)

Qtプログラミング入門―使いやすいフレームワークを基礎から解説 (I・O BOOKS)

Qtプログラミング入門―使いやすいフレームワークを基礎から解説 (I・O BOOKS)

PythonでGUIをつくろう─はじめてのQt for Python (技術書典シリーズ(NextPublishing))

PythonでGUIをつくろう─はじめてのQt for Python (技術書典シリーズ(NextPublishing))

注目記事

「Amazon.co.jpアソシエイト」または「[乙の名称を挿入]は、Amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイトプログラムである、Amazonアソシエイト・プログラムの参加者です。