この投稿は、Qt Advent Calendar 2016の13日目の投稿です。
#お約束
昨日は”はりきらない”こと@helicalgearさんの「Windows 10 MobileでQtQuickアプリを動かしてみる。Part-II」でした。さすがの変態端末クラスタですね。日本人の9割9分9厘が持っていないWindowsMobileをターゲットにされると、すぐに試せないもどかしさが有ります( ゚д゚ )!! 個人的にはWebSocketのバイナリ送信に対応してる5.8betaの方に興味を惹かれました。
#tl;dr
昔作っていた以下の機能を持つQtアプリを形にしました。 1 2
- iTunesを操作+再生中のトラック名を取ってきて表示する(QAxScriptManager)
- iTunesからアートワーク(ジャケット)を取ってきて表示する(QPaintEvent)
- iTunesの再生中トラックを自分のアカウントにTweetする(twitter4qml)
順序立てて書くと↑の様になりますが、一言で書くと「@kimitakeさんが twitterでやっている#NowPlaying ってやつを再生中のiTunesの曲でやりたかった」が本当の所です。
対象の環境は下記の通りです。
Qt | 5.7 |
---|---|
OS | Windows10 (たぶんwindows7とかでも可) |
iTunes | 12.3.5.17 |
その他 | twitter4qml, VBScript |
プラットフォーム非依存のQtですが、Windows10でiTuneにコマンドを送ったりするのにVBScriptを使っていたり、そもそもiTuneを間接的に利用しているのでLinux環境では100%無用の長物です。MacOSではVBScript部分をJavaScriptに直す等すれば多少は使えるかも知れませんが確認はしていません。
#iTunesを操作+再生中のトラック名を取ってきて表示する(QAxScriptManager)
本当の意味で難しいiTunesの操作自体はVBScriptでやっていて、Qtはそのスクリプトを内部に持って実行するだけになっています。実はこのQAxScriptManagerクラスはJavaScriptとか外部スクリプトを実行できる賢い子だったはずですが、当時試した限りVBScript以外では期待通りの動作をしなかったので今回は使えていません。JavaScriptの方が汎用性はあるはずなので何とかしたい気はします。
Artwork::Artwork(QWidget *parent) : QGraphicsView(parent)
{
~~(略)~~
m_manager = new QAxScriptManager(this);
bool isRegistered = m_manager->registerEngine("VBScript", "vbs");
if(isRegistered){
qDebug() << "OK to register VBScript";
m_script = m_manager->load("://test.vbs", "HelloWorld");
}
else
qDebug() << "NG to register VBScript";
connect(m_script, static_cast<void(QAxScript::*)()>(&QAxScript::entered),
[=](){ enterScript(); });
connect(m_script, static_cast<void(QAxScript::*)()>(&QAxScript::finished),
[=](){ finScript(); });
}
でqrcに入れたtest.vbsファイルをメモリ上にロードすると
QString Artwork::getArtworkPath()
{
QString rStr = "";
if (m_script){
QVariant var = m_script->call("getArtworkAndPath()");
rStr = var.toString();
}
else
qDebug() << "didn't load";
return rStr;
}
call("hogehoge")
と言う感じで、VBScript側の関数を文字通り呼び出すことができます。ちなみにこのgetArtworkAndPath()の中身はこんな感じ。3
Function getArtworkAndPath()
Dim objShell
Dim tmp
Dim iTunesApp
Dim current
Dim artworks
Dim artwork
Dim format
Dim c_str
Set objShell = CreateObject("WScript.Shell")
tmp = objShell.ExpandEnvironmentStrings("%TMP%")
Set iTunesApp = CreateObject("iTunes.Application")
Set current = iTunesApp.CurrentTrack
Set artworks = current.Artwork
Set artwork = artworks.Item(1)
format = artwork.Format
Select Case format
Case 1
c_str = "jpg"
Case 2
c_str = "jpg"
Case 3
c_str = "jpg"
End Select
artwork.SaveArtworkToFile(tmp + "\\z." + c_str)
getArtworkAndPath = tmp + "\\z."+ c_str
End Function
Qtと直接関係ないので詳細は省きます・・・。
#iTunesからアートワーク(ジャケット)を取ってきて表示する(QPaintEvent)
地味に面倒くさかったのがこっち。.NetFrameworkを使う場合だと、Imageクラスのloadメソッド呼び出しだけで矩形更新して画面に画像データが反映されるのでそう言うものだと思ってやってみようにもそう言うサンプルが無くて、実装はまずQGraphicsViewを継承したクラスを作ってpaintEventをオーバーライドして該当イベントの中でゴニョゴニョやらなきゃいけないという不親切な感じ。なので、先程出てきたArtworkクラスはQGraphicsViewを継承していて
void Artwork::paintEvent(QPaintEvent *event)
{
QString path;
qDebug() << __FUNCTION__ << " called";
path = getArtworkPath();
path = path.replace("\\\\", "\\");
if(path != "")
updateImage(event, path);
else
setNoImage(event, "No Image.");
}
paintEventのコールバック内でiTunesのアートワークを取ってきて画面にロードするという割りと面倒なことをやっています。画像サイズの縱橫がおかしかったり、そのせいかエッジにバリが出たりしてるのでこれも気になるところ。そもそももっと綺麗に書く方法があるような気がする。
#iTunesの再生中トラックを自分のアカウントにTweetする(twitter4qml)
本稿のオリジナル記事を読んでいた貴特な方がいらっしゃったら申し訳ないのですが
知る限り一番の近道は@task_jpさんのTwitter4QMLを使うことだと思われます
はりきらないひと: 【一夜漬け】Twitter4QMLを使ってみようか。
艦メモのビルド方法(Windows編) - 理ろぐ
使ってみようとpriをプロジェクトに追加してブログ記事とコードをにらめっこしておりましたが実力不足でついていけないのでTwitterにポストするだけの低機能クラスを作ることにしました。
と、書いていたのにtwitterのpostにはtwitter4qmlを使わせて頂いております。そもそも自前でOAuth解決してPostするまでゴリゴリQtで書くほうが遥かにレベルが高かったです。。。orz
Windows向けの導入部分は@ioriayaneさんの記事を、実際に動かす部分は@helicalgearさんの記事を参考というのもおこがましい位にコードをまるまる拝借した感じで動かしています。
bool Tw::init()
{
bool r_inf = false;
// FIXME: it's so tentative implementaion...
this->oauth.setConsumerKey(consumer_key);
this->oauth.setConsumerSecret(consumer_secret);
this->oauth.setToken(token);
this->oauth.setTokenSecret(token_secret);
this->oauth.user_id(user_id);
this->oauth.screen_name(screen_name);
//this->status.statusesUpdate();
if(oauth.state() == 0) {
QString pin = "";
oauth.request_token(pin);
oauth.access_token(pin);
}
if(oauth.state() == 2) {
oauth.authorize();
}
if(oauth.state() == 5){
qDebug() << "success token authorize";
r_inf = true;
}
return r_inf;
}
bool Tw::postTweet(QString title)
{
bool r_inf = false;
QVariantMap map;
QString sTweet = "#NowPlaying " + title;
map.insert("status", sTweet);
if(oauth.state() == 5)
{
status.statusesUpdate(map);
r_inf = true;
}
return r_inf;
}
取りあえずタイトルをポストするだけならコレだけで出来ました。@task_jpさんはやはりスゴかった。。。
#動かしてみると
こんな感じ。
アートワークがある楽曲はオリジナルデータを展開して表示します。無い(iTunes以外のファイル等)ものは無いので”No Image”って出してるんですが悲しいことになってます。何とかしたい。
#NowPlaying リフレクティア
— Yuya Adachi [bot] (@moguriso) 2016年12月12日
tweet ボタンを押すと上記の感じで問答無用でポストします。これも、ポスト内容を事前確認させたりとか、ユーザーを切り替えたりとか、アートワークも(著作権的にOKか微妙だけど)ポストしたりとか機能追加したい気はします。やるかどうかは判りません。。。
#まとめ
職業SIerだとcmd.exeやらscriptで所々の作業を自動化することは結構あると思うので(すが、CLI使ってくれない人も多々いるので)QAxScriptManagerは上手に使えばトテモ便利なんじゃないかなと思いました。
明日は@tetsuromさんの”Widgetの階層構造を可視化するツールを作った話を書きます”です。期待して待ちましょう。
-
ソースコードはこちら( https://github.com/moguriso/gawa_sample ) ↩
-
このあたりの仕様はApple公式で(一般には?)公開されなくなったようなのでココでは http://www.joshkunz.com/iTunesControl/interfaceIiTunes.html#z1_4 を参考にしていました ↩