t-hom’s diary

主にVBAネタを扱っているブログ…とも言えなくなってきたこの頃。

VBA フローチャート作成ツール(BreadChart)の更新 Version 1.2

GitHubで公開しているフローチャート作成ツールをアップデートしたので変更点のご案内。

入手場所

こちらからアクセスしていただき、
github.com

以下のDownloadボタンで入手できます。
f:id:t-hom:20190309220543p:plain

更新内容

Configシートでシェイプの色を指定できるように変更

Process・Judge・Connectorの色をそれぞれ指定できます。
ただしそんなに頻繁に変えるものではないので、GUIは実装してません。気が向いたら実装します。
f:id:t-hom:20190309222921p:plain

色は0~16777215までのLong値で指定します。
色から値を調べる方法はいくつかあります。
たとえばセルに色を塗り、そのセルを選択した状態でイミディエイトウインドウで「?selection.interior.color」を実行すると良いでしょう。
色名(XlRgbColor定数名)が分かっていれば、イミディエイトウインドウですぐに値を調べられます。例「?rgbWhiteSmoke」

コマンドボタンをリボンUIに変更

実はver 1.1としてしれっと変更してましたがTwitterでしか言ってなかったので改めて紹介。

Before

f:id:t-hom:20190309221226p:plain

After

f:id:t-hom:20190309221249p:plain

「チャート完成」を「チャート発行」に変更し、別ファイル出力に変更。

実はver 1.1としてしれっと変更してましたがTwitterでしか言ってなかったので改めて紹介。
といっても内容は見出しの通りなので特に書くことなし。

以前の更新

前回の更新

thom.hateblo.jp

忙しすぎる人に確実に読んでもらう為のメールの送り方について考察

今回は忙しすぎる人に確実にメールを読んでもらうためのテクニックについて自分の考察を紹介する。

ここでいう忙しすぎる人とは、「忙しすぎて諦めの境地にいる人」のことだ。諦めの境地なので、基本的に人の頼み事なんて知らんぷり。電話しても日中に繋がることは稀なので、基本コミュニケーションはメールになる。

にもかかわらず、メールはメールで返事が来ないのだ。

しつこく電話をかけ続けてやっと捕まえると、だいたい以下のような反応。

「あれ?そんなメール来てたかな?てへぺろ(・ω<)」
「あれ?何かしないといけないんでしたっけ?てへぺろ(・ω<)」
「あ、私関係ないと思ってました。てへぺろ(・ω<)」

彼らは、「本当に重要なことは、伝える側に責任がある」と考えている。だから、基本的によく読まないと分からないような案件は完全無視を決め込む。さもなくば自分が過労で倒れてしまうからだ。

大事なことなのでもういちどいう。
さもなくば自分が過労で倒れてしまうからだ。


。。。とはいえ、絶対に無視してもらっては困る情報もあるわけで。

今回はなぜ重要なメールが無視されてしまうのかを明らかにし、無視できなくなるメールの送り方について考察してみたい。

忙しすぎる人の情報フィルタリングフロー

メールに限らず、忙しすぎる人は情報を瞬時にフィルタリングする。なんのためかって?なるべく多くの情報を無視するためだ。

送り手は、自分が送った情報がこのように処理されることを想定している。
f:id:t-hom:20190306214613p:plain

しかし、私の経験上、忙しすぎる人のフローは以下のようになっている。
f:id:t-hom:20190306211905p:plain

どうしても読んでもらう必要がある場合

どうしても読んでもらわないと困るメールの場合、忙しすぎる人の情報フィルタリングフローを踏まえて、特に強調すべきメッセージが4つある。

それは、
「アクションが必要です!」
「あなたが対象者です!」
「デッドラインは〇日〇時です!」
「無視すると後で面倒なことになります!」

ということ。

この4点を示唆する文は、冒頭の数行で瞬時に把握できるようにしたい。

簡単なことなんだけど、送り手はつい「やってほしいこと」を強調してしまう。
極端な話、「重要さ」さえ伝われば、後は強調するまでもなく隅々まで読んでもらえるので、そんなところは強調しなくて良い。

アクションが必要であることを示す方法

私の場合、アクションが必要であることは、件名のタグで明示するケースが多い。
たとえば、【要返信】・【要対応】など。

多くの人が、「で、結局何かするのこれ?」っていう、ぼやっとしてよく分からないメールを受け取った経験があると思う。
件名に要対応とあれば、「とりあえず、何かアクションを求めているな」ということが分かるので、メールを読む優先度が上がる。

あなたが対象者ですと示す方法

相手が少数なら宛名で十分だけど、複数名に送る場合、「このメールは〇〇で××な方宛にお送りしています。」と冒頭に一文入れ、そのあと続けて「宛先各位」という宛名にしてることが多い。

なぜ自分に送られたのか分からないメールは、関係ないと判断される可能性が高いからだ。

デッドラインを示す方法

普通に日付を書く場合もあるけれど、基本的に社内ではHTMLメールなので、全体を赤字かつ数字をサイズアップして強調ことが多い。
201936日 (水) 17:00まで

このとき気を付けたいのが、日付と曜日のアンマッチあるあるだ。
特に、日付固定で何かをお願いする場合、絶対に外してはならない。真面目に確認してくれる人ばかりならまだいいんだけど、忙しい人は「やらない言い訳」を渇望しているので、誤解や勘違いを装った確信犯テクで依頼をかわされる危険がある。

また、時刻は基本的に入れる。なぜなら、時刻を入れることで、「何か根拠があってその時間になっているのでは?→バッファなしの本物のデッドライン」感が出るから。(ぶっちゃけてしまうと、もちろんバッファは積んでるので17時をデッドラインに設定する根拠はどこにもなかったりする。あー、言っちゃった。)

定時とかにせずに中途半端な時間にしておくと、より一層、何か事情がある感が増す。

無視すると後で面倒である旨を示す方法

これはケースバイケースだけど、基本的に面倒くささを想起させる文節を単体で強調する。

こんな感じ。
もし、今回の期日までにご対応いただけない場合、後程ご自身で〇〇していただく必要がございます。」

まとめ

今回は忙しすぎる人を対象にどうすれば読んでもらえるのかを考察した。
でもこれは相手がそれほど忙しくなくても使えるテクニックだと思う。基本的に、だらだら書かれたメールをきちんと読むのって面倒くさい。「無視して良いか」を速やかに判定できるメールは誰にとっても好ましいことである。

私のやり方が参考になるかどうかは分からないけど、各自工夫されると良いと思う。

VBA 狙った範囲だけをAutoFitするマクロ

以下のような表を作ったとき、普通にオートフィットをかけるとA1セルに入力されたタイトルの幅もカウントされてしまい、幅がおかしくなる。
f:id:t-hom:20190306001403p:plain

↓AutoFit後
f:id:t-hom:20190306001709p:plain

今回はこれを何とかするマクロ。

元ネタはこちら。


では、早速コードを紹介しよう。

ただし、注意点として、このマクロはシート上のデータをすべて変数に退避させて一旦消す処理を含むので、マクロがコケた場合は対象データ全消失もありえる。試す場合はくれぐれもファイルを保存してからどうぞ。
念のため言ってるだけなので、実際そんなことにはならないと思うけど。

Sub SmartFit()
    Const MAX_COLUMN_WIDTH = 80
    If TypeName(ActiveSheet) <> "Worksheet" Then
        MsgBox "このマクロはワークシート上で実行してください。", vbExclamation
        Exit Sub
    End If
    
    Dim sh As Worksheet: Set sh = ActiveSheet
    
    Dim targetArea As Range: Set targetArea = Selection.CurrentRegion
    targetArea.Select
    If vbYes <> MsgBox("選択エリアに対してSmartFitを適用しますか?", vbQuestion + vbYesNo, "確認") Then
        MsgBox "キャンセルしました。", vbInformation
        Exit Sub
    End If
    
    
    With sh
        Dim wholeArea As Range: Set wholeArea = .Range(.Cells(1, 1), .Cells.SpecialCells(xlCellTypeLastCell))
        Dim wholeBackup: wholeBackup = wholeArea.Formula
        Dim targetBackup: targetBackup = targetArea.Formula
        
        wholeArea.ClearContents
        targetArea.Formula = targetBackup
        
        Dim zoomLevel As Long
        zoomLevel = ActiveWindow.Zoom
        ActiveWindow.Zoom = 100
        With targetArea
            .EntireColumn.ColumnWidth = MAX_COLUMN_WIDTH
            .EntireRow.AutoFit
            .EntireColumn.AutoFit
        End With
        ActiveWindow.Zoom = zoomLevel
        
        wholeArea.Formula = wholeBackup
    End With
    
    MsgBox "実行しました。", vbInformation
End Sub

使い方

対象の表内の特定セルを選択した状態で実行する。
f:id:t-hom:20190306002236p:plain

※シート内ならどこでも良いわけではなく、実際にオートフィットさせたい表内の任意セルを選ぶこと。例えばタイトルを書いたA1セルを選んで実行すると上手く行かない。CurrentRegionで範囲を決定しているため、表とそれ以外の要素は空行・空列で区切られていなければならない。

実行すると表全体が選択され、この範囲で実行して良いかの確認メッセージが表示される。
f:id:t-hom:20190306002414p:plain

「はい」を選ぶと対象範囲のみを基準にAutoFitされる。

解説

※画像のサイズが不ぞろいなのはご愛敬。。
以下の画像を見ていただけると、大体何をやってるか分かると思う。

f:id:t-hom:20190306003344p:plain

f:id:t-hom:20190306003454p:plain

f:id:t-hom:20190306003802p:plain

f:id:t-hom:20190306003917p:plain

以上

VBA そのマクロ集約、ちょっとまった!

たとえば、とあるデータを次のようなステップで加工しているとする。

f:id:t-hom:20190305073629p:plain

それぞれのマクロはボタンを押すだけで完了する。
だったら、わざわざ3つもボタンを押させる意味はあるのか。
普通はそう考える。

そしてボタンを3つ押す作業を一つのボタンに集約する。

f:id:t-hom:20190305073838p:plain

これは本当に良いことなんだろうか?

もちろん、ケースバイケースだとは思うが、私はマクロを作るときに、最悪マクロが動かなくなっても、データの変換が人手でも行えることが重要だと考えている。
中間データをあえて見せることで、加工の過程が明らかになるのであれば、その方が良い。
ボタンを集約してしまうと内部の加工ステップがブラックボックス化し、入力データから出力データへの変換をどうやるのかという手順が想像しづらくなる。

この仕事は何ですか?→ボタンを押すことです。こうなったらおしまいだ。
人がきちんと仕事の意味を考えて仕事を遂行できるようにしておくこと。マクロはあくまで繰り返し行われる単調で面倒くさい作業の緩和や、人が行う作業のミス防止などのサポートに徹するべきだと思う。

マクロを集約したくなったら、ブラックボックス化しないか、作業者がきちんと何をしているか意識して作業できるかを考慮して慎重に行うようにしたい

参考(手前味噌)

thom.hateblo.jp

thom.hateblo.jp

マクロ作成者が覚えておきたい重要な概念「冪等性(べきとうせい)」について

冪等性(べきとうせい)とは、ある操作を1回行っても複数回行っても結果が同じであることをいう概念である。
言葉の響きは難しいが、これはプログラマーなら何度も遭遇するシチュエーションなので、是非とも覚えておきたい。

たとえばExcelマクロにおいて、以下のコードは冪等ではない。
実行するたびに、オートフィルターのON / OFFが切り替わってしまうからだ。

Sub NotIdempotent()
    Selection.AutoFilter
End Sub

先日業務であるツールを使用する様子を見たが、そのツールで使われていたオートフィルターを設定するコードは冪等ではなかった。
すると、加工するブックは予めオートフィルターを外しておかなければならない。

冪等性に注意を払ってこのコードを作り直すと、以下のようになる。

Sub Idempotent()
    If Not Selection.Parent.AutoFilterMode Then
        Selection.AutoFilter
    End If
End Sub

選択範囲の親(ワークシート)を確認し、フィルターがかかっていたらそれ以上は実行しないという処理である。
ただこの処理でもまだ詰めが甘い。なぜなら、全く意図しない場所にフィルターが掛かっていたとしても、処理済と見做してしまうからだ。

改良したのがこちらのコード。まず狙った位置にオートフィルターをかけ、もし掛かっていなければ外れたと判断してやり直す。

Sub IdempotentFixed()
    Do
        Selection.AutoFilter
    Loop Until Selection.Parent.AutoFilterMode
End Sub

あるいは、最初からフィルターを解除してしまい、それからフィルターを掛ける。

Sub IdempotentFixed2()
    Selection.Parent.AutoFilterMode = False
    Selection.AutoFilter
End Sub

前者はバグにより無限ループを作らないとも限らないので、最初からフィルターを外す後者の方が良いかもしれない。

いずれのコードも冪等性は確保されている。
この冪等性という概念を知っていると、自分のマクロが冪等性を担保しているかという批判的な眼を養うことができ、より安定したコード作成ができるようになると思う。

注意:今回は簡単に紹介する目的でSelectionを使用していますが、実際に組み込むマクロではRangeやWorksheetを特定して使っています。

VBA 私のコーディング風景垂れ流し動画紹介

先日Twitterでは公開したんだけど、ツイッターは鍵垢なのでこちらでも公開。

20分くらいあるので倍速再生でも良いかも。

以下に最初に動画内でテキスト入力してる内容を転記したので、1:30まで飛ばしてそこから見ていただけたら良いかと思う。

みなさんこんにちは。
この動画は、私が普段マクロを作成している様子をなるべくそのまま撮影したものです。
趣旨は、「慣れるとこれくらいのスピードで作れるよ」というのを実感してもらうことです。
ノーカットでお送りしますので、固まったように見えたら考え中です。ではつくっていきます。

なんちゃって個人情報から、個別のファイルに転記するマクロです。

※動画データを少しでも軽くするために、画面の解像度をわざと落として録画してるので、その影響で画面配置やイミディエイトウインドウの移動に苦労している。お見苦しいところを申し訳ない。
www.youtube.com

視聴にあたってのオススメポイントは、メタプログラミング。
メタプログラミングってのは、雑に言えば、コードを書くこと自体をプログラミングで行うテクニック。

以下の記事の末尾にメタプログラミングに関する記事のリンクをまとめている。
thom.hateblo.jp

特に、クラスのフィールドを準備するのは結構面倒なので、メタプログラミングを頻繁に使用する。

以上

VBA イミディエイトウインドウを使って簡単にカレンダーを作る方法

Excelでは色んな方法でカレンダーを作ることができるが、割と面倒くさい。

今回はイミディエイトウインドウを使って、以下のようなカレンダーをサクっと作る方法を紹介。
f:id:t-hom:20190301021717p:plain

まず、曜日を描きたい場所を範囲選択しておく。
f:id:t-hom:20190301022034p:plain

そしてイミディエイトウインドウで次のコードを順に実行する。

n = 1
for each r in selection:r.value = format(n,"aaa"):n=n+1:next

これで曜日の出来上がり。
f:id:t-hom:20190301022934p:plain

次に、下図のように選択する。(選択を開始する位置は、その月の1日の曜日)
f:id:t-hom:20190301023151p:plain

やり方は、Ctrlキーは選択がすべて終わるまで押しっぱなしで、各行をマウスドラッグすればOK。
このときに選択する順は必ず上からになるように注意。
f:id:t-hom:20190301023935p:plain

そして先ほどイミディエイトウインドウに書いたコードを少しだけ修正する。

n = 1
for each r in selection:r.value = n:n=n+1:next

n = 1にカーソルを合わせてEnterで実行し、次の行もEnterで実行すると、ほぼ完成。
f:id:t-hom:20190301024326p:plain

最後に、余分な日付を手動削除しておしまい。

解説

イミディエイトウインドウでは変数宣言こそできないものの、変数への代入は普通にできる。宣言も特に必要ない。
別のプロシージャが実行されるか、明示的にリセットボタン(下図)が押されるまでは有効なので、イミディエイトウインドウでコマンドを入れ続ける間は変数が保持される。
f:id:t-hom:20190301022638p:plain

また、VBAのFormat関数では、format(日付, "aaa")で曜日を取り出すことができる。VBAにおける日付型はDouble型で表され、その整数部が日付、小数部が時刻である。

数値1は1899年12月31日(日)なので、Format(1, "aaa")は日曜日を返す。

?cdate(1)
1899/12/31 
?format(1,"aaa")
日

イミディエイトウインドウでは、一度実行したコードでもカーソルを合わせると編集でき、Enterで再実行できる。実行時はカーソルが行内のどこにあっても構わない。Enterは改行ではなくコード実行になるので注意。改行したい場合はCtrl + Enter。
(マルチラインコードの実行ができるわけではないので改行という表現は微妙。改ステートメントと言ったほうが実態に近い。そんな言葉ないけど。)

For EachにSelectionを渡した場合、セルの選択順に実行される。
セル範囲の場合は1ドラッグを1範囲とみなし、選択した範囲順になる。ただし範囲内の順は左上から右下へ向かう。

分かりやすくテストしたのが以下。
f:id:t-hom:20190301025910p:plain

今これを書きながら気づいたんだけど、以下のように選択しても同じ結果になる。
f:id:t-hom:20190301030255p:plain

こっちの方が楽だ。

以上

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。