ぼっちの夜には知らないクラスについて調べてみよう

More than 1 year has passed since last update.

ぼっちの夜には知らないクラスについて調べてみよう

クリスマスイブにも関わらずボッチで過ごしている同士諸君、こんばんは。
ぼっちで生きてるQtおやつ部部長のhermit4です。

どうせ、多くの人が楽しく浮かれている日でしょうから、ビューもさほど伸びないことでしょう。そんな寂しい夜なので、Qt5.4の1170のクラスの中から、ほとんどの人には関係なさそうな、寂しいクラスについてちらほらと調べてみようかと思います。

実は、候補をいくつか見てはいますが、何せ使い方も知らないクラス達なので、中々調査が進んで行きません。どうせ今夜は暇なので、燃え尽きて眠りにつくまで、少しずつ書き足しながら更新して行きたいと思います。

24:30 : メリークリスマス。残念伸(@kassy_kz)の呪いのせいで更新が遅れました。
26:00 : 楽園追放の再生を開始
27:00 : とりあえず、coreで見慣れない2つを動かしてみました。

extras

x11extras

QX11Info

さて、Xmasにちなんで、まずはXからということで、QX11Infoから行きましょうか。
Qtというとクロスプラットフォームが売りなので、見落としがちですが、いくつか、プラットフォーム依存のクラスというものが存在しています。

QX11Infoは、X Window Systemの環境で利用できるクラスです。
すべてstaticなメンバ関数で、数も少ないので一覧を抜き出しておきましょう。

int appDpiX(int screen = -1)
int appDpiY(int screen = -1)
unsigned long appRootWindow(int screen = -1)
int appScreen()
unsigned long appTime()
unsigned long appUserTime()
xcb_connection_t* connection()
Display* display()
unsigned long getTimestamp()
bool isPlatformX11()
QByteArray nextStartupId()
void setAppTime(unsigned long time)
void setAppUserTime(unsigned long time)
void setNextStartupId(const QByteArray & id)

あえて、これを使うときというと、Display*が必要な場合でしょうか。
殆どの事はQtのみで実現可能なのですが、あえてXSendEventしたい場合とか、どうしても・・・という時に使う事になるかもといった程度の寂しいクラスです。

追記: よく使うって人もいるようですが

macextras

X11があるなら、Macはどうなの?というわけですが、当然あります。

QMacPasteboardMime

Qtではドラッグ&ドロップやクリップボードを使う時は、ファイル種別の識別にMIME Typeが利用されています。これに対し、Macでは、Apple UTI formatという形式があるのですが、QMacPasteboardMimeはこのUTIとMIME Typeを相互変換するのに利用できます。

QMacToolBar/QMacToolBarItem

QMacToolBarは、QtのNative Tool BarのNSToolbarのラッパーとの事なのですが、QToolBarとの違いはいまいちわかりません。Nativeで実装されているので、OS側の更新に従ってUIも変動するといった当たりなんでしょうかねぇ。

使い方は特に難しくありません。以下のようなコードでToolBarをウィンドウに追加できます。


QMacToolBar *toolBar = new QMacToolBar(this);
QMacToolBarItem *toolBarItem = toolBar->addItem(QIcon(), QStringLiteral("foo"));
connect(toolBarItem, SIGNAL(activated()), this, SLOT(fooClicked()));
toolBar->attachToWindow(window()->windowHandle());

winextras

Xmときたらaだろ!というツッコミは心に刺さるのでしないで下さい。
X11(Linux), MacときたらWindowsです。X11, Macともすごく微妙なクラスばかりだったのですが、Windows側はあるとちょっと嬉しい機能とかが存在しています。

QWinJumpList/QWinJumpListCategory/QWinJumpListItem

Windowsのタスクバーのアイコンを右クリックすると開くウィンドウをジャンプリストと言いますが、Qtでこれを制御する事ができます。

でまぁ、簡単に利用できるか・・・と思ったのですが、これが意外と難しい。思った通りに動きません。WindowsのNativeの機能を使うためか、FileTypeをSupportTypeとしてレジストリに登録したり、色々準備がいるようです。

とりあえず、ExampleにあるMusic Playerのexampleを見てみましょう。

main.cpp

static void associateFileTypes(const QStringList &fileTypes)
{
    QString displayName = QGuiApplication::applicationDisplayName();
    QString filePath = QCoreApplication::applicationFilePath();
    QString fileName = QFileInfo(filePath).fileName();

    QSettings settings("HKEY_CURRENT_USER\\Software\\Classes\\Applications\\" + fileName, QSettings::NativeFormat);
    settings.setValue("FriendlyAppName", displayName);

    settings.beginGroup("SupportedTypes");
    foreach (const QString& fileType, fileTypes)
        settings.setValue(fileType, QString());
    settings.endGroup();

    settings.beginGroup("shell");
    settings.beginGroup("open");
    settings.setValue("FriendlyAppName", displayName);
    settings.beginGroup("Command");
    settings.setValue(".", QChar('"') + QDir::toNativeSeparators(filePath) + QString("\" \"%1\""));
}

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    app.setApplicationName("MusicPlayer");
    app.setOrganizationName("QtWinExtras");
    app.setOrganizationDomain("qt-project.org");
    app.setApplicationDisplayName("QtWinExtras Music Player");
    app.setWindowIcon(QIcon(":/logo.png"));

    associateFileTypes(QStringList(".mp3"));

    MusicPlayer player;
    const QStringList arguments = QCoreApplication::arguments();
    if (arguments.size() > 1)
        player.playFile(arguments.at(1));
    player.resize(300, 60);
    player.show();

    return app.exec();
}

SupportedTypesに.mp3を登録し、openのshellとして、自分に引数を1つ引き渡すようコマンドを登録しています。

この指定をした上で、最近使ったファイルを可視設定にしています。

musicplayer.cpp

    QWinJumpList jumplist;
    jumplist.recent()->setVisible(true);

これで、QFileDialogでopenすれば、最近使ったファイルにリストされてリストが表示されるようになるようです。

menu.png

QWinMime

Macの時にも記述しましたが、QtのクリップボードはMIME typeを利用しています。このクラスはWindowsのクリップボード形式とMIME typeとを変換するためのクラスです。

QWinTaskbarButton

QWinTaskBarButtonは、タスクバーに表示されるアイコンそのものを表すクラスです。

musicplayer.cpp
void MusicPlayer::createTaskbar()
{
    taskbarButton = new QWinTaskbarButton(this);
    taskbarButton->setWindow(windowHandle());

MusicPlayerでは、状態に応じてアイコン上にsetOverlayIconを使ってStateを示す画像を表示しています。

musicplayer.cpp
void MusicPlayer::updateTaskbar()
{
    switch (mediaPlayer.state()) {
    case QMediaPlayer::PlayingState:
        taskbarButton->setOverlayIcon(style()->standardIcon(QStyle::SP_MediaPlay));
        taskbarProgress->show();
        taskbarProgress->resume();
        break;
    case QMediaPlayer::PausedState:
        taskbarButton->setOverlayIcon(style()->standardIcon(QStyle::SP_MediaPause));
        taskbarProgress->show();
        taskbarProgress->pause();
        break;
    case QMediaPlayer::StoppedState:
        taskbarButton->setOverlayIcon(style()->standardIcon(QStyle::SP_MediaStop));
        taskbarProgress->hide();
        break;
    }
}

buttons.png

QWinTaskbarProgress

QWkinTaskbarButtonには進捗バーが表示できます。QProgressBarと同じで、RangeとValueを設定して利用します。

musicplayer.cpp

void MusicPlayer::createTaskbar()
{
    taskbarButton = new QWinTaskbarButton(this);
    taskbarButton->setWindow(windowHandle());

    taskbarProgress = taskbarButton->progress();
    connect(positionSlider, SIGNAL(valueChanged(int)), taskbarProgress, SLOT(setValue(int)));
    connect(positionSlider, SIGNAL(rangeChanged(int,int)), taskbarProgress, SLOT(setRange(int,int)));

progress1.png
progress2.png

WindowsでQtCreatorを使ってコンパイルかけてる時にも使われていますよね。

QWinThumbnailToolBar/QWinThumbnailToolButton

サムネイルというのは、アイコンにマウスカーソルを当てた時に表示される小さなウィンドウです。ここにはツールバー領域があります。

musicplayer.cpp

void MusicPlayer::createThumbnailToolBar()
{
    thumbnailToolBar = new QWinThumbnailToolBar(this);
    thumbnailToolBar->setWindow(windowHandle());

    playToolButton = new QWinThumbnailToolButton(thumbnailToolBar);
    playToolButton->setEnabled(false);
    playToolButton->setToolTip(tr("Play"));
    playToolButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
    connect(playToolButton, SIGNAL(clicked()), this, SLOT(togglePlayback()));

    forwardToolButton = new QWinThumbnailToolButton(thumbnailToolBar);
    forwardToolButton->setEnabled(false);
    forwardToolButton->setToolTip(tr("Fast forward"));
    forwardToolButton->setIcon(style()->standardIcon(QStyle::SP_MediaSeekForward));
    connect(forwardToolButton, SIGNAL(clicked()), this, SLOT(seekForward()));

    backwardToolButton = new QWinThumbnailToolButton(thumbnailToolBar);
    backwardToolButton->setEnabled(false);
    backwardToolButton->setToolTip(tr("Rewind"));
    backwardToolButton->setIcon(style()->standardIcon(QStyle::SP_MediaSeekBackward));
    connect(backwardToolButton, SIGNAL(clicked()), this, SLOT(seekBackward()));

    thumbnailToolBar->addButton(backwardToolButton);
    thumbnailToolBar->addButton(playToolButton);
    thumbnailToolBar->addButton(forwardToolButton);

    connect(&mediaPlayer, SIGNAL(positionChanged(qint64)), this, SLOT(updateThumbnailToolBar()));
    connect(&mediaPlayer, SIGNAL(durationChanged(qint64)), this, SLOT(updateThumbnailToolBar()));
    connect(&mediaPlayer, SIGNAL(stateChanged(QMediaPlayer::State)), this, SLOT(updateThumbnailToolBar()));
}

thumbnail.png

さて、この他にも、たすくさんにツッコミされた、androidextrasもあるのですが、これは環境構築とまとめて、Android系の勉強会か何かで発表したいので、また後日という事で。

ちなみにクラス名だけでAndroidクラスタの方は何に使うかわかりそうなので、クラス名だけリストしておきましょう。

core

続いて、Qtのメイン機能であるcoreから見慣れないクラスをあげていきます。

QAssociativeIterable

QVariant型に格納された連想コンテナをQVariantとして反復処理するためのイテレータです。

main.cpp
#include <QHash>
#include <QAssociativeIterable>
#include <QVariant>
#include <QDebug>

int main(int argc, char *argv[])
{
    Q_UNUSED(argc);
    Q_UNUSED(argv);
    QHash<int, QString> mapping;
    mapping.insert(7, "Seven");
    mapping.insert(11, "Eleven");
    mapping.insert(42, "Forty-two");

    QVariant variant = QVariant::fromValue(mapping);
    if (variant.canConvert<QVariantHash>()) {
        QAssociativeIterable iterable = variant.value<QAssociativeIterable>();

        qDebug() << "foreach";
        foreach (const QVariant &v, iterable) {
            qDebug() << v;
        }

        qDebug() << "for loop";
        QAssociativeIterable::const_iterator it = iterable.begin();
        const QAssociativeIterable::const_iterator end = iterable.end();
        for ( ; it != end; ++it) {
            qDebug() << "*it : " << *it;
            qDebug() << "key : " << it.key();
            qDebug() << "value : " << it.value();
        }
    }
    return 0;
}

QHashを生成後、QVariantに入れた後、QVariant型でkeyや値を取り出し反復処理を行う事ができます。

 foreach
 QVariant(QString, "Forty-two")
 QVariant(QString, "Eleven")
 QVariant(QString, "Seven")
 for loop
 *it :  QVariant(QString, "Forty-two")
 key :  QVariant(int, 42)
 value :  QVariant(QString, "Forty-two")
 *it :  QVariant(QString, "Eleven")
 key :  QVariant(int, 11)
 value :  QVariant(QString, "Eleven")
 *it :  QVariant(QString, "Seven")
 key :  QVariant(int, 7)
 value :  QVariant(QString, "Seven")

QException

QExceptionは、Threadを超えてWorker Threadから例外を届ける事ができるExceptionです。12月3日に実装したQtConcurrentの例をちょっと改造してみましょう。


#include <QCoreApplication>
#include <QtConcurrent>
#include <QVector>
#include <QThread>
#include <QDebug>
#include <QException>

class MyException : public QException
{
public:
    void raise() const { throw *this; }
    MyException *clone() const { return new MyException(*this); }
};

struct Increment
{
    Increment() : i_(0) {}
    int operator()() { return i_++; }

    int i_;
};

struct Calc
{
    typedef void result_type;

    Calc(const int& a, const int&  b) :a_(a), b_(b) {}
    result_type operator()(int& x)
    {
        x = a_*x+b_;
        qDebug() << "thread : " << x;
        QThread::usleep(800);
        if (x > 512) {
            MyException exception;
            exception.raise();
        }
    }
    int a_;
    int b_;
};

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    QVector<int> data(100);

    // vectorを0-99で設定する
    std::generate(data.begin(), data.end(), Increment());
    foreach (int i, data) {
        qDebug() << i;
    }

    int a = 12;
    int b = 3;
    qDebug() << " --- Calc " << a << " * data[n] + " << b;
    try {
        QFuture<void> ret = QtConcurrent::map(data,Calc(a,b));
        ret.waitForFinished();
        foreach (int i, data) {
            qDebug() << i;
        }
    } catch (const MyException& e) {
        Q_UNUSED(e);
        qDebug() << "Exception";
    }
    return 0;
}

Exceptionを追加する前の結果は以下の通りでした。

0
1
2
3
4
:
:
95
96
97
98
99
 --- Calc  12  * data[n] +  3 
3
15
27
39
51
:
:
1143
1155
1167
1179
1191

これを、512でExceptionが起きるようにすると

0
1
2
3
4
:
:
95
96
97
98
99
 --- Calc  12  * data[n] +  3 
thread :  3
thread :  27
thread :  15
thread :  39
thread :  51
thread :  63
thread :  75
thread :  99
thread :  87
 :
 :
thread :  447
thread :  471
thread :  495
thread :  483
thread :  507
thread :  519
thread :  531
thread :  543
Exception

waitForFinished()が最後まで終わらず、main threadのcatchへ移行して終了します。
QtConcurrentを使うときの悩み事が、途中で深刻なエラーが起きた時にどう止めるかなんですが、これで解決かな。

何せ、いままで使ってこなかったクラスを動かしながら、試しながらで進めてきたので、当初期待していた半分も進まなかったですが、そろそろ世の中も寝静まっているでしょうし、ここらへんにしておきましょうか。

ちなみに、特に利用方法がよくわからなかったものとしては

  • QCollator
  • QCollatorSortKey
  • QGlobalStatic

といった当たりがありました。QCollatorあたりはうまく利用すると、文字列中の数字の並びをうまい事sortできるようになるとか・・・・。使い方をよくご存知の方、教えてくれるとうれしいです。

利用方法はわかるけど、動かすまでが面倒だなってのが

  • QAx.*

でしょうか。Windowsオンリーの機能ですが、ActiveXとかCOMを利用したり、あるいは逆に提供したりといったクラスも存在しています。

他にも、OpenGL系が刷新されてたり、QBackingStoreですとか、QOffscreenSurfaceとか、まぁ、色々見慣れないクラスも多い事。いくつかサンプルを書いてみたものの、いまいちなできなので、もう少し調査が必要そうです。本当、最近のものは、Exampleもあまり用意されてないものもあったりするので、どう使うのか悩ましかったりしますね。

Qtは、特にQt5以降、猛烈な勢いで変化が起こっています。なかなか追いついて行くのも難儀なものですが、あるいは、あなたが必要としていたクラスもこっそり追加されているかもしれません。
寝付けない、やる事の無い、暇なぼっちの夜には、ぜひQtのクラス一覧を開いて、ちらほらとクラス名一覧を眺めてみてあげて下さい。

そんなこんなで、なるほど4時じゃねーのって当たりで、24日を超えた、ぐだぐだであまり役にたたなさそうなネタ記事を終了します。こんなぐだぐだな適当記事でもいいんだって思って、来年は新規で参加してくれる人が増えるとうれしいなぁ・・・。

前日の記事がまだまとまっていないようではありますが、とりあえず、カレンダーとしては残す所あと1日分。最終日は、Qtユーザー会発足の引き金を引いた現役高校生 @luyikei 君による記事となりますので、あと一日、おつきあいください。