「JavaScript」 カテゴリーの記事です。

QtでMacOSXアプリケーションメニューに「環境設定」項目を追加、表示する


一昨日の土曜日、はじめてQt 勉強会 #11 @Tokyoにも参加。まとめは、こちらです。

めっちゃ濃いことやっている人たちばかりで腰引けました(笑)。

僕はせいぜいライブラリの寄せ集めで、さくっとアプリつくっちゃうという軽さが、とても恥ずかくなります(笑)。続々成果が発表されるなか、勉強会でやりたかったこと、をやる前に、ちがう問題が発生し、さらにそれもその日に解決できずに、宿題になってしまったという・・・

ただ、主催者の @task_jpさんからのアドバイスもあり、その後、それら宿題を解消できたのでここにシェアします。

メインメニュー「環境設定」というは、いわゆるコレです。

環境設定メニューの画像

Qtの公式ページを見ると、
http://qt-project.org/doc/qt-5/qmenubar.html#qmenubar-on-mac-os-x

If you want all windows in a Mac application to share one menu bar, you must create a menu bar that does not have a parent. Create a parent-less menu bar this way:

  
QMenuBar *menuBar = new QMenuBar(0);
  

親のないメニューバーから生成する、とある。
ただ、すでにそのようなメニューは作っていて、どこに「それら」を挿入するのかが分からない。

  
QMenu *fileMenu = menuBar()->addMenu("&File");
fileMenu->addAction(tr("&New Window"), this, SLOT(fileNew()), QKeySequence::New);
fileMenu->addAction(tr("&Open..."), this, SLOT(fileOpen()), QKeySequence::Open);
fileMenu->addAction(tr("&Save"), this, SLOT(fileSave()), QKeySequence::Save);
fileMenu->addAction(tr("Save &As..."), this, SLOT(fileSaveAs()));
  

ファイルメニュー

結論を言うと、なんか、実に微妙な実装なのですが、AddMenu() にある特定の文字列があれば、
QMenu のどこに存在してようと、良いみたい。

マニュアルにもそう書いてある。
http://qt-project.org/doc/qt-5/qmenubar.html#qmenubar-on-mac-os-x

String matches Placement Notes
about.* Application Menu | About <application name> The application name is fetched from the Info.plist file (see note below). If this entry is not found no About item will appear in the Application Menu.
config, options, setup, settings or preferences Application Menu | Preferences If this entry is not found the Settings item will be disabled
quit or exit Application Menu | Quit <application name> If this entry is not found a default Quit item will be created to call QApplication::quit()

なので、ソースコードには、こんなふうにしました。

   
QMenu *fileMenu = menuBar()->addMenu("&File");
fileMenu->addAction(tr("&New Window"), this, SLOT(fileNew()), QKeySequence::New);
fileMenu->addAction(tr("&Open..."), this, SLOT(fileOpen()), QKeySequence::Open);
fileMenu->addAction(tr("&Save"), this, SLOT(fileSave()), QKeySequence::Save);
fileMenu->addAction(tr("Save &As..."), this, SLOT(fileSaveAs()));

//ファイルメニューにぶら下がるようにコードを挿入
fileMenu->addAction(tr("about.*"), this, SLOT(aboutPaneView()));
fileMenu->addAction(tr("preferences"), this, SLOT(optionPanelView()));

こう書くと、「ファイル」メニューのプルダウン項目に表示されそうですが、そこには表示されず、
実際は、アプリケーションメニュー内に表示されます。

アプリケーションメニュー

「about.*」は、ママです。そのままアスタリスクを書きます。.plistにある「アプリケーション名」をQt が自動的に引っ張ってきて、「About AppName」という感じに表示してくれます。たぶん、アプリケーションが日本語表示対応なら、「AppNameについて」というメニューになるはずです。

「preferences」も同様に、日本語環境に対応させると「環境設定」と表示されるようです。

File メニューの中に AddMenu() したのに、実際はアプリケーションメニューに表示されるという、なんだか気持ち悪い仕様ですが、MacOSXがそうなのであって、他のOSでは、ちがう実装になるということに注意が必要でしょう。

一昨日のQt 勉強会 #11 @Tokyoでも、「気持ち悪い」「ホントに大丈夫なの?」との感想がありましたが、「マニュアルがそうなっているので、そうなんでしょう」っていう結論でした(笑)。
 
 

Qt上でJavaScriptの実行をデバッグする


Qtで、WebKit Bridge で作成していると、Qt側での処理は、
QtCreator上でデバッグ可能ですが、値がWebKitへ渡った後の、
JavaScript実行までは追えません。

ただ、一昨日のQt 勉強会 #11 @Tokyoにて、
そのことについて口にしたところ、主催者の @task_jpさんから、

QWebInspectorクラスを使うといいよ」とのアドバイスをいただきました。

こんな便利なものまで、クラスであるのか!と喜び勇んで実装してみました。

  
//QWebInspector instance.
QWebInspector *inspector = new QWebInspector;
inspector->setPage(this->page());
inspector->show();
  

this->page() は、WebKitの、QWebView::page() のポインタを指定します。

ところが、とても残念な表示に。。。

空のWebインスペクタ

ちゃんと、show() しているんですけどね、、、なぜ出ないのだぁ〜、と悩むこと小一時間、もうすでに消えてしまったWebページで、検索キャッシュの中に答えを見つけました(笑)。

  
//↓これを追加。
view->page()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);

QWebInspector *inspector = new QWebInspector;
inspector = new QWebInspector;
inspector->setPage(this->page());
inspector->show();
  

表示する側、Webページの方の、設定も変更しないと出ないということがわかりました。

検索しても、あまり事例が出ないところを見ると、QWebInspectorクラスがQtにあることを知らないか、
かつ、Web Bridgeとかする前に、HTML状態のJavaScriptをデバッグしてから載せろってことでしょうかね。。。

とりあえず、これも勉強会の宿題だったので、一つ解決っと。
 
 

CodeMirriorコンストラクタの罠


いま、Qt の QtWebKit Bridge(http://qt-project.org/doc/qt-5/qtwebkit-bridge.html)という機能を使っての、MacOSXアプリケーションをつくっています。

いわゆるMarkdown記法を元に、アウトラインを作りながら書いていく、日本語入力のエディタを考えていて、
エディタ部分には、CodeMirroir というライブラリを使います。

ただ、どうもCodeMirrior のコンストラクタで、エディタのインスタンスが、生成されていないみたい。

  
  editor = CodeMirror(document.body, {
    value: "\n",
    mode:  "markdown",
    smartIndent: false,
    tabSize: 2,
    indentWithTabs: true,
    electricChars: false,
    lineWrapping: true,
    lineNumbers: true,
    foldGutter: true,
    enableCompositionMod: true,
    gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"]
  }).on(
    //Changeイベント
    'change', function() {
        //alert('changes!');
        jsBridge.changes();
    });

  editor.setValue("abc");  //←ここでエラー
  

こんな、エラーが発生します。↓

JavaScriptエラー

で、よくよく調べてみたら、意外なところに落とし穴がありました。

ヒントがあったのは、このページ。
https://groups.google.com/d/msg/codemirror/DfR57zf6k18/9kLma4I6dagJ

CodeMirrior は、jQueryみたいにオブジェクトを返さないよー、とある。

な、ん、だ、と・・・

完全に、jQuery脳っていうか、JavaScript的に考えれば、オブジェクトを返すはずで、メソッドチェーンするのは当然だと思ってました。
いや、ふつうそうじゃないのか・・・

なんで、CodeMirrior ではできないのか?

答え:そういう仕様だから(笑)。

コンストラクタした内容をオブジェクト変数に入れて、使い回すことはできない。これ、仕様。

だから、こう書きます。

  
  editor = CodeMirror(document.body, {
    value: "\n",
    mode:  "markdown",
    smartIndent: false,
    tabSize: 2,
    indentWithTabs: true,
    electricChars: false,
    lineWrapping: true,
    lineNumbers: true,
    foldGutter: true,
    enableCompositionMod: true,
    gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"]
  }).on(

  editor.on(
    //Changeイベント
    'change', function() {
    //alert('changes!');
    jsBridge.changes();
  });

  editor.setValue("abc");
  

最初のコンストラクタのみ、オブジェクトを返すので、メソッドチェーンはできません。

なぜ? どうせならオブジェクトをそのまま返してくれればいいだけなのに(笑)。

 
 

jQueryで独自スライドショー作成チュートリアル(1)(スライドショーの基本動作)


車輪の再発明ですか。そうです。

いまさらすぎやしませんか。今でしょ!(強がり)

いや、たしかに、世の中にはスライドショーを実現するのに、便利なjQueryプラグインはすでにゴマンと存在します。

僕のオススメは、<div>単位で扱え、ライセンスも緩やかな、bxSlider や、jQuery Slider2でしょうか。

とはいえ、いろいろいじくっていると、「この機能が無い・・・」とか、この演出を実現するには、「無理くりプラグインの仕様に合わせる必要がある・・・」など出てきます。

ああ、やっぱり、仕組みを押さえながら、自分で一から、シンプルに実装してみたいなあ、といつしか思うように。

今回の連載記事では、僕がどうやってスライドショーを実現するところまで至ったか、その過程を順番に辿っていきます。最終的には、軽量でシンプルなjQueryスライドショー・プラグインをつくってみようと思っています。

また、段階的に、その時点でまとめたサンプルソースも公開していきますので、それを元に、いろいろ参照してくださるなり、勝手に改良してくださってかまいません。

ここで配布されるサンプルデータすべてのライセンスは、前述のbxSliderプラグインに敬意を表し、WTFPL licenseとします(笑)。

(さらに…)

CoffeeScriptでつくったクラス内でjQueryメソッドのコールバック関数を呼ぶ


前回の記事「CoffeeScriptでクラスをつくりその中にjQueryイベントを持たす」の関連、というか続きです。

前回も、thisの所在に悩まされたのですが、今回も同じような感じでハマったので一応メモ。

これに関する解は、わりとアチコチ(Stack Overflowなど)にあるのですが、この流れでハマると、その解に辿り着けない(ググってもそれが解答だと気づけない)ような気がするので、ここに書いておきます。

たとえば、以下のようにclassで作った<div>をこれまたclass内で定義したメソッドからアニメーションをさせます。

今回の例で言えば、右に移動したら(animate {‘left’:’128′}) 、下へ(animate {‘top’:’128′})、次に左へ(animate {‘left’:’128′})移動し、最後は元の位置(animate {‘top’:’0′})に戻る、といった動作です。

[サンプルプログラム]

これはjQueryのanimate()関数を呼びますが、完了時の引数にコールバック関数を仕込んで呼ぶことができます。つまり、animate()を入れ子のようにして連鎖的に呼ぶことができるのですが、そのとき、階層深くに押し込まれていくthisは、どういうコンテキストになるんだろう?って疑問が浮かびます。

現に、動かなくて、かなり四苦八苦しました。

結論としては、こうやります。

(さらに…)

s