QtScriptを動作させる簡単な画面を作成しましょう。
まず、QtScriptの動作を確認するための簡単な画面を作成してみましょう。
作成する画面は、以下のような画面です。
画面上部にスクリプトを記述し、最後の出力結果を画面下部へ出力するように作成してみましょう。
メニューには、スクリプトの実行のみです。
ファイル
- sample.cpp
-- MainWindowを表示しているだけなのです省略します。
- mainwindow.h
- mainwindow.cpp
ここでは、画面構成に関しての説明は省きます。
以下は、メニューのスクリプトの実行をクリックした時の動作(slot)部分です。
[mainwindow.cpp]
1
2
3
4
5
6
7
8
9
10
| :
:
void MainWindow::excuteScript()
{
QString sscript=m_script->toPlainText();
if(!sscript.isEmpty()){
QScriptEngine engine;
m_output->append( engine.evaluate(sscript).toString() );
}
}
|
簡単に解説します。
最初にQTextEditで入力されている文字列(QtScript情報)を取得し、
その情報が、空でなければ、スクリプトを実行しています。
スクリプトの実行は、"evaluate"のメソッドへ、先の文字列(QtScript情報)を渡して実行されるだけです。
ここでは、その実行結果をm_output(画面下のQTextEdit)へ出力しています。
make時は、以下の点に注意します。
qmake実行時に、以下のように入力するか、xxx.proへ"QT+=script"を追加します。
では、この画面から簡単な計算を実行してみましょう。
画面の上部に、以下のように入力して、"Ctrl+G"をタイプします。
画面の下部に、以下のように入力して、出力されたと思います。
これだけのものでも、単純なQtScriptで閉じたツールを作成するには、十分かもしれません。
続けて、QtアプリケーションのパーツへQtScriptから出力させてみましょう。
QtScriptでデバッグ情報を出力しましょう。
QtScriptの中で、情報を出力する場合、デバッグ機能の中にprintというfunctionが使えます。
ただし、通常、これは、QtScriptのデバッガの出力部分に出力されますので、デバッガを起動していないと見れません。
そこで、QtScriptから簡単に画面へ出力するようにしてみましょう。
※通常は、printを独自の関数へ割り当てなおして、printを使えるようにしますが、ここでは、QtScriptとQtアプリケーション
との連動を説明するために、異なる方法で実現します。
QtScriptへ画面にパーツ(m_output(画面下のQTextEdit))を認識させ、そのパーツへスクリプトから出力するようにしてみます。
[mainwindow.cpp]
1
2
3
4
5
6
7
8
9
10
11
12
| :
:
void MainWindow::excuteScript()
{
QString sscript=m_script->toPlainText();
if(!sscript.isEmpty()){
QScriptEngine engine;
QScriptValue scriptTextEdit = engine.newQObject(m_output);
engine.globalObject().setProperty("debugPrint", scriptTextEdit);
m_output->append( engine.evaluate(sscript).toString() );
}
}
|
上記の行数で、8行目、9行目の2行が追加されただけです。
QScriptEngineで、新しいオブジェクトとしてm_output(画面下のQTextEdit)を宣言し、そのオブジェクトの名前を"debugPrint"と定義しています。
つまり、m_output(画面下のQTextEdit)をアクセスするためにQtScriptで"debugPrint"として記述しればアクセスできるということです。
では、この画面から簡単なスクリプトを記述して、出力させてみましょう。
画面の上部に、以下のように入力して、"Ctrl+G"をタイプします。
画面の下部に、以下のように入力して、出力されたと思います。
"undefined"は、スクリプトの結果がなかったことを意味します。
(上記のスクリプトでは、単純にm_output(画面下のQTextEdit)へ出力しているのみで結果がありません。)
通常、print文で、独自の画面へ出力させたい場合、以下のように実施します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| QScriptValue scriptPrintFunction(QScriptContext *context, QScriptEngine *engine)
{
QString result;
for (int i = 0; i < context->argumentCount(); ++i) {
if (i > 0){
result.append(" ");
}
result.append(context->argument(i).toString());
}
QScriptValue calleeData = context->callee().data();
QTextEdit *edit = qobject_cast<QTextEdit*>(calleeData.toQObject());
edit->append(result);
return engine->undefinedValue();
}
:
:
QScriptValue printfunc = m_engine->newFunction(scriptPrintFunction);
printfunc.setData(m_engine->newQObject(m_output));
m_engine->globalObject().setProperty("print", printfunc);
|
ここでは、オブジェクトでなく関数(function)を新たに定義して、その名前を"print"としています。
また、その関数にm_outputを関連付けています。
これで、QtScriptの中で"print"と記載すると、m_outputへ出力されます。
このようにQtアプリケーションの画面オブジェクトをQtScriptの中で利用することができます。
では、続けて、Qtアプリケーションの画面オブジェクトでないものを、QtScriptで作成してみましょう。
QtScriptで扱えるメソッドの範囲
Qtアプリケーションの画面オブジェクトをQtScriptで利用することができろことは、先に記述しました。
まずは、その利用できる範囲を、確認しておきましょう。
QObjectを継承したクラスで、signals, slots, propertiesは、全て使えます。
また、通常のメソッドでも、先頭に"Q_INVOKABLE" というキーワードを指定すると使えるようになります。
簡単な、QTextEditを継承したクラスを作成し、appendするだけのメソッドを用意してみましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| #ifndef DEBUGWIDGET_H
#define DEBUGWIDGET_H
#include <QtGui>
#include <QtScript>
class DebugWidget : public QTextEdit
{
Q_OBJECT
public :
DebugWidget(QWidget *parent=0);
Q_INVOKABLE void output_invoke(const QString &text) { append(text); };
void output_noinvoke(const QString &text) { append(text); };
protected :
Q_INVOKABLE void output2_invoke(const QString &text) { append(text); };
private :
Q_INVOKABLE void output3_invoke(const QString &text) { append(text); };
public slots:
void output(const QString &text) { append(text); };
protected slots:
void output2(const QString &text) { append(text); };
private slots:
void output3(const QString &text) { append(text); };
};
#endif
|
output_noinvoke以外は、public,protected,privateに差があるだけです。
これをそれぞれ実行してみましょう。
debugPrint.output("test");
debugPrint.output2("test");
debugPrint.output3("test");
debugPrint.output_invoke("test");
debugPrint.output2_invoke("test");
debugPrint.output3_invoke("test");
debugPrint.output_noinvoke("test");
最後だけが失敗します。
つまり、
public,protected,privateなどは、全く関係なく使用できます。
propertiesは、ここまであまり登場していませんが、他のサイトなどでのQtスクリプトの説明やチュートリアルで頻繁に説明があります。
この考え方は、VCLにそっくりです。
登録の仕方は、以下のように登録します。(QWidgetを継承しているクラス全てで使えます)
class DebugWidget : public QTextEdit
{
Q_OBJECT
Q_PROPERTY( bool visible WRITE setVisible READ isVisible )
:
この例では、"visible"というプロパティを定義しています。
書き込み時は、"setVisible"メソッドを使い、読み出しの時は、"isVisible"メソッドを使うように宣言しています。
このように宣言すると、QtScriptの中では、以下のように記述できます。
if(obj.visible==false) { // =isVisible()
obj.visible = true; // =show
} else {
obj.visible = false; // =hide
}
QtScriptでダイアログを作る
オブジェクトの使えるメソッドが、大方理解できたと思います。
早速、QtScriptでダイアログを作ってみましょう。
あらかじめQtアプリで用意されたものを利用するのは、理解できたと思います。
では、QtScriptの中で新しくオブジェクトを作成するには、どうしたらよいでしょう。
以下にその方法を例で記述します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| Q_SCRIPT_DECLARE_QMETAOBJECT(QDialog, QWidget*)
:
:
void MainWindow::excuteScript()
{
QString sscript=m_script->toPlainText();
if(!sscript.isEmpty()){
QScriptEngine engine;
QScriptValue objectwin= engine.scriptValueFromQMetaObject<QDialog>();
engine.globalObject().setProperty("Dialog", objectwin);
m_output->append( engine.evaluate(sscript).toString() );
}
}
|
1行目、cpp先頭で、QtScriptで、Metaオブジェクトを使うことを宣言してる文が追加されました。
11 - 12行目、QScriptEngineで、新しいMetaオブジェクトを作成し、その名前を"Dialog"と宣言しています。
画面の上部に、以下のように入力して、"Ctrl+G"で実行してみましょう。
以下のような大きなダイアログ画面が表示されたと思います。
ここで、画面サイズを指定したいところですが、slotの中には、
画面サイズを変更するメソッドはありません。
そこで、QDialogを継承させたクラスで、画面サイズと位置を指定できるsetGeometryをコールするメソッドを追加してみましょう。
1
2
3
4
5
6
7
8
9
10
11
| class SDialog : public QDialog
{
Q_OBJECT
public:
SDialog(QWidget *parent) : QDialog(parent) {};
public slots:
void setPosition(int x, int y, int w, int h ){
setGeometry ( x, y, w, h );
};
};
|
このクラスを使えば、"setPosition"がQtScriptで使用できるようになります。
画面の上部に、以下のように入力して、"Ctrl+G"で実行してみましょう。
以下のようなダイアログ画面が小さく表示されたと思います。
このようにして、QLabel,QPushButton,QTextEdit を同様にQtScriptで使用できるようにすれば、
以下のようなスクリプトで、簡単な画面を作成できます。
var win=new Dialog;
var btn=new Button(win);
var txt=new TextEdit(win);
var lbl=new Label(win);
win.setPosition(100,100,200,100);
btn.setPosition(150,40,40,25);
txt.setPosition(10,40,130,25);
lbl.setPosition(10,15,130,25);
lbl.setText("Hellow Script Window!!");
win.exec();
上記のスクリプトで作成された画面です。
QtScriptでシグナルとスロットを制御する
最後に、QtScriptで作成した画面でシグナルとスロットを制御してみましょう。
先にも記述したとおり、QtScriptの中では、
QObjectを継承したクラスのsignals, slots, propertiesは、全て使えます。
つまり、シグナルとスロットもコントロールできるわけです。
では、connectの仕方、desiconnect、signalを発生させるには、どうしたらよいでしょう。
QtScriptでは、connectする際、signalをメンバーとして扱います。
例えば、PushButtonのクリックsignalは、以下のようになります。
var btn=new Button(win);
btn.clicked.connect(cleckButton);
ここでは、PushButtonのクリックsignalをcleckButtonという関数(function)へコネクトしています。
disconnectも同様に行うことができます。
var btn=new Button(win);
btn.clicked.connect(cleckButton);
:
:
btn.clicked.disconnect(cleckButton);
signalを発生させる場合は、signal用のメソッドをコールするだけです。
var btn=new Button(win);
btn.clicked();
では、先のスクリプトで作成したダイアログ画面で、ボタンをクリックしたら、
TextEditへ"append"させましょう。
var win=new Dialog;
var btn=new Button(win);
var txt=new TextEdit(win);
var lbl=new Label(win);
win.setPosition(100,100,200,100);
btn.setPosition(150,40,40,25);
txt.setPosition(10,40,130,25);
lbl.setPosition(10,15,130,25);
lbl.setText("Hellow Script Window!!");
function cleckButton()
{
txt.append("Clicked!!");
}
btn.clicked.connect(cleckButton);
print(win.exec());
上記のスクリプトで作成された画面です。
ボタンをクリックすると右のTextEditへ、"Clicked!!"と表示されたことと思います。
QtScriptは、良くできています。
簡単なツールであれば、このスクリプトで、ほとんど実現できてしまいます。
また、Qtアプリケーションに組み込めば、更に面白いこともできそうです。
最後にQtScriptには、デバッグツールもあります。デバッグ実行すれば、ステップ実行などもできます。
サンプルソースには、デバッグ実行まで含まれていますので、ご参考ください。
もっと、Qt関連について詳しく知りたい方は、以下の本なども良いと思います。
Qtに関する日本語の本が少ないですね。「入門書」は、さすがに、このページを読まれるくらいの方は不要だと思います。
やっぱり、本+ネット+試してみる!!の3本柱でやっていく以外にないように思います。
コメントをどうぞ