VSTOとは、Visual Studio Tools for Office の略です。前回WebのTableでやったLINQによるソートをExcelのシートでやってみました。ExcelのシートのプロクラムといえばVBAですが、前からC#でやってみたいと思っていて、だったらLINQだよね、となり、今ちょうど調査しているし、という流れでテストしてみました。
環境 VSTO / Visual Studio 2010 & Excel 2010 / Windows 7
プロジェクトは、Visual C# -> Office -> 2010 -> Excel 2010 ブック です。
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml.Linq;
using Microsoft.Office.Tools.Excel;
using Microsoft.VisualStudio.Tools.Applications.Runtime;
using Excel = Microsoft.Office.Interop.Excel;
using Office = Microsoft.Office.Core;
namespace ExcelWorkbook
{
public partial class Sheet1
{
private void InternalStartup()
{
this.button1.Click += new System.EventHandler(this.button1_Click);
}
private void button1_Click(object sender, EventArgs e)
{
DataTable table = new DataTable();
Excel.Worksheet activeSheet = ((Excel.Worksheet)Application.ActiveSheet);
table.Columns.Add("c1");
table.Columns.Add("c2");
table.Columns.Add("c3");
for (int r = 1; r <= 6; r++)
{
table.Rows.Add(activeSheet.Cells[r, 1].Value, activeSheet.Cells[r, 2].Value, activeSheet.Cells[r, 3].Value);
}
var data = from dt in table.AsEnumerable()
orderby dt.Field<string>("c2")
select new
{
a = dt.Field<string>("c1"),
b = dt.Field<string>("c2"),
c = dt.Field<string>("c3")
};
int i=1;
foreach (var d in data)
{
activeSheet.Cells[i, 1].Value = d.a;
activeSheet.Cells[i, 2].Value = d.b;
activeSheet.Cells[i, 3].Value = d.c;
i++;
}
}
}
}
ボタンを押すと、中央の列がソートされます。
VSTOって興味がありつつもなかなか使う機会が少ないのですが、やっばりまだVBAなのでしょうか。という私もVBAの手軽さはとても便利なので、よく使うのですが・・
最近LINQを使っていて、いろいろと調べていたところJavaScriptでの実装があることを知りました。
jQueryと「linq.js」を連携させてDOMをLINQにより処理するには?
http://www.atmarkit.co.jp/fdotnet/dotnettips/1059linqjquery/linqjquery.html
jQueryのオブジェクトに変換できるということなので、すごく興味がありテストしてみました。
jQueryは、メソッドチェーンで少ないコーディング量で複雑な処理が記述できることでとても好きなのですが、LINQもメソッドチェーンが使えることから、とても相性がいいと感じました。
環境:IE9 / Windows7
<html>
<head>
<script src="jquery-1.9.1.js" type="text/javascript"></script>
<script src="jquery.linq.js" type="text/javascript"></script>
<script type="text/javascript">
var dict;
var dt;
$(document).ready(function(){
dict = $.Enumerable.Empty().ToDictionary();
dict.Add("k1", [1001,"be","JKL"]);
dict.Add("k2", [2, "abc","IIIII"]);
dict.Add("k3", [300, "d1", "H"]);
dict.Add("k4", [40, "col", "EFG"]);
dict.Add("k5", [55, "f0000", "BCD"]);
dict.Add("k6", [666, "eeee", "ABC"]);
dataSet();
});
function dataSet(flg){
var d = dict.ToEnumerable();
if(flg > 0){
flg --;
d = d.OrderBy("$.Value["+flg+"]")
}
dt = d.Select(function (x) {
return $("<tr/>").append($("<td/>").append(x.Value[0])).append($("<td/>").append(x.Value[1])).append($("<td/>").append(x.Value[2]))[0]
})
.TojQuery();
dt.appendTo("#dt");
}
function btn(n){
$('#dt tr').remove();
dataSet(n);
}
</script>
</head>
<body>
<table border="1" id="dt"></table>
<input type="button" value="o1" onClick="btn(1)">
<input type="button" value="o2" onClick="btn(2)">
<input type="button" value="o3" onClick="btn(3)">
</body>
</html>
ボタンを押すとそれぞれのカラムのデータでソートして表示します。
LINQの特徴の一つとして、DBのSQL文を文字列として分断して記述するのではなく、プログラム言語の一部として記述できる点があります。これもいいのですが、私はDBのSQL文がメモリのデータに対して使える点が便利に思っています。(LINQのクエリ式とSQL文はイコールではありませんが) SQL文と同じことをプログラム言語で書き換えようとすると、結構手間だったりするからです。
このlinq.jsライブラリは、jQueryとLINQとオブジェクトが相互変換可能なので、一つのメソッドチェーンで記述が可能です。これ、すごい!
サンプルは以前JavaScriptのフレームワークでこんな機能を使っていたと思いだし、作ってみました。JavaScriptでサーバからデータをJSONで取得してそれをブラウザで加工するということは、結構あります。そういった用途に使えそうです。ちなみに最新のJQuery 2.0.0では、IE9上で動作しませんでした。Firefox,ChromeはOKです。
ライブラリの中には詳しいリファレンスと実行テストができるファイルがあります。LINQの勉強するのにとてもいいかもしれませんね。
C#5.0の言語仕様で追加された、非同期処理を同期的に記述できるasync/awaitメソッドについても興味があるのですが、次々と新しい仕様を取り込むC#自体にとても興味があります。その中でもLINQはとても面白い深い存在なので、ちょっとまとめてみたいと思います。
環境: VisualStudio 2012 / Windows 8
プロジェクトは、C#コンソールアプリケーションです。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LinqTest
{
class Program
{
static void Main(string[] args)
{
var q0 = Enumerable.
Select(Enumerable.Where(Enumerable.Range(0, 10), p => p > 5), q => q * q);
foreach (var v in q0)
{
Console.Write("q0:{0}\n", v);
}
var q1 = Extensions.MyAdd(Enumerable.
Select(Enumerable.Where(Enumerable.Range(0, 10), p => p > 5), q => q * q), 2);
foreach (var v in q1)
{
Console.Write("q1:{0}\n", v);
}
var q2 = Enumerable.
Range(0, 10).Where(p => p > 5).Select(q => q * q);
foreach (var v in q2)
{
Console.Write("q2:{0}\n", v);
}
var q3 = Enumerable.
Range(0, 10).Where(p => p > 5).Select(q => q * q).MyAdd(2);
foreach (var v in q3)
{
Console.Write("q3:{0}\n", v);
}
var q4 = from p in Enumerable.Range(0, 10) where p > 5 select p * p;
foreach (var v in q4)
{
Console.Write("q4:{0}\n", v);
}
Console.Read();
}
}
public static class Extensions
{
public static IEnumerable<int> MyAdd(
this IEnumerable<int> x, int a)
{
foreach (int v in x)
{
yield return v + a;
}
}
}
}
q0,q2,q4とq1,q3は、それぞれ同じ値を出力します。
36,49,64,81
38,51,66,83
クエリ式の3つのバリエーションのテストをしました。
1つ目が、スタティックメソッドの入れ子、2つ目がメソッドチェーン、3つ目がクエリ式です。
そして、スタティックメソッド、メソッドチェーンについては、MyAddという拡張メソッドを定義してそれを利用しています。(クエリ式でもやる方法はあるのかな? Rxを使う?)
言語仕様がどんどん変化している様子をまとめてみました。
オブジェクト指向言語の次の言語として注目されている関数型言語というものがありますが、新しいもの好きの私ももれなくHaskellとか勉強したことがありました。しかしどのように使うと便利なのか等、なかなかその概念が理解できませんでした。このLINQを触っていると、関数型言語の特徴の一つの遅延評価というものが、とてもよく理解できます。上の例でMyAddメソッドがまさにそれにあたります。このメソッド中のyield return という構文が、それを実現しているのですが、これは必要になったときに実行されます。必要になったときというのは、定義されたときではないということです。もともと関数というのは、宣言・定義・記述しただけでは実行されず、呼び出されたとき・使われたときに実行するので、関数型という言い方も腑に落ちるというわけです。
あと、冒頭に非同期処理のことを少し触れましたが、この非同期を記述するのにもLINQを使うことができるようです。ReactiveExtensions(以下Rx)と呼ばれるものですが、これはこれでとても興味深いので、また別の機会に掘り下げたいと思います。このRxの非同期処理のプログラムを見ていると、JavaScriptのjQuery.Deferredに似ています。もともとAjaxという非同期処理を有名にしたJavaScriptなので、これも腑に落ちるところですが、jQueryの非同期処理を連続して実行してもコードが見やすいしくみになっています。(メソッドチェーンという言い方も、私はjQueryのことをイメージして使っています。)
WindowsストアアプリのAPIがほとんど非同期になるということからも、このようなトレンドはどの言語も取り入れているということなのでしょう。
関数型言語にいきなりジャンプアップするのは難しいかもしれませんが、このようにLINQとかRxで徐々に体を慣らしていくと、いいのかもしませんね。オブジェクト指向もRubyで学習すると理解がしやすいという意見もあったりしますし、いろんな側面から技術を見るということは大切なことかもしれません。
C++/CXでもない、標準のC++からWinRTのAPIが使用できる方法が以下のサイトであったので、いろいろと調べてみました。
(C++/CLIからできないことはテスト済み)
WRL を使用して非同期操作を完了する
http://msdn.microsoft.com/ja-jp/library/vstudio/hh973451.aspx
エラー処理を省略するなどしてソースを見やすくしました。
環境 : VisualStudio 2012 / Windows 8
プロジェクトは、C++ の空のプロジェクトを使い、runtimeobject.libを「追加の依存ファイル」に追加します。
#include <Windows.Foundation.h>
#include <Windows.System.Threading.h>
#include <wrl/event.h>
#include <stdio.h>
#include <Objbase.h>
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::System::Threading;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
int main()
{
// Initialize the Windows Runtime.
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
// Get the activation factory for the IThreadPoolTimer interface.
ComPtr<IThreadPoolTimerStatics> timerFactory;
HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_System_Threading_ThreadPoolTimer).Get(), &timerFactory);
// Create an event that is set after the timer callback completes. We later use this event to wait for the timer to complete.
// This event is for demonstration only in a console app. In most apps, you typically don't wait for async operations to complete.
Event timerCompleted(CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS));
// Create a timer that prints a message after 2 seconds.
TimeSpan delay;
delay.Duration = 20000000; // 2 seconds.
auto callback = Callback<ITimerElapsedHandler>([&timerCompleted](IThreadPoolTimer* timer) -> HRESULT
{
TimeSpan delay;
HRESULT hr = timer->get_Delay(&delay);
if (SUCCEEDED(hr))
{
printf("Timer duration: %2.2f seconds.\n", delay.Duration / 10000000.0);
}
// Set the completion event and return.
SetEvent(timerCompleted.Get());
return hr;
});
ComPtr<IThreadPoolTimer> timer;
hr = timerFactory->CreateTimer(callback.Get(), delay, &timer);
printf("Timer started...\n");
// Wait for the timer to complete.
WaitForSingleObjectEx(timerCompleted.Get(), INFINITE, FALSE);
// All smart pointers and RAII objects go out of scope here.
}
cl.exe wrl-consume-asyncOp.cpp runtimeobject.lib
VisualStudioを使わなくても、コンパイルは上のように簡単にできますが、使うプロジェクトとか、ヘッダの中身を確認したかったので、あえて使ってみました。
(たしかに標準C++であるという確認のため)
実行すると以下のようにコンソールに出力します。
Timer started.
Timer duration: 2.00 seconds.
二秒後にコールバックを登録した処理を動作させるものです。WinRTのランタイムでなくてもできることですが、現在は標準C++からでもWinRTのAPIがコールできるという確認にとどめたいと思います。もっとWinRTらしい使い方を見つけたらまたテストしたいと思います。
あと、WRTが低レベル、C++/CXが高レベルという位置づけのようです。
ストアアプリがネイティブコードを出力しているということが少し実感できました。(よりハードに使いところで動作させることは、逆行する動きなのかそれともこれかのトレンドなのか・・)
前々回のC++/CX + XAML に比較して、C++/CLIでもできるのかどうかテストしたところ、一応(形の上では?)できたので備忘録としておきたいと思います。
(WebViewもトライしてみましたができませんでした。方法はあるのかな?)
環境 : VisualStudio2012 / Windows 8
プロジェクトは、C++ CLRの空のプロジェクトを選びました。
using namespace System;
using namespace System::IO;
using namespace System::Windows;
using namespace System::Windows::Markup;
using namespace System::Windows::Navigation;
using namespace System::Threading;
[STAThreadAttribute]
int main(array<System::String ^> ^args)
{
FileStream^ fs = gcnew FileStream("xamltest.xaml", FileMode::Open, FileAccess::Read);
Window^ win = safe_cast<Window^>( XamlReader::Load(fs) );
if(win != nullptr){
win->Show();
}
Thread::Sleep(1000);
return 0;
}
xamltest.xaml
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Title="XamlTest" Height="300" Width="500">
<Grid>
<Label VerticalAlignment="Center" HorizontalAlignment="Center">Xaml Test</Label>
</Grid>
</Window>
実行すると、1秒間だけ以下の画面が表示されます。
WinRTと呼ばれるWindowsストアアプリのランタイムライブラリが、Windows8の大きな特徴になります。これは、MS-DOS、Windows、.NETという変化に匹敵するものだと思います。
これまでの環境との互換性のしくみはなかなか複雑です。C++/CLIとC++/CXの違いについて誤解しやすいので、このあたり実際にプログラムを作りながら理解を深めているところです。
これら二つは共存できないもので、入れ替えるのもののようです。しかしコードはとてもよく似ています。C++/CLIの特徴であったマネージ領域のポインタの代替えのトラッキングハンドラ(キャレット文字(ハット記号)で表す)が使われます。
今回は、JavaScriptのストアアプリからC++/CX関数への値の受け渡しのテストをしました。C++/CXをランタイムコンポーネントとしてビルドして、これをストアアプリのプロジェクトから参照をします
前回も、JavaScriptからC++/CXを呼び出すことをしていますが、意味的にはかなり違っていると思います。
サンプルは、JavaScriptで配列をつくり、C++/CX側で合計を計算して引数の値と掛け合わせたあと、JavaScriptに値を戻しています。
環境 : VisualStudio2012 / Windows8
C++側
プロジェクトで、C++ -> ストアアプリ -> Windowsランタイムコンポーネントを選択します。
Class1.cpp
#include "pch.h"
#include "Class1.h"
using namespace cppStoreRunCompo02;
using namespace Platform;
Class1::Class1(){}
int Class1::arrSum(const Array<int>^ arr, int num)
{
int sum = 0;
for(int i = 0 ; i < arr->Length; i++)
{
sum += arr[i];
}
return sum * num;
}
JavaScript側
プロジェクトで、JavaScript -> ストアアプリ -> 空のアプリケーションを選択します。
default.js
(function () {
"use strict";
WinJS.Binding.optimizeBindingReferences = true;
var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;
app.onactivated = function (args) {
if (args.detail.kind === activation.ActivationKind.launch) {
if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
} else {
}
args.setPromise(WinJS.UI.processAll());
}
};
app.oncheckpoint = function (args) {
};
app.start();
var cpp = new cppStoreRunCompo02.Class1();
var arr = new Array(10);
for (var i = 0; i < arr.length; i++) {
arr[i] = i;
}
var sum = cpp.arrSum(arr, 2);
alert("SUM : " + sum);
})();
function alert(str) {
var md = new Windows.UI.Popups.MessageDialog(str);
md.showAsync();
}
WinRTは、C++/CXを使う以外にも、標準C++でWRT(Windows Runtime C++ Template Library)というものから使えるようです。試しにC++/CLIでWRTを使おうとしましたが、やはりだめでした。
これはCOMの改良版ということのようですが、.NETから進化しているものなのか、それともシンプル化したものなのか、そしてこの先どの方向に進んでいくのか、とても気になります。
このWinRTの存在は、今後のWindowsを占う上でも、とても重要なものとなりそうです。
Windowsプラットホームの開発でいろいろと気になったことをテストしてきましたが、いよいよWindowsストアアプリの開発をしようということで、いろいろと準備を始めました。(本当はWindows Phone 8アプリの開発をやりたかったのですが、端末がないのでそれまではストアアプリでやれることはやっておこうと・・)
しかしこのストアアプリ、どんな言語で開発したらいいか、WindowsPhoneアプリへの移行がスムースのスタイルは、過去のライブラリを使う方法等、選択肢がいろいろとある分迷ってしまいます。JavaScript+HTML5かC#+XAMLか、と迷っていたらXAMLでWebViewを使う方法があると知ったので、テストしてみました。
環境: VisualStudio2012 / Windows8
新しいプロジェクトで、Visual C++ -> Windowsストア -> 新しいアプリケーション(XAML) を選びました。
(C#でもいいのですが、新しいC++/CXにも触れたかったので)
MainPage.xaml
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<WebView x:Name="WebView1" ScriptNotify="wv_notify" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,88,0,0"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="40,28,0,0" VerticalAlignment="Top" Click="Button_Click_1"/>
<TextBox x:Name="TextBox1" HorizontalAlignment="Left" Margin="166,28,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Height="38" Width="363" TextChanged="TextBox_TextChanged_1"/>
</Grid>
MainPage.xaml.cpp
void MainPage::OnNavigatedTo(NavigationEventArgs^ e)
{
String^ html = "<html><head><script type='text/javascript'>\n" +
"function jsFunc1(){ document.getElementById('test1').innerText = 'Hello!';}\n" +
"function callCpp1(){ window.external.notify('js String');}\n" +
"</script></head><body><h3>Test Program</h3><div id='test1'>Message area from C++ Program. Press button.</div>\n" +
"<input type='button' value='btn1' onClick='callCpp1()'>" +
"</body></html>";
WebView1->NavigateToString(html);
}
void cppxamlWebView1::MainPage::Button_Click_1(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
WebView1->InvokeScript("jsFunc1", nullptr);
}
void cppxamlWebView1::MainPage::wv_notify(Platform::Object^ sender, Windows::UI::Xaml::Controls::NotifyEventArgs e)
{
TextBox1->Text = e.Value;
}
VisualStudioのデザイナーで作成すると、自動的にたくさんコードを生成するので、重要な部分のみ引用しました。(デフォルトのファイルからこれに関連する部分も修正する必要はあります)使用するXAMLコントロールは、ボタン、テキストボックス、WebViewの3つです。
ボタンを押すと、WebViewのJavaScript関数を呼び出し、divの内容を書き換えます。WebView内のボタンを押すと、JavaScriptがC++の関数を呼び出しテキストボックスを書き換えます。
起動時
変更後
偶然、最近のWebViewがらみの話題の延長みたいになってしまいました。
前々回、WebViewでのC#、JavaSCript間の連携の話題、そして前回IronPythonを使ったWebページの話題という流れて、どうしてもテストしておきたかったのが、Qtを使ったWebViewのPython、JavaScript間相互呼び出しです。
Qtは、LinuxZaurusの開発で経験がありますが(組み込みLinux用なのでQtopiaと呼ばれていましたが)、C++でクロスプラットフォームのGUIアプリケーションを開発できます。歴史は古いのですが、まだまだ現役のようです。最近また触れる機会があり、いろいろと調べてみたところ、WebKitを使ったプログラムが面白そうだということで、ちょっとはまっています。PySideというライブリを使って、Pythonからダイレクトに操作できる点も魅力です。
次の例は、起動するとPythonのプログラムからJavaScript関数を呼び出し、表示されたボタンをクリックすると、Python関数を呼び出すというものです。これはコンソールにメッセージを出力します。
環境: Python 2.7.3 + PySide 1.1.2 / Windows7
import sys
from PySide import QtWebKit,QtGui,QtCore
html = """
<html>
<script>
function pyMsg(str){
pyObj.console(str);
}
function jsFunc(str){
alert(str);
}
</script>
<body>
<input type="button" value="btn" onClick="pyMsg('js String')">
</body>
</html>
"""
class jsTest(QtCore.QObject):
def __init__(self, parent=None):
super(jsTest, self).__init__(parent)
@QtCore.Slot(str)
def console(self, msg):
print msg
app = QtGui.QApplication(sys.argv)
view = QtWebKit.QWebView()
frame = view.page().mainFrame()
js = jsTest()
view.setHtml(html)
frame.addToJavaScriptWindowObject('pyObj', js)
frame.evaluateJavaScript("jsFunc('from py Function');")
view.show()
sys.exit(app.exec_())
他にも、クリックイベント等をフックできるので、デバッグやその他いろいろと楽しいことができます。
自分ブラウザみたいなものを作ってみたくなりますね。
(いまさらですが)SilverlightはFlashと同じように、ウェブブラウザにプラグインをインストールして、リッチコンテンツを動作させることができます。アニメーションやゲームなど、JavaScriptでは難しかったことができました。またFlashには、Flashムービ(コンテンツ)だけでなく、ActionScriptを使ったFlexというWebリッチクラアントの開発もできます。Silverlightも同様のことができます。後発なだけに、Flexでは既存HTMLと完全に分断してしまっているところを、HTMLとの親和性が高い設計になっています。どちらも個人的には、ウェブブラウザがリッチになるというよりも、インターネットクライアントアプリとしての自由度の高さがとても好みでした。しかし、最近はこれら(ブラウザプラグインによるリッチアプリ機能)がちょっと脇役にまわりつつある傾向にあります。
(ブラウザにプラグインを導入できないようにする傾向にある)
最初に顕在化したのは、iPhone,iPadによるFlashの排除したのが始まりでした。消費電力やセキュリティの問題が理由のようですが、PCだけでなくスマホ、タブレットを含めた未来を考えたとき、ある意味必然だったのかもしれません。
ブラウザ上で動かすリッチコンテンツには、ただリッチなだけでなく、マルチプラットホームで同じアプリケーションを走らせるという目的もあります。このマルチとは、Windows,Mac,Linuxというカテゴリーでかたられてきましたが、スマホ、タブレットといったものを含んで考えると、またちがったプラットホームを考える必要がでてくるのでしょう。
そこで最近勢いがあるのが、JavaScriptです。実行速度の向上により最近あらゆるものがJavaScriptで記述されるようになり、万能化しているようにさえ感じます。(Flashもコンバートできたり、ゲームエミュレータまでも)
CPUの違い、OSの違いがあっても、マルチプラットホームで動作するアプリケーションは、長年開発者がめざしてきたところです。
最近はJavaScript+HTML5という組み合わせが現実的に、その答えとなってきているようです。
Javaや.NETの中間レイヤーでマルチプラットホームを実現する方法も、中間言語レベルの互換性から記述言語レベルの互換性になったりするかもしれませんね。.NETのDLRはまた違った可能性を感じるのですが・・
と、いままでになく前置きが長くなりましたが、Silverlightのような環境が残って欲しいな、という思い(言語、実行環境に自由度が欲しいという観点)から、SilverlightによるIronPythonを使ったWebページと、JavaScriptの勢いを示す意味から、JavaScriptライブラリによるPythonを使ったWebページを同じプログラムでテストしてみました。
Silverlight
<html>
<head>
<script src="/dlr/dlr.js" type="text/javascript"></script>
</head>
<body>
<script type="text/python">
import System
import datetime
from System import TimeSpan
from System.Windows.Threading import *
def show_time(s,t):
document.clock.innerHTML = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
timer1 = DispatcherTimer()
timer1.Interval = TimeSpan(0, 0, 1.0)
timer1.Tick += show_time
timer1.Start()
</script>
<span id="clock"></span>
</body>
</html>
JavaScript Library
<html>
<head>
<script src="brython.js"></script>
</head>
<body onLoad="brython()">
<script type="text/python">
import sys
import datetime
import time
def show_time():
doc['clock'].text = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
show_time()
time.set_interval(show_time,1000)
</script>
<span id="clock"></span>
</body>
</html>
どちらも日時を表示するだけのプログラムです。インターバルタイマの記述に大きな違いがあります。
SilverlightでIronPythonを実行する環境には、Gestaltを使いました。
http://www.visitmix.com/labs/gestalt/
これは2009年11月から更新がありません。
Pythonが実行できるJavaScriptライブラリは、Brythonを使いました。
http://code.google.com/p/brython/
最新バージョンは、2013年3月ですから、まだピチピチですね。
Silverlightの方が起動に時間がかかります。どちらもFirefox18でテストしました。
比較した感想ですが、将来性はBrython、わくわく度はSilverlightといったところでしょうか。。
iPhoneやAndroidでは、WebViewを使って、ネイティブの機能をJavaScriptから使うことができたり、ネイティブコードからJavaScriptが実行できます。これと同じことをWindowsでもやってみました。(まだやってませんがWindows Phoneでも同様なのかな)
これは何かと便利で、Titanium Mobileのようにスマホアプリをハイブリッドで開発できるのも、このしくみを利用するからですね。
環境:VisualStudio 2010 / Windows 7
まず、以下のようにブラウザをC#で簡単につくれます。(フォームにツールボックスからWebBrowserをはりつけ)
namespace CSbrowser
{
[System.Runtime.InteropServices.ComVisibleAttribute(true)]
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
webBrowser1.ObjectForScripting = this;
webBrowser1.Navigate(new Uri("http://localhost/test.html"));
}
public void CSfunc1(string value)
{
MessageBox.Show(value);
}
private void button1_Click(object sender, EventArgs e)
{
object[] args = { "arg1" };
webBrowser1.Document.InvokeScript("JSfunc1", args);
}
private void button2_Click(object sender, EventArgs e)
{
string js = "alert('Click!')";
webBrowser1.Url = new Uri("javascript:" + Uri.EscapeDataString(js) + ";");
}
}
}
次は、読み込むhtmlファイルです。(test.html)
<html>
<script>
var jsdata1 = 'jsdata1';
function JSfunc1(arg)
{
alert('JSfunc1! : ' + arg);
}
function clk()
{
window.external.CSfunc1(jsdata1);
}
</script>
<body>
<br><hr>
<center>
<h3><font color=blue>Test Page for C# WebBrowser.</font></h3>
<hr>
<input type='button' value='btn1' onClick='clk()'>
</center>
</body>
</html>
test.htmlをローカルのWebサーバで読み込めるようにして(XAMPPなど)、C#ソースをビルドしてたちあげると、
このような画面になります。
中央の”btn1″ボタンをクリックすると、C#のCSfunc1()が呼ばれます。また、Windowsフォームの”button1″ボタンを押すと、JavaScriptのJSfunc1()が呼ばれ、”button2″ボタンを押すと、JavaScriptのalert()関数が実行されます。
まだいろいろと応用が考えられそうですね。