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

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

wxWidgetsを使ったGUI版HTTPクライアント

ようやくGUI版HTTPクライアントが完成した。本当はこの前使ったReadHtmlを組み込んで使うつもりだったんだけど、wxWidgetsにWinInetを組み入れるとエラーが出てどうにもならんかった(※まあマルチプラットフォームとは言えどもUNIX中心に開発された言語だからかも)。で、実際そんなことをする必要はなくて、もともとwxURLとかいうクラスがあるんで割と簡単にHTTPを使った通信ができるようだ。そこはかとなくJavaに近いような…。それにしてもクソみたいに読みにくいコード&エラー処理がない…なのでまたコメントなどを書き足したい。

/*
 * Main.cpp
 *
 *  Created on: 2011/06/26
 *      Author: Nantonaku-Shiawase
 */

#include "Main.h"
#include "WxBrowserFrame.h"

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
	WxBrowserFrame *wxbrowserframe = new WxBrowserFrame(wxT("WxBrowserFrame"));
	wxbrowserframe->Show(true);

 return true;
}
/*
 * Main.h
 *
 *  Created on: 2011/06/26
 *      Author: Nantonaku-Shiawase
 */

#ifndef MAIN_H_
#define MAIN_H_

#include <wx/wx.h>

class MyApp : public wxApp
{
  public:
    virtual bool OnInit();
};


#endif /* MAIN_H_ */
/*
 * WxBrowserFrame.cpp
 *
 *  Created on: 2011/06/26
 *      Author: Nantonaku-Shiawase
 */

#include "iostream"
#include "WxBrowserFrame.h"

using namespace std;

WxBrowserFrame::WxBrowserFrame(const wxString& title) :
	wxFrame(NULL, -1, title, wxPoint(-1, -1), wxSize(640, 480)) {

	wxPanel *panel = new wxPanel(this, -1);

	wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);

	//hbox1 説明ラベルとURL入力欄
	wxBoxSizer *hbox1 = new wxBoxSizer(wxHORIZONTAL);
	wxStaticText *st1 =
			new wxStaticText(panel, wxID_ANY, wxT("Input URL Here"));

	hbox1->Add(st1, 0, wxRIGHT, 8);

	//外部からアクセスできるようにprotectedで宣言
	m_tc = new wxTextCtrl(panel, wxID_ANY);
	hbox1->Add(m_tc, 1);
	vbox->Add(hbox1, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10);

	vbox->Add(-1, 10);

	//hbox2 ラベル部分
	wxBoxSizer *hbox2 = new wxBoxSizer(wxHORIZONTAL);
	wxStaticText *st2 = new wxStaticText(panel, wxID_ANY, wxT("HTML Sources"));

	hbox2->Add(st2, 0);
	vbox->Add(hbox2, 0, wxLEFT | wxTOP, 10);

	vbox->Add(-1, 10);

	//hbox3 HTML文表示部分
	wxBoxSizer *hbox3 = new wxBoxSizer(wxHORIZONTAL);
	//外部からアクセスできるようにprotectedで宣言
	m_tc2 = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxPoint(-1, -1),
			wxSize(-1, -1), wxTE_MULTILINE);

	hbox3->Add(m_tc2, 1, wxEXPAND);
	vbox->Add(hbox3, 1, wxLEFT | wxRIGHT | wxEXPAND, 10);

	vbox->Add(-1, 25);

	//hbox 5 ボタン配置部分
	wxBoxSizer *hbox5 = new wxBoxSizer(wxHORIZONTAL);
	//ボタン1はOK HTTPクライアントを起動させる
	wxButton *btn1 = new wxButton(panel, wxID_GETHTTP, wxT("Ok"));
	hbox5->Add(btn1, 0);
	//ボタン2はクローズ アプリケーションを終了させる
	wxButton *btn2 = new wxButton(panel, wxID_EXIT, wxT("Close"));
	hbox5->Add(btn2, 0, wxLEFT | wxBOTTOM, 5);
	vbox->Add(hbox5, 0, wxALIGN_RIGHT | wxRIGHT, 10);

	panel->SetSizer(vbox);

	Centre();
}

void WxBrowserFrame::OnButtonQuit(wxCommandEvent& WXUNUSED(event)) {
	Close(true);
}

void WxBrowserFrame::OnButtonGetHttp(wxCommandEvent& WXUNUSED(event)) {
	wxString inputURL = m_tc->GetValue();
	wxURL url(inputURL);
	wxInputStream* readHtml = url.GetInputStream();

	wxTextInputStream in(*readHtml);
	wxString HTTPtext;
	while (readHtml->CanRead()) {
		HTTPtext += in.ReadLine();
	}
	delete readHtml;

	m_tc2->SetValue(HTTPtext);
}

BEGIN_EVENT_TABLE(WxBrowserFrame, wxFrame)
//ボタン1はOK HTTPクライアントを起動させる
EVT_BUTTON(wxID_EXIT, WxBrowserFrame::OnButtonQuit)
//ボタン2はクローズ アプリケーションを終了させる
EVT_BUTTON(wxID_GETHTTP, WxBrowserFrame::OnButtonGetHttp)
END_EVENT_TABLE()
/*
 * WxBrowserFrame.h
 *
 *  Created on: 2011/06/26
 *      Author: Nantonaku-Shiawase
 */

#ifndef WXBROWSERFRAME_H_
#define WXBROWSERFRAME_H_

#include <wx/wx.h>
#include <wx/url.h>
#include <wx/stream.h>
#include <wx/txtstrm.h>

// control ids
enum
{
	wxID_GETHTTP
};

class WxBrowserFrame : public wxFrame
{
public:
	WxBrowserFrame(const wxString& title);

	void OnButtonQuit(wxCommandEvent& event);

	void OnButtonGetHttp(wxCommandEvent& event);

protected:
	//URL入力用文字列
	wxTextCtrl *m_tc;

	//HTTP文出力用文字列
	wxTextCtrl *m_tc2;

private:
    DECLARE_EVENT_TABLE()
};


#endif /* WXBROWSERFRAME_H_ */
# Makefile
CC = gcc
CXX = $(shell wx-config --cxx)
TARGET = WxBrowser
OBJECTS = Main.o WxBrowserFrame.o
#
all : $(TARGET)
$(TARGET) : $(OBJECTS)
		$(CXX) $(OBJECTS) -o $(TARGET) `wx-config --cxxflags --libs`
WxBrowserFrame.o : WxBrowserFrame.h
		$(CXX) -c WxBrowserFrame.cpp `wx-config --cxxflags --libs` 
Main.o : Main.h WxBrowserFrame.h
		$(CXX) -c Main.cpp `wx-config --cxxflags --libs`
.PHONY: clean
clean:
	rm -f *.o

wxWidgets続き

First programs in wxWidgets
ここを見ながら初めてのwxWidgetsをやってみる。開発環境をDEVC++にしようとしたんだが、プロジェクトを作って開くたびにエラーが出て嫌な感じなのでeclipse CDTでMakefileを書いて作る(IDEとはなんだったのか…)。

自分が書いたやつはこんな感じ。またまたHttpClientを作りたいので、最終的な実行ファイルの名前がwxHttpClient.exe。(アイコンを読み込む段階で躓いてるので完成はしてないが…)

# Makefile
CC = gcc
CXX = $(shell wx-config --cxx)
TARGET = wxHttpClient
OBJECTS = main.o frame.o

# 
all : $(TARGET)

$(TARGET) : $(OBJECTS) 
		$(CXX) $(OBJECTS) -o $(TARGET) `wx-config --cxxflags --libs`

frame.o : frame.h
		$(CXX) -c frame.cpp `wx-config --cxxflags --libs`

main.o : main.cpp main.h frame.h Tutorial.xpm
		$(CXX) -c main.cpp `wx-config --cxxflags --libs`

wxWidgetsでの日本語の表示

追記:2012/01/23 現在wxWidgetsはソースをUTF-8で書けば日本語をそのままwxStringに入れることができます。よって以下の記述はアプリケーションの国際化のためだと思ってください。国際化ってのはひとつのアプリケーションで英語、ドイツ語、フランス語バージョンを作れるってことね。


===


さて、wxWidgets自体は導入できたが、アプリケーションに日本語を表示させるにはどうしたらよいのか。以下のサイトを見ると、その工程がめんどくさいことだけはわかる。
wxWidgetsでの国際化(翻訳) | あるデベロッパーの空間
プログラミング/C_C++/wxWidgets/多国語対応にしてみる - Flightless wing

とにかくそのままでは日本語を表示できないので、
たとえば一番簡単なHelloWorldのコードなら「_T("Minimal wxWidgets App")」→「_("Minimal wxWidgets App")」と編集して

$ xgettext --from-code=utf-8 -k"_" minimal.cpp  //messages.poができる
$ msginit --locale=ja --input=messages.po      //ja.poができる。適当なエディタ編集

※xgettextを使うにはgettextが必要。まず最初にmsysのsourceforgeで配布されてるバイナリを使おうとしたんだが、依存関係の欠如でうまくいかなかった。じゃあソースからコンパイルできるのかってことで、やってみたけどこれがまたキツイ。0.17はバグがあるので手直しが必要。gettextMinGWだけでコンパイルすることはできるんかね…?。公式のREADMEにmno-cygwinオプションを使ってconfigureするよう書いてある。gettextにはlibiconvが必要なので、もしgettextをソースからコンパイルする場合はiconvをまず作らなければいけない(…はず?)。自分は諦めてcygwinからxgettextを使った。cygwinの最初のセットアップでgettextのパッケージを入れておいたので普通に使えた。(もちろんcygwinとmsysをいちいち起動するのではなく、環境変数cygwinを追加しておくのですよ。。。)

2011/09/03追記:xgettextはmingwのパッケージにちゃんと含まれている。ビルドしなくてもよい。

"Content-Type: text/plain; charset=ASCII\n" → "Content-Type: text/plain; charset=UTF-8\n"

#: minimal.cpp:35
msgid "hello, world!"
msgstr "こんにちは、世界!"

※ja.poを編集したあとファイルの文字コードUTF-8に変えるのだが、その時にファイルの先頭にバイナリ単位でゴミが混じる。バイナリエディタでその部分を削る必要がある。
※追記:私はTeraPadを使っているのだが、保存時にUTF-8とUTF-8Nを選べる。普通のUTF-8にはファイルの先頭に目印となるコードがつくらしい(参考:utf8とutf8Nって? (タスクマザー))。こいつがmsgfmtを使う際に邪魔になる。この現象に困っているのは日本人だけではないことがググッたらわかるのだが…とりあえずUTF-8Nで保存しておけばいいでしょう。

試しにここプログラミング/C_C++/wxWidgets/多国語対応にしてみる - Flightless wingにあるコードをちょっといじって

$ msgfmt ja.po -o minimal.mo

あとはコードの中で指定した位置にminimal.moを置いてコンパイルすればいけるはず。

ここまでの行程はMinGW/MSYS での wxWidgets について - PukiWikiここにも書いているように、最初のwxWidgetsコンパイルの際に --enable-unicodeを指定しておく必要があると思う。