(19日目) blockdiag を Pukiwiki で使ってみよう

昨日の記事では Trac の Wiki に blockdiag の図を埋め込む方法をご紹介しました。
今日は他のドキュメントツールである Pukiwiki に blockdiag の図を埋め込む方法をご紹介しましょう。

PukiwikiPHP で動作する Wiki クローンのひとつで、現在は開発が止まっていますがそれでも強い人気を誇っています。
Pukiwiki 用の blockdiag プラグインは @hekyou さんが開発されたものです。
@hekyou さんは他にも複数人で blockdiag の図を編集できる Interactive shell live!! を開発されるなど、
blockdiag にいろいろ貢献されています :-)

インストール

Pukiwiki のインストールを行います。
Pukiwiki はアーカイブをダウンロードして設定ファイルを書き換えるだけで動くお手軽パッケージです。
ダウンロードした後、/var/www/wiki にファイルを設置します。

また、blockdiag プラグインも一緒に設置します。
ファイルをダウンロードしたあと、/var/www/wiki/plugin に設置します。

$ cd /tmp
$ wget -O pukiwiki-1.4.7_notb_utf8.tar.gz 'http://sourceforge.jp/frs/redir.php?m=jaist&f=%2Fpukiwiki%2F12957%2Fpukiwiki-1.4.7_notb_utf8.tar.gz'
$ wget https://raw.github.com/hekyou/diag-pukiwiki-extension/master/diag.inc.php
$ cd /var/www
$ sudo tar xzvf /tmp/pukiwiki-1.4.7_notb_utf8.tar.gz
$ sudo mv pukiwiki-1.4.7_notb_utf8 wiki
$ sudo mv /tmp/diag.inc.php wiki/plugin
$ sudo chown -R www-data.www-data wiki
$ sudo chmod -R o-w wiki

次に設定ファイル(pukiwik.ini.php)を編集します。
途中で管理用のパスワードを埋め込むところがあるので、パスワードの MD5 を生成しておきます。

$ echo -n 'password' | md5sum
5f4dcc3b5aa765d61d8327deb882cf99  -

そして、pukiwiki.ini.php を編集します。

$ sudo vi pukiwiki.ini.php

ここでは編集した箇所だけピックアップしました。

// Site admin's name (CHANGE THIS)
$modifier = 'tk0miya';

// Site admin's Web page (CHANGE THIS)
$modifierlink = 'http://capella.monochrome.jp/wiki/';

// Default: always fail
$adminpass = '{x-php-md5}5f4dcc3b5aa765d61d8327deb882cf99';

define('PKWKEXP_DISABLE_MULTILINE_PLUGIN_HACK', 0); // 1 = Disabled

// variables for blockdiag
defined('PLUGIN_DIAG_IMAGE_DIR') or define('PLUGIN_DIAG_IMAGE_DIR', '/tmp/');
defined('PLUGIN_DIAG_BLOCKDIAG_PATH') or define('PLUGIN_DIAG_BLOCKDIAG_PATH', '/usr/local/bin/blockdiag');
defined('PLUGIN_DIAG_NWDIAG_PATH')    or define('PLUGIN_DIAG_NWDIAG_PATH', '/usr/local/bin/nwdiag');
defined('PLUGIN_DIAG_SEQDIAG_PATH')   or define('PLUGIN_DIAG_SEQDIAG_PATH', '/usr/local/bin/seqdiag');
defined('PLUGIN_DIAG_ACTDIAG_PATH')   or define('PLUGIN_DIAG_ACTDIAG_PATH', '/usr/local/bin/actdiag');
define('PLUGIN_DIAG_FONT_PATH', '--font=/usr/share/fonts/truetype/ipafont/ipagp.ttf');

使ってみる

Pukiwiki の画面から Wiki の編集を選択します。
サンプルとして以下のテキストを入力してみます。

#diag(block){{
blockdiag {
   A -> B -> C, D;
}
}}
#diag(seq){{
seqdiag{
  A -> B -> C -> B;
}
}}

保存すると次のように Pukiwiki のページ内に blockdiag や seqdiag の図を埋め込むことができます。

Trac の場合と同じように、前後に {{ や }} などの記号が必要ですが、
それ以外は普段の blockdiag の書き方をそのまま指定することができます。


個人的には Sphinx が一押しですが、既存のドキュメントが pukiwiki でまとめられていた場合でも、
プラグインをいれることでかんたんに埋め込むことができます。
Pukiwiki ユーザーの方も是非試してみてはいかがでしょうか。

(18日目) blockdiag を Trac wiki で使ってみよう

Sphinx & blockdiag アドベントカレンダーに戻って 18日目です。
もう少しでクリスマスですね。この荒行のような毎日が終わると思うと心も弾みます。

今日は blockdiag を Trac 上の Wiki で動かしてみます。
TracPython で記述されたプロジェクト管理ツールで、
イシュートラッカーや Wiki などのメインの機能に加えて、
豊富なプラグインが提供されているので多くの人に利用されているツールです。

Trac プラグインのひとつとして、Wiki に blockdiag の図を埋め込む TracBlockDiagPlugin があります。
TrackBlockDiagPlugin は @wonderful_panda さんによって開発されたプラグインです。
今日はこのプラグインを利用して blockdiag を使ってみようと思います。

インストール

TracDebian パッケージになっていますが、最新版の 0.12 が強くおすすめとのことなので、
easy_install でインストールすることにします。
TracBlockDiagPlugin も URL を指定することで easy_install 経由でインストールできます。

$ sudo easy_install trac
$ sudo easy_install http://trac-hacks.org/svn/tracblockdiagplugin/0.12

Trac はインストールした後、trac-admin コマンドをつかってプロジェクト関係のファイルを生成する必要があります。
この部分はちょっと Sphinx に似ているかもしれません。
ここでは /var/lib/trac 以下にプロジェクト関係のファイルを設置しています。

途中で尋ねられる質問はふたつで、プロジェクト名称とデータベース設定です。
プロジェクト名称はちゃんと設定しておきましょう。

$ hash -r
$ sudo mkdir -p /var/lib/trac/
$ cd /var/lib/trac/
$ sudo trac-admin test-proj initenv
Creating a new Trac environment at /var/lib/trac/test-proj

Trac will first ask a few questions about your environment
in order to initialize and prepare the project database.

 Please enter the name of your project.
 This name will be used in page titles and descriptions.

Project Name [My Project]>

 Please specify the connection string for the database to use.
 By default, a local SQLite database is created in the environment
 directory. It is also possible to use an already existing
 PostgreSQL database (check the Trac documentation for the exact
 connection string syntax).

Database connection string [sqlite:db/trac.db]>
ask a few questions about your environment

Creating and Initializing Project
 Installing default wiki pages
  TracBackup imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracBackup
  TitleIndex imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TitleIndex
  WikiRestructuredText imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/WikiRestructuredText
  WikiPageNames imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/WikiPageNames
  TracPlugins imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracPlugins
  TracRepositoryAdmin imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracRepositoryAdmin
  TracEnvironment imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracEnvironment
  WikiRestructuredTextLinks imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/WikiRestructuredTextLinks
  TracReports imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracReports
  TracPermissions imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracPermissions
  InterMapTxt imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/InterMapTxt
  TracGuide imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracGuide
  TracModWSGI imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracModWSGI
  TracUpgrade imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracUpgrade
  TracBrowser imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracBrowser
  TracQuery imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracQuery
  WikiStart imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/WikiStart
  TracWiki imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracWiki
  TracNotification imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracNotification
  TracTickets imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracTickets
  TracIni imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracIni
  TracAdmin imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracAdmin
  TracTicketsCustomFields imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracTicketsCustomFields
  TracChangeset imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracChangeset
  TracRoadmap imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracRoadmap
  TracUnicode imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracUnicode
  TracLogging imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracLogging
  SandBox imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/SandBox
  TracNavigation imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracNavigation
  TracAccessibility imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracAccessibility
  InterWiki imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/InterWiki
  TracFineGrainedPermissions imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracFineGrainedPermissions
  InterTrac imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/InterTrac
  WikiNewPage imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/WikiNewPage
  WikiFormatting imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/WikiFormatting
  TracSearch imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracSearch
  TracSupport imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracSupport
  WikiProcessors imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/WikiProcessors
  WikiMacros imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/WikiMacros
  TracTimeline imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracTimeline
  TracRss imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracRss
  TracInterfaceCustomization imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracInterfaceCustomization
  TracInstall imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracInstall
  TracFastCgi imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracFastCgi
  TracStandalone imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracStandalone
  TracLinks imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracLinks
  RecentChanges imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/RecentChanges
  PageTemplates imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/PageTemplates
  TracSyntaxColoring imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracSyntaxColoring
  TracWorkflow imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracWorkflow
  TracImport imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracImport
  TracCgi imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracCgi
  TracModPython imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracModPython
  WikiHtml imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/WikiHtml
  WikiDeletePage imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/WikiDeletePage
  CamelCase imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/CamelCase
  TracRevisionLog imported from /usr/local/lib/python2.6/dist-packages/Trac-0.12.2-py2.6.egg/trac/wiki/default-pages/TracRevisionLog

---------------------------------------------------------------------
Project environment for 'My Project' created.

You may now configure the environment by editing the file:

  /var/lib/trac/test-proj/conf/trac.ini

If you'd like to take this new project environment for a test drive,
try running the Trac standalone web server `tracd`:

  tracd --port 8000 /var/lib/trac/test-proj

Then point your browser to http://localhost:8000/test-proj.
There you can also browse the documentation for your installed
version of Trac, including information on further setup (such as
deploying Trac to a real web server).

The latest documentation can also always be found on the project
website:

  http://trac.edgewall.org/

Congratulations!

次に TracBlockDiagPlugin の設定を行います。
プロジェクトのディレクトリ配下の conf/trac.ini を以下のように編集します。

$ sudo vi /var/lib/trac/test-proj/conf/trac.ini
[components]
blockdiagplugin.* = enabled

[blockdiag]
font = /usr/share/fonts/truetype/ipafont/ipagp.ttf
>||

プロジェクトを作成した後、Web 公開を行うための設定をします。
trac-admin コマンドの deploy オプションで公開用ファイル群を生成します。
>|sh|
$ sudo trac-admin /var/lib/trac/test-proj/ deploy /var/www/trac/test-proj
$ sudo chown -R www-data /var/lib/trac
$ sudo chown -R www-data /var/www/trac

次に認証用のアカウント定義ファイルを作成します。
htpasswd 形式ですので他のツールで生成してもよいでしょう。

$ sudo htpasswd -c /var/lib/trac/test-proj/trac.htpasswd tk0miya
$ sudochown -R www-data /var/lib/trac/test-proj/trac.htpasswd

Trac の動かし方には CGI, FastCGI, デーモン起動などいくつかやり方があるようですが、
今回はデーモンとして起動して、Apache をリバースプロキシとして利用する設定にします。

まずは Trac デーモンを自動起動させるために init スクリプトを設置します。
以下のスクリプトを /etc/init.d/tracd-test-proj として保存します。
ポート番号やディレクトリなどは必要に応じて適宜設定を書き換えてご利用ください。

#!/bin/sh

set -e

TRACD=/usr/local/bin/tracd
USER=www-data
PORT=8082
OPTION='/var/lib/trac/test-proj'
PIDFILE='/var/lib/trac/test-proj/tracd.pid'
AUTH=`basename $OPTION`',/var/lib/trac/test-proj/trac.htpasswd,realm'

case "$1" in
'restart')
        $0 stop
        sleep 1
        $0 start
        ;;

'start')
        /bin/su - $USER -c "$TRACD --daemonize --port $PORT --pidfile $PIDFILE \
                                   --basic-auth $AUTH $OPTION"

'stop')
        if [ -e $PIDFILE ]; then
                /bin/su - $USER -c "/bin/kill `cat $PIDFILE`"
                rm $PIDFILE
        fi
        ;;

*)
        echo "Usage: $0 { start | stop | restart }"
        exit 1
        ;;
esac

生成した init スクリプトに実行権限を与えて、起動設定をします。

$ sudo chmod +x /etc/init.d/tracd-test-proj
$ sudo update-rc.d tracd-test-proj defaults 80
$ sudo /etc/init.d/tracd-test-proj start

次に Apache の設定をします。
mod_proxy を有効にし、trac 用のプロキシ設定を追加します。
これも VirtualHost やパスなどは必要に応じて適宜書き換えてください。

$ sudo a2enmod proxy proxy_http
$ sudo sudo vi /etc/apache2/conf.d/trac
ProxyRequests Off # Disable forward proxy
ProxyPass /test-proj http://localhost:8082/test-proj
ProxyPassReverse /test-proj http://localhost:8082/test-proj
$ sudo /etc/init.d/apache restart

使ってみる

Trac の画面から Wiki の編集を選択します。
サンプルとして以下のテキストを入力してみます。

{{{#blockdiag(type=png)
{
  A -> B -> C, D;
}
}}}
{{{#!seqdiag(type=png)
{
  A -> B -> C -> B;
}
}}}

保存すると次のように Trac の Wiki ページ内に blockdiag や seqdiag の図を埋め込むことができます。

すこし前後に {{{ や }}} などの記号が必要ですが、それ以外は普段の blockdiag の書き方と一緒です。

blockdiag を使うことで、Trac の Wiki にも図を簡単に埋めこむことができるようになりました。
これで少しドキュメンテーションが楽になるのではないでしょうか。

(17日目) わがままドリブン

昨日は次にやろうと考えている blockdiag シリーズの話でしたが、
今日はそこに至る源泉であるわがままドリブンという考え方について紹介します。

わがままドリブン って?

ユーザの要望や希望だけではなく "わがまま" まで聞いて開発を行おう、という
blockdiag のキャッチフレーズです。
どこかから引用してきたものではなく完全に僕の造語なので、ググッても blockdiag ぐらいしかヒットしません。

blockdiag シリーズはユーザのリクエストを受けて改良を進めてきたという経緯があります。
blockdiag 自体もユーザとしての僕が欲しくて作ったものですし、
他のシリーズもだれかのニーズに合わせて作ってきました。

  • blockdiag
    • 画面遷移図を簡単に作りたくて開発した
    • 公開した後はいろんなリクエストにあわせてバージョンアップを続けた
  • seqdiag
    • @shimizukawa からの要望で作った
      • 曰く「sdedit は Java 製でインストールが面倒。Python 製のが欲しい」
    • その後他の人からの強烈なリクエストを受けてバージョンアップを繰り返す
  • actdiag
    • 友人からの要望で作った
  • nwdiag
    • プロトタイプをネットワーク屋さんに見せたら好評だったのでバージョンアップを繰り返す
  • rackdiag
    • 大学の先輩のリクエストで開発した


昨日挙げた他の図も、いろんな人と話すなかでもらった要望がほとんどです。

blockdiag はだれかのリクエストによってバージョンアップを繰り返しているので、
リクエストがないと開発が停滞してしまう可能性が多いにあるのです。
わがままドリブン は blockdiag へのリクエストをもらうためのわかりやすいキャッチフレーズなのです。

なぜ わがまま なの?

blockdiag では「わがまま」というキーワードを掲げています。
国語辞典には

わが‐まま【我が×儘】
1. [名・形動]自分の思いどおりに振る舞うこと。また、そのさま。気まま。ほしいまま。自分勝手。「―を通す」「―な人」
2. [連語]《代名詞「わ」+助詞「が」+名詞「まま」》自分の思いのまま。
  「―に誇りならひたる乳母の」〈源・常夏〉

とあるように、一般的にはやや悪いイメージのある単語です。
他にもいくつかフレーズがある中で blockdiag でこのキーワードを選んだのには理由があります。


ひとつは心理的な障壁を下げるためです。
普通は「ご要望お待ちしています」と伝えるんじゃないかと思うのですが、
そう書かれていてもなかなか要望って出しづらいと思いませんか?
「もうちょっとこうなったらいいのになぁ」というのを飲み込み「これができないのか」と使うのを諦める…
僕はその様子を見たことがあるし、自分でやったこともあります。

なかなか要望を出すというのは勇気がいるものですが、
「どんなわがままでも聞きますよ」という看板を掲げることで、
その心理的障壁を取り払いたいと考えたのです。


もうひとつは強いキーワードを使いたかったこと。
わがまま って言葉、インパクトがあると思いませんか?
そして、わかりやすい。
僕の考えていたものを覚えてもらえる強いメッセージ性のある、しっくりと来ることばだと思ったのです。

まとめ

まぁ、そんなマーケティングっぽいことはどうでもいい*1ので、敢えてもう一度言います。
図を書いていて、blockdiag を使っていて、違和感を感じたことや
あれが欲しいなと思ったことはとりあえず声を上げませんか。
Twitter (@tk0miya) でもメール(i.tkomiya at gmail.com) でもかまいませんので是非僕に教えてください。

あなたの感じたもの、思いついたものが実現できると他の人も楽ができるかもしれません。
実はすごくかんたんなことで悩んでいるだけかもしれません。
自分のためでもいいし、誰かのためでもいいのでそのアイディアを叫んでみてください。
せっかくのアイディアやフィーリングが消えてしまうのはとても勿体無いですよ。

*1:そもそも僕自身マーケティングを意識したわけではないし、勉強したこともないですし。

(Python3 アドベントカレンダー/18日目) Python3 で blockdiag を動かしてみたかった。

Python 界の芸人、@tk0miya です。
突然ですが今日は Python3 Advent Calendar のエントリを書きます。
昨日 Python mini hack-a-thon で @terapyon に強制されるまで
Python3 なんて全く触ったことがなかったのになぜか参加する羽目になりました。

ぼやいていてもしょうがないので早速進めていきましょう。
せっかく Python3 を触るチャンスができたので、今日は Python3 で拙作 blockdiag を動かしてみようと思います。
僕が blockdiag をつくるために Python を使い始めたので、
僕にとって Python というのは blockdiag 開発用言語と言っても過言ではありません。*1

というわけで、blockdiag を Python3 で動かしてみるために試行錯誤してみることにします。

まずは Python3 の環境をつくろう。

僕は Debian パッケージになっていないソフトウェアをいれるのに拒否反応を示す程度の
かわいそうな子 Debian 使い見習いです。
しかし、現在リリースされている Debian 6.0 (squeeze) では
パッケージングされている Python は 2.5, 2.6, 3.1 の 3つだけです。
Python3 の最新版である 3.2.2 を利用するには squeeze のパッケージは利用できません。

解決策として、unstable 版を使用することにしましょう。
unstable では今日現在 3.2.2 のパッケージが提供されています。

手元の環境は squeeze で動いているので、実験用の chroot 環境を作ります。

$ sudo debootstrap unstable /chroot/python3
$ sudo chroot /chroot/python3 /bin/bash

次に Python3.2 をインストールします。

# apt-get update
# apt-get install python3.2 python3.2-dev python3-setuptools

はい、簡単ですね。
試行錯誤することなくパッケージを入れることができるのはパッケージメンテナの方々のおかげなので、
apt-get yeah! と言って彼らに感謝しましょう。

PIL を動かそう。

次に依存ライブラリのひとつである PIL をインストールします。
PIL の公式パッケージは Python3 に対応していないので、
Unofficial Windows Binaries for Python Extension Packages に置いてある PIL のソースを利用します。

本来は Debian パッケージに加工すべきですが、今回は時間がないので諦めます。*2

まずは PIL のビルドに必要なツールや依存ライブラリをインストールしましょう。

# apt-get install build-essential
# apt-get install unzip
# apt-get install libjpeg8 libjpeg8-dev
# apt-get install libfreetype6 libfreetype6-dev


PIL-1.1.7-py3 のソースは Windows 用に書き換えられているため、Linux ではそのまま利用することはできません。
以下のパッチを当ててビルドできるようにします。

--- setup.py.orig       2011-12-17 16:58:43.000000000 +0000
+++ setup.py    2011-12-17 17:04:18.000000000 +0000
@@ -33,6 +33,13 @@
 #
 # TIFF_ROOT = libinclude("/opt/tiff")

+TCL_ROOT = None
+JPEG_ROOT = None
+ZLIB_ROOT = None
+TIFF_ROOT = None
+FREETYPE_ROOT = "/usr/lib/i386-linux-gnu", "/usr/include/freetype2"
+LCMS_ROOT = None
+
 if sys.platform == "win32":
     if '64 bit' in sys.version:
         TCL_ROOT = ('C:/TCL85-x64/lib', 'C:/TCL85-x64/include')

そしてビルドを実行します。

$ python3.2 setup.py build

ビルドの結果が以下のように表示されていれば成功です。
確認すべき箇所は JPEG support と FREETYPE2 support が available になっていることです。

--------------------------------------------------------------------
PIL 1.1.7 SETUP SUMMARY
--------------------------------------------------------------------
version       1.1.7
platform      linux2 3.2.2+ (default, Dec  2 2011, 11:11:47)
              [GCC 4.6.2]
--------------------------------------------------------------------
*** TKINTER support not available
--- JPEG support available
--- ZLIB (PNG/ZIP) support available
--- FREETYPE2 support available
*** LITTLECMS support not available
--------------------------------------------------------------------

最後にインストールを行います。

# python3.2 setup.py install

PIL の動作確認

PIL がちゃんとインストールされたのか、かんたんなプログラムを書いて確認してみます。
libfreetype と正しくリンクされているか確認するために、OpenType フォントによる描画も確認します。

動作確認のため、IPA フォントをインストールしておきます。
これまでフォント系のパッケージは ttf- で始まるパッケージ名でしたが、
次のリリースからは fonts- で始まる名前になったようです。パッケージを探す際は注意しましょう。

# apt-get install fonts-ipafont

そしてサンプルスクリプトです。rectangle と ellipse, text と基本要素を並べたプログラムです。

#!/usr/bin/python3.2

from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont

image = Image.new('RGB', (800, 600), (256, 256, 256))
alpha = Image.new('L', image.size, 1)
image.putalpha(alpha)
drawer = ImageDraw.ImageDraw(image)

drawer.rectangle((100, 100, 200, 200), outline='black', fill='red')
drawer.ellipse((300, 100, 500, 200), outline='black', fill='red')
drawer.rectangle((150, 150, 400, 400), outline='green')

fontpath = '/usr/share/fonts/opentype/ipafont-gothic/ipagp.ttf'
ttfont = ImageFont.truetype(fontpath, 24)
drawer.text((100, 50), "Hello world", fill='black', font=ttfont)
drawer.text((100, 150), "Good-bye world", fill='black', font=ttfont)

image.save('output.png', 'PNG')

これを実行するとこんな画像が出力されます。

他の依存ライブラリを入れよう

PIL がインストールできたので他の依存ライブラリを入れていきましょう。
blockdiag は webcolors, funcparserlib というふたつのライブラリに依存しています。
それぞれを easy_install3 コマンドでインストールしていきます。

# easy_install3 webcolors
# easy_install3 funcparserlib
Searching for funcparserlib
Reading http://pypi.python.org/simple/funcparserlib/
Reading http://code.google.com/p/funcparserlib/
Best match: funcparserlib 0.3.5
Downloading http://pypi.python.org/packages/source/f/funcparserlib/funcparserlib-0.3.5.tar.gz#md5=52dfec49f2d2c4d816fe8d8c90f7dcf1
Processing funcparserlib-0.3.5.tar.gz
Running funcparserlib-0.3.5/setup.py -q bdist_egg --dist-dir /tmp/easy_install-2vhcgm/funcparserlib-0.3.5/egg-dist-tmp-18znip
warning: no files found matching '*' under directory 'tests'
  File "build/bdist.linux-i686/egg/funcparserlib/parser.py", line 118
    except NoParseError, e:
                       ^
SyntaxError: invalid syntax

  File "build/bdist.linux-i686/egg/funcparserlib/lexer.py", line 80
    def match_specs(specs, str, i, (line, pos)):
                                   ^
SyntaxError: invalid syntax

zip_safe flag not set; analyzing archive contents...
  File "/usr/local/lib/python3.2/dist-packages/funcparserlib-0.3.5-py3.2.egg/funcparserlib/parser.py", line 118
    except NoParseError, e:
                       ^
SyntaxError: invalid syntax

  File "/usr/local/lib/python3.2/dist-packages/funcparserlib-0.3.5-py3.2.egg/funcparserlib/lexer.py", line 80
    def match_specs(specs, str, i, (line, pos)):
                                   ^
SyntaxError: invalid syntax

Adding funcparserlib 0.3.5 to easy-install.pth file

Installed /usr/local/lib/python3.2/dist-packages/funcparserlib-0.3.5-py3.2.egg
Processing dependencies for funcparserlib
Finished processing dependencies for funcparserlib

easy_install でインストールしてみると、何箇所かで SyntaxError が出ていますね。
はい、ご想像のとおり funcparserlib は Python3 に非対応なのです。

というわけで、blockdiag を動かす前に依存ライブラリがうまく動かないという問題にぶつかってしまいました。
ちなみに blockdiag 自身も Python3 用には書かれていないのでうまく動かないというオチが待っています。

まとめ

blockdiag とその依存ライブラリの Python3 対応状況を確認してみました。

  • PIL: 非公式パッケージを使えば Python3 に対応している
  • webcolors: 問題ないようだ
  • funcparserlib: 非対応
  • blockdiag: 非対応

ちょっと残念な結果になりましたが、一番の強敵かと思われた PIL が動くようになっているので
もうしばらく待つと blockdiag を Python3 対応に書き換える日がやってくるかもしれませんね。
ちなみに blockdiag は python2.4 をサポートする方針にしているので、
もし Python3 対応をする場合はかなり辛い道のりがが待っているようです…。

それでは明日のアドベントカレンダーは PyConJP の座長をつとめておられる @terapyon さんにお願いしたいと思います。
なにか楽しい記事が見られるのを期待しております :-)



おまけ

というところで終わるのは非常に悔しいので、ちょちょいとコードを書いて、
blockdiag archetype みたいなものを作ってみました。

#!/usr/bin/python3.2

from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
from PIL import ImageFilter


class Constants(object):
    node_width = 120
    node_height = 80
    span_width = 50
    span_height = 40


class Node(object):
    def __init__(self, name, x, y):
        self.name = name
        self.x = x
        self.y = y


def create_canvas(size):
    image = Image.new('RGB', size, (256, 256, 256))
    alpha = Image.new('L', size, 1)
    image.putalpha(alpha)

    return image


def create_drawer(canvas):
    return ImageDraw.ImageDraw(canvas)


def create_font():
    #fontpath = '/usr/share/fonts/truetype/ipafont/ipagp.ttf'
    fontpath = '/usr/share/fonts/opentype/ipafont-gothic/ipagp.ttf'

    return ImageFont.truetype(fontpath, 24)


def smooth_canvas(canvas):
    for i in range(15):
        canvas = canvas.filter(ImageFilter.SMOOTH_MORE)

    return canvas


def draw_node(drawer, node, font):
    x = node.x * Constants.node_width + (node.x + 1) * Constants.span_width
    y = node.y * Constants.node_height + (node.y + 1) * Constants.span_height

    # draw outline
    box = (x, y, x + Constants.node_width, y + Constants.node_height)
    drawer.rectangle(box, outline='black', fill='white')

    # draw label
    size = font.getsize(node.name)
    textxy = (x + (Constants.node_width - size[0]) / 2,
              y + (Constants.node_height - size[1]) / 2)
    drawer.text(textxy, node.name, fill='black', font=font)


def draw_node_shadow(drawer, node):
    shift = 5
    x = node.x * Constants.node_width + (node.x + 1) * Constants.span_width
    y = node.y * Constants.node_height + (node.y + 1) * Constants.span_height

    # draw shadow
    box = (x + shift, y + shift,
           x + Constants.node_width + shift, y + Constants.node_height + shift)
    drawer.rectangle(box, outline='black', fill='black')


def draw_edge(drawer, edge):
    node1 = [n for n in nodes  if n.name == edge[0]].pop()
    x1 = (node1.x + 1) * (Constants.node_width + Constants.span_width)
    y1 = int((node1.y + 0.5) * Constants.node_height) + (node1.y + 1) * Constants.span_height

    node2 = [n for n in nodes  if n.name == edge[1]].pop()
    x2 = node2.x * Constants.node_width + (node2.x + 1) * Constants.span_width
    y2 = int((node2.y + 0.5) * Constants.node_height) + (node2.y + 1) * Constants.span_height

    # draw edge (straight line :p)
    drawer.line((x1, y1, x2, y2), fill='black')


def main():
    size = (800, 300)
    canvas = create_canvas(size)
    drawer = create_drawer(canvas)
    font = create_font()

    # draw shadow
    for node in nodes:
        draw_node_shadow(drawer, node)

    canvas = smooth_canvas(canvas)
    drawer = create_drawer(canvas)

    for node in nodes:
        draw_node(drawer, node, font)

    for edge in edges:
        draw_edge(drawer, edge)

    canvas.save('output.png', 'PNG')


#######################
# diagram definitions #
#######################
nodes = [Node('A', 0, 0),
         Node('B', 1, 0),
         Node('C', 2, 0),
         Node('D', 2, 1)]

edges = [('A', 'B'), ('B', 'C'), ('B', 'D')]


main()

このスクリプトは "diagram definitions" で定義しているノードやエッジの情報に基づいて図を生成します。
レイアウト部分は複雑になるので、ノード名と座標(x, y)を指定するようにしています。
この例は blockdiag の

{
  A -> B -> C, D;
}

と同じ構成のノードを書いています。

上記のコードを実行すると以下のような図が得られます。

まるで blockdiag のような出力ですね!Python3 で blockdiag が動いているようにみえます!
…などと Python3 で動かすことができなかった切なさを紛らわせるのでした。
おしまい。

*1:実際仕事は PHP ばかりですし、Perl 使いに憧れて生きています

*2:ソースから入れてもいいように実験環境作ったわけですしね

(16日目) 平行世界の blockdiag シリーズ(2)

今日のアドベントカレンダーはおとといに引き続き、未だ見ぬ blockdiag の紹介をしたいと思います。

フローチャート

最初の図はフローチャートです。プログラムの流れを可視化するための図ですね。

フローチャート自体は blockdiag でも書くことができますが、
アイディアとしてはフローチャート用のツールをつくろうと考えています。
フローチャート専用の blockdiag をつくろうと思っているのは主に文法面です。
blockdiag を使って簡単なフローチャートを書いてみると、実は結構記述数が多くなります。

blockdiag {
  edge_layout = flowchart;

  B [shape = flowchart.condition];
  A -> B;
       B -> C [label = Yes];
       B -> D [label = No];
}

記述量が多くなってしまうのは、分岐のところでノードの形をひし形に変えたり、
分岐条件のラベルを定義するのに記述しなくてはならないことが多いためです。

まだ時間をかけて考えたわけではありませんが、
次のような文法にすると書きやすく読みやすくなるのではないかと考えています。

flowchartdiag {
  A -> <B>: Yes -> C,
            No  -> D;
}

書きやすさについては何度か実際に書いてみるのがいいと思っているので、
どこかまとまった時間が取れるようになったら手をかけてみる予定です。

でも、文法を増やすことで混乱してしまうかも…という考えも頭をよぎるので、
形になるのはしばらく後になるかもしれません:p

その図は必要なものなのか: あればやや嬉しい
図である必要はあるか: はい
すでにツールは存在しないか: あると思う
文法: 練り込みが必要
レイアウト方法: blockdiag のもので OK

物理ネットワーク図

次の図は物理ネットワーク図です。
nwdiag で扱っている論理ネットワーク図は IP をベースにした図ですが、
物理ネットワーク図では物理的な結線を表現します。

nwdiag 系のツールとして次に取り組もうと思っているものです。
物理と論理、両方のツールを組み合わせることで、
ネットワークのドキュメントがカバーできるんじゃないかと考えています。

とはいえ、物理ネットワーク図は統一化されたフォーマットが存在しないため、
どういった図にすると利用者が満足できるのかがまだつかめていません。
また、担当しているネットワークの規模やレイヤーによって記述する対象が変わってくるため、
物理ネットワーク図に正解はないのではないかと感じています。

nwdiag をきっかけにネットワーク技術者とお話する機会がよくあるので、
物理ネットワーク図については気長にアイディアをまとめていく予定です。


その図は必要なものなのか: はい
図である必要はあるか: はい
すでにツールは存在しないか: 大量にある。有償ツールもある。自動レイアウトできるツールは要調査。
文法: 未定
レイアウト方法: 未定 (まずは書くべき図を理解する)

回路図

次の図は電子回路などの配線を示した回路図です。
僕自身はほとんど関わらない分野なのですが、
ブロック図に比較的近い図としてリクエストをもらったことがああります。

シンプルな図なので描画自体は大変ではないと思うのですが、
どういうルールで並べるのかというのがさっぱり理解できないので、
作る上ではそこが問題になりそうです。

おそらくこのツールの延長線上にハードウェア記述言語(HDL)があるんじゃないかと思うのと、
LSI のレイアウト部分は集積度を上げるためにいろんな会社が苦労している部分だったはずなので、
いろいろ考えると終わりのない世界に向かっていきそうな気がしています。

その図は必要なものなのか: おそらく…
図である必要はあるか: はい
すでにツールは存在しないか: あるはず。
文法: 考えてません
レイアウト方法: ルールを把握しないといけない

プロパティグラフ

プロパティグラフは GraphDB の根底にある概念で、ノード間の関係にプロパティが付与されたものです。
MongoDB などの定義の可視化用にツールをリクエストされました。

詳しく調べてはいないのですが、レイアウト面で非常に手こずる予感がしています。
簡単な所ではばねモデルを使って均等配置すればよいのではないかと思っていますが、
数式を見てやる気が落ちているところです。

その図は必要なものなのか: おそらく必要
図である必要はあるか: はい
すでにツールは存在しないか: 似たようなツールはいくつかあるみたい
文法: まだ考えてない
レイアウト方法: まずはばねモデルを理解するところから…

時計

なんという図なのかは分からないのですが、
一日の時間の使い方などを模した図が簡単に作れるとおもしろいかなーと考えたことがあります。

間取り図、座席表

部屋の間取り図や座席表が作れると面白いんじゃないか、と言われたことがあります。
…がそもそもテキストで表現できるものではないので、
聞き流しているネタツールのひとつです。

その図は必要なものなのか: 知らんがな
図である必要はあるか: 知らんがな
すでにツールは存在しないか: 知らんがな
文法: 知らんがな
レイアウト方法: 知らんがな

まとめ

今日は今後作るかもしれない blockdiag シリーズを紹介しました。

手元にスタックしてあったものをとりあえず書いてしまおうと思って書き始めたのですが、
前回の分と合わせると随分数がありますね。
とりあえず今僕の手元にあるアイディアはこれでおしまいです。

今はまだ blockdiag や seqdiag など、いまあるツールのメンテナンスで手一杯ですが、
そろそろ視点を変えるためにこれらのツールにも手を出せると面白いな、と思っています。

もしあなたも面白いアイディアがあれば是非教えて下さい。

(15日目) ぼくが Sphinx ハンズオンをはじめたわけ

今日は昨日行われたばかりの Sphinx ハンズオンについて触れたいと思います。

Sphinx ハンズオンでは 16人の参加者を 3つのチームに分かれて
それぞれ課題に取り組むということをしました。

  • Sphinx をインストールしてない: 5人
  • ちょっとだけ Sphinx を使った: 7人
  • 熟練組: 4人

課題として用意したのは、会議の議事録っぽい書式の印刷物を一枚(両面印刷してあって、内容は 1.5面)。
これを題材として Sphinx をインストールしてみたり、使ってみたり、ということをやりました。
他に説明に使った資料は Sphinx-users.jp の Sphinx をはじめよう というページです。


やっていたことというとコレくらいで、この材料を渡して各々がひたすら Sphinx と格闘するという 2時間半でした。
当日はてんやわんやでずっとばたばたし続けていたので、終わった途端かなりぐったりしてました。
講師をしていた @shimizukawa、@takanory、@shkumagai も本当にお疲れ様でした。

参加されてた方のほとんどが Sphinx でドキュメントを書く楽しさに出会えたんじゃないでしょうか。
僕からは楽しんでいただけたように見えたので勝手にほっとしています。*1

Sphinx ハンズオンのゴール

最初に考えていた Sphinx ハンズオンのゴールはひとつだけで、Sphinx を使ってみてもらうことです。
そのため、イベントのターゲットとして考えていたのは次のような人たちです。

  • Sphinx に興味がある
  • 興味はあるがなかなか使う機会がない
  • 使い方がよくわからない
  • Sphinx を知らない

なお、会場の広さの都合もあるため次のような人たちはターゲットには含んでいませんでした。

  • すでに Sphinx に慣れ親しんでいる
  • Sphinx を改造したい
  • 自分で解決する力を持っている

別の言葉でいうと、Sphinx に興味のある "予備軍" から
Sphinx が好きな "Sphinx ユーザ" になってもらおうという狙いでした。

今回参加された 16人についてはそれが達成できたんじゃないかと思っています。

イベントを開いたきっかけ

話は変わりますが、Sphinx ハンズオンを始めてみようという考えは、
少し前にアート・オブ・コミュニティを読んでいるときに思いつきました。*2
この本の "Buzz の波を起こす" という章で、「メッセージ」を送ることについて紹介しています。
僕はこの本を読むときには blockdiag や Sphinx-users.jp を思い浮かべながら読むのですが、
この章を読んでいてピンと来たのが Sphinx ハンズオンでした。

このアイディアには、今までいくつものイベントに Sphinx を紹介してきた経験も関係します。
紹介したときのリアクションから Sphinx に興味を持っている人が一定数いるのを認識していましたが、
実際に使ってみる・触ってみるというほどの熱狂は得られなかったと思っていたので、
今回は発表ではなくイベントと言うメッセージを送ることで、彼らの背中を押そうと思ったのです。

あと、とっても個人的なことですが Sphinx ユーザが増えると
blockdiag ユーザも増えるんじゃないか、と考えたところもあります。
まさに僕にとっては一粒で二度おいしいイベントだったのです。*3

今後の予定

いろんな人に参加してもらうために、あと数回おなじ内容で Sphinx ハンズオンをやろうと思っています。
今までの反応から Sphinx に興味を持っている、気になっている人はまだまだ沢山いるようなので、
その人たちのきっかけになることができればいいな、と思っています。

もちろんそのためには僕ひとりでは実現できません。
協力してくれる人の存在が欠かせないと思っているので、
是非この Sphinx を広めるという企みに協力してもらえると嬉しいです。

Sphinx ハンズオンのアイディアは、遠く神戸の地でも Sphinx 朝会という形でスタートしています。(@lab1092 さんありがとう!)
僕は Sphinx ハンズオンの所有権を主張するつもりはまったくないので、
みんな思い思いのかたちで Sphinx を広げていけると楽しいんじゃないかな、と思っています。

あと、なかなか言う機会がないのでついでに書きますが、
Sphinx をもっと使いこなしてたい人のためのイベントもやるつもりです。
今年は関係者が忙しくてほとんど開催できていなかった Sphinx 翻訳ハッカソンです。

謝辞と煽り

Sphinx は非常に良くできたツールです。
日本では @shibukawa さんがマニュアルの翻訳やユーザ会の立ち上げなど、
精力的に活動してきたからこそ今のユーザ会があると思っています。
だとすれば、来年はその影響を受けた僕らが広げていけるといいんじゃないでしょうか。

今からならお祭りに参加できるかもしれませんよ。Sphinx のバスに乗り遅れるな、ってね :-)

*1:昨日、最後に楽しかったか聞けばよかったですね。失敗しました。

*2:面白いんじゃないかと思ってツイートしたら、思わぬ反響があったので "言いだしっぺ" になってしまったというのもあります。

*3:もちろんみんなに Sphinx を勧めたいという気持ちはウソじゃないですよ!

(14日目) 並行世界の blockdiag シリーズ(1)

今日はちょっと毛色の違う話をしてみようと思います。
blockdiag はこれまで 5つの図に対応しています。

  • ブロック図
  • シーケンス図
  • アクティビティ図
  • 論理ネットワーク図
  • ラック構成図

blockdiag シリーズはひとつのツールでひとつの図をサポートしているので、
これまでいくつもの要望やアイディア、ネタをもらってきました。
完全にジョークのものを除いて、どれも面白そうだな、と思っているのですが
なかなか時間が取れずに手を付けられずにいます。

今日はこの "もしかしたら作られているかもしれない blockdiag シリーズ" を
紹介してみたいと思います。

新しいツールを作るときに最初に考えること

blockdiag シリーズのツールの要望をもらったときに、
なるべく考えるようにしていることがいくつかあります。
未知の blockdiag を紹介する前に、この「最初に考える事」を見ていきましょう。

それぞれがツールの将来を決める大事な要素なので、
この部分にはできるだけ時間を割くようにしています。

その図は必要なものなのか

まず、求められた図が本当に必要とされているものなのか、考えたり調べたりします。
誰も使わない/読まないような図であれば、ツールを作るのではなくその図をなくすよう働きかけるべきだと考えています。

また、利用する人が少ないような図の場合は優先度が下がります。
せっかく作るのですからなるべく多くの人に使って欲しいですものね。

blockdiag シリーズである必要はあるか

次にその図は blockdiag シリーズで作る必要があるのかを考えます。
blockdiag の特徴は

  • テキストベースであること
  • 自動レイアウトしてくれること
  • 書き換えがかんたんであること

なので、この特徴が活かせるものが望ましいです。

例えば提案書に載せる一枚絵なんてのは、向いてないもののひとつだと思います。

  • 営業さんたちはわざわざテキストは望まれない
  • 一枚絵なのでレイアウトが固定でない。むしろ自由に書きたい
  • 書き換えはあまり必要とされない

こういう場合は Cacoo や VisioPowerPoint や Excelなどのツールで書けばいいと思うのです。

図である必要はあるか

もらったアイディアは図である必要がないものもあります。
たとえばガントチャート(線表)を出すツールをリクエストされたことがあるのですが、
画像で出力するととても長い/複雑なスケジュールだと読めなくなってしまう問題があります。

図にすると情報が可視化されてわかりやすくなるというメリットがありますが、
情報の整理方法には表やリスト、データベース化など他の方法もあるので本当に図で出すべきかを検討します。*1

すでにツールは存在しないか

時間は有限なので、すでに同種のツールがある場合はあまり手をかけないようにしています。
UML 系の図へのリクエストを何度かもらったことがありますが、
PlantUML や astah* などのツールがあるので、これらも優先度を落としています。

他のツールも気になることや弄りたいところはたくさんあるのですが、
対応する図のレパートリーを増やす方向ですすめるように考えています。

文法

やや設計に踏み込んでいくのですが、どういう定義をするのかという文法を考えます。
ユーザーが触るのはこの部分だけなので、書きやすさ、読みやすさ、直感的かどうかなどを考えます。

いくつかサンプル定義を書いたりして、書き心地のよい文法*2にたどり着ければ、
ツールを書こうかという気分になります。
とりあえず作っちゃおうか、は厳禁です。

レイアウト方法

最後にどういう風にレイアウトを行うのかを考えます。
blockdiag の売りのひとつが自動レイアウトなのですから、
この部分を考えておかないとあとで行き詰まることになります。

要素が増えた時にどうするか、どう並べると自然に見えるのかというのを
脳内でシミュレートしてみます。
その場で思いつく一番マニアックパターンが何とかなりそうであれば先に進みます。

作り始める

ここまで考えてから実際に作り始めます。
ノリで作り始めた例もないわけではないのですが、
これらを意識しないと一発ネタになってしまうのでなるべく事前にイメージをふくらませてから
一気に作るようにしています。

後半のアドベントカレンダーで触れようと考えていますが、
blockdiag シリーズは大まかな骨格が出来上がっているので、
実際にツールをつくり上げるのは大変なことではありません。

苦労するのはユーザーが求めている図とのズレを埋め続けていくことと、
使いやすさの面でいかにチューニングをし続けていくかのふたつなので、最初に間違えると残念なコトになります。


では、前置きが長くなりましたが未だ見ぬ blockdiag シリーズを紹介します。

特性要因図

最初の図は特性要因図です。かなり昔に情報処理試験で QC 7つ道具と紹介されたらしいです。*3
フィッシュボーン図、イシカワ・ダイアグラムとも呼ばれるそうで、
fish-bone の別名の通り魚の骨のような図です。

中央にメインの課題を用意し、そこから特性や要因やを枝分かれで書いていくという図です。
いまこの記事を書きながら気づいたのですが、これってマインドマップと似ていますね。
用途が限定されているので少し違うのかもしれませんけど、近いものを感じます。

  • その図は必要なものなのか: よくわからない & 自分で使わないのでやや優先度低めにしてます。
  • 図である必要はあるか: はい
  • すでにツールは存在しないか: ざっと調べたけど見当たらなかった
  • 文法: まだ考えてない
  • レイアウト方法: 枝を再帰的に作ってくだけみたい

クラス図

次の図はクラス図です。UML で定義された図のひとつで、ソフトウェアの設計書で用いられます。

これはすでに PlantUML で対応している図なのですが、PlantUML はレイアウトを自分で記述する必要があり、
書く手間が非常に大きいので作る候補に入っています。

  • その図は必要なものなのか: 必要。PlantUML があるのでやや優先度は低い
  • 図である必要はあるか: はい
  • すでにツールは存在しないか: ある。
  • 文法: まだ考えてない
  • レイアウト方法: シンプルな継承、集約はできそう。複雑なものを考える必要がある。

ER図

今度は ER図 です。データベースの各テーブル(エンティティ)間の関係を記述する図です。
僕は仕事で Web アプリを書いているのでよく目にする図のひとつです。

graphviz を使って可視化するツールや、MS Visio による定義の自動抽出など、
いくつかの方法があるものの、どれも今ひとつといったところなので
そのうちつくろうかな、と思っているもののひとつです。

大きいシステムになるとテーブルが増えてくるので、
その時にどうやって綺麗に見せるのかというのがポイントになるんだろうな、とぼんやり考えています。

  • その図は必要なものなのか: 必要
  • 図である必要はあるか: はい
  • すでにツールは存在しないか: たくさんある
  • 文法: まだ考えてない
  • レイアウト方法: 要素が増えた場合の対処が思いつかない

家系図

次の図は家系図です。ジョークとしてよくリクエストされます。
サザエさんの家系図とかが出力できると一発ネタとしてはよさそうです。
でも、徳川家の家系図までくると結構大変そうです。

  • その図は必要なものなのか: いらない
  • 図である必要はあるか: はい
  • すでにツールは存在しないか: テキストベースでなければあった
  • 文法: papa, mama -> child1, child2, ... まで考えた
  • レイアウト方法: 一見簡単そうなんだけど重婚などのケースが大変そう
    • 簡単なのはクラス図と似たようなレイアウトになる気がしています。

人物相関図

これもジョーク系の図である人物相関図です。ドラマやアニメの紹介でみますね。
著作権などが怪しいので、サンプルとして一番最初に見つけたページにリンクしておきます。

シンプルに人物間の相関が書かれているものや
人物がどのグループに所属しているかを書いたものなどがあるようです。

  • その図は必要なものなのか: いらない
  • 図である必要はあるか: はい
  • すでにツールは存在しないか: 探してすらいない
  • 文法: 考えるつもりもない
  • レイアウト方法: すごい大変そう…

ルービックキューブ

先週の Sphinx 朝会 というイベントで出たらしいリクエストです。
ルービックキューブを可視化するというものらしいです。

  • その図は必要なものなのか: いらない
  • 図である必要はあるか: 図では 3面しか表現できないので足りない。しかも図だと遊べないよ!
  • すでにツールは存在しないか: 探したらありそう…
  • 文法: 考えてません。
  • レイアウト方法: 単に描画するだけなので楽。

トーナメント図

これもきっとジョークだと思っているのですが、トーナメント表を作るツールです。
いいじゃん、アスキーアートで!と思わなくもないのですが、
一発ネタとしてはちょっと欲しくなるのはとてもわかるのでリストに載せました。

これ、レイアウトは全然大したことがなさそうなんですが、
どういう風な文法にすればいいのか、しっくりくる文法が思いつきません。
対戦、勝敗、シード、2回戦以降の対戦などをすっきり表現できれば作れそうな気がしています。
要は何も考えてないってことですけどね!

  • その図は必要なものなのか: いらない
  • 図である必要はあるか: はい
  • すでにツールは存在しないか: ありそう…
  • 文法: 思いつかない
  • レイアウト方法: 工夫はいらない

まとめ

今日はちょっと息抜きに今後作るかもしれない blockdiag シリーズを紹介しました。

どちらかと言うと前半の「新しいツールを作るときに最初に考えること」がメインで
後半部分はジョークまみれだったりしたわけですが、
僕が blockdiag 系のツールを作るときに何を考えているのかというのが伝わると嬉しいです。

手元にはまだまだアイディアが眠っているので、この記事だと作るのが楽なのでまた折を見て紹介しようと思います。

*1:図でないのであれば、blockdiag ではない他のツールとしてやることに>なります

*2:個人的には「肌触りのいい」文法と呼んでいます。日常的に書くものですから。

*3:試験を合格したのに 1つも名前を上げることができません:p