ロジックコードを記述する
ロジックコードを記述する
リスト2 (MainPage.xaml.vb)
Option Strict On
KinectServiceに接続するための機能を提供する、2つの名前空間をインポートしておきます。
Imports Coding4Fun.Kinect.KinectService.Common
Imports Coding4Fun.Kinect.KinectService.PhoneClient
Imports System.IO
仮想ファイルシステムを作成および使用するための型が含まれている、System.IO.IsolatedStorage名前空間をインポートします。分離ストレージによって、安全なクライアント側のストレージが提供されます。
Imports System.IO.IsolatedStorage
XML to LINQを使用するためSystem.Xml.Linq名前空間をインポートしておきます。
Imports System.Xml.Linq
タイマーの機能を提供するクラスであるSystem.Windows.Threading名前空間をインポートしておきます。
Imports System.Windows.Threading
Partial Public Class MainPage
Inherits PhoneApplicationPage
' コンストラクター
Public Sub New()
InitializeComponent()
End Sub
Kinectセンサーの視界内にプレイヤーが存在するかどうかを判別するブール型メンバ変数flagを宣言し、Falseで初期化しておきます。
Dim flag As Boolean = False
リスナーに接続するためのクライアントであるSkeletonClientクラス型のメンバ変数_skeletonClientを宣言します。同様にColorClientクラス型のメンバ変数_colorClientを宣言します。
Dim _skeletonClient As SkeletonClient
Dim _colorClient As ColorClient
Kinectセンサーの視界から外れた時刻を記録するメンバ変数nowTimeと、視界に入ってきた時刻を記録するメンバ変数inTimeを宣言します。
Dim nowTime As String = String.Empty
Dim inTime As String
タイマーを表すクラスであるDispatcherTimerクラス型のメンバ変数myTimerを宣言します。
Dim myTimer As DispatcherTimer
ページがアクティブになった時の処理
新しいDispatcherTimerのインスタンスを作成します。Intervalプロパティに1000ミリセコンド(1秒)を指定し、タイマーをスタートします。AddHandlerステートメントで、指定したタイマーの間隔が経過し、タイマーが有効である場合に発生するTickイベントにイベントハンドラを追加します。イベントハンドラ内では、非表示となっているinTextBlockのTextプロパティに現在の年月日時間分秒の値を入れています。
変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。分離ストレージ内にkinectというフォルダが存在しない場合は、CreateDirectoryメソッドでkinectというフォルダを作成します。
Path.Combineメソッドで、2つの文字列を1つのパスに結合します。ここでは、kinectフォルダとFileList.xmlを結合しています。kinect\FileList.xmlとなります。この値をxmlFilePath変数に格納します。FileExistsメソッドでxmlFilePathに格納したファイルが存在する場合は、dataButton(データ一覧)の使用を可能にします。そうでない場合は、不可のままです。
Protected Overrides Sub OnNavigatedTo(e As System.Windows.Navigation.NavigationEventArgs)
myTimer = New DispatcherTimer
myTimer.Interval = New TimeSpan(1000)
AddHandler myTimer.Tick, Sub(timerSender As Object, timerArgs As EventArgs)
inTextBlock.Text = DateTime.Now.ToString("yyyy年MM月dd日HH時mm分ss秒")
End Sub
myTimer.Start()
Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication
If storage.DirectoryExists("kinect") = False Then
storage.CreateDirectory("kinect")
End If
Dim xmlFilePath As String = Path.Combine("kinect", "FileList.xml")
If storage.FileExists(xmlFilePath) = True Then
dataButton.IsEnabled = True
Else
dataButton.IsEnabled = False
End If
MyBase.OnNavigatedTo(e)
End Sub
[OK]ボタンがクリックされた時の処理
Address入力ボックスに入力されたIPアドレスを、変数myIPAddressに格納しておきます。IPアドレスが入力されていない場合は、警告メッセージを表示し、処理を抜けます。IPアドレスが入力されている場合は、新しいSkeletonClientのインスタンスとColorClientのインスタンスを作成します。ColorClientが接続されていなかった場合は、ConnectメソッドでIPアドレスとポートを指定して接続します。
SkeletonClientが接続されていなかった場合は、同じくConnectメソッドでIPアドレスとポートを指定して接続します。
AddHandlerステートメントでRGBカメラのフレームが更新された場合のイベントハンドラを追加します。同じく、スケルトンのフレームが更新された場合のイベントハンドラを追加します。
Private Sub okButton_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles okButton.Click
Dim myIPAddress As String = addressTextBox.Text
If myIPAddress = String.Empty Then
MessageBox.Show("Addressを入力してください。")
Exit Sub
Else
_skeletonClient = New SkeletonClient()
_colorClient = New ColorClient()
If Not _colorClient.IsConnected Then
_colorClient.Connect(myIPAddress, 4530)
Else
_colorClient.Disconnect()
End If
If Not _skeletonClient.IsConnected Then
_skeletonClient.Connect(myIPAddress, 4532)
Else
_skeletonClient.Disconnect()
End If
AddHandler _colorClient.ColorFrameReady, AddressOf client_ColorFrameReady
AddHandler _skeletonClient.SkeletonFrameReady, AddressOf client_SkeletonFrameReady
End If
End Sub
RGBカメラのフレームが更新された時に発生するイベント
Image1のSourceプロパティにRGBカメラのフレームをビットマップイメージに変換して表示します。
非表示となっているTextBlock1にメンバ変数flagの値を格納します。flagの値がTrueの場合、つまりKinectセンサーの視界内にプレイヤーが存在した場合は、タイマーを停止し、ListBoxコントロールをクリアして、現在の年月日時間分秒をListBoxに追加し、選択状態にします。
それ以外、つまりKinectセンサーの視界内にプレイヤーが存在していなかった場合は、DataSaveプロシージャーを実行し、タイマーをスタートします。
Private Sub client_ColorFrameReady(ByVal sender As Object, ByVal e As ColorFrameReadyEventArgs)
If e.ColorFrame.BitmapImage Is Nothing = False Then
Image1.Source = e.ColorFrame.BitmapImage
End If
TextBlock1.Text = flag.ToString
If flag = True Then
myTimer.Stop()
ListBox1.Items.Clear()
ListBox1.Items.Add(DateTime.Now.ToString("yyyy年MM月dd日HH時mm分ss秒"))
ListBox1.SelectedIndex = 0
Else
DataSave()
myTimer.Start()
End If
End Sub
スケルトンフレームが更新された時に発生するイベント
Skeletonクラス用のオブジェクト変数であるmySkeletonを使って、スケルトンデータを持つe.SkeletonFrame.Skeletons内で、全ての関節の位置がトラッキングされた状態にある、シーケンスの最初の要素を取得していきます。
プレイヤーの骨格が追跡されていない場合は、メンバ変数flagをFalseで初期化し、タイマーをスタートさせます。それ以外の場合、つまり、プレイヤーの骨格が追跡されていた場合は、flagをTrueで初期化しタイマーを停止します。
Private Sub client_SkeletonFrameReady(ByVal sender As Object, ByVal e As SkeletonFrameReadyEventArgs)
Dim mySkeleton As Skeleton = (From s In e.SkeletonFrame.Skeletons Where s.TrackingState = SkeletonTrackingState.Tracked Select s).FirstOrDefault()
If mySkeleton Is Nothing Then
flag = False
myTimer.Start()
Return
Else
flag = True
myTimer.Stop()
End If End Sub
プレイヤーがKinectセンサーの視界に入った時刻と、視界から外れた時刻をXMLファイルとして保存する処理
ListBoxコントロールに現在の年月日時間分秒が追加されている場合の処理です。メンバ変数nowTimeにListBoxコントロールの項目を格納します。メンバ変数inTimeに非表示となっているinTextBlockに格納されている時刻を格納します。
変数xmlStorageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。Path.CombineでkinectというフォルダとFileList.xmlに格納されている画像名とを連結し、変数xmlFilePathに格納しておきます。
kinectというフォルダ内にFileList.xmlが存在していない場合は、Visual Basic の埋め込み式を用いて、XML宣言とルート要素、その子要素として、その子要素としてとを作成し、埋め込み式の構文である を用いてとの内容テキストに、変数inTimeとnowTimeを指定します。これは ASP.NET で使用される構文と同じです。
分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数xmlStreamを用意し、IsolatedStorageFile.CreateFileメソッドで、分離ストレージ内にxmlFilePath変数の持っているフォルダ名付きXMLファイルを作成します。
kinectフォルダ内にFileList.xmlファイルが存在すれば、新しいStreamWriter を生成し、IsolatedStorageFile.OpenFileメソッドで、指定したファイルアクセスを使用して指定したモードでファイルを開き、初期化します。Writeメソッドで埋め込み式のXMLをストリームに書き込みます。[データ一覧]ボタンの使用を可能にします。
次は、既にkinectフォルダ内にFileList.xmlが存在する場合の処理です。
IsolatedStorageFileクラスのOpenFileメソッドでkinectフォルダ内のFileList.xmlファイルを、指定したファイルアクセスを使用して指定したモードで開きます。開いたファイルをStreamReaderで読み込みます。ReadToEndメソッドでファイルの最後まで読み取り変数readXmldoc変数に格納しておきます。読み込んだXMLテキストをParseメソッドでXElementとして読み込みます。
追加するとその子要素とを作成します。と要素の内容テキストに、埋め込み式を用いてinTimeとnowTime変数の値を指定します。新しく生成したXML要素を、読み込んだXMLにAddメソッドで追加します。IsolatedStorageFileStreamを閉じます。
FileList.xmlファイルが存在する場合は、新しいStreamWriter を生成し、IsolatedStorageFile.OpenFileメソッドで、指定したファイルアクセスを使用して指定したモードでファイルを開き、初期化します。Writeメソッドで新しいXML要素の追加されたXMLを、ストリームに書き込みます。[データ一覧]ボタンの使用を可能にし、ListBoxの内容をクリアします。
Private Sub DataSave()
If ListBox1.Items.Count > 0 Then
nowTime = ListBox1.SelectedItem.ToString
inTime = inTextBlock.Text
Dim xmlStorage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication
Dim xmlFilePath As String = Path.Combine("kinect", "FileList.xml")
If xmlStorage.FileExists(xmlFilePath) = False Then
Dim xmldoc As XDocument = <?xml version="1.0" encoding="utf-8"?>
<人物記録>
<情報>
<入時間><%= inTime %></入時間>
<出時間><%= nowTime %></出時間>
</情報>
</人物記録>
Using xmlStream As IsolatedStorageFileStream = xmlStorage.CreateFile(xmlFilePath)
End Using
If xmlStorage.FileExists(xmlFilePath) = True Then
Using xmlwriter As StreamWriter = New StreamWriter(xmlStorage.OpenFile(xmlFilePath, FileMode.Open, FileAccess.Write))
xmlwriter.Flush()
xmlwriter.Write(xmldoc.ToString)
'xmlwriter.Close()
End Using
dataButton.IsEnabled = True
End If
Else
Dim xmlStream As IsolatedStorageFileStream = xmlStorage.OpenFile(xmlFilePath, FileMode.Open, FileAccess.Read)
Using xmlreader As StreamReader = New StreamReader(xmlStream)
Dim readXmldoc As String = xmlreader.ReadToEnd
Dim doc As XElement = XElement.Parse(readXmldoc)
Dim addXml As XElement = <情報>
<入時間><%= inTime %></入時間>
<出時間><%= nowTime %></出時間>
</情報>
doc.Add(addXml)
xmlStream.Close()
If xmlStorage.FileExists(xmlFilePath) = True Then
Using xmlwriter As StreamWriter = New StreamWriter(xmlStorage.OpenFile(xmlFilePath, FileMode.Open, FileAccess.Write))
xmlwriter.Flush()
xmlwriter.Write(doc.ToString)
dataButton.IsEnabled = True
End Using
End If
End Using
End If
ListBox1.Items.Clear()
End If
End Sub
[データ一覧]ボタンがクリックされた時の処理
KinectServiceを切断します。DataIchiranPageに遷移します。
Private Sub dataButton_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles dataButton.Click
RemoveHandler _colorClient.ColorFrameReady, AddressOf client_ColorFrameReady
RemoveHandler _skeletonClient.SkeletonFrameReady, AddressOf client_SkeletonFrameReady
_colorClient.Disconnect()
_skeletonClient.Disconnect()
NavigationService.Navigate(New Uri("/DataIchiranPage.xaml", UriKind.Relative))
End Sub
End Class
次に、ソリューションエクスプローラー内のDataIchiranPage.xamlを展開して表示される、DataIchiranPage.xaml.vbをダブルクリックしてリスト3のコードを記述します。
ロジックコードを記述する
リスト3 (DataIchiranPage.xaml.vb)
Option Strict On
Imports System.Xml.Linq
Imports System.IO
Imports System.IO.IsolatedStorage
Imports Microsoft.Phone
Imports System.Windows.Media.Imaging
Partial Public Class DataIchiranPage
Inherits PhoneApplicationPage
Public Sub New()
InitializeComponent()
End Sub
ページがアクティブになった時の処理
変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。Path.Combineメソッドで、2つの文字列を1つのパスに結合します。ここでは、kinectフォルダとFileList.xmlを結合しています。kinect\FileList.xmlとなります。この値をfilePath変数に格納します。
IsolatedStorageFileクラスのOpenFileメソッドでkinectフォルダ内のFileList.xmlファイルを、指定したファイルアクセスを使用して指定したモードで開きます。開いたファイルをStreamReaderで読み込みます。ReadToEndメソッドでファイルの最後まで読み取り変数readXmldoc変数に格納しておきます。読み込んだXMLテキストをParseメソッドでXElementとして読み込みます。
文字列型の新しいリストである、myListInfoオブジェクトを作成します。読み込んだXMLファイルの要素コレクション内を変数resultで反復処理しながら、以下の処理を実行します。
文字列型のリストオブジェクトにAddメソッドで、と要素の内容テキストを、文字列と改行を付加して追加していきます。ListBoxのItemsSourceプロパティに時間の追加されたmyListInfoオブジェクトを指定します。ListBoxコントロール内に「入時間」と「出時間」の一覧が表示されます。
Protected Overrides Sub OnNavigatedTo(e As System.Windows.Navigation.NavigationEventArgs)
Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication
Dim filePath As String = Path.Combine("kinect", "FileList.xml")
Using myStream As IsolatedStorageFileStream = storage.OpenFile(filePath, FileMode.Open, FileAccess.Read)
Using reader As StreamReader = New StreamReader(myStream)
Dim readXmldoc As String = reader.ReadToEnd
'myStream.Close()
Dim doc As XElement = XElement.Parse(readXmldoc)
Dim myListInfo As New List(Of String)
For Each result In From c In doc.Descendants("情報") Select c
myListInfo.Add("入時間=" & result.Element("入時間").Value & vbCrLf & "出時間=" & result.Element("出時間").Value & vbCrLf & "---------------------------")
Next
ListBox1.ItemsSource = myListInfo
End Using
End Using
MyBase.OnNavigatedTo(e)
End Sub
[全データの削除]ボタンをクリックした時の処理
変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。Path.Combineメソッドで、2つの文字列を1つのパスに結合します。ここでは、kinectフォルダとFileList.xmlを結合しています。kinect\FileList.xmlとなります。この値をfilePath変数に格納します。kinect\FileList.xmlが存在していた場合は、DeleteFileメソッドで削除します。その後MainPageに遷移します。
Private Sub Button1_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles Button1.Click
Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication
Dim filePath As String = Path.Combine("kinect", "FileList.xml")
If storage.FileExists(filePath) = True Then
storage.DeleteFile(filePath)
End If
NavigationService.Navigate(New Uri("/MainPage.xaml", UriKind.Relative))
End Sub
End Class
今回で全14回にわたってお届けしたKinect応用編は終了です。基本編と応用編で全28個のサンプルを紹介してきましたが、いかがだったでしょうか。自分なりのKinectアプリケーション作成のヒントにはなりましたか。もし、少しでもお役に立てたならうれしい限りです。どうも長い間ありがとうございました。
PROJECT KySS 薬師寺国安