2017-06-18

Qtウィジェットアプリケーションで日本語入力(IME : Input Method Editor)を使う

基本的なアプリケーションを新規作成

Qt Creator で新規アプリケーションを作成します。

最も基本的なQtウィジェットアプリケーションです。

MyWidget

新しいクラスを作ります。

名前はお好みですが、例としてMyWidgetにします。このクラスはQWidgetを継承します。

ここで一度 [qmake の実行] を行います。

メインウィンドウにQWidgetを貼り付けます。

ウィジェットを選択して、[格上げ先を指定...]を実行します。

先ほど作成したクラス名を入力して、[追加]、[格上げ]を行います。

ウィジェットの見た目が何もないのは寂しいので、paintEventを実装します。(必須ではありません)

void MyWidget::paintEvent(QPaintEvent *) 
{
	QPainter pr(this);
	pr.drawEllipse(0, 0, width() - 1, height() - 1);
}

実行してみます。

この状態では、IMEをオンにして日本語入力モードにすることはできません。

IMEを有効にする

ウィジェットを選択して、focusPolicyをStrongFocusに設定します。

WA_InputMethodEnabled 属性を設定します。
MyWidget::MyWidget(QWidget *parent) : QWidget(parent)
{
	setAttribute(Qt::WA_InputMethodEnabled);
}

inputMethodQuery 関数をオーバーライドします。

後で改造しますが、とりあえず、基底クラスの inputMethodQuery 関数を呼ぶだけにしておきます。

QVariant MyWidget::inputMethodQuery(Qt::InputMethodQuery q) const 
{
	return QWidget::inputMethodQuery(q);
}

プログラムを実行して、日本語を入力してみます。入力中の文字は表示されませんが、IMEがオンになっていることは分かります。

下図はATOKを使用している例です。

IMEの候補ウィンドウが、MyWidgetの下に表示されています。

候補ウィンドウの表示位置を制御

inputMethodQuery 関数を変更します。

引数が ImCursorRectangle のとき、ウィジェットの中央の座標を返すようにします。

QVariant MyWidget::inputMethodQuery(Qt::InputMethodQuery q) const 
{
	if (q == Qt::ImCursorRectangle) {
		int x = width() / 2;
		int y = height() / 2;
		return QRect(x, y, 1, 1);
	}
	return QWidget::inputMethodQuery(q);
} 

およそ中央付近に候補ウィンドウが表示されるようになりました。

入力文字を受け取る

編集中の状態を表示するためのラベルを追加します。

テキストが入力されたことを通知するためのシグナルを宣言します。

signals: 
	void imPreeditChanged(QString const &s);
	void imCommitChanged(QString const &s);
inputMethodEvent 関数を実装します。
void MyWidget::inputMethodEvent(QInputMethodEvent *e) 
{
	QString preedit = e->preeditString();
	QString commit = e->commitString();
	emit imPreeditChanged(preedit);
	emit imCommitChanged(commit);
}

メインウィンドウ側で、ラベルにテキストを設定する処理を追加します。

MainWindow::MainWindow(QWidget *parent) : 
	QMainWindow(parent),
	ui(new Ui::MainWindow)
{
	ui->setupUi(this);
	setAttribute(Qt::WA_InputMethodEnabled);

	connect(ui->widget, &MyWidget::imPreeditChanged, [&](QString const &s){
		ui->label_preedit->setText(s);
	});

	connect(ui->widget, &MyWidget::imCommitChanged, [&](QString const &s){
		ui->label_commit->setText(s);
	});
}
変換中は preedit 、確定したものは commit に表示されます。

Linuxで日本語入力に対応する際の注意

inputMethodQuery 関数をオーバーライドして、候補ウィンドウの位置を指定する際に、Linux版Qtでは注意する点があります。QtまたはQt用IMプラグインのバグによると思われる現象で、対象のウィジェットがメインウィンドウから見て左上原点と一致しているとき、Qt::ImCursorRectangleが送られてこないため、候補ウィンドウの位置を指定したくてもできない、候補ウィンドウがスクリーン左上に表示される、という不具合があります。対策としては、

一般的なプログラムでは、メニューバーかツールバーはあると思いますので、ウィジェットがメインウィンドウの原点と一致することは希ですが、メニューバーとツールバーを両方取り去ったプログラムを作成する場合は注意が必要です。

候補ウィンドウの表示位置の更新

矢印キーなどでカーソルを移動するようなプログラム(テキストエディタなど)を作成する場合、カーソル位置に合わせて、候補ウィンドウの位置も調整する必要があります。その場合、カーソル位置を変更したタイミングで、以下のようなコードを実行します。

	QApplication::inputMethod()->update(Qt::ImCursorRectangle);

これを行うことによって、inputMethodQuery に Qt::ImCursorRectangle が送られてくることを促します。