なんとな~くしあわせ?の日記

「そしてそれゆえ、知識そのものが力である」 (Nam et ipsa scientia potestas est.) 〜 フランシス・ベーコン

wxWidgetsにおけるプラットフォームごとのアイコン読み込み方法

Windows,Linux,Macその他での共通コードからのビルドが可能なwxWidgetsであるが
アイコンの読み込み方法はかなりプラットフォーム依存である。やっとMacでの正しいやり方が分かったので、まとめて書き留めておく。


簡単な方から順に説明する

1. Linuxの場合:Linuxの場合XPMファイルをアイコンとする

// xpmファイルを読み込む
#include"sample.xpm"

wxFrame(xxx, yyy) {
   // wxFrameを継承したメソッドの中で
   // xpmファイルの拡張子を抜いた名前をSetIconメソッドに投げる
   SetIcon(wxICON(sample));
...

Linuxの場合はこれでおしまい。とても簡単である。



2. Windowsの場合:Windowsの場合ICOファイルをアイコンとする

手順
(a) ICOファイルをC++のソース上で利用するためのリソースファイルを作成する
(b) リソースファイルをwindresコマンドを使ってオブジェクトファイルに変換する
(c) オブジェクトファイルを実行ファイルにリンクする

リソースファイルの作成例
sample.rc

# "C++ソース上での名前" "ICON(固定)" "ICOファイル名"
sample ICON "sample.ico"
#include "wx/msw/wx.rc"

Makefileの記述例

# 下のようにicon.oを作る命令を書いておいて、最後にリンクしてやると良い
sample.o : sample.rc
		$(WINDRES) -i $^ -O coff -o $@  $(RCFLAGS)

$(RCFLAGS)は「-I/(環境依存)/include/wx-2.*」である。つまりは /include/wx-*.* がインクルード出来ていればいい
Makefile中で環境依存なく出力したい場合は「$(shell wx-config --cxxflags | awk '{ print $$2 }')」とか入れるといい感じ。
後はリンクするだけなので省略。

追記:単なるMakefileではなくAutotoolsを使う場合、automakeの変数として@WX_RESCOMP@が使用出来る。以下のようなMakefile.amを使えば楽に書ける。

resource.o : $(top_builddir)/icon.rc
	@WX_RESCOMP@ -i $^ -O coff -o $@

3. Macの場合:Macの場合ICNSファイルをアイコンとする

Macはまるで異教徒を排斥するかのごとく邪悪なMakefileを作ることを要求する
まず実行ファイルを作るにはapplication bundle形式のディレクトリ構造をMakefileで作成し
その後にアイコンをリソース用のディレクトリ以下にコピーする必要がある

Makefileの記述例

# ビルド対象とソース
TARGET  = Sample
SOURCES = sample1.cpp sample2.cpp
OBJECTS = $(SOURCES:.cpp=.o)

# Mac用の設定
OUTPUTPATH	= .
PROGRAM		= $(TARGET)
PROGVER		= 1.0
ICONPATH	= rc/sample.icns
ICON		= sample.icns

# Info.plist用の設定
COMPANY		= Personal
BUNDLEPACKAGE	= APPL
BUNDLESIGNATURE	= ????

# application bundle用の設定
BUNDLE		=$(OUTPUTPATH)/$(PROGRAM).app
MACPKGINFO	=$(BUNDLE)/Contents/PkgInfo
MACINFOPLIST	=$(BUNDLE)/Contents/Info.plist 

# 基本のコマンド
CXX	:= g++ -m64
RM 	:= rm
MAKE	:= make

# コンパイルオプション
CXXFLAGS = -Wall `wx-config --cxxflags`
LDFLAGS  = `wx-config --libs`
VPATH    = include src rc

# ダミーターゲットの定義
.PHONY	: clean $(BUNDLE) $(MACICON) $(MACPKGINFO) $(MACINFOPLIST)

# make all
all : $(BUNDLE) $(MACICON) $(MACPKGINFO) $(MACINFOPLIST) $(TARGET)

$(TARGET): $(OBJECTS)
		$(CXX) $^ -o $@ $(LDFLAGS)
		SetFile -t APPL $(PROGRAM)
		cp -f $(TARGET) $(BUNDLE)/Contents/MacOS/$(PROGRAM)

# バンドルのディレクトリ構造を作る
$(BUNDLE):
	mkdir -p $(OUTPUTPATH)
	mkdir -p $(BUNDLE)/Contents
	mkdir -p $(BUNDLE)/Contents/MacOS
	mkdir -p $(BUNDLE)/Contents/Resources

# アイコンファイルをリソース用のディレクトリにコピーする
	$(MACICON):
	cp -f $(ICONPATH) $(BUNDLE)/Contents/Resources/$(ICONFILE)

#  PkgInfoファイルを作成する
$(MACPKGINFO):
	touch $(MACPKGINFO)
	@echo "$(BUNDLEPACKAGE)$(BUNDLESIGNATURE)" > $(MACPKGINFO)

#  Info.plistファイルを作成する
$(MACINFOPLIST):
	touch $(MACINFOPLIST)
	@echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" >> $(MACINFOPLIST)
	@echo "<!DOCTYPE plist PUBLIC " >> $(MACINFOPLIST)
	@echo "\"-//Apple Computer//DTD PLIST 1.0//EN\" " >> $(MACINFOPLIST)
	@echo "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" >> $(MACINFOPLIST)
	@echo "<plist version=\"1.0\">" >> $(MACINFOPLIST)
	@echo "<dict>" >> $(MACINFOPLIST)
	@echo "   <key>CFBundleDevelopmentRegion</key>" >> $(MACINFOPLIST)
	@echo "   <string>English</string>" >> $(MACINFOPLIST)
	@echo "   <key>CFBundleExecutable</key>" >> $(MACINFOPLIST)
	@echo "   <string>$(PROGRAM)</string>" >> $(MACINFOPLIST)
	@echo "   <key>CFBundleIconFile</key>" >> $(MACINFOPLIST)
	@echo "   <string>$(ICON)</string>" >> $(MACINFOPLIST)
	@echo "   <key>CFBundleName</key>" >> $(MACINFOPLIST)
	@echo "   <string>$(PROGRAM)</string>" >> $(MACINFOPLIST)
	@echo "   <key>CFBundleIdentifier</key>" >> $(MACINFOPLIST)
	@echo "   <string>com.$(COMPANY).$(PROGRAM)</string>" >> $(MACINFOPLIST)
	@echo "   <key>CFBundleInfoDictionaryVersion</key>" >> $(MACINFOPLIST)
	@echo "   <string>6.0</string>" >> $(MACINFOPLIST)
	@echo "   <key>CFBundlePackageType</key>" >> $(MACINFOPLIST)
	@echo "   <string>$(BUNDLEPACKAGE)</string>" >> $(MACINFOPLIST)
	@echo "   <key>CFBundleSignature</key>" >> $(MACINFOPLIST)
	@echo "   <string>$(BUNDLESIGNATURE)</string>" >> $(MACINFOPLIST)
	@echo "   <key>CFBundleVersion</key>" >> $(MACINFOPLIST)
	@echo "   <string>$(PROGVER)</string>" >> $(MACINFOPLIST)
	@echo "   <key>CFBundleShortVersionString</key>" >> $(MACINFOPLIST)
	@echo "   <string>$(PROGVER)</string>" >> $(MACINFOPLIST)
	@echo "   <key>CFBundleGetInfoString</key>" >> $(MACINFOPLIST)
	@echo "   <string>$(PROGRAM), Version $(PROGVER), $(COMPANY)</string>" >> $(MACINFOPLIST)
	@echo "</dict>" >> $(MACINFOPLIST)
	@echo "</plist>" >> $(MACINFOPLIST)

# サフィックスルール
.SUFFIXES: .cpp .o
.cpp.o:
		$(CXX) $(CXXFLAGS) -c $<

以上のMakefileを実行すると

$ make all で 以下の部分が実行される
all : $(BUNDLE) $(MACICON) $(MACPKGINFO) $(MACINFOPLIST) $(TARGET)


(1) $(BUNDLE)の項を実行

./Sample.app/
./Sample.app/Contents
./Sample.app/Contents/MacOS
./Sample.app/Contents/Resources を作成

(2) $(MACICON)の項を実行

./Sample.app/Contents/Resources 以下にアイコンファイルをコピー

(3) $(MACPKGINFO)の項を実行

PkgInfoファイルを作成

(4) $(MACINFOPLIST)の項を実行

Info.plistファイルを作成

(5) $(TARGET)の項を実行

ソースのビルド、実行ファイルの作成、コピー

※ここでSetFileコマンドを使っているが、これを行わなければ正常にアイコンが表示できなかった

これでようやくMac用のアプリケーションがビルドできる