2012年1月30日月曜日

( Qt C++ )アプリアイコンへドラッグ&ドロップしそのパスを取得


はいそれでは表題の件やっていきます。

windowsなんかではアプリアイコンへファイルをドラッグ&ドロップしアプリを起動するなんてことがあるかと思います。その時にこの記事が役に立つかと思います。

サンプルは独自のものを使用します。
そして、いつものようにQtCreaterの使用を前提とします。(QtCreaterなどの使い方は ”Qtをはじめよう" を見てください。)

ではコードを


(main.cpp)
#include <QtGui/QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    MainWindow w;
    w.setText_to_List(a.arguments());//ここ重要
    w.show();
    return a.exec();
}

はい簡単ですね。わかっているとは思いますがこれはGUIアプリケーションです。

QApplication クラスは、GUI アプリケーションの制御フローとメインの設定を管理します。
QtCreaterを使用している場合は自動で記述されているので特に気にしたことはないかと思いますがいろんな設定をここでやっているそうです。(QApplicationリファレンス

それでこのQApplication aにmainのc++の各引数argc(argvの数)とargv(起動の際の引数)を引数にした後は,aから起動引数にアクセスできます。別に char* argv[]に直接アクセスしてもいいのですが、QStringList, QStringなどとして扱う場合はQApplication aからアクセスしたほうが最初からその型で返ってくるので処理が楽です。

上記サンプルではa.arguments()で起動引数すべてをQStringListで返し、それをMainWindowのsetText_to_List(独自関数)の引数にして渡しています。

※なおargvには[0]に自身のアプリケーションの絶対パス。[1]以降にドラック&ドロップしたファイルのパス、もしくは起動時設定した文字列が格納されます。

MainWindowは以下のようなコードになります。


(mainwindow.h)
#include <QMainWindow>
#include <QStringList>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT//マクロ
    
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    void setText_to_List(QStringList strlist);//この関数を使用し起動引数を表示
    
private:
    Ui::MainWindow *ui;//uiにはGUI部品についての記述
};

(mainwindow.cpp)
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)//コンストラクタ
{
    //uiにはGUI部品について記述
    //ここで初期化しています。
    ui->setupUi(this);
}

MainWindow::~MainWindow()//デストラクタ
{
    delete ui;
}

//argvをQStringListで受け取りQListWidgetにすべて表示する
void MainWindow::setText_to_List(QStringList strlist)
{
    foreach(QString str , strlist)
        ui->listWidget_2->addItem(new QListWidgetItem(str));
}

はい簡単ですね。uiについては”Qtをはじめよう"か何かで確認してください。ここでの理解にはあまり重要ではないので説明は省きます。

ヘッダ部、cppともに見ての通りです。重要なのはsetText_to_List関数で起動引数をQListWidgetにセットしているとこだけです。

実行すると以下のようになります。



(ドラック&ドロップしアプリアイコンの上にカーソルを持ってきている場面)
 

(起動後の画面)

 

ちゃんとドラッグ&ドロップしたファイルの絶対パスが取得できていますね。

以上です。

2012年1月29日日曜日

( Qt C++ )カスタムウィジットでドラック&ドロップ(QListWidget派生)


はいそれでは表題の件やっていきます。
今回は同アプリケーションのウィジット同士のドラッグ&ドロップの作成となります。

サンプルはC++ GUI Programming with Qt4 207ページのQListWidget派生のサンプルをほぼそのまま使用します。
そして、いつものようにQtCreaterの使用を前提とします。(QtCreaterなどの使い方は ”Qtをはじめよう" を見てください。)

※また、Qt Createrのデザイナで派生ウィジットを使いたい場合があるかと思います。その場合は「Qtをはじめよう!:独自ウィジェットを作成しデザイナで使用しよう 」の記事が役に立つかと思います。そちらを参考にしてください。

ではコードを

(custumlist.h)
#include <QListWidget>
#include <QMouseEvent>
#include <QApplication>

class CustumList : public QListWidget
{
    Q_OBJECT//マクロ

public:
    explicit CustumList(QWidget *parent = 0);//コンストラクタ

protected:
    void mousePressEvent(QMouseEvent *e);//マウス左をクリックしたら呼ばれる
    void mouseMoveEvent(QMouseEvent *e);//マウスカーソルを移動したら呼ばれる
    void dragEnterEvent(QDragEnterEvent *e);//ドラッグされたものが来たら呼ばれる
    void dragMoveEvent(QDragMoveEvent *e);//ドラッグ&ドロップが操作されている間呼ばれる
    void dropEvent(QDropEvent *e);//ドロップの動作

private:
    void startDrag();//自分のウィジットでドラッグを開始した場合
    QPoint startPos;//マウスの位置
};

(custumlist.cpp)
#include "custumlist.h"

CustumList::CustumList(QWidget *parent) :
    QListWidget(parent)//コンストラクタ
{
    //ドラッグ&ドロップを受け付ける
    //trueで許可。
    setAcceptDrops(true);
}

void CustumList::mousePressEvent(QMouseEvent *e)
{
    //マウス左ボタンをクリックしたら
    if(e->button() == Qt::LeftButton)
    {
        //そのマウスの位置を保存
        startPos = e->pos();
    }

    //デフォルトの動作へ
    QListWidget::mousePressEvent(e);
}

void CustumList::mouseMoveEvent(QMouseEvent *e)
{
    ////マウス左ボタンをクリック中なら
    if(e->buttons() & Qt::LeftButton)
    {
        //保存していたマウスの位置と現在のマウスの位置の差を求める
        //manhattanLengthは絶対値を求めるもの
        int distance = (e->pos() - startPos).manhattanLength();

        //上で求めた差分とアプリで設定してあるドラッグ&ドロップを開始する差分
        //を比べ、それ以上ならドラッグを開始する。
        //なおstartDragDistance()はデフォルトでは4が返る
        //変更はsetStartDragDistance(int)
        if(distance >= QApplication::startDragDistance())
        {
            startDrag();
        }

        QListWidget::mouseMoveEvent(e);
    }
}

void CustumList::startDrag()
{
    //現在選択中のアイテム
    QListWidgetItem *item = currentItem();

    if (item)
    {
        QMimeData *mimeData = new QMimeData;
        mimeData->setText(item->text());

        //ドラッグ中のアイコンのセット
        QDrag *drag = new QDrag(this);
        drag->setMimeData(mimeData);
        drag->setPixmap(QPixmap("//home//ubuntu001//icon.png"));

        //ドラッグ処理の開始。
        //startはドラック&ドロップが終わるまで処理が返らない
        if (drag->start(Qt::MoveAction) == Qt::MoveAction)
            delete item;
    }
}

void CustumList::dragEnterEvent(QDragEnterEvent *e)
{
    CustumList *source =
            qobject_cast<CustumList *>(e->source());
    
    //自分意外からのドラック&ドロップなら
    if (source && source != this)
    {
        e->setDropAction(Qt::MoveAction);
        e->accept();
    }
}

void CustumList::dragMoveEvent(QDragMoveEvent *e)
{
    CustumList *source =
            qobject_cast<CustumList *>(e->source());
    
    //自分意外からのドラック&ドロップなら
    if (source && source != this)
    {
        e->setDropAction(Qt::MoveAction);
        e->accept();
    }
}

void CustumList::dropEvent(QDropEvent *e)
{
    CustumList *source =
            qobject_cast<CustumList *>(e->source());
    
    //自分意外からのドラック&ドロップなら
    if (source && source != this)
    {
        //自分のアイテムへドロップされたものを追加
        addItem(e->mimeData()->text());
        e->setDropAction(Qt::MoveAction);
        e->accept();
    }
}

はいややこしいですね。ヘッダ部は見ての通りです。説明は省略します。

.cppは順に説明します。
まずコンストラクタ。ここではただドラッグ&ドロップを受け付けるよう設定しているだけです。(setAcceptDrops

次はmousePressEventここではマウスが左ボタンを押したことを確認し、もし押していたらマウスの位置を変数に保存しています。これはmouseMoveEventで使用します。
(QMouseEventリファレンス

次にmouseMoveEventです。ここでも左クリックされていることを確認し、されているなら現在のマウスの位置とmousePressEvent時に保存したときのマウスの位置との差を求め、その絶対値がデフォルトで設定されているドラック&ドロップを開始する差分以上であるかを比較し、以上ならドラッグを開始します。これは手の振るえだとかの理由でドラッグを開始させないように差分を比較しているのだそうです。
(QMouseEventリファレンス

startDragでは現在選択中のアイテムを使ってドラッグ&ドロップするMIMEデータを作成したり、ドラッグ時のアイコンの設定などを行い。drag->startでドラッグ処理を開始します。(QMimeDataリファレンス)

dragEnterEventでは自分以外からのドラッグ&ドロップなら許可するようにしています。
(QDragEnterEventリファレンス

dragMoveEventも同じく自分以外からのドラッグ&ドロップなら許可するようにしています。
(QDragMobeEventリファレンス

最後にdropEventですが、自分以外のドラッグ&ドロップならアイテムを追加しています。
(QDropEventリファレンス


このCustumListを2つメインウィンドウに配置し適当な初期値をセットして実行したものが以下のものです。


(ドラッグ&ドロップ中。独自のアイコンが表示されている)
 

(ドラッグ&ドロップを終えた時)
 

以上です。

※ちなみにこのCustumListへのアイテムの追加はQListWidgetと同じです。以下のような感じで私は追加しました。
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)//uiにはGUI部品を記述(CustumListもこの中に記述)
{
    ui->setupUi(this);//uiのGUI部品の初期化

    //ui->listWidgetはCustumListです。
    ui->listWidget->addItem(new QListWidgetItem("Oak"));
    ui->listWidget->addItem(new QListWidgetItem("Banana"));
    ui->listWidget->addItem(new QListWidgetItem("Apple"));
    ui->listWidget->addItem(new QListWidgetItem("Orange"));
    ui->listWidget->addItem(new QListWidgetItem("Grapes"));
    ui->listWidget->addItem(new QListWidgetItem("Jayesh"));
}

2012年1月28日土曜日

( Qt C++ )メインウィンドウでドラッグ&ドロップを受け付ける


はいそれでは新しい章「Drag and Drop」をやっていきます。
C++ GUI Programming with Qt4ですと205ページからの章です。

今回はまずメインウィンドウへのドラッグ&ドロップについてやっていきます。

サンプルはC++ GUI Programming with Qt4ですと205ページからのものを少し改変し使用します。
そして、いつものようにQtCreaterの使用を前提とします。(QtCreaterなどの使い方は ”Qtをはじめよう" を見てください。)
ではコードを


(mainwindow.h)
#include <QMainWindow>
#include <QDropEvent>
#include <QUrl>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT//マクロ
    
public:
    explicit MainWindow(QWidget *parent = 0);//コンストラクタ
    ~MainWindow();//デストラクタ
    void dragEnterEvent(QDragEnterEvent *e);//----ここ重要!
    void dropEvent(QDropEvent *e);//----ここ重要!
    
private:
    Ui::MainWindow *ui;//uiにはGUI部品関連の記述
};

(mainwindow.cpp)
#include "mainwindow.h"
#include "ui_mainwindow.h"

//コンストラクタ
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)//uiにはGUI部品に関する記述
{
    ui->setupUi(this);//uiのGUI部品初期化

    //重要!ドラックandドロップを受け付ける
    //ように設定する。(trueで受け付ける)
    setAcceptDrops(true);
}

MainWindow::~MainWindow()//デストラクタ
{
    delete ui;
}

void MainWindow::dragEnterEvent(QDragEnterEvent *e)
{
    if(e->mimeData()->hasFormat("text/uri-list"))
    {
        //視覚的にドロップを受付られることを
        //表示し、ドラッグ&ドロップを受け付ける
        //これがないと受付られない。
        e->acceptProposedAction();
    }
}

void MainWindow::dropEvent(QDropEvent *e)
{
    //dragEnterEventの後にくるイベント
    //ドロップの際の動作を記述する
    ui->label->setText(
                e->mimeData()->urls().first().toLocalFile());
}

はい簡単ですね。uiは今回の理解にはあまり関係ないので無視します。

まずヘッダ部で重要なのはdragEnterEvent、dropEventをオーバーライドすることを宣言する部分です。あとは見たまんまです。

次に.cppですがまずコンストラクタでsetAcceptDrops(true);を記述しています。
これはドラック&ドロップを許可するように設定しています。許可する場合は必ず書いてください。
なおsetAcceptDrops(false);を記述するとドラック&ドロップは禁止されます。
(デフォルトでドラック&ドロップ許可のウィジットもあります。これは各ウィジット、リファレンスで確認してください。)

次にdragEnterEventの実装ですが引数のeにドラック&ドロップされたファイルの情報が格納されます。そこからMIMEデータを取り出しそのタイプをhasFormatでチェックしています。
今回はtext/uri-listであるかどうかをチェックし、そうならばe->acceptProposedAction();を記述しドラック&ドロップを許可するようにしています。
(QDragEnterEventリファレンス)(QMimeDataリファレンス)

次にdropEventですが、これはdragEnterEventでドラック&ドロップを許可された場合に呼び出されます。これも引数のeにドラック&ドロップされたファイルの情報が格納されますのでそこからMIMEデータを取り出して、そのファイルパスをQLabelに表示しています。
(QDropEventリファレンス)(QMimeDataリファレンス)

※なお正式なMIMEリストはこちら(英語)です。


これらを実行すると以下のようになります。(画像はファイルシステム直下のvmlinuzファイルをドラック&ドロップしたところ。)



以上です。