HIDARI日記(右)

そのときどき興味ある技術を中心にだらだら書いてます。内容は個人の見解であり、所属する企業を代表するものではありません。

DeveLove関西「リモートワークを実際にやってみてどう?」に参加してきました #devkan

2014/07/08(火)に開催されたDevLOVE関西 - リモートワークを実際にやってみてどう?に参加して来たときのメモです.

スピーカーの話の内容のメモと,僕の感想とかのメモがごっちゃになったものがまあ,いいや,です.

リモートワークの理想と現実

桶谷 拓也(@okeee0315)さんのお話のメモ.

20140708 リモートワークの理想と現実 - DevLoveKansai remotework

リモートワークのつらみとか

  • リモートワークは集中できる場所さがし
    • 電源に困る
    • お金に困る
  • 他の人の時間に合わせるので時間に縛られる
  • ネットを介したコミュニケーション
    • リモートじゃない人は使わない
    • 喫煙所の会議みたいに知らない間に決まることも
  • 自己管理
    • 仕事しすぎる
  • リモートワークされる側の問題も
    • リモート側をリモートだと思わない
    • 情報を流さない
  • リモートと非リモートは対等ではない

リモートワークで成果を出すための4つのヒント

谷川 周平(チャットワーク(ChatWork))さんのお話から.

ツールについて

メリット・デメリット

  • リモートワークは士業(弁護士とか)と非常に相性がいい
    • お客さん同士を繋ぐことでみんな幸せに成れた
  • ただ空気感の共有が難しい
    • 言った言わないは起こらないけど,「わかった」か「わかってないか」は伝わらない
    • 気持ちは積極的に言葉にしないといけない
      • ありがとう
      • ごめんなさい
      • 助けて
      • 気持ちや成果ははっきりと伝える

マインド

質問タイム

37Signalsと現在の自分の働き方との間にある差ってなんでしょうか?

  • 会社としての在り方,37Signalsの評価制度はどうしてるのかが気になる.
  • 最近の会社は優秀な人がいる場所にオフィスを出すみたいなのが多い

リモートで喧嘩したときの仲直りの方法は?

  • テキスト打って,もう一度動画通話する.
  • あと機会があればお菓子持ってく.

メールのかわりのツールは?

サボってる人をどうやって見つける?

  • ツール上の動きをみてると自然と見えてくる.
  • そもそもサボる人と一緒に仕事は…w

リモートで仕事を頼む場合の準備はどこまでやればいいか?

  • リモート側の準備で一番大事なのは,目的,目的に至った背景とゴールの設定
  • タスクのお願いはやりやすいけど,事業の企画とかはなかなかつらい

交通費とか必要経費はどうなってるの?

  • 会社が出す
  • EX-ICはあると非常に便利
    • ぜひ会社に持ってもらいましょう
  • PCやサービスも会社が出す
    • ChatWorkの場合,一人頭で考えると月2万くらい
    • クラウドは安い.

感想

(小学生並み

git-svnのSVNのバージョンの違い

git-svnしてたらサーバの管理者の方から「君のsvnのクライアントのバージョン古くね?」って言われたから調べて見ました.

試しに

いつも使ってるGit for Windows

PS C:\svnrepo.git> git svn --version
git-svn version 1.9.2.msysgit.0 (svn 1.4.6)

svnは1.4.6ですね.確かに古い.

ちなみにMac Portsでインストールした場合は

hidari$ git svn --version
git-svn version 1.9.3 (svn 1.8.9)

となっており,Apache Subversion News Archivesによると1.8系としては現時点で最も新しいバージョンになっています.

なお

1.6.x and earlier   No longer supported

from Apache Subversion Release Notes

海外での反応

How can I upgrade the SVN version used by git svn in Windows? - Stack Overflow

古くても問題ないはずだから我慢して使えってことらしい.

おわりに

これってみなさんどうしてるでしょ?問題にしてない?

勉強会の配信方法をいくつか試した

とある勉強会を開催するにあたって遠方の会場とつないでテレビ会議的に双方向でやりとりしたいなという話がでたので,先日から数人の方と一緒に共有する方法をいくつか試していました.なので各ツールの特徴と所感を書きなぐってみます.

試した主なプラットフォームは

でした.

この中でSkypeGoogle+ ハングアウトはデスクトップ配信も試してます. PowerPoint 2013 オンラインプレゼンテーションは文字通りプレゼンテーション(スライド)を配信する機能ですので用途が微妙に違いますが.

双方向コミュニケーション

Skype

  • 映像がきれい
  • タイムラグは多少ある
  • デスクトップ配信が可能
  • Skypeのアカウントが必要(相手のIDも)

appear.in

  • 超新しい
  • ブラウザをつなぐだけで使えてお手軽
  • 動作が不安定
  • IEには未対応
  • 映像の品質はいまいち

Google+ ハングアウト

  • デスクトップ配信可能
  • タイムラグが非常に小さい
  • 映像の品質はいまいち
  • Google+のアカウントが必要(相手のIDも)

品質の所感

配信プラットフォームの配信品質は Skype > Hungout = Ust > Appear.in な感じ.さすがSkypeは映像,音声ともにかなり綺麗に感じました,

映像重視ならSkype,レスポンスならハングアウトって気がしました.

ちょっと残念感のあったappear.inだけど,シンプルさは結構好き.まだまだ新しいサービスなので今後に期待.

ストリーミング配信

Ustream(のWeb)

  • 映像,音声ともになかなかよい
  • タイムラグは大きい(リアルから10秒とかあるけど,双方向じゃないから問題ない)

Ustream Producer

  • CPU全開になる
  • 複数の映像ソースが使い分けられる
  • デスクトップ配信が可能
  • 割とよく落ちる
  • タイムラグがひどい(特に映像の遅れがひどい)

品質の所感

Ustream のWeb >> 超えられない壁 >> Ustream Producer

デスクトップ/プレゼンテーション配信

Skype

  • それなりに画質は悪くない
  • 音声と同時に配信できる
  • スピーカーのPCでSkypeを使う必要がある

appear.in

  • あくまで「デスクトップ配信できます」っていうレベル

Google ハングアウト

  • レスポンスはかなり良い
  • 音声と同時に配信できる

PowerPoint 2013 オンラインプレゼンテーション

  • PowerPoint 2013 に限定の機能
  • レスポンスは良い
  • 音声は配信されない

品質の所感

プレゼンテーションは ハングアウトのデスクトップ配信 = PPT > Skypeのデスクトップ配信 >apear.inのデスクトップ配信 という感じがしました.

まとめない

あとからわかったけどハングアウトって参加してない人が自由に配信内容を観られる オンエア っていう機能があるみたいですね.今度試してみよう.

なお,ここでは僕が感じた内容を書きなぐっているので実際の品質と乖離がある可能性があります.ので,興味がある方は一度試してみることをおすすめします.

そういえばニコ生は考えになかった.

MVVMを目指すTodoアプリのその後的な話

なんのこと?

ちょっと前に書いたこちらの記事MVVMパターンを少しでも身に付けたかったからTodoアプリを作ることにした - HIDARI日記(右)で作り始めたTodoアプリですが,ちょっとずつ,ほんとにちょっとずつ進めてました.

hidari/WhatNeedToBeDone

まあ時間のやりくりが下手だってのもあるけど,Commandの使い方に慣れてなくて ListBoxのItemTemplateに設定したDataTemplateにRelativeSourceのメソッドなりコマンドなりをバインドしよう - HIDARI日記(右)みたいな状況になってのも大きいです.

頭悪いってとてもつらい.

追加した機能

具体的には,タスク全てにチェックが入っているときには全選択/解除チェックボックスがオンに.

f:id:hidari-yori:20140514224200j:plain

逆に一つでもタスクのチェックボックスがオフになっているときは全選択/解除チェックボックスもオフになるようにしました.

f:id:hidari-yori:20140514224213j:plain

まあそれくらいさっさとやっとけよみたいなね.はい.

おわりに

もう少し短いスパンで定期的に触っていきたいきたいところです.

テキストデータの実体はそのままで型だけ変えたいとかなんとか

お前今更何言ってんだ的な内容です…

何がやりたいかというと

メモリ上に,例えば以下のようなchar型配列に格納されたデータがあるときを考えます。このデータは実際にはchar配列に格納されたUTF-16の文字列「hoge」です。

char:    |__|__|__|__|__|__|__|__|
          68 00 6F 00 67 00 65 00

これをデータそのものは変更ぜずにUTF-16の文字列として扱えるようにしたい.こんなイメージ.

char   : |__|__|__|__|__|__|__|__|
          68 00 6F 00 67 00 65 00
wchar_t: |_____|_____|_____|_____|

そこで

char配列と同じバイト数のwchar_t配列を作ってmemcpyでメモリ単位でコピーする方向でやってみました. コードは以下.

// 事前にchar[]のバイト数 source_byte_size を求めておき
// wchar_t[]に必要な要素数を割り出す。
int wchar_t_element_count = source_byte_size / sizeof(wchar_t);

// wchar_t[]の要素を確保する。確保されるメモリは source_byte_size と等しくなるはず。
wcahr_t* dest_buffer = new wchar_t[wchar_t_element_count];
SecureZeroMemory(dest_buffer, source_byte_size);

// source_byte_size ぶんコピーする
memcpy_s(
    dest_buffer,
    source_byte_size,
    source,
    source_byte_size
);

おわりに

これってどういうやり方が賢いんですかね?

ListBoxのItemTemplateに設定したDataTemplateにRelativeSourceのメソッドなりコマンドなりをバインドしよう

メソッド(CallMethodAction)の場合

まずはListBoxの項目がダブルクリックされたらViewModelのCommand(or メソッド)を呼ぶ - かずきのBlog@hatenaのサンプルを.確実に動くコードで基本を整理.

エッセンシャル WPF:Windows Presentation Foundation (Programmer’s SLECTION―Microsoft .net Development Series)

エッセンシャル WPF:Windows Presentation Foundation (Programmer’s SLECTION―Microsoft .net Development Series)

ListBoxということで,まずは中に表示するためのクラスを定義.丸パクリで申し訳無さを感じつつもPersonクラスで.

// 名前だけを持つPersonクラス
public class Person
{
    public string Name { get; set; }
}

で,それを扱うViewModelを作成.ItemsSourceにバインドするためのコレクションはObsevableCollectionクラスを使用しています.

public class MainWindowViewModel
{
    // バインディングソースにするコレクション
    public ObservableCollection<Person> People { get; private set; }

    public Person SelectedPerson { get; set; }

    public MainWindowViewModel()
    {
        // ざっくりと初期化.25個のPersonを用意しておく.
        this.People = new ObservableCollection<Person>(
            Enumerable.Range(1, 25).Select(i => new Person { Name = "Hidari" + i })
        );
    }

    // 動作の確認がしたいだけなのでMessageBoxを表示するだけの実装
    public void Execute()
    {
        if (this.SelectedPerson == null)
        {
            MessageBox.Show("ぬるですやん…");
            return;
        }

        MessageBox.Show(this.SelectedPerson.Name);
    }
}

で,View(XAML)側は以下.わかりやすくするために全体を示しますね.

と,その前に1点だけ注意. xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" を宣言してることからも分かる通り,事前に以下のアセンブリの参照をプロジェクトに追加しておくのを忘れずに.

<Window x:Class="WpfApplication4.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
        xmlns:vm="clr-namespace:WpfApplication4"
        Title="MainWindow"
        SizeToContent="WidthAndHeight">
    <Window.DataContext>
        <vm:MainWindowViewModel/>
    </Window.DataContext>
    <Grid>
        <ListBox ItemsSource="{Binding Path=People}"
                 SelectedItem="{Binding Path=SelectedPerson}"
                 HorizontalContentAlignment="Stretch">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <ContentControl Content="{Binding Path=Name}">
                        <i:Interaction.Triggers>
                            <i:EventTrigger EventName="MouseDoubleClick">
                                <ei:CallMethodAction MethodName="Execute" TargetObject="{Binding Path=DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Mode=OneWay}"/>
                            </i:EventTrigger>
                        </i:Interaction.Triggers>
                    </ContentControl>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>

上記のコードでは,CallMethodActionにDataContextに設定したMainWindowViewModelで定義したExecuteメソッドをバインドしている.CallMethodActionの部分は以下の通り.

  • TargetObjectでは先祖要素を遡り一番初めに出てきたListBoxのDataContextを指定.これによってTargetObjectにMainWindowViewModelが設定される.
  • MethodNameでTargetObject(MainWindowViewModel)で定義されたExecuteメソッドを指定する.

ここで例えばRelativeSourceをListBoxItemに設定した場合,そのDataContextに設定されるのはPersonクラスであり,Personクラスに定義されたExecuteメソッドが呼ばれることになる.この場合の該当部分のコードは以下のようになる.

まずこちらがExecuteメソッドを定義した状態のPersonクラス

public class Person
{
    public string Name { get; set; }

    public void Execute()
    {
        MessageBox.Show("This is a Person class.");
    }
}

で,こっちがView側の修正した部分.

<i:EventTrigger EventName="MouseDoubleClick">
    <ei:CallMethodAction MethodName="Execute" TargetObject="{Binding Path=DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}, Mode=OneWay}"/>
</i:EventTrigger>

コマンド(InvokeCommandAction)の場合

メソッドを使った上記のサンプルをコマンドを使うパターンに書き換えます(Personクラスは一番最初のサンプルから変わっていないので省略).

まずはViewModel.

こちらでは事前に簡単なRelayCommandクラスを用意して使っています.実装はMVVM入門 その1「シンプル四則演算アプリケーションの作成」 言語: C#, XAML Visual Studio 2010 用のものを使っていると考えていただければほぼ間違いないかと. 他は極力使い回しする方向で.

public class MainWindowViewModel
{
    public ObservableCollection<Person> People { get; private set; }

    public Person SelectedPerson { get; set; }

    public MainWindowViewModel()
    {
        this.People = new ObservableCollection<Person>(
            Enumerable.Range(1, 25).Select(i => new Person { Name = "Hidari" + i })
        );
    }

   #region Command
    private RelayCommand _HogeCommand;

    public RelayCommand HogeCommand
    {
        get
        {
            if (this._HogeCommand == null)
            {
                _HogeCommand = new RelayCommand(Execute, CanHoge);
            }

            return _HogeCommand;
        }
    }
   #endregion

    // ラムダ式にしたい気持ちもありました
    private bool CanHoge()
    {
        return true;
    }

    // 使い回し
    public void Execute()
    {
        if (this.SelectedPerson == null)
        {
            MessageBox.Show("ぬるですやん…");
            return;
        }

        MessageBox.Show(this.SelectedPerson.Name);
    }
}

で,次にView.変更したのはEventTriggerの中だけ.

<ListBox.ItemTemplate>
    <DataTemplate>
        <ContentControl Content="{Binding Path=Name}">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="MouseDoubleClick">
                    <i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}},Path=DataContext.HogeCommand}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </ContentControl>
    </DataTemplate>
</ListBox.ItemTemplate>

ここでの肝は,CallMethodActionではメソッド名をMethodNameで指定してたところを,CommandのPathで DataContext.HogeCommand と指定すること.Pathの始めはDataContextとなるから混乱しやすい.注意していきたい.

おわりに

初歩の初歩で躓いてたので書いてみました. 間違いなどありましたら,教えていただけると幸いです.

WPFのRelativeSourceのはなし

WPFバインディングソースを指定するのに使うRelativeSource.その中で使う「関係」についてよくわかってなかったので,@Posauneさんとこの【WPF】RelativeSource - 亀岡的プログラマ日記で勉強したのを整理がてら書いてみました.

Pro Wpf 4.5 in C#: Windows Presentation Foundation in .net 4.5 (Professional Apress)

Pro Wpf 4.5 in C#: Windows Presentation Foundation in .net 4.5 (Professional Apress)

「関係」は以下の4つが指定できるけど,今回はその中でもSelfFindAncestorをコードを書きながら.

  • Self
  • FindAncestor
  • PreviousData
  • TemplateParent

Self

Selfは自分自身をソースとして使いたいときに使う.そのクラス自身が持つプロパティをバインドできる.

<Grid Background="Coral">
    <StackPanel Margin="20"
                Background="LightGreen">
        <TextBlock Text="{Binding RelativeSource={RelativeSource Self}, Path=HorizontalAlignment}" 
                   HorizontalAlignment="Center" 
                   Background="Bisque" 
                   Margin="10"
                   Padding="15"/>
    </StackPanel>
</Grid>

上記の場合,Textプロパティには自身(つまりTextBlock)のHorizontalAlignmentプロパティをバインドしているため,その値である「Center」が表示される.

f:id:hidari-yori:20140330114459j:plain

他に,例えば,このようにSelfを指定した状態で Path=Padding にするとこんな感じになります.

f:id:hidari-yori:20140330114525j:plain

FindAncestor

FindAncestorは要素をさかのぼって先祖要素をソースとして指定することができる. このFindAncestorには以下の2つのオプション?を指定することができる.

  • AncestorType
    • 参照したい要素の型を指定する.
  • AncestorLevel
    • 先祖要素を遡ったとき,何番目に見つかった要素を使うかを指定する.

オプションとは書いたけど,AncestorTypeは必ず指定する必要がある模様. 一方で,FindAncestorそれ自体は省略可能だったりする.そのせいか,FindAncestorってインテリセンスで出てこないんですよね…まあVS2013使ってると,省略した時に警告を示す下線が出てくることがあって鬱陶しいから書いたらいいんじゃないでしょうか.

<Grid>
    <StackPanel Background="Coral">
        <ContentControl Margin="10" 
                        Background="DarkOrchid">
            <Border BorderBrush="Aquamarine" 
                    BorderThickness="5" 
                    Background="Indigo" 
                    Margin="10">
                <StackPanel Margin="15">
                    <TextBlock HorizontalAlignment="Center"
                               Background="PaleGreen"
                               Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StackPanel}}, Path=Margin}"/>
                </StackPanel>
            </Border>
        </ContentControl>
    </StackPanel>
</Grid>

上記の例のようにAncestorLevelを指定しなかった場合には,デフォルトでAncestorLevel = 1が適用される.

f:id:hidari-yori:20140330115258j:plain

で,以下のコードではAncestorLevelを明示的に2を示した場合を示している.

<Grid>
    <StackPanel Background="Coral" Margin="1">
        <ContentControl Margin="10" 
                        Background="DarkOrchid">
            <Border BorderBrush="Aquamarine" 
                    BorderThickness="5" 
                    Background="Indigo" 
                    Margin="10">
                <StackPanel Margin="15">
                    <TextBlock HorizontalAlignment="Center"
                               Background="PaleGreen"
                               Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StackPanel}, AncestorLevel=2}, Path=Margin}"/>
                </StackPanel>
            </Border>
        </ContentControl>
    </StackPanel>
</Grid>

TextBlockには先祖要素を遡って行き,2番目に現れたStackPanelのMarginがバインドされていることが確認できる.

f:id:hidari-yori:20140330115407j:plain

おわりに

細かい仕様なんかは未だに理解できたとは言えないけど,ある程度の動きはわかってきたように思います.残りの「関係」についてはまたそのうち……汗

あ,何か間違いありましたらご一報頂けると幸いです.