つくねの手帳

C++およびAndroidアプリ開発メインで何か書きたい

Qt 同じ単語に複数の翻訳をあてるのに少し頑張った話1

この記事はQt4.8.4基準で記載します。

Qtにはソース上の文字列を拾い上げて、翻訳用のファイルを作っておくことで、プログラム実行時に動的言語変換ができます。
具体的な手順は割愛しますが、

QLabel text;
text.setText(tr("string"));

のように、trでくくった文字列をlupdateで抜き出し、xml形式の.tsファイルを生成します。
その.tsファイルをQtLinguistで手動翻訳し、lreleaseで.qmファイルを作り、その.qmファイルをプログラムで読み込みます。


.qmファイルがどのように生成されているかわからないので、実際に動作させてみて確かめただけなのですが、.tsファイル上に同じ単語が複数ある場合、別々な翻訳をしても、一番上に書かれたもので表示されてしまうようです。

QLabel text;
text.setText(tr("start")); // スタートと表示したい

QLabel text2;
text2.setText(tr("start")); // 開始と表示したい

このような場合、以下のように.tsファイルを記述しても、一律スタートと表示されてしまいます。

<!-- 日本語でスタートと表示したいやつ。 -->
    <message>
        <location filename="xxx" line=""/>
        <source>start</source>
        <translation type="unfinished">スタート</translation>
    </message>

<!-- 日本語で開始と表示したいやつ。 -->
    <message>
        <location filename="xxx" line=""/>
        <source>start</source>
        <translation type="unfinished">開始</translation>
    </message>


そこで、ソース上に書く文字列は識別用のIDとしてユニークなものとして扱う手段を取りました。

下準備として、以下のものを用意しました。
・文字列を指定するenum
enumに対応する文字列 QT_TR_NOOP("")の形で記載
・指定されたenumから文字列をsetText(tr(""));へ渡すラッパー関数

typedef enum
{
  E_TEXT_1,
  E_TEXT_2
}stringId;


QString* stringData[] = 
{
  QT_TR_NOOP("E_TEXT_1"),
  QT_TR_NOOP("E_TEXT_2"),
};
void setStringId(stringId id)
{
  this->m_pLabel->setText(tr(stringData[id])); //実際は配列直指定はやめましょう
}

かなりざっくり書きましたが、いままで""でくくって文字列としていた場所をenumの文字列で書くイメージです。

この場合、文字列が増えるたびにenum、文字列定義、.ts/.qmファイル作成が発生してとても面倒です。
そこで、.ts/.qmファイル作成まで自動でやってしまえ!というのが次回のお話。

Qt クリック可能なQLabelの実装と独自シグナル/スロット

QLabelクラスはマウスクリック時動作の関数が用意されていませんが、独自にクリック時のシグナルを作成することでクリック時の動作を行いやすくなります。

// QClickableLabel.h

// QLabelの拡張クラス定義

class QClickableLabel : public QLabel
{
    Q_OBJECT // シグナル/スロット使用に必要

public:
    QClickableLabel();
    ~QClickableLabel();

signals:
    // クリック時に発生させるシグナル
    void clicked();

protected:
    // 左クリック(LButtonDown)時に呼ばれる
    void mousePressEvent(QMouseEvent *e);
};
//QClickableLabel.cpp


void QClickableLabel::mousePressEvent(QMouseEvent *e)
{
    // シグナル送信
    emit cliced();
}


このクラスを使用してLabelを作ることで、クリック時にclicedのシグナルが発生することになります。
connectも以下のようにできます。

// MyClass.h

// クリック可能ラベルクラス前方宣言
class QClickableLabel

class MyClass : public QWidget
{
    Q_OBJECT // シグナル/スロット使用に必要

public:
    MyClass();
    ~MyClass();

public slots:
    // クリック時に呼びたい関数
    void OnClickedLabel();

private:

    QClickableLabel* m_pLabel;
};
// MyClass.cpp

#include "MyClass.h"
#include "QClickableLabel.h"

MyClass::MyClass()
{
    // クリック可能ラベルクラス生成
    this->m_pLabel = new QClickableLabel();

    // m_pLabelのcliced()シグナル発生時に、this(自クラス)のOnClickedLabel()が呼ばれるようにする
    connect(this->m_pLabel , SIGNAL(cliced()) , this , SLOT(OnClickedLabel()));
}

void MyClass::OnClickedLabel()
{
    // クリック時に行う処理
}

installEventFilterとeventFilterでもイベントハンドリングはできます。
しかし、よく使うイベントや、同一クラス内オブジェクトが多く、eventFilterが煩雑化する場合などは、シグナルとスロットを実装したほうがよさそうです。

Qt QWidgetの派生クラスへのスタイルシート設定

今日は、先日少しはまったQWidgetを基底クラスとしたクラスへのスタイルシート設定について。

QWidgetクラスは背景色のスタイル設定がサポートされています。
しかし、QLabelなどのように、そのままsetStyleSheet()を呼んでも反映されません。
以下のように、Q_OBJECTの指定と、paintEventを処理する必要があります。

// MyWidget.h
#include <QWidget>

class MyWidget : public QWidget
{
    Q_OBJECT

public:
    MyWidget();
    ~MyWidget();

    void paintEvent(QPaintEvent*); // これが必要
};
// MyWidget.cpp

// スタイルシート設定を反映させるためのペイントイベント処理
void MyWidget::paintEvent(QPaintEvent *)
{
    QStyleOption opt;
    opt.init(this);
    QPainter p(this);
    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}

QLabelなどはそのクラス内で処理してくれているようなのですが、QWidgetクラスでは実装されていないようです。
※なぜQWidgetクラスで処理が実装されていないのかはいまいちわからいままです…

Qtの公式リファレンスにも記載されているので参考に。
Qt Style Sheets Reference | Qt 4.8

Qt スタイルシート優先度と追加指定

前回スタイルシート設定について書いたので、その少し続きを…


スタイルシート設定は後から設定した物が優先で使用されます。


widget->setStyleSheet("background-color : rgb( 0 , 0 , 0 );");

widget->setStyleSheet("background-color : rgb( 255 , 255 , 255 );");


この場合、widgetの背景色は白になります。



また、後からスタイル設定をすると、前回の設定は引き継がれません。

widget->setStyleSheet("background-color : rgb( 0 , 255 , 0 );color :
 rgb( 255,255,255);"); // 背景緑 文字色白

widget->setStyleSheet("color : rgb( 255 , 0 , 0 );"); // 文字色赤

この場合、文字色の変更は反映されますが、背景色緑の設定が失われてしまいます。


後からスタイルの情報を追加したい場合は、QWidgetクラスからstylesheetのプロパティを取得し、スタイルを追加します。

void CreateLabel()
 {
     QLabel* label = new QLabel("text",this);
     label->setStyleSheet("color : rgb( 255 , 0 , 0 );") // 文字色は赤

     addStyleSheet(label);
 }

 void addStyleSheet(QLabel* label)
 {
     // スタイルシート情報取得
     QString str = label->styleSheet();
     // 追加したいスタイルを連結
     str += "background-color : rgb( 0 , 255 , 0 );"; // 背景色は緑

     label->setStyleSheet(str);
 }

Qt スタイルシート指定でのウィジェットデザイン

ウィジェットのスタイル指定が出来るsetStyleSheet(const QString &str)で、よく使いそうな記述をまとめようと思います。

・背景色の指定

widget->setStyleSheet("background-color : rgb(255,255,255);");

・文字色の指定

widget->setStyleSheet("color : rgb(255,255,255);");

以下のようにすれば、背景、前景に透過度も指定できます。

widget->setStyleSheet("background-color : rgba(255,255,255,128);");

・枠線幅指定

widget->setStyleSheet("border-width : 1px;");

・枠線スタイル指定

widget->setStyleSheet("border-style : solid;"); // 実線枠

・枠線色指定

widget->setStyleSheet("border-color : rgb(255,255,255);");

基本的に複数の指定を行うと思いますが、それぞれを""でくくって一括指定ができます。

widget->setStyleSheet("border-style : solid;" "border-width : 1px;" "border-color : rgb(255,255,255);" "background-color : rgba(255,255,255,128);" );

もちろん引数の文字列はQStringを別に生成し関数に渡してもOKです。

QString str;
str = "border-color : rgb(255,255,255);";
widget->setStyleSheet(str);