つくねの手帳

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

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

前回からの続きです。
例によって、Qtのバージョンは4.8.4です。


.tsファイルと.qmファイルを自動的に作れるようにexcelのマクロを組みます。
excelのシート上に、enum(識別子)として使うID、対応する表示文言を列挙します。
マクロでヘッダファイルにenumと文字列を出力。
さらに、.tsファイルも作成しますが、lupdateを使うと不都合があるため、べた書きします。


もし、.tsファイル作成時にlupdateを使うと、

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.0">
<context>
    <name>QWidget</name>

クラス化されていないファイルへQT_TR_NOOP()で文字列を記載していると、QWidgetとして.tsが作成されます。
しかし、の部分は実際にtr()で文字列をシステムに渡すクラスを指定する必要があります。
前回の記事であれば、setStringIdを実装しているクラスとなります。


.tsファイルができたら、lreleaseコマンドをたたいて.qmファイルを作成しておしまいです。


すごーくざっくりしている上に、コードサンプルがないのでひどくわかりにくい感じになってしまった…

androidアプリのお勉強1 WebApiを使った天気予報アプリを目指して

Android Studioを使用して、Androidアプリ作成勉強を兼ねて、WebApiを使った天気予報アプリを作りたいなと思います。

WebApiを使用するには、当然インターネットアクセスが不可欠です。
そこで、アプリからandroidOSへインターネットアクセス許可をもらう必要があります。
そのために必要な設定を指定する必要があります。それがパーミッションです。
今回は、そのパーミッションについて少し書こうかと思います。



先ずは、プロジェクトを作成し、Web Apiを使うためにパーミッションを追加します。
Project>app>src>main>AndroidManifest.xmlに以下のコードを追加します。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="プロジェクト名">

<!-- これを追加 -->
    <uses-permission android:name="android.permission.INTERNET"/>

今回はWebApiを使うためのパーミッションなので、インターネットアクセスを許可するINTERNETを指定しましたが、このほかにも色々なパーミッションが存在します。

・READ_CONTACTS
 ユーザーのアドレス帳データの読出し

・WRITE_CONTACTS
 ユーザーのアドレス帳データ書き込み

・RECEIVE_SMS
 SMS受信の監視

ACCESS_COARSE_LOCATION
 携帯中継局、WiFiなどのロケーションプロバイダを使用する。

ACCESS_FINE_LOCATION
 GPS情報など精度の高いロケーションプロバイダを使用する。

・READ_CALWNDAR
 カレンダー情報の読出し

・WRITE_CALENDAR
 カレンダーへの書き込み、メール送信の機能も使用できる。

このほかにもかなりたくさんのパーミッションがあります。
利用したい機能に合わせて指定を増やす必要があります。


また、Android6.0からは、各パーミッションについてユーザーがインストール時に利用可否を個別に指定できるようになりました。

Android6.0未満の場合は、アプリにパーミッションを与える→インストールで、与えたパーミッションの全てが許可されていましたが、Android6.0からはユーザーが選べるのです。
そのため、許可されなかったパーミッションに対して、その機能が使えない可能性を考慮してロジックを組む必要があります。

パーミッションが使用できるかはcheckSelfPermission関数で確認できます。
さらに、requestPermissions関数でユーザーに許可を求めることもできます。

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);