2008/05/31
----------------------------------------------------------------------
今までに、<a href="http://www.limo.fumi2kick.com/tips/rrda/rrda01.html">RRDA</a>(Rero2's Restrict Data Archiver)リソースのデータ読み出しまわりは
動作してたので、
今日は、時計の描画まわりを実装する。

とりあえず、既存のフォームに対して QPainter で画像を描画させると、こんな感じ。

<SOURCE>
    QPixmap* pixmap = theme_resource_.pixmap.value(tag_name, NULL);
    painter.drawPixmap(QPoint(0, 0), *pixmap);
</SOURCE>

<img src="img/qtmclock_no_resize.jpg">

部分しか描画されてない...。
で、ベース画像サイズで Widget をリサイズさせたら、ちゃんと描画された。

<SOURCE>
    QPixmap* base_pixmap = theme_resource_.pixmap.value("base/base_org");
    region_ = new QRegion(base_pixmap->mask());
    parent->resize(base_pixmap->size());
</SOURCE>

<img src="img/qtmclock_resized.jpg">

いい感じだな。
あとは、リサイズのときに一緒に取得した region_ を用いて、描画画像のくりぬきを行うあたり。

どうやら、最も上位の Widget で setMask() をする必要があるらしいので、
ThemeClockWidget クラス内の resizeEvent() 内で設定を更新する。

<SOURCE>
void ThemeClockWidget::resizeEvent(QResizeEvent* event) {
  static_cast<void>(event);

  QRegion* region = pimpl->draw_widget_->getRegion();
  if (region) {
    setMask(*region);
  }

</SOURCE>

<img src="img/qtmclock_regioned.jpg">

いい感じ。
こういうことが、クロスプラットフォームで実現できるあたり、Qt は便利だと思う。
日本では、あまり開発環境として使われている気がしないが...。

日本で使われないのは、C++ 開発数とか、価格が理由なのかな？
1 ライセンスで初年度の購入価格が 480,900 ってのは、個人では無理だろうし。(価格の単位不明)

----------------------------------------

で、以下が、時計の針を描画するあたりを実装した結果。
QPainter の rotate(), translate() のあたりに少しとまどったが、
まぁ、動作したのでいい感じ。

<img src="img/qtmclock_analog_draw.jpg">

----------------------------------------

で、最後にデジタル情報の描画。
まぁ、snprintf() に出力した内容をもとに、所定の位置に描画するだけ。

以下の画像を登録しておいてから、

<img src="img/segment.png">

こんなソースコードで時間を文字列にして、

<SOURCE>
      snprintf(buffer, BufferSize, "%02d:%02d_%02d_%c",
               hour, minute, second, (hour >= 12) ? 'P' : 'A');
</SOURCE>

表示すると、こんな感じ。

<img src="img/qtmclock_default.jpg">

----------------------------------------

で、よくみると、回転させた針の画像にジャギーが出ている。

<img src="img/qtmclock_antialias_before.png">

なので、アンチエイリアスを QPainter に対して指示しておく。

<SOURCE>
    QPainter painter(parent);
    painter.setRenderHint(QPainter::SmoothPixmapTransform);
</SOURCE>

適用後は、こんな感じ。

<img src="img/qtmclock_antialias_after.png">

いい感じだな。


2008/06/01
----------------------------------------------------------------------
ドラッグ＆ドロップでのテーマ読み出しを実装する。
現状では、テーマを指定せずに qtmclock を実行すると、こんなフォームが表示される。

<img src="img/qtmclock_default_form.jpg">

が、作ったは良いものの、実際には Windows でデスクトップ上のテーマファイルを
ドロップしても、エラー扱いになる...。

調べてみると、Qt のフォーム上に配置したパス名は、UTF-16 に変換されるが、
Windows の場合は Shift-JIS でないと、だめらしい。

なので、locale が ja のときに、"UTF-16" -> "Shift-JIS" の変換を行うように調整する。

<SOURCE>
  QList<QUrl> url_list = mime_data->urls();
  for (int i = 0; i < url_list.count(); ++i) {

    QString path = url_list.at(i).path();

    // 先頭の '/' を取り除く
    path.replace(0, 1, "");

    // 日本語ロケールの場合、文字列を SJIS に変換する (Windows 用)
    if (! QLocale::system().name().compare("ja")) {
      QTextCodec* codec = QTextCodec::codecForName("Shift-JIS");
      QString path = codec->fromUnicode(path);
    }
    if (pimpl->draw_widget_->loadThemeFile(path)) {
      resize(pimpl->draw_widget_->size());
      return;

    } else {
      QString out;
      QMessageBox::warning(this, tr("qtmclock"),
			   tr("%1 is not correct theme file.").arg(path),
			   QMessageBox::Ok);
    }
  }
</SOURCE>

まぁ、こんな感じかな？
日本語独自の実装が入り込んだが、仕方ない。

つか、Qt って、"Shift-JIS" なんかの変換もできるのね..。
やつら、頭おかしい。(もちろん、良い意味で)

----------------------------------------

前回起動時の情報を覚えるように調整する。
具体的には、テーマファイルの指定がなしで起動されたときに前回のテーマを使ったり、
前回と同じ位置に時計を配置するあたり。

<SOURCE>
  void readSettings(bool position_specified, ThemeClockWidget* parent) {

    QSettings settings("Hyakuren Soft LTD.", "qtmclock");
    pre_theme_file_ = settings.value("theme_file", "").toString();

    if (! position_specified) {
      // 位置が指定されてないときのみ、設定を反映させる
      QRect position = settings.value("geometry", parent->geometry()).toRect();
      parent->setGeometry(position);
    }
  }

  void writeSettings(ThemeClockWidget* parent) {

    QSettings settings("Hyakuren Soft LTD.", "qtmclock");
    settings.setValue("theme_file", theme_file_);

    QRect position = parent->geometry();
    QSize size = parent->size();

    settings.setValue("geometry", QRect(position.x(), position.y(),
                                        size.width(), size.height()));
  }
</SOURCE>

これらを、それぞれコンストラクタ、デストラクタ内で呼び出せば、おしまい。
いい感じかな？

----------------------------------------

とりあえず、テーマ作成用フォームの作成を開始した。

<img src="qtmclock_config_first.jpg">

左側で部品の位置を調整し、右側で部品そのものの設定をする予定。
で、これを Linguist で日本語化したものが、これ。

<img src="qtmclock_config_first_ja.jpg">

なんか、アプリケーションっぽくなるから、不思議だ。


2008/06/02
----------------------------------------------------------------------
テーマ作成用ツールの続きから。
とりあえずのフォームは Qt Designer で作成したので、ソースコードに
なにをどうするか、をコメントで記述。

あと、QAction にボタンを張り付けて、ツールチップ化してみた。

<img src="qtmclock_config_tooltips.jpg">

素材は、http://snow.if.tv/ のを利用。
私も、絵描きのスキルが欲しいです。


2008/06/03
----------------------------------------------------------------------
QTreeView の使い方が分かってきた。

ツリー項目に値を設定するには、QTreeView::itemAt(), QTreeView::itemBlow() とか、
QTreeWidgetItem::child() なんかを使うらしい。

<SOURCE>
    // ベース画像
    QTreeWidgetItem* base = parent->parts_tree_->itemAt(0, 0);
    base->setText(1, tr("base image"));
</SOURCE>

<img src="img/qtmclock_config_tree_first.jpg">

次は、この "base image" になっている箇所を、

<img src="qtmclock_config_image_select.jpg">

こんな感じにするあたりかな？
まぁ、それはまた明日。


2008/06/05
----------------------------------------------------------------------
メニュー処理 (QAction) とか、ツールボックス (QToolButton) の実装が、ほぼ終わる。
見た目、何も変化ないですが...。

<img src="qtmclock_config_menu_created.jpg">

しかし、変更があったら保存を enable にして、保存したら disable に戻すあたりとか、
なんかテンプレート的な実装は、どうにか共通化したいと思った。

今回でメニューの仕組みを確認したら、共通化の枠組みを模索していきたい。

----------------------------------------

さて、残りは、いよいよ部品画像を表示して、
配置位置を調整できるようにするあたりかな？

本題を最後に残しておくあたり、だめな作り方な気がするよ！


2008/06/07
----------------------------------------------------------------------
メニュー中のツリー表示に、部品アイコンと、そのファイル名が表示できた！
画像は、ほとんど見えないが...。

<img src="img/qtmclock_config_tree_icons.jpg">

しかし、これで QTreeWidget まわりの実装は、ほぼ終わりかな？
いい感じ。


2008/06/08
----------------------------------------------------------------------
休みを利用して、部品の配置とかもできるようになった。
いい感じ。

<img src="qtmclock_config_graphics.jpg">

が、データを zip 圧縮してリリースファイルを生成するあたりが、まだ。
というか、実装したつもりだが、出力結果がオリジナルと異なる。


--- オリジナル配布のスクリプトで生成したもの
RRDA - Ver1.00
000000c3
000053c4,0000004b
000000c3,00000020,config
000000e3,00004c56,base
00004d39,00000232,digital
00004f6b,00000137,hari_h
000050a2,00000128,hari_m
000051ca,000000e0,hari_s
END


--- 自作プログラムで生成したもの
RRDA - Ver1.00
000000c3
0000521d,00000000
000000c3,00000008,config
000000cb,00004c3e,base
00004d09,0000021a,digital
00004f23,00000120,hari_h
00005043,00000111,hari_m
00005154,000000c9,hari_s
END

なんか、gzip 圧縮のヘッダ分だけ、サイズが小さい気がする...。
まぁ、細かいあたりは、また明日かな？

----------------------------------------

と思ったが、gzip はヘッダをあっても読み飛ばせるようなので、
オリジナルソースでヘッダがなければ戻っていた箇所を、戻らないようにしたら、
とりあず、既存のテーマ、自分のプログラムから生成されたテーマ、の両方とも動作した。

<SOURCE>
    const unsigned char* work = gz_header_check(data_first, area_size);
    if (work == NULL) {
      // zip ヘッダがない場合
      work = data_first;
    }
</SOURCE>

いい感じ。


2008/06/10
----------------------------------------------------------------------
Windows XP の場合、取得パス名が SJIS になるように調整。
これは、QFileDialog において取得されるパスが UTF-16 であり、
そのままだと日本語パスを含むファイル名が fstream で操作できないため。

<SOURCE>
const QString qrk::toSjisString(const QString& text) {

#ifdef Q_WS_WIN

  if (QLocale::system().name().compare("ja_JP")) {
    // 日本語ロケールでない
    return text;
  }

  QSysInfo::WinVersion version = QSysInfo::windowsVersion();

  if (version == QSysInfo::WV_XP) {
    // 日本語ロケールの場合、SJIS に変換する
    QTextCodec* codec = QTextCodec::codecForName("Shift-JIS");
    return codec->fromUnicode(text);

  } else {
    return text;
  }
#else
  return text;
#endif
}
</SOURCE>

おそらく、Linux とか、Windows 2000, Windows Vista とかでも、
その OS 毎の固有の文字変換が必要になりそう。
手持ちの環境があれば、試すんだけどなぁ...。


2008/06/12
----------------------------------------------------------------------
日本語についての考察。
QFileDialog なんかは、QString に UTF-8 形式で格納したパスを返す。
で、そのまま Qt の関数でファイルオープンが可能。

fstream で処理したい場合は、SJIS でファイル名を扱う。(Windows XP)

ってことなのかな？


2008/06/24
----------------------------------------------------------------------
カレンダー機能を実装したが、準備した文字画像が当幅でないらしく、
表示がずれる。

<img src="img/qtmclock_preview_align.png">

※ 針に隠れて、文字が見えない、ってのものあるが...。

Gimp で生成した文字列を、当幅と見なして使っていたのが、まずいっぽい。
仕方がないので、指定したフォント、で時計部品用の画像を生成する
プログラムを作成することにした。

がんばろう。
