ゆるおたノート

Tomorrow is another day.

【Google Apps Script】クラスの練習をしていたらハマった話。

今日は簡単にメモします。

クラスの練習でメソッドを呼び出してみたら、何度やってもなぜかreturnしてほしい値ではなく「関数の中身」が出力されてしまうっていう時は…

メソッドの後ろに()をつけるのを忘れているかもしれません。

サンプル

今回は、Timerクラス*1の作成に挑戦しました。

クラス

/**
 * 実行時間計測用のクラス
 * ※あらかじめ使用するプロジェクトにMoment.jsライブラリを導入しておくこと!
 * プロジェクトキー:MHMchiX6c1bwSqGM1PZiW_PxhMjh3Sh48
 *
 * @param {sheet} ログに使用するシート
 */
var Timer = function(logSheet) {
  /* 作成中 */
  this.logSheet = logSheet;
}

Timer.prototype = {
  start : function() {
    return new Date;
  },

  finish : function() {
    return new Date;
  }
};

メイン

処理時間を見たかったのですが、思ったとおりに計算ができていません。

function testTimer() {
  var Timer = new Timer();

  var start = Timer.start;
  Logger.log('\n' + 'start time: ' + start);
  /*
    start time: 
    function () {
        return new Date();
    }
   */
  // (値が欲しいのに関数の中身が出てるな…???)
  
  var finish = Timer.finish;
  Logger.log('\n' + 'finish time: ' + finish);
  /*
    finish time: 
    function () {
        return new Date();
    }
  */
  // (値が欲しいんだってば…)

  var time   = finish - start;
  Logger.log('\n' + 'Process time: ' + time); // 
  /* Process time: NaN */
  // (#NaNってなんなん?)
}

そもそも計算できない値…?

そんなわけない。

function getTime() {
  var directStart  = new Date;
  Logger.log('\n' + 'directStart time: ' + directStart);
  // directStart time: Tue May 21 2019 01:34:46 GMT+0900 (JST)
  // (こっち↑は値を返してくれてる。)

  var directFinish = new Date;
  Logger.log('\n' + 'directFinish time: ' + directFinish);
  // directFinish time: Tue May 21 2019 01:34:46 GMT+0900 (JST)
  // (これ↑もOK。)

  Logger.log((directStart - directFinish).toFixed(3));
  // 3.000
  // (計算もできてる。)
}

こちらは問題なさそう。

じゃあMomentは…?

momentオブジェクト同士では計算できないようです*2…うーむ…

function getMoment() {
  var start = new Date;
  Logger.log(Moment.moment(start).format('YYYY/MM/DD hh:mm:ss.SSS'));
  // 2019/05/21 02:01:35.304
  // (ok.)
  
  var finish = new Date;
  Logger.log(Moment.moment(finish).format('YYYY/MM/DD hh:mm:ss.SSS'));
  // 2019/05/21 02:01:35.307
  // (ok.)

  var time = finish - start;
  Logger.log(time.toFixed(3));
  // 3.000
  // (あ、これ↑は変換前どうしだから「ミリ秒」か…)

  var timeMoment = Moment.moment(time).format('YYYY/MM/DD hh:mm:ss.SSS');
  Logger.log(timeMoment);
  // 1970/01/01 09:00:00.003
  // (ん???これ↑はシリアル値かな…???)

  // これならどうだ!
  var startMoment = Moment.moment(start).format('YYYY/MM/DD hh:mm:ss.SSS');
  var finishMoment = Moment.moment(finish).format('YYYY/MM/DD hh:mm:ss.SSS');
  Logger.log(finishMoment - startMoment);
  // NaN
  // (だから#NaNって(略)
}

ちなみに、(大きい処理はあまりしませんが)ほかにも私が何回か試した限りでは、処理時間はMomentライブラリ経由でもミリ秒単位で違うくらいのようですね。これなら安心して使える…

そういえば型はどうなってる…?

typeof演算子*3で型を出力してみます。

function getType() {
  var time = new Date;
  Logger.log(typeof time); // number
}

new Dateしただけならnumber型じゃないですか…
それなら計算できるでしょ…???

じゃあMoment使うとどうなる…?

Logger.log(typeof Moment.moment(time)); // object
Logger.log(typeof Moment.moment(time).format('YYYY/MM/DD hh:mm:ss.SSS')); // string

あれ、momentオブジェクトは変換前後で型が変わるんですね!?
しかも変換後はnumber型じゃなくてstring型!?

最終的にMomentライブラリで計算して出力するつもりだった…あっぶなー。

じゃあ問題はどこ???

型は問題ないとして…

momentオブジェクトのことは一旦置いといて、new Dateしてもnumber型のままなら計算できるはずなんだけどなぁ…

じーっ…(元のコードを眺める)

function testTimer() {
  var Timer = new Timer();

  var start  = Timer.start; // ← んんん!?

  /* ~以下略~ */

あれ、なにか忘れてる…!?

あれ???

startメソッドに()が抜けていました。結局たいぽ…

var start  = Timer.start(); // ← これ

本日の答え。

というわけで、正しいTimerクラスの使い方はこんな感じです。

function testTimer() {
  var Timer2 = new Timer;

  var start  = Timer2.start(); // ← '()'を追加
  Logger.log('\n' + 'start time: ' + start);
  // start time: Tue May 21 2019 02:31:41 GMT+0900 (JST)
  
  var finish = Timer2.finish(); // ← '()'を追加
  Logger.log('\n' + 'finish time: ' + finish);
  // finish time: Tue May 21 2019 02:31:41 GMT+0900 (JST)  

  var time   = finish - start;
  Logger.log('\n' + 'Process time: ' + time.toFixed(3));
  // Process time: 2.000
}

これで、ひとまず時間の計測は出来るようになりました。

めでたしめでたし。

参照

*1: こちらを参考にさせていただきました。 tonari-it.com

*2: 別途メソッドを使用するようです。これをクラスに実装しよう。
tonari-it.com

*3: 詳しくは、以下(外部サイト)をご参照ください。
qiita.com
developer.mozilla.org
オペランド (operand)とは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

【HTML×CSS】<kbd>タグをいじってみた

ブログの書き方について調べていたら、<kbd>タグというものを知りました。

普段は基本Markdownで書いていて、足りない部分をHTMLタグで補うようしている*1のですが、 コードブロックとボタンが書き分けられないなーとずっと疑問に思っていたのです。

こんな便利なタグがあったのですね…

早速デザインCSSを修正したので、それを記しておきたいと思います。

タグの使い分け

コードブロック

また、コードブロックは、HTMLの場合は<code></code>で挟みます。

<code>文中のコード</code>

Markdownなら、`*2で。

`文中のコード`

ボタン

ボタンは<kbd></kbd>で挟みます。

<kbd>ボタン</kbd>

Markdownには、残念ながらこれに相当する記法がありません…

<kbd>タグの使い方

上では<kbd>で挟むと書きましたが、正しくは二重に挟んで使うのだそうです。

【例】Ctrl+Shift+Delete

<kbd><kbd class="xxxx">Ctrl</kbd>+<kbd class="xxxx">Shift</kbd>+<kbd class="xxxx">Delete</kbd></kbd>

ボタンを1つずつ生成して、さらに全体を囲むイメージですかね。

<kbd>タグで遊ぶ。

少し検索した中で、2つほど試してみました。

【参考】本文のフォント
※こちらを基準にサイズを調整しています。

font-family Noto Sans CJK JP Medium
font-size 16px
line-height 180%

Mac

おしゃれなブログに使えそうです。

サンプル

空くのかな
空くのかな
どのくらい空くのかな

改行するとどのくらい空くのかな
押せないよ!→Alt+Tab←押せないよ!
改行するとどのくらい空くのかな

コード
.mac-key {
  /* ----Box Properties---- */
  display      : inline-block;
  padding      : 0.05em 0.6em;
  margin       : 0px 4px;
  background   : #fff;
  border-radius: 4px;
  box-shadow   : 0px 1px 3px 1px rgba(0, 0, 0, 0.5);
  
  /* ----Text Properties---- */
  font-family: Helvetica, serif;
  text-align : center;
  color      : #666;
}

エンボス加工風

文字部分が凹んで見えるので、より立体的です。

クリックするとボタンも凹むので、無限プチプチみたいで気持ちいいです。
何も起きないけど。笑

色はグレー系にしてみました。

サンプル

空くのかな
空くのかな
どのくらい空くのかな

改行するとどのくらい空くのかな
これは押せるよ!→Alt+Tab←これは押せるよ!
改行するとどのくらい空くのかな

コード
.btn-square-emboss {
  display        : inline-block;
  padding        : 0.05em 0.6em;
  margin         : 0px 4px;
  text-decoration: none;
  background     : #f0f0f0; /*ボタン色*/
  color          : #666666; /*ボタン色より暗く*/
  box-shadow     : 0px 1px 3px 1px rgba(0, 0, 0, 0.5);
  border-bottom  : solid 3px rgba(0, 0, 0, 0.3);
  border-radius  : 3px;
  font-size      : 90%;
  text-shadow    : 1px 1px 1px #ffffff;
}

.btn-square-emboss:active  {
  -webkit-transform: translateY(4px);
  transform        : translateY(4px);
  box-shadow       : 0px 1px 0px rgba(0, 0, 0, 0.3);
  border-bottom    : solid 3px rgba(0, 0, 0, 0.1);
}
注意

元のコードだと.btn-square-emboss:activeの方でborder-bottom: noneと設定されていて、Enterのように1行にボタンが1つだけだと、ボタンを押すたびにそれ以下の画面が揺れちゃうようです。

そのため、ボタン下側の影の幅はそのままに、若干明るくして揺れないように調整してみました。

ただ、「下にズレてる」感が否めないのですが…

文字列を選択できなくする

ちなみに、お気づきの方もいるかと思いますが、実は後者の方は文字列の選択(≒コピペ)が出来ないようにしてあります。

連打すると青い範囲が重なってしまうのが、個人的にしっくり来なくて。

コピペは出来ないけど、これでF5連打し放題です。笑

コード
  -webkit-user-select: none;
     -moz-user-select: none;
      -ms-user-select: none;
          user-select: none;

ついでに<code>タグも調整。

初期設定だとボタンコードブロックが文中に並んだときに後者が埋もれてしまうので、黒っぽくしてみました。色は調整中です。

フォントは過去記事の流用です。

コード

/*--------------------------------------
  <code>タグ(文中のコード)
--------------------------------------*/
.entry-content code {
  border-radius   : 3 px;
  font-size       : 90 %;
  font-family     : Consolas,
                    'Meiryo UI',
                    'メイリオ',
                    Meiryo,
                    '游ゴシック Medium',
                    '游ゴシック体',
                    'Yu Gothic Medium',
                    YuGothic,
                    'ヒラギノ角ゴ ProN',
                    'Hiragino Kaku Gothic ProN',
                    'MS Pゴシック',
                    'MS PGothic',
                    sans-serif;
  color           : #ffffff; /* 文字色 white */
  background-color: #666666; /* 背景 black */
}

後記

「ブログを書く時は見た目に時間かけちゃいけない」とよく聞くのですが、まさにその通りだと思います。

私のような入門者だと、1度始めてしまうととんでもなく時間がかかってしまうのですよね…
しかも、1ついじると他の部分もどんどん気になってしまうし…

同じような方のお役に立てたら幸いです。

参照

<kbd>タグについて

webmem.hatenablog.com

ボタンのCSSについて

coliss.com

saruwakakun.com

テキストの選択不可について

its-office.jp

注釈

*1: これで良いのだろうか…

*2: この記号をバッククォートと言います。なんとなく響きが好き。笑

【Excel VBA】テーブル変換とスタイル変更 ~ブックの保存先を選ぶ~

母校のバンドが予選をトップ通過したと聞いて、ここ数日浮足立っている私です。
VBAでテーブル化と好みのテーブルスタイル設定のマクロ化に取り組んでいます。

前回は、オリジナルのテーブルのスタイルを作成できるようになりました。
【Excel VBA】テーブル変換とスタイル変更 ~標準モジュールでスタイルを新規作成する~ - ゆるおたノート

今回はこれをマクロで保存できるようにしたいと思います。

進捗

挑戦!!と付いているモノが本記事のテーマです。


  1. Clear!!選択範囲をテーブル化
    • Clear!!指定範囲にテーブルがあると「実行時エラー」が発生するので、エラーが出てからOn Errorで「テーブルの解除」に進んだ方が良いかも。
  2. Clear!!しましまの無い、罫線・見出しだけのテーブルスタイルを作る
    • Clear!!テーブルスタイル関連のオブジェクト
    • Clear!!TableStyleオブジェクト
    • Clear!!TableStyleElementオブジェクト / TableStyleElementsコレクション
  3. Clear!!ブックの既定のスタイルに登録する
  4. 挑戦!!保存先をダイアログで指定
  5. クラスで共通処理を分離
  6. ユーザーフォームで操作簡略化
    • (もし出来れば)色は自由に選べるようにしたい

コード

Module1: ConvertIntoTable

メイン

コメント行を含めて、ブックを保存の2行と後処理の2行だけ増えました。
ひとまずこれで完成です。疲れた…

Public Sub Main()

    '念のため変数に入れておく
    Dim myRange As Range: Set myRange = Selection.CurrentRegion

    Dim myBook As Workbook:    Set myBook = ActiveWorkbook
    Dim mySheet As Worksheet: Set mySheet = myBook.ActiveSheet

    'エラー対策
    If hasListObjectOnSelection(myRange) = False Then Exit Sub

    Dim listObj As ListObject
    Set listObj = convertRangeIntoTable(mySheet, myRange)

    Set myRange = Nothing
    Set mySheet = Nothing

    '▼追加
    Call setTableStyle(myBook, listObj)

    'ブックを保存
    Call selectOverwriteOrNew(myBook)

    MsgBox "処理が完了しました。"

    '後処理
    Set listObj = Nothing
    Set myBook = Nothing

End Sub

Module3: saveWorkbook

ユーザーに保存方法を確認

また選択肢が増えました…流石にウザいかな。
でも、使う状況によって、上書きの場合と新規作成の場合とありますよね…?

Option Explicit
Option Private Module

Public Sub selectOverwriteOrNew(ByRef targetBook As Workbook)

    If MsgBox("このブックを新しいブックとして保存しますか?", vbYesNo) = vbNo Then

        If MsgBox("上書き保存しますか?", vbYesNo) = vbNo Then
            Exit Sub
        End If

        targetBook.Save

        Dim currentPath As String
        currentPath = ThisWorkbook.Path & "\" & ThisWorkbook.name

        MsgBox ("保存しました。" & vbCrLf _
              & currentPath)
        Exit Sub

    End If

    Dim newFilePath As String
    newFilePath = saveViaDialogBox(targetBook)
    MsgBox ("保存しました。" & vbCrLf _
          & newFilePath)

End Sub
新規作成する

今日の本丸。
ApplicationオブジェクトのGetSaveAsFilenameというメソッドを使って、ユーザーが保存先とファイル名を入力できるようにします。
(途中で長々書いているコメントは、GetSaveAsFilenameメソッドの使い方をメモしたものです。)

Private Function saveViaDialogBox(ByRef targetBook As Workbook) As String

    Dim newFileName As String
    Const MACRO_BOOK As String = "Excelマクロブック,*.xlsm"

    '保存先のフォルダを指定していない場合は、ダイアログには カレントフォルダ が表示される。
    '事前にChDirしたり、InitialFileNameをフルパスで指定したりも可。今回は カレントフォルダ のままで。

    ' @param {string} InitialFilename (省略可)既定値として表示するファイル名
    ' @param {string} FileFilter ファイルの候補(ファイルフィルター文字列)をカンマ区切りで
    ' @param {num} FilterIndex (省略可)FileFilterの既定のインデックス(1始まり)
    ' @param {string} Title (省略可)ダイアログボックスのタイトル
    ' @param {string} ButtonText  ※Macintosh専用
    '
    ' @return {string/boolean} ファイル名(かパス)。「キャンセル」を押すと"False"が返ってくる。

    newFileName = Application.GetSaveAsFilename(InitialFileName:=getInitialFileName(targetBook), _
                                                FileFilter:=MACRO_BOOK)

    targetBook.SaveAs Filename:=newFileName

    saveViaDialogBox = targetBook.Path & "\" & targetBook.name

End Function

これで、(新規作成の場合は)ダイアログボックスから保存先を選択出来るようになりました*1
ダイアログボックスの出現

既定値を設定

日付でファイル名を管理したいので、ファイル名の先頭に実行日を付与するようにしました。

Private Function getInitialFileName(ByRef targetBook As Workbook) As String

    Dim fileNameWithDate As String
    '当日の日付を付与しておく
    '【例】20190516_テーブルサンプル.xlsm
    fileNameWithDate = Format(Date, "yyyymmdd") & "_" & targetBook.name

    getInitialFileName = fileNameWithDate

End Function

本日はここまで。

補足(GetSaveAsFilenameメソッドの挙動)

キャンセルボタンを押した場合

ダイアログボックスキャンセルボタンを押すと、ファイル名がFalse.xlsmとして保存されました。
`False.xlsm`という名前で保存された…

これは、GetSaveAsFilenameメソッドの戻り値として「何も選択されてない」という意味でFalseが返ってきたからのようです。

…ということは、途中まで操作したところで間違いに気づいたりして「やっぱり保存は止めよう」となった時*2のために、分岐を作ると良いのかもしれません。

ファイル形式の制限

今回はマクロをExcelのブックに直接書いているので、「マクロ付きブック」の状態です。

このブックを、ダイアログボックスでファイル名を入力して「マクロ無しブック」として保存しようとすると、実行時エラーになりました。
実行時エラー:1004

実行時エラー'1004':

この拡張子は、選択したファイル形式には使用できません。[ファイル名]ボックスでファイル拡張子を変更するか、[ファイルの種類]ボックスで別のファイル形式を選択してください。

ブックのタイトルバーにも「保存に失敗」との表示が…
タイトルバーにも「保存に失敗しました」との表示。

つまり、今回のコードは「マクロ付きブック」で実行しているので、FileFilterという引数には実質「Excelマクロブック形式」しか選択できない状態のようです。

この点は手動で操作した場合と同じ仕様だと思いますが、処理を足さないと保存形式の選び直しはさせてくれないのですね。めんどくさいな…

FileFilterの指定

これを踏まえると、FileFilterの条件を増やす必要があるのは、取引先とかのバージョンに合わせて拡張子を変える時くらいでしょうか?

この場合のサンプルコードも書いてみました。先述の参照先のコードをお借りしました。

Const XLSX_BOOK As String = "Excelファイル,*.xlsx"
Const XLS_BOOK As String = "Excel2003以前,*.xls"

newFileName = Application.GetSaveAsFilename(InitialFileName:=getInitialFileName(targetBook), _
                                            FileFilter:=XLSX_BOOK & "," & XLS_BOOK)

カンマ区切りをさらにカンマ区切りするのが面白いですね。
ワークシートのCOUNTIFS関数みたいな。

後記

今回のコード自体はそんなに難しくなかったと思うのですが、引数の使い方がちょっと注意が要りそうだなと思いました。

あと、ファイルの操作ならFileSytemObjectなのかなと予想してたのに、違うみたいです(よく分かってない)。

入門者からの出口はまだまだ遠そうです…

ちなみに…

本日限定ですが、Twitterでアンケートを作ってみました*3
ご回答いただけたら嬉しいです。
<2020/10/21追記>
その後4名の方にご回答頂きました。ご協力ありがとうございました!

ネストの付け方の違いについて、アンケートしました。

無知のくせに思い込みしやすい性格なので、出来るだけ多くの方のご意見を聞いて知識をアップデートして行きたいと思います。

このシリーズについて

テーブルの変換とテーブルスタイルの新規作成をマクロ1発で使えるように考えています。主に自分向け。

もし間違いやヘンなところ等ありましたら、コメント欄やTwitterお問い合わせフォーム等でご指摘いただけたら嬉しいです。

次回

Coming Soon...

連載目次

  1. 【Excel VBA】テーブル変換とスタイル変更 ~一発で変換とスタイル変更を済ませたい~ - ゆるおたノート
  2. 【Excel VBA】テーブル変換とスタイル変更 ~処理の流れを整理してみる~ - ゆるおたノート
  3. 【Excel VBA】テーブル変換とスタイル変更 ~テーブル変換するところまで標準モジュールで書いてみる~ - ゆるおたノート
  4. 【Excel VBA】テーブル変換とスタイル変更 ~標準モジュールでスタイルを新規作成する~ - ゆるおたノート
  5. 当記事【Excel VBA】テーブル変換とスタイル変更 ~ブックの保存先を選ぶ~ - ゆるおたノート

注釈

*1:こちらを参考にさせていただきました。 名前を付けて保存ダイアログ(GetSaveAsFilename)|VBA入門

*2:「そこまで考える必要ある?」と思うかもしれませんが、私はよくあるのです…

*3:ツイート2件とも同じこと言ってたり、画像のメッセージボックスが足りてないのは見逃してください…

【Google Analytics × Google Apps Script】総合ランキングを別シートに転記して、毎日チェックしたい

GASを使ってGoogle Analyticsのデータを日々Slackに飛ばしています。
毎日自分のデータを見ることが出来て結構楽しいですね。
yuru-wota.hateblo.jp

だんだん欲が出てきたので、今度は「総合ランキング」シートを作ってみました。 Google Analyticsからインポートしてみた

ただ、これはこれで良いですが、下の方まで行くと見出しが見られないのです。
行や列を固定してみると、再Runの時にエラーになります。

「だったらGoogle Analyticsで見なさい」と言われそうですが、
ちょっと重いし…あんまり見た目いじれないし…ちょっと不便だなぁ…

あと、空いたセルにちょっとした関数を足そうと思っても
翌朝には初期化されます。(悲)

そんなわけで、せっかく作ったのに使えなくなってしまうのは困るので、
他のシートに転記することにして運用したいと思います。

今日のコードを使うと…

こんな感じになります。
「最低限、見られる」感じかなと思います。

進捗_20190516

完成イメージ

最終的にこんな感じを目指します。

値を調整したり、色を付けたり。

PVで言うと何故か「記事一覧」が1番多いのですが、リライト前の記事も
ぽつぽつとアクセスが続いてたりします。
嬉しいですが、「どげんかせんといかん」てやつですねぇ…

また、新しめの記事もちょいちょい上位に入っているのが最近のモチベーションです。
数字は小さくても結構嬉しいものですね。皆様いつもありがとうございます!

1. 準備

さて、実際の作業に入ります。
前回のつづきとして進めるので、必要に応じてご参照頂ければ幸いです。

Spreadsheet

1. Google Analyticsのレポートを追加

まずはReport Configurationシートにレポートを追加します。
今回の設定はこちら。

※この時点で何のこっちゃな方は、隣IT様(外部サイト)のこちらの記事をご参照ください。

Report Name Comp. Ranking*1
Start Date ブログ開始日
End Date =today()-1
Metrics ga:pageviews,
ga:avgTimeOnPage,
ga:users,
ga:newUsers,
ga:sessions,
ga:pageviewsPerSession,
ga:avgSessionDuration,
ga:bounceRate
Dimenstions ga:pageTitle,
ga:pagePath
Order -ga:pageviews,
-ga:avgTimeOnPage*2
2. スケジュールを設定

変更せず、そのままにします。

3. コンテナバインドスクリプトスクリプトファイルを追加

前回のプロジェクトにスクリプトファイルを追加します。
名前は「03_CompRanking」としました。

2. コード

1-1 メイン

GASに慣れていない方へザックリ流れをご説明すると、こんな感じです。

  1. レポートの値をまとめて取得
  2. 値を少し調整
  3. 前日分として別のSpreadsheetに転記
  4. シートの書式や表示を調整
function transcriptReport() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  
  // レポートを取得
  var dataSheet = ss.getSheetByName('Comp. Ranking');
  var dataRange = dataSheet.getDataRange();
  var rawData   = dataRange.getValues();

  const FIRST_ROW = 15; // 見出しから
  var lastRow     = dataRange.getLastRow();

  var transValues = editReportValues(rawData, FIRST_ROW, lastRow);
  var transSS     = SpreadsheetApp.openById('xxxxxxxx');
  
  var today = new Date();
  // 前日分としてシート名を設定
  var momentToday = Moment.moment(today).add(-1, 'day').format('YYYYMMDD')

  deleteTempSheetName(momentToday, transSS);
  
  transcriptToNewSheet(transSS, transValues, momentToday);
}

1-2 値を調整

後で使いやすいように、いろいろ調整します。

var editReportValues = function(values, FIRST_ROW, lastRow) {
  var firstIndex = FIRST_ROW - 1;
  var lastIndex  = lastRow - 1;
  var returnValues = [];
  
  for (var i = firstIndex; i < lastIndex; i++) {
    var currentRowValues = values[i];
    
    if (i !== firstIndex) {
      editPageTitleAndUrl(currentRowValues)
    }
    
    currentRowValues = inputPageCategory(currentRowValues); // ページ種別の追加
    
    var rank =  i - 14;
    currentRowValues.unshift(rank); // 順位を追加
    
    returnValues.push(currentRowValues);
    returnValues[0][0] = 'Rank';
  }
    
  return returnValues;
};
1-2-1 文字列をいじる

タイトルが冗長だったりURLが一部だけだったりするので、見やすいように
編集しておきます。

var editPageTitleAndUrl = function(rowValues) {
  rowValues[0] = rowValues[0].replace(' - ゆるオタクのすすめ', '');
  rowValues[1] = 'https://yuru-wota.hateblo.jp' + rowValues[1];
};
1-2-2 ページの種別を追加

ザっと書いてしまったのですが、ちょっと分岐が多すぎて…

コメントにあるように「オブジェクト」とかを使ったら
もう少しスッキリ出来るんじゃないかなーと思っています(希望的観測)。

修正が出来たら後ほど更新しますね。

var inputPageCategory = function(rowValues) {
  // オブジェクトの方がスッキリできるかも
  switch (true) {
    case (rowValues[0].indexOf('Page Title') !== -1):
      rowValues.unshift('Type');
      break;
      
    case (rowValues[0].indexOf('カテゴリーの記事一覧') !== -1):
      rowValues.unshift('カテゴリ');
      break;
      
    case (rowValues[0].indexOf('日間の記事一覧') !== -1):
      rowValues.unshift('日別');
      break;
      
    case (rowValues[0].indexOf('ヶ月間の記事一覧') !== -1):
      rowValues.unshift('月別');
      break;
      
    case (rowValues[0].indexOf('年間の記事一覧') !== -1):
      rowValues.unshift('年間');
      break;
      
    case (rowValues[0].indexOf('記事一覧') !== -1):
      rowValues.unshift('Archive');
      break;
      
    case (rowValues[0].indexOf('not found') !== -1):
      rowValues.unshift('Error');
      break;
      
    case (rowValues[1].indexOf('/reporting/') !== -1):
      rowValues.unshift('Report');
      break;
      
    default:
      rowValues.unshift('記事');
  }
  
  return rowValues;
};

1-3 (エラー対策)シートを更新

エラー等で同じ日付のシートと当たってしまったら、改めて作成することにします。

var deleteTempSheetName = function(sheetName, ss) {

  var Sheets = ss.getSheets();
  for (var i = 0; i < Sheets.length; i++){
    if (Sheets[i].getSheetName() === sheetName) {
      ss.deleteSheet(Sheets[i]);
    }
  }
  
  return;
};

全消しして上書きでも良さそうですね。

1-4 別のSpreadSheetに転記

ここから転記に入ります。

数値の書式設定も独立できると嬉しいのだけど…列も直接指定してるし…
時間かかりそうなのでここは保留します…

var transcriptToNewSheet = function(ss, values, SheetName) {
  // 最後尾にシートを作成
  var sheetCounts = ss.getNumSheets();
  var transSheet = ss.insertSheet(SheetName, sheetCounts);
  
  var lastRow_transSheet = values.length;
  var transSheetLastCol  = values[1].length;
  var transDataRange     = transSheet.getRange(1, 1, lastRow_transSheet, transSheetLastCol);
  transDataRange.setValues(values);
  
  // 書式設定
  var rankCol        = transSheet.getRange('A:A').setNumberFormat('#,000 ');
  var pvCol          = transSheet.getRange('E:E').setNumberFormat('#,##0 ');
  var avgTimeCol     = transSheet.getRange('F:F').setNumberFormat('#,##0.00 "sec. "');
  var someNumCols    = transSheet.getRange('G:I').setNumberFormat('#,##0 ');
  var pagesCol       = transSheet.getRange('J:J').setNumberFormat('#,##0.00 "pages "');
  var avgDurationCol = transSheet.getRange('K:K').setNumberFormat('#,##0.00 "sec. "');
  var bounceRateCol  = transSheet.getRange('L:L').setNumberFormat('#,##0 % ');

  adjustColumnWidths(transSheet);

  // 見出し行の書式を設定
  setHeaderFormat(transSheet, transSheetLastCol)

  return;
};

ちなみに、書式設定は「Excelとほぼ同じように使える」*3ので、
勉強コストが抑えられて良いですね。Googlesさんさすがです。

1-4-1 列幅を調整

文法はこんな感じです。

sheet.setColumnWidths(●●行目, ●●行分, ●●px)

メソッドチェーンというものを初めて使いました…!(嬉)

var adjustColumnWidths = function(sheet) {
  sheet.setColumnWidths( 1, 1,  40)  // Rank
       .setColumnWidths( 2, 1,  60)  // Type
       .setColumnWidths( 3, 2, 280)  // Page Title, Page Url
       .setColumnWidths( 5, 1,  60)  // PV
       .setColumnWidths( 6, 1, 100)  // Avg. Tme on Page
       .setColumnWidths( 7, 3,  60)  // Users, New Users, Sessions
       .setColumnWidths(10, 1, 100)  // Pages/Sessions
       .setColumnWidths(11, 1, 100)  // Avg. Session Duration
       .setColumnWidths(12, 1,  80); // Bounce Rate
  return;
};
1-4-2 見出し行に色付け

メソッドチェーンというものをh(略)(嬉)

var setHeaderFormat = function (sheet, lastCol) {
  var headerRange = sheet.getRange(1, 1, 1, lastCol);
  const DEEP_BLUE = '#20124d';
  const WHITE     = '#ffffff';
  headerRange.setBackground(DEEP_BLUE)
             .setFontColor(WHITE)
             .setFontWeight('Bold');
  
  return;
};

3. トリガーを設定

とりあえず転記は出来るようになったので、元のシートが
更新されたら動くように設定します。

実行する関数 transcriptReport
デプロイ時に実行 Header
イベントのソース 時間主導型
時間ベースのトリガー 日付ベースのタイマー
時刻 午前6時~7時 (GMT+09:00)
エラー通知設定 1週間おき

※追加予定

  • 条件付き書式
    セルの値によって、シートに色付けしていきます。
    0(ゼロ)を薄くしたり、数値の大小で赤くしたり。

  • 行・列の固定
    今回の1番の目的です。
    画面をスクロールしても見出しがついてくるように。

  • フィルターを設定
    目的(&気分)によって、自由に並び替えが出来るようにします。

乞うご期待…!

後記

VBAと同じで、動きを目で見てプログラミングしていくのが
結構楽しいです。

それに加えて、GAS(というかG Suite)はWebサービス
簡単に連携できたりするので、達成感2割増し。
初心者の特権かもしれないけど。

会社によっては使えないところもあったりしますが、
上手く使い分けできるよう、どちらもスキル上げていきたいですね。

参照

書籍

組み込みオブジェクトとSheetクラス・Rangeクラスで
度々お世話になっております…!
読むたびに新しいことを知るような気が…

Webサイト

日付を変えれば総合ランキングに早変わり! tonari-it.com

「めとりくす」とか「でぃめんしょん」とかの和訳一覧です。
最終更新がちょっと前ですが、まだ大丈夫そうですね。 ummmummm.hatenablog.com

みんな大好きリファレンス。 developers.google.com

developers.google.com

developer.mozilla.org

最終的に、慣れているindexof()にしましたが、条件が多いなら
正規表現test()が良いみたいです。
iwb.jp

手前味噌ですが…昔の自分、ありがとう。
yuru-wota.hateblo.jp

注釈

*1:レポート名が英語なのは、ほんの少しでも英語に触れる時間を増やしたいから…

*2:PVが同じなら、avgTimeOnPage(ページの滞在時間)でさらにソートします。

*3: 曜日とかは少し混乱するのですが。

【Excel VBA】テーブル変換とスタイル変更 ~標準モジュールでスタイルを新規作成する~

VBAでテーブル化と好みのテーブルスタイル設定のマクロ化に取り組んでいます。

前回は、先日のフローチャートをもとに、テーブル化までコードを書きました。
【Excel VBA】テーブル変換とスタイル変更 ~テーブル変換するところまで標準モジュールで書いてみる~ - ゆるおたノート

今回も引き続きテーブルのスタイルを指定するところまで書いてみます。

進捗

挑戦!!と付いているモノが本記事のテーマです。


  1. Clear!!選択範囲をテーブル化
    • Clear!!指定範囲にテーブルがあると「実行時エラー」が発生するので、エラーが出てからOn Errorで「テーブルの解除」に進んだ方が良いかも。
    • NGListObject.Addメソッドの戻り値で既定の名前も選択可。
  2. 挑戦!!しましまの無い、罫線・見出しだけのテーブルスタイルを作る
    • 挑戦!!テーブルスタイル関連のオブジェクト
      • 挑戦!!TableStyleオブジェクト
      • 挑戦!!TableStyleElementオブジェクト / TableStyleElementsコレクション
  3. 挑戦!!ブックの既定のスタイルに登録する
  4. 保存先をダイアログで指定
  5. クラスで共通処理を分離
  6. ユーザーフォームで操作簡略化
    • (もし出来れば)色は自由に選べるようにしたい

<2019/05/17追記>
3. のラベルが抜けていたので修正しました。

コード

Module1: ConvertIntoTable

Mainプロシージャには、Call setTableStyle~の1行だけ追加になりました。

また、「選択範囲」の反映方法を調整しています。
この場合、以下のような挙動になります。


  • 表の一部分のみ選択されている場合は、表全体*1を取得しテーブル化。
  • 表が複数含まれている場合は、左上の表のみテーブル化。

Public Sub Main()

    '念のため変数に入れておく
    Dim myRange As Range
    Set myRange = Selection.CurrentRegion '※選択範囲のうち、左上にある表をターゲットとする

    Dim myBook As Workbook:    Set myBook = ActiveWorkbook
    Dim mySheet As Worksheet: Set mySheet = myBook.ActiveSheet

    'エラー対策
    If hasListObjectOnSelection(myRange) = False Then Exit Sub

    Dim listObj As ListObject
    Set listObj = convertRangeIntoTable(mySheet, myRange)

    Set myRange = Nothing

    '▼追加
    Call setTableStyle(myBook, listObj)


    '~ブックを保存する処理~


    MsgBox "処理が完了しました。"

End Sub

長くなるので、特に変更のないModule2は省略しまして…

Module3: createTableStyle

このモジュールのメイン

初期のコードにあったMainプロシージャから、一連の処理を丸ごと独立したモジュールとしました。
Do While ~ Loopの部分をさらにプロシージャ化できないかな…

今回はクラス構文は使っていませんが、プログラミング用語的には「単一責任原則」*2と言うようです。

Option Explicit
Option Private Module

Public Sub setTableStyle(ByRef targetBook As Workbook, _
                         ByRef targetListObj As ListObject)

    If isOkToCreateTableStyle = False Then Exit Sub

    Dim defaultStyleName As String
    defaultStyleName = targetListObj.TableStyle
    Dim newStyleName As String
    newStyleName = inputStyleName(defaultStyleName)

    Do While (newStyleName = defaultStyleName) _
        Or isExistStyleName(targetBook, newStyleName)

        If MsgBox("指定の名前は既に存在しています。こちらを適用しますか?", vbYesNo) = vbYes Then
        MsgBox "既定のテーブルスタイルを適用します。"
        Exit Sub 'スタイルは変更せずに終了
        End If

        MsgBox "お手数ですが、もう1度スタイルの名前を登録して下さい。"
        newStyleName = inputStyleName(defaultStyleName)

    Loop

    Dim newStyle As TableStyle
    Set newStyle = targetBook.TableStyles.Add(newStyleName)
    Call changeTableDesign(newStyle)

    '既定のスタイルに設定しておく
    targetBook.DefaultTableStyle = newStyleName

    targetListObj.TableStyle = newStyle

End Sub
一旦ユーザーに意向確認

不要なら今回の処理はすべて飛ばします。

Private Function isOkToCreateTableStyle()

    If MsgBox("テーブルのスタイルも新規作成しますか?", vbYesNo) = vbNo Then
        MsgBox "承知しました。スタイルの作成は中止します。"
        isOkToCreateTableStyle = False
        Exit Function
    End If

    isOkToCreateTableStyle = True

End Function
名前を入力してもらう

やっていることは初期のコードと同じなので、説明は割愛します。

Private Function inputStyleName(ByRef defaultStyleName As String) As String

    Dim m As String
    m = "新しいスタイルの名前を入力してください" & vbCrLf
    m = m & "既定の名前:" & defaultStyleName

    Dim newStyleName As String
    newStyleName = InputBox(Prompt:=m, _
                            Default:=defaultStyleName)

    inputStyleName = newStyleName

End Function

ところで、昨日もテーブル名の設定で触れた「文字列の入力問題」について、Twitterにて下記のご意見をいただきました。

ご意見ありがとうございます!
スタイル名の入力と合わせて、シート上で設定出来るようにすればユーザーにとっても使いやすそうですね。

ユーザーフォームも作成不要・学習も不要で一石三鳥!

既定の名前と見比べる

スタイルの名前で既存のものと被ってしまうと、新規作成する時にエラーになります。
対策として、事前にチェックしておきます。

Private Function isExistStyleName(ByRef targetBook As Workbook, _
                                  ByVal newStyleName As String) As Boolean

    Dim i As Long
    For i = 1 To targetBook.TableStyles.count
        If newStyleName = targetBook.TableStyles(i).Name Then
            isExistStyleName = True
            Exit Function
        End If
    Next

    isExistStyleName = False

End Function

ただし、TableStylesコレクションはインデックスが「0始まり」ではなく「1始まり」みたいなんですよね…不思議…

<2020/10/21追記>
これはコレクション型の仕様だそうです…やっぱり不思議。

コレクションのメンバーの位置を式で指定します。 数式の場合、index には、1 以上からコレクションの Count プロパティ値までの数値を指定します。

Item メソッド (Visual Basic for Applications) | Microsoft Docs

テーブルスタイルの内容を変更

TableStyles.Add()を同モジュールのプロシージャ1つ目に移動したことで初期コードと役割が変わったので、プロシージャ名を変更しました。

引き数は一部省略も可能(後述)ですが、読みやすさのために今回は省略せずに書いています。

Private Sub changeTableDesign(ByRef tableStyleObj As Variant)

    'テーブル全体(WholeStyle)
    Dim black As Long:         black = RGB(0, 0, 0)
    Dim lightGray As Long: lightGray = RGB(208, 206, 206)

    Call setWholeStyle(tableStyleObj, black, lightGray)

    '見出し行(HeaderRowStyle)
    Dim deepBlue As Long: deepBlue = RGB(0, 32, 96)
    Dim white As Long:       white = RGB(255, 255, 255)

    Call setHeaderStyle(tableStyleObj, deepBlue, white, True)

End Sub
テーブルの罫線を設定

お好みですが、罫線の色についてあらかじめ既定値を決めます。
私はイミディエイトウィンドウでRGB()関数を使って数値を確認してみました。
(今回は↑のコードと同じ値としています)

? RGB(0, 0, 0) 'black
0

? RGB(208, 206, 206) 'lightGray
13553360

こちら↑を基準にOptionalキーワードで既定値を設定。
これで、引数を省略しても動くようになります。

便宜的に改行も足してみましたが、元の方が読みやすかったかも…?

Private Sub setWholeStyle(ByRef tableStyleObj As Variant, _
                          Optional ByVal outerLineColor As Long = 0, _
                          Optional ByVal innerLineColor As Long = 13553360)

    Dim wholeTableElements As Variant
    Set wholeTableElements = tableStyleObj.TableStyleElements(xlWholeTable)

    Dim outerLineConstants As Variant
    outerLineConstants = Array(xlEdgeTop, _
                               xlEdgeBottom, _
                               xlEdgeLeft, _
                               xlEdgeRight _
                               )

    Call setLines(wholeTableElements, _
                  outerLineConstants, _
                  outerLineColor, _
                  xlContinuous, _
                  xlMedium _
                  )

    Dim innerLineConstants As Variant
    innerLineConstants = Array(xlInsideVertical, _
                               xlInsideHorizontal _
                               )

    Call setLines(wholeTableElements, _
                  innerLineConstants, _
                  innerLineColor, _
                  xlContinuous, _
                  xlThin _
                  )

End Sub
罫線を付ける

既定値に指定しているxlContinuousはいわゆる「実線」、xlThinは「Excelで初期設定されている太さ」を表します。

Private Sub setLines(ByRef StyleElements As Variant, _
                     ByRef linePositions As Variant, _
                     ByVal targetColor As Long, _
                     Optional ByVal lineStyle As Long = xlContinuous, _
                     Optional ByVal thickness As Long = xlThin)

    With StyleElements
        Dim i As Long
        For i = 0 To UBound(linePositions)
            With .Borders(linePositions(i))
                .Color = targetColor
                .lineStyle = lineStyle
                .Weight = thickness
            End With
        Next i
    End With

End Sub
見出し行の色を設定

罫線と同様、イミディエイトウィンドウで確認して既定値を設定しています。

? RGB(0, 32, 96) 'deepBlue
16777215

? RGB(255, 255, 255) 'white
6299648

色や.Boldプロパティはお好みで変更可ということで、こちらもOptionalとしました。

Private Sub setHeaderStyle(ByRef tableStyleObj As Variant, _
                           Optional ByVal interiorColor As Long = 16777215, _
                           Optional ByVal fontColor As Long = 6299648, _
                           Optional ByVal isBold As Boolean = True)

   Dim headerRowElements As Variant
   Set headerRowElements = tableStyleObj.TableStyleElements(xlHeaderRow)

    With headerRowElements

        .Interior.Color = interiorColor

        With .Font
            .Color = fontColor
            .Bold = isBold
        End With

    End With

End Sub

本日はここまで!

後記

最近気づいたのですが、コードを書くだけなら何時間でもPCに向かっていられることに気付きました。
お陰でかなり夜更かし気味ではありますが…

相変わらず勘違いが多かったり難しいことはあまり出来なかったりで「100%楽しい!」とは言い難いものの、VBAは特に書いた結果がすぐ見えるというのが大きい気がします。

これが生活にも生かせると良いんですけどね…

あとは、もっと文章力が付いたらブログ書くのももっと楽しいんだろうな、と。
今は、とにかく時間がかかってしまうのでやや苦行気味…
サクッとさっぱり書くにはどうしたら良いのかな。

ライティングの効率化とあわせて勉強していきたいです。

このシリーズについて

テーブルの変換とテーブルスタイルの新規作成をマクロ1発で使えるように考えています。主に自分向け。

もし間違いやヘンなところ等ありましたら、コメント欄やTwitterお問い合わせフォーム等でご指摘いただけたら嬉しいです。

次回

【Excel VBA】テーブル変換とスタイル変更 ~ブックの保存先を選ぶ~ - ゆるおたノート

連載目次

  1. 【Excel VBA】テーブル変換とスタイル変更 ~一発で変換とスタイル変更を済ませたい~ - ゆるおたノート
  2. 【Excel VBA】テーブル変換とスタイル変更 ~処理の流れを整理してみる~ - ゆるおたノート
  3. 【Excel VBA】テーブル変換とスタイル変更 ~テーブル変換するところまで標準モジュールで書いてみる~ - ゆるおたノート
  4. 当記事【Excel VBA】テーブル変換とスタイル変更 ~標準モジュールでスタイルを新規作成する~ - ゆるおたノート
  5. 【Excel VBA】テーブル変換とスタイル変更 ~ブックの保存先を選ぶ~ - ゆるおたノート

注釈

*1:CurrentRegionプロパティそのものの働きについては、こちらが参考になるかと思います。
【エクセルVBA】表が変更されても、表全体の範囲を簡単に取得する方法

*2:詳しくは、こちらが参考になるかと思います。
責任(関心)を意識したアプリケーション設計 - Qiita

【Excel VBA】テーブル変換とスタイル変更 ~テーブル変換するところまで標準モジュールで書いてみる~

VBAでテーブル化と好みのテーブルスタイル設定のマクロ化に取り組んでいます。

前回書いたフローチャートをもとに、早速コーディングしていきます。
【Excel VBA】テーブル変換とスタイル変更 ~処理の流れを整理してみる~ - ゆるおたノート

今回は、テーブル(ListObjectオブジェクト*1)を生成するところまで書きました。

進捗

挑戦!!と付いているモノが本記事のテーマです。


  1. 挑戦!!選択範囲をテーブル化

    • 挑戦!!指定範囲にテーブルがあると「実行時エラー」が発生するので、エラーが出てからOn Error ~で「テーブルの解除」に進んだ方が良いかも。

    • ListObject.Addメソッドの戻り値で既定の名前も選択可。
      ※こちら↑は誤りでした!!大変申し訳ありません!!!!汗*2

  2. しましまの無い、罫線・見出しだけのテーブルスタイルを作る

    • テーブルスタイル関連のオブジェクト
      • TableStyleオブジェクト
      • TableStyleElementオブジェクト / TableStyleElementsコレクション
  3. ブックの既定のスタイルに登録する

  4. 保存先をダイアログで指定

  5. クラスで共通処理を分離

  6. ユーザーフォームで操作簡略化

    • (もし出来れば)色は自由に選べるようにしたい

コード

Module1:ConvertIntoTable

Mainプロシージャ

On Error GoTo ~はなるべく使いたくないので、一旦canContinueProcess()関数で状況を判定してから先に進めます。

このまま行けば、Mainプロシージャの長さはほぼ1画面に納められそう。(my PC調べ)

Option Explicit

Public Sub Main()

    '念のため変数に入れておく
    Dim myRange As Range: Set myRange = Selection
    Dim myBook As Workbook: Set myBook = ActiveWorkbook
    Dim mySheet As Worksheet: Set mySheet = myBook.ActiveSheet

    'エラー対策
    If canContinueProcess(myRange) = False Then
        Exit Sub '=続行不能なら処理は中止する
    End If

    Dim listObj As ListObject
    Set listObj = convertRangeIntoTable(mySheet, myRange)

    Set myRange = Nothing

    '
    '~コーディング中~
    '

    MsgBox "処理が完了しました。"

End Sub
選択範囲にテーブルがあるかチェック

If文などの制御構文は、可能な限りネストさせないようにすると可読性が上がってコードの安全性も向上するので*3、単純なIf文2つで書いています。

ただし、そうすることで条件式が二重否定(Not ~ = False など)になってしまうような時は、かえって読みづらくなりがちなので、ほかの表現を考えます。
例えば肯定文に書き換えるとか。

Private Function canContinueProcess(ByRef targetRange As Range) As Boolean

    If targetRange.ListObject Is Nothing = True Then
        canContinueProcess = True
        Exit Function
    End If

    If MsgBox("指定範囲にテーブルがあります。" & vbCrLf & _
              "こちらをすべて解除してもよろしいですか?", vbYesNo) = vbYes Then

        Call unlistAllTables(targetRange)
        canContinueProcess = True
        Exit Function
    End If

    MsgBox "かしこまりました。処理を中止します。"
    canContinueProcess = False

End Function
選択範囲上あるテーブルは解除する

前回の記事で書いた通り、.TableStyleプロパティに空文字""を代入するとテーブルのスタイルを「まっさら」状態に出来ます。

Private Sub unlistAllTables(ByRef targetRange As Range)

    Do
        With targetRange.ListObject
            .TableStyle = "" '(お好みで)テーブルのスタイルも初期化
            .Unlist
        End With
    Loop Until targetRange.ListObject Is Nothing

    MsgBox "解除しました。"

End Sub

Module2:createListObj

ここから本番です。
処理のテーマが変わるので、念のためモジュールを分離しました。

Privateなプロシージャをモジュール分けするときは、モジュールの宣言セクションにOption Private Moduleと書くと、ツールバーの選択肢に上がらなくなります!すごい…!*4

Option Explicit
Option Private Module '他モジュールからアクセス可能なまま、一覧からは隠す

Public Function convertRangeIntoTable(ByRef targetSheet As Worksheet, _
                                      ByRef targetRange) As ListObject

    On Error GoTo checkAutoFilter 'エラーが出たらジャンプ
    Dim newListObj As ListObject
    'エラーが出るならココ↓
    Set newListObj = targetSheet.ListObjects.Add(SourceType:=xlSrcRange, _
                                                 Source:=targetRange, _
                                                 XlListObjectHasHeaders:=xlYes)

    On Error GoTo 0 'ジャンプ命令を解除

    Call setTableName(newListObj)
    Set convertRangeIntoTable = newListObj

    Exit Function '書き忘れると"End Function"(↓)まで進んで更にエラー処理される…

'エラー処理
checkAutoFilter:

    unsetAutoFilter (targetSheet)
    Resume 'エラー発生箇所に戻る

    MsgBox "予期せぬエラーが発生しました。処理を中止します。" '起こるかな…?
    Stop 'もし"Resume"を越えたら異常事態

    Set convertRangeIntoTable = Nothing

End Function
オートフィルターを解除

既に実行時エラーが発生しないように対策していますが、それでもエラーになる時用に。
上記プロシージャのcheckAutoFilterラベル以下のエラー処理に入って、こちらのunsetAutoFilterプロシージャに飛んできます。

この段階ではフィルターが掛ったままの可能性があるので、一旦これをOff。
それでもエラーになる場合の対処法は…ごめんなさい、まだ分かりません…

Private Sub unsetAutoFilter(ByRef targetSheet As Worksheet)

    With targetSheet
        If .AutoFilterMode = True Then
            .AutoFilterMode = False
        End If
    End With

End Sub
テーブルの名前を決める

エラー処理が終わって無事テーブルを生成できたら、今度はテーブルの名前を決めます。

Private Sub setTableName(ByRef targetListObj As ListObject)

    With targetListObj
        Dim defaultListName As String
        defaultListName = .Name

        Dim m As String
        m = "テーブル名を入力して下さい。" & vbCrLf
        m = m & "既定の名前:" & defaultListName

        .Name = InputBox(Prompt:=m, Default:=defaultListName)
    End With

End Sub

InputBox()関数の引数のうちDefaultに値を渡しておくと、はじめから入力欄に値が入った状態でInputBoxが開きます。

既定値をユーザーに知らせたい時に便利です。
InputBox()関数にDefault値を与えると、既に値が入った状態でボックスが開く

ただ、入力値とデフォルト値が一致した時の処理も追加した方が良さそう…
キリがない。サグラダファミリアになってしまう…

感想

やってることは初回のコードとほぼ同じなのですが、エラー処理の流れを修正してみました。
読みやすさが上がっていると良いのですが…

ただ、あまり細分化し過ぎると逆に読みづらくなるような気がしていて、その塩梅が難しいです。
これはどこで学べるんだろう…写経を繰り返すしかないのかな…?

余談

実は、このコードを書くためにVSCode使ってみたりRubberduckなるVBEのアドインを入れてみたり*5しました。

が、操作が理解できず、コメント増やしつつ数時間分かけて書いたコードを全消去してしまって絶望…脱線した罰かな。泣

でも、1回書くと何となく流れを覚えているみたいで、その日のうちに戻せました。(良かった…)
メゲずに続けます。

このシリーズについて

テーブルの変換とテーブルスタイルの新規作成をマクロ1発で使えるように考えています。主に自分向け。

もし間違いやヘンなところ等ありましたら、コメント欄やTwitterお問い合わせフォーム等でご指摘いただけたら嬉しいです。

次回

【Excel VBA】テーブル変換とスタイル変更 ~標準モジュールでスタイルを新規作成する~ - ゆるおたノート

連載目次

  1. 【Excel VBA】テーブル変換とスタイル変更 ~一発で変換とスタイル変更を済ませたい~ - ゆるおたノート
  2. 【Excel VBA】テーブル変換とスタイル変更 ~処理の流れを整理してみる~ - ゆるおたノート
  3. 当記事【Excel VBA】テーブル変換とスタイル変更 ~テーブル変換するところまで標準モジュールで書いてみる~ - ゆるおたノート
  4. 【Excel VBA】テーブル変換とスタイル変更 ~標準モジュールでスタイルを新規作成する~ - ゆるおたノート
  5. 【Excel VBA】テーブル変換とスタイル変更 ~ブックの保存先を選ぶ~ - ゆるおたノート

注釈

*1:「Objectオブジェクト」って「頭痛が痛い」みたいな…

*2:詳しくはこちらを… listobjects. Add メソッド (Excel) | Microsoft Docs

*3:詳しくはこちら。
本職でなくても勉強になります。サンプルはJavaScriptなのかな?
<2020/10/13追記>
JavaScriptだけでなく、RubyScalaもあるみたいです。
新人プログラマに知ってもらいたいメソッドを読みやすく維持するいくつかの原則 - Qiita

*4:素敵な記事をありがとうございます!!
※出典?とされているimihitoさんの記事は見つけられませんでした…
VBA Publicなプロシージャをマクロの実行メニューから隠す方法 - t-hom’s diary VBA応用(マクロの起動にプロシージャ名を表示させなくする。)

*5:Rubberduckは私の環境では何故か認識されなかった…
また今度、時間を作って試します…

【Excel VBA】テーブル変換とスタイル変更 ~処理の流れを整理してみる~

前回の記事で、「テーブルを好きなデザイン(スタイル)で使いたい」というお話をしました。
【Excel VBA】テーブル変換とスタイル変更 ~一発で変換とスタイル変更を済ませたい~ - ゆるおたノート

色々アイディアは浮かぶのですが、実際これを採り入れるとしたらどこに何を足したら良いだろう…?と毎回悩みます。
そこで、前回1度書き直したコードは一旦忘れて、処理の流れを整理しつつゼロから書いてみようかなと思います。

考えながら書いているので、たぶん「コーディングの効率化」からは離れることもあると思いますが、悪しからず気長にお付き合いください。

前回のまとめ

やりたいこと

  1. 選択範囲をテーブル化
  2. しましまの無い、罫線・見出しだけのテーブルスタイルを作る
  3. ブックの既定のスタイルに登録する
  4. (もし出来れば)後々色は自由に選べるようにしたい

【推察】入れたら良さそう

  • テーブルスタイル関連のオブジェクト

    • TableStyleオブジェクト
    • TableStyleElementオブジェクト / TableStyleElementsコレクション
  • 指定範囲にテーブルがあると「実行時エラー」が発生する

    • エラーが出てからOn Errorで「テーブルの解除」に進んだ方が良いかも。
  • テーブルの名前

    • ListObject.Addメソッドの戻り値で既定の名前も選択可。
  • コーディング改善

    • クラスで共通処理を分離
  • UI改善

    • 保存先をダイアログで指定
    • ユーザーフォームで操作簡略化(カラーパレットもここで?)

フローチャート

これを踏まえて、改めて流れを書き出してみました。
ほぼ前回のコードの再現かな?

なお、ユーザーフォームの件は、一通り完成してから考えようと思います。
まずはパンクしないように、ということで。
したがって、これはもしかしたら実装まで行かないかもしれません。
もし期待されている方がいらっしゃったらごめんなさい。

それでは、今回のフローチャートです。
赤い矢印 は、Falseまたはエラーを表します。

※画像1枚で上げたら「はてなブログ」の仕様で文字がぼやけてしまうようなので、今回も少し分割して掲載します。
ぶつ切りガタガタですが何卒ご了承ください…!

フローチャート(その1)
フローチャート(その2)
フローチャート(その3)
フローチャート(その4)

メモ

今回も調べながら知ったことですが、テーブルスタイルは空文字""を代入することで「まっさら状態」を指定できるようです。
Office TANAKA - VBAでテーブルの操作[セル範囲をテーブルにする]

好みにもよるかと思いますが、前回書いたテーブルを解除する処理に、リセットの一環として「テーブルスタイルを解除する処理」も足してみようと思います。

Private Sub convertTablesIntoRange(ByRef targetSheet As Worksheet)
    
    Dim list As ListObject
    For Each list In targetSheet.ListObjects
        with list
            .TableStyle = "" '追加(お好みで削除してください)
            .Unlist
        End with
    Next list
    
End Sub

疑問

テーブルの範囲の判定

「シート上でテーブルがあるか」を判定しているけど、例えば同じシートでも離れた範囲にテーブルがある場合に、それを除外できるのかな?

式が壊れたりするのが怖いので、出来るだけUnlistは実行しない方向に持っていきたいのです…

そもそもエラー処理は合ってる…?

「シート上にテーブル無し」かつ「フィルタも無し」の場合、「予期せぬエラー」ということにしてしまって良いのかどうか?

というかフローの書き方に自信が無いので、一応文章でも説明してみます。

  • 本当の脳内イメージ

    No アクション 備考
    1 テーブルに変換
    2 エラー
    3 テーブルがあれば解除
    4 再度テーブル変換を試す 抜けてた
    5 エラーになったら選択範囲と一部被るようなフィルタがあれば解除 分岐が変?
    6 もう1度テーブル変換を試す 抜けてた
    7 それでもエラーなら予期せぬエラー???
    • 本当は「予期せぬエラー」という言葉はあまり好きではないのです。
      「手の施しようが無い」と言われているように感じるというか、必要以上に不安感を煽られるような気がして…
      かと言って、他に適切な言葉も分からないという。

    • 1回1回テーブル化を試行するのも無駄かなぁ。

感想

くろうとのすなるふろうちゃあとといふものをしろうともかいてみむとてするなり」な感じで勢いで書いてみました。

思ってたより難しいですねぇ、、、
記号のコピペも結構めんどくさい*1し、思い通りに矢印を引けない…もやもや。

ただ、処理の流れの理解に不足があると、↑のようにイメージと図*2が違うなんていうことも分かりました。
まるで、学生時代の楽器を練習していた時のような…*3

そういうわけで、書き方にヘンなところがあったらご指摘いただけると嬉しいです。
勉強していきたいので…!

次回からは実際にコードを書いていきます。
ちょっとずつ進むと思いますが、長い目でお付き合いいただければ幸いです。それでは!

このシリーズについて

テーブルの変換とテーブルスタイルの新規作成をマクロ1発で使えるように考えています。主に自分向け。

もし間違いやヘンなところ等ありましたら、コメント欄やTwitterお問い合わせフォーム等でご指摘いただけたら嬉しいです。

次回

【Excel VBA】テーブル変換とスタイル変更 ~テーブル変換するところまで標準モジュールで書いてみる~ - ゆるおたノート

連載目次

  1. 【Excel VBA】テーブル変換とスタイル変更 ~一発で変換とスタイル変更を済ませたい~ - ゆるおたノート
  2. 当記事【Excel VBA】テーブル変換とスタイル変更 ~処理の流れを整理してみる~ - ゆるおたノート
  3. 【Excel VBA】テーブル変換とスタイル変更 ~テーブル変換するところまで標準モジュールで書いてみる~ - ゆるおたノート
  4. 【Excel VBA】テーブル変換とスタイル変更 ~標準モジュールでスタイルを新規作成する~ - ゆるおたノート
  5. 【Excel VBA】テーブル変換とスタイル変更 ~ブックの保存先を選ぶ~ - ゆるおたノート

注釈

*1:これも何か準備する余地がありそうですね。さすがに今回はやめましたけど!

*2:なんか「画像はイメージです」みたいな日本語。笑

*3:「口で歌えないなら楽器ではもっと演奏できないよ」と教わるのです。

【Excel VBA】テーブル変換とスタイル変更 ~一発で変換とスタイル変更を済ませたい~

Excelでデータを管理する時は、テーブル機能が便利です。
1行だけ関数を設定すれば列の中は自動で式を補完してくれたり*1、範囲の変更も自動で追跡*2してくれたり*3

ところが、テーブル化したはいいものの、Excelに元からあるテーブルスタイルがちょっと使いづらい、ということがあります。
やたらカラフルなものや、しましまが無いけど「まっさら」ものとか。極端だなぁ…
これでも使えなくはないですが、「もっと別のデザインを使いたい」こともあります。

VBAerとしてはこれを毎回手動で変更するのも面倒なので、マクロ1つで好みのスタイルでテーブル化出来るように考えてみたいと思います。

目標

実現したいこと

今考えているのは、下記の4つです。

  1. 選択範囲をテーブル化
  2. しましまの無い、罫線・見出しだけのテーブルスタイルを作る
  3. ブックの「既定のスタイル」に登録する
  4. (もし出来れば)配色は自由に選べるようにしたい

4つめは必要になった時でも良さそうですが、少しずつやってみたいと思います。

完成イメージ

こんなテーブルを作ります。罫線もうっすらと。
完成イメージ(ヘッダーだけ色を付けて、各罫線もうっすらつける)
※何の表か気になる方はこちらへ…→*4

ちなみに…

新規作成のブックで使えるようにしたいだけであれば、「実現したいこと」の3.まで行った時点で、Excelテンプレート(.xltx)としてシステムに保存する手段もあります。ご参考まで…

マクロ

自動記録の流用

テーブル関係の設定で使うオブジェクトが分からなかったので、まずは自動記録の力を借りて.Selectionを消したり変数を使ったりして軽く整理してみました。

あわせて、MsgBoxや名前を入れるInputBox関数を少し追加しています。

コード(整理の結果)

※かなり長いので、プロシージャごとに分割して掲載します。

【1】メイン

コレが1番長いです。

Public Sub setTable()
    
    Dim myBook As Workbook    
    Dim thisSheet As Worksheet
    Set myBook = ActiveWorkbook    
    Set thisSheet = myBook.ActiveSheet
    
    '実行準備
    Call prepareAgainstRuntimeError(thisSheet)
    
    Dim newTable As ListObject
    Set newTable = convertRangeIntoTable(thisSheet)
    
    'スタイルを作成
    Dim m As String
    m = "テーブルのスタイルも新規作成してよろしいですか?" & vbCrLf
    m = m & "適用予定のスタイル名:" & newTable.TableStyle
    If MsgBox(m, vbYesNo) = vbYes Then
        Dim newStyleName As String
        newStyleName = createStyle(myBook)
        
        newTable.TableStyle = newStyleName 'スタイルを適用
        
        '既定のスタイルに設定しておく
        myBook.DefaultTableStyle = newStyleName
    End If
    
    MsgBox "現在のテーブル・スタイル:" & newTable.TableStyle
    
    '後処理
    thisSheet.Cells(1, 1).Select
    MsgBox "テーブルに変換しました。"
    
    m = "最後に、このブックを上書き保存してよろしいですか?"
    If MsgBox(m, vbYesNo) = vbNo Then
        MsgBox "承知しました。保存は中止します。"
        Exit Sub
    End If
    
    myBook.Save
    MsgBox "保存しました。"
    
End Sub
【2】一応エラー対策
[1]ユーザーに許可をとってから次の処理へ
Private Sub prepareAgainstRuntimeError(ByRef targetSheet As Worksheet)
    
    With targetSheet
        .Cells(1, 1).Activate
        
        'テーブルの解除
        Dim m As String
        m = "選択しているシート上のテーブルを、すべて解除してもよろしいですか?" & vbCrLf
        m = m & "※選択範囲でテーブルが有効になっていると、この機能が使えません。"
        If MsgBox(m, vbYesNo) = vbYes Then Call convertTablesIntoRange(targetSheet)
            
        'オートフィルターの解除
        If .AutoFilterMode = True Then .AutoFilterMode = False
    End With
    
End Sub
[2]テーブルがあったら解除する

「範囲が被ったらエラー」になるので、エラーが出てからOn Errorで飛んできた方が良いかも。

Private Sub convertTablesIntoRange(ByRef targetSheet As Worksheet)
    
    'すべて範囲に変換
    Dim list As ListObject
    For Each list In targetSheet.ListObjects
        list.Unlist
    Next list
    
End Sub
【3】テーブルに変換
Private Function convertRangeIntoTable(ByVal targetSheet As Worksheet) As ListObject
    
    '使用範囲をテーブルに変換
    Dim newList As ListObject
    Dim newTableRange As Range
    Set newTableRange = targetSheet.Cells(1, 1).CurrentRegion
    Set newList = targetSheet.ListObjects.Add(xlSrcRange, _
                                              newTableRange, _
                                              , _
                                              xlYes)

    'テーブルの名前を設定    
    Dim newTableName As String
    newTableName = InputBox("新規作成するテーブルの名前を入力してください。")
    newList.Name = newTableName
    
    Set convertRangeIntoTable = newList
    
End Function
【4】テーブルスタイルを新規作成
[1]テーブル全体と見出し行で処理を分ける
Private Function createStyle(ByVal targetBook As Workbook) As String
    
    'スタイル名を設定
    Dim newStyle As TableStyle
    Dim newName As String
    newName = InputBox("続いて、新しいスタイルの名前を入力してください。")
    Set newStyle = targetBook.TableStyles.Add(newName)
    
    '▼テーブル全体のスタイル
    Dim black As Long:         black = RGB(0, 0, 0)
    Dim lightGray As Long: lightGray = RGB(208, 206, 206)
    
    Call setWholeStyle(newStyle, black, lightGray)
   
    '▼見出し行(ListHeaderRow)のスタイル
    Call setHearderStyle(newStyle)
   
    createStyle = newName
    
    Set newStyle = Nothing
    
End Function
[2]「テーブル全体」のスタイルを設定
Private Sub setWholeStyle(ByRef targetStyle As Variant, _
                          Optional ByVal outerLineColor As Long =  0, _
                          Optional ByVal innerLineColor As Long = 13553360)
    
    Dim wholeTableElements As Variant
    Set wholeTableElements = targetStyle.tableStyleElements(xlWholeTable)
    
    '外側のスタイル
    Dim outerLineConstants As Variant
    outerLineConstants = Array(xlEdgeTop, xlEdgeBottom, xlEdgeLeft, xlEdgeRight)
    Call setLines(wholeTableElements, outerLineConstants, outerLineColor, xlContinuous, xlMedium)
    
    '内側のスタイル
    Dim innerLineConstants As Variant
    innerLineConstants = Array(xlInsideVertical, xlInsideHorizontal)
    Call setLines(wholeTableElements, innerLineConstants, innerLineColor, xlContinuous, xlThin)
    
End Sub
[3]罫線を付ける
Private Sub setLines(ByRef StyleElements As Variant, _
                     ByRef linePositions As Variant, _
                     ByVal targetColor As Long, _
                     Optional ByVal lineStyle As Long = xlContinuous, _
                     Optional ByVal thickness As Long = xlThin)
    
    With StyleElements
        
        '罫線を1本ずつ設定
        Dim i As Long
        For i = 0 To UBound(linePositions)
            With .Borders(linePositions(i))
                .color = targetColor
                .lineStyle = lineStyle
                .Weight = thickness
            End With
        Next i
        
    End With
    
End Sub
[4]「見出し行」のスタイルを設定
Private Sub setHearderStyle(ByRef targetStyle As Variant)
    
    Dim headerRowElements As Variant
    Set headerRowElements = targetStyle.tableStyleElements(xlHeaderRow)
   
    Dim deepBlue As Long: deepBlue = RGB(0, 32, 96)
    Dim white As Long:       white = RGB(255, 255, 255)
    
    '見出し行のスタイルを設定
    With headerRowElements
        .Interior.color = deepBlue
        With .Font
            .Bold = True
            .color = white
        End With
    End With
    
End Sub

感想と気づいたこと

コードが汚い…

For文やプロシージャ化しつつ少し整理してみましたが、上から順に書き換えただけなのでなんだか流れが分かりづらいですね…
もうちょっとキレイなコードに出来ないものかと。

テーブル作成してから名前変えてる*5し、上の方の「スタイルを作成」の分岐も独立できそう。

何よりメインのプロシージャが長い!
操作が多いから仕方ないですが、1画面で全体像が見えないのでもうちょっと短くしたいです。

テーブルスタイルのオブジェクト

スタイルの変更にはこの辺をいじると良さそうです。

  • TableStyleElementsコレクション / TableStyleElementオブジェクト
  • TableStyleオブジェクト

Addメソッドの戻り値

この記事を書きながら調べていて分かったのですが、ListObjects.Add()するとテーブル(ListObject)ではなくテーブル名が返ってくるそうです。

さっき↑と少し矛盾しますが)これで既定の名前を使うかどうかの選択肢が作れそうです。

<2019/05/14追記>
自分で確認し直したところ筆者の勘違いでした!大変失礼いたしました…
普通にListObjectオブジェクトが返ってきます!!

これ使える…?

保存先のダイアログ

正直まだダイアログの使い方があまり分かってないのですが苦笑、実践の方が身に付くはず、ということでチャレンジしてみます。

上記のコードでは確認無しに既存のパスに上書きしてますが、保存場所もダイアログで選べる方が良さそうです。

クラス

正直まだクラスの使い方があまり(以下略)…

setWholeStyleプロシージャ以下は共通の処理なので、色や変更する場所あたりを渡せばメインモジュールは少しスッキリするんではないかと…?(推測)

ユーザーフォーム

正直まだユーザーフォームの(以下略)…

選択肢や名前の入力が多いので、最初に一気に選べた方がウザくないし、処理もぶつ切りにならなくてユーザー目線的に良いかもしれません。

改善点がいっぱい。

書けば書くほど気になる点がガンガン出てくるのですが、全部実装できるかは全く自信がありません。
最近偉そうに書いたばっかりですが…

1つずつ学んでいきます。時間だけなら今はたっぷりあるので…!

このシリーズについて

テーブルの変換とテーブルスタイルの新規作成をマクロ1発で使えるように考えています。主に自分向け。

もし間違いやヘンなところ等ありましたら、コメント欄やTwitterお問い合わせフォーム等でご指摘いただけたら嬉しいです。

次回

【Excel VBA】テーブル変換とスタイル変更 ~処理の流れを整理してみる~ - ゆるおたノート

連載目次

  1. 当記事【Excel VBA】テーブル変換とスタイル変更 ~一発で変換とスタイル変更を済ませたい~ - ゆるおたノート
  2. 【Excel VBA】テーブル変換とスタイル変更 ~処理の流れを整理してみる~ - ゆるおたノート
  3. 【Excel VBA】テーブル変換とスタイル変更 ~テーブル変換するところまで標準モジュールで書いてみる~ - ゆるおたノート
  4. 【Excel VBA】テーブル変換とスタイル変更 ~標準モジュールでスタイルを新規作成する~ - ゆるおたノート
  5. 【Excel VBA】テーブル変換とスタイル変更 ~ブックの保存先を選ぶ~ - ゆるおたノート

注釈

*1:書いたことは無いけど、たぶんイベントトリガーでGASを動かせば同じようなことはできるのかもしれませんが…

*2:構造化参照というそうです。

*3:最近は一部Googleスプレッドシートに頼ることもありますが、クラウドでデータ保管することにもまだ不安があったりして、完全にExcelから卒業するにはもうちょっと時間がかかりそうです。

*4:8月にメジャーデビュー予定の「BEYOOOOONDS」です。"びよーんず"と読みます。

まだ公式MVが無いのでこちら↓を…
<2020/08/22追記>
更新忘れてましたが公式MVが出ました!
ピコピコ8bit音が楽しいミュージカル風の奇曲です。
BEYOOOOONDS『眼鏡の男の子』(BEYOOOOONDS [The boy with the glasses.])(Promotion Edit) - YouTube

*5:これは自動記録あるあるですね。

【Excel VBA】ブックのフォント、まとめて変更しませんか?

リファレンスに載せたコードを独立した記事として編集しております。

皆さんは、お気に入りのフォントはありますか?

私は、Meiryo UIが好きです*1
ポップで、ハッキリしていて、堅すぎない雰囲気を感じます。あと、小さくても読みやすい。
なので、自分のPCが変わるたびにExcel既定のフォントMeiryo UIに設定しています。

ところが、ファイルをダウンロードしたり誰かからメールでもらったりすると、MS ゴシックや游ゴシック等のWindowsOS標準のゴシック体になっていることが多いのです。

これが読みづらいのなんのって。
特に游ゴシックとかはうっす――――い。視力の弱い私には見えないです…
「Clear Typeテキストの調整」*2も試していますが、それでも私には薄く感じるのです…

世の中には特にフォントにはこだわらずファイルを作成・編集する方も多いですが、その方自身に罪は無いとは思います。
その方が必要に迫られていないだけだと思うので…

ただ、会社でも解像度の高いPCを使うことが増えてきて、標準フォントだと文字がギザギザに見える*3ことが増えてきました。

つまり、読みづらい。*4

私は「仕事で使うものなら誰でもはっきり見えるものを使いたい!」ので、その対策を探しました。
その方法の1つ、ワークブックのデータを好きなフォントに変更するマクロをご紹介します。

マクロ

コード

フォント名やフォントサイズの値は、お好きなものに変更してお使いください。

Sub setFont()

    '変数の定義
    Dim strFontName As String: strFontName = "Meiryo UI"
    Dim numFontSize As Long:   numFontSize = 9

    'Excelアプリケーションの標準フォントを変更
    With Application
        .standardFont     = strFontName
        .standardFontSize = numFontSize
    End With

    '各シートのフォントを変更
    Dim ws As Worksheet
    For Each ws In Worksheets
        With ws.Cells.Font
            .Name = strFontName
            .Size = numFontSize
        End With
    Next ws

    'メッセージボックスを作成
    Dim msg As String
    Dim strFontSize As String: strFontSize = CStr(numFontSize)

    msg = "設定を変更しました。"
    msg = msg & vbCrLf & "----------------------------"
    msg = msg & vbCrLf & "▼Excelアプリケーション"
    msg = msg & vbCrLf & "フォント:" & strFontName
    msg = msg & vbCrLf & "サイズ:" & strFontSize & " px"
    msg = msg & vbCrLf & "(ダウンロードしたファイルには反映されません。)"
    msg = msg & vbCrLf & "----------------------------"
    msg = msg & vbCrLf & "▼ワークシート"
    msg = msg & vbCrLf & "フォント:" & strFontName
    msg = msg & vbCrLf & "----------------------------"

    'メッセージボックスを出力
    MsgBox msg
    MsgBox "設定反映のため、Excelを再起動してください。"

End Sub

処理の流れ

  1. Excelアプリケーションの標準フォントを変更。

    オブジェクト プロパティ 設定値
    Applicationオブジェクト standardFontプロパティ Meiryo UI
    standardFontSizeプロパティ 9px
  2. ブックに含まれるシート1枚ずつ、シート内の全セルを変更。

    オブジェクト プロパティ 設定値
    Rangeオブジェクトから取得したFontオブジェクト Nameプロパティ Meiryo UI
    Sizeプロパティ 9pxI
  3. 変更の結果をメッセージ出力。
    ※1.が設定変更された場合は反映させるためにExcelの再起動が必要になるので、その旨も追記。

補足

すぐ使えるようにしよう

この部分↓は、フォントの違うブックに遭遇する度に適用したくなると思います。(極端?)

'シートの標準フォントを変更
Dim ws As Worksheet
For Each ws In Worksheets
    With ws.Cells.Font
        .Name = strFontName
        .Size = numFontSize
    End With
Next ws

毎日起動するブック個人用マクロブックなどに登録して、いつでも呼び出せる状態にしておくのをおすすめします。

デキる方は、アドイン化しても良いのかもしれません。

標準フォントとは?

シートを新規作成した時の既定のフォントとして設定されます。

また、この↓赤枠部分のように、列番号、行番号のフォントも変わります。

※雑でごめんなさい…
行番号と列番号

ただし、これが適用されるのは、自分で新規作成したExcelファイルのみです。
それ以外のファイルは残念ながらガマンです…
本当は他の人から貰ったファイルにも反映してくれると嬉しいんだけどなぁ。

注意!

フォントを変更する際は、必要なフォントが端末にインストールされていることを事前に確認してから実行してください。

例えば、Windows XPなど古いOSの端末ではそもそもMeiryo UI自体がインストールされていないので、フォントの変更は反映されません。
オリジナルの業務アプリを使っているとかでなければ、ここまでの環境はもう流石に無いかとは思います*5が、頭の片隅に置いておいていただけたら幸いです。

豆知識

さて、今回は記事のテーマからそれるのでコードには入れていませんが、追加情報を更にいくつかご紹介します。

行の幅も調整しましょう

行の幅を少し広めにとった上で下揃えにすると、「文字が詰まってる感」が減って少しだけ見栄えよく&読みやすい表になります。*6
※今回のように「Meiryo UIで9px」の場合、行の幅は「22~24px」がオススメです。

  • 変更前(フォントを9px、幅や高さは自動調整)
    高さの変更前

  • 変更後(フォントを9px、行の高さを24pxに指定)
    高さの変更後

変更前と後でフォントはいじってませんが、若干「余白」が増えて見た目に少しスッキリしたのが伝わるでしょうか?

予実分析のようなデータが詰まった表形式でデータを提出する時などに、可読性の面で力を発揮すると思います。良かったらお試しください。

ギザギザ・フォントのワケ

PCディスプレイの解像度が上がると、それまで問題なく見られていたフォントが「ギザギザして見える」ようになることがあります。
これはフォントのエイリアスによるもので、フォントによって設定度合いが異なります。

これに対して「ギザギザにならないように工夫したもの」をアンチエイリアスフォントと呼び、最近のものはこの点を考慮されたものが多いようです。

アンチエイリアスフォントは、アンチエイリアシングを施したフォントのことです。 白黒2値の点で構成されているビットマップフォントは斜線部、曲線部に階段状の ギザギザ(ジャギー)が生じてしまいますが、これを目立たなくするために文字の背景色と 文字色の境界に中間色を補うこと(アンチエイリアシング)によって、低解像度でも滑らかに 見せるよう工夫されたフォントです。

アンチエイリアスフォントのこと|フォントのこと|株式会社カルチ

理想のフォントを検索するときに、キーワードで使ってみると良いかもしれません。

もっと、ゴシック体を追い出したい方へ

Excelから追い出す

ことりちゅん (id:Kotori-ChunChun)さんより、今回の方法以外にもExcelから可能な限り游ゴシックを追い出す方法が詳しくご紹介されています。

良かったらこちらもご参照ください。
太ったり痩せたりすることりさんがかわいいです。 Excelから游ゴシック体を徹底的に駆逐する Part1 - えくせるちゅんちゅん

<2019/07/21追記>
人気記事のようで、その後Part3まで出てます!
マクロ1つで徹底的に排除できるようです!すてき!

PCから追い出す

Excelに加えて、メニューバーやタイトルバー等のWindowsのシステムフォント自体も変えたい方は、以下2つの記事も役に立つと思います。

私は、後者を参考に「Noto Sans CJK JP」というフォントを設定しています。
<2019/07/21追記>
その後、フォントは丸ゴシック系の「Kosugi Maru(MotoyaLMaru)」に変更しました。
※画像も置き換えています。

サイズと太さを調整して、視認性が上がりました。
システムフォントを変更した結果

ただし、下記画像のようにWindowsの設定(システム設定)」など一部変わらないところもあります
残念ながら「Windowsの設定(システム設定)」画面はフォントが変わりません…

こちらを除けば、端末内のほぼ全てを好きなフォントに変えることができます。
※変わらないところは、そもそも頻繁に触らない部分なのでストレスに感じるほどでは無いはず…

後記(持論も少し…)

こういうこと気にするような方が私の周りには居ないようで、私が気にし過ぎなだけなのかなと不安に思ったりもします…
でも、ネット上には結構情報があるのです。同志を見つけたような気持ちで安心します。

もちろん「1番大事なのは本文」です。
しかし、業務でExcelを使うのであれば、誰かに見せるほどでもないファイルでも、表を作ったりマクロを書いたりで「長時間触れる」ことも必ずあるはずです。
それであれば、作業者自身がデータを探しやすいようにすることで、作業をかなりラクにすることができます。
これも時短テクニックの1つと考えています。

個人の好みも大きいので、どのフォントが良いかまで強制するつもりはありませんが、プレゼン資料を作るときに相手に伝わりやすいように見た目を調整するのと同じように、Excelで表を作るときも「どう見えるか」を考えてほしいなと思うのです。
こういうこと言うと「神経質なやつ」扱いされるのですが…あ、ネ申エクセルは別の話ですよ。

…というわけで、つい話が長くなってしまいますが、「見せるデータ」に集中するために「事前に出来ることは済ませてしまおう!」というお話でした。

ここまでお付き合いいただきありがとうございました。ちゃんちゃん。

注釈

*1: このブログでは読みやすいようにフォントを変えてみています。
が、個人的に少し冷たい感じがしているので後で戻すかもしれません。
Mac風フォントの「美しさ」がイマイチ理解できないようなデザイン音痴ですが…

<2019/07/21追記>
その後、丸ゴシック系の「Kosugi Maru(MotoyaLMaru)」に再度変更しました。

*2: よく分からない方は是非ググってみてくださいね!
これだけでもかなり改善はするはずです。

*3: PC新調して解像度が上がってから、Meiryo UIさえギザギザに見えるようになってきたので、これもそろそろ潮時かもしれない(悲)

*4: 「あなたの眼が悪いだけでは?」と言われたらそれまでですが…

*5:そもそもこの場合は「OS自体も早くアップデートした方が良い」とは思います。

*6:参考書籍

【Visual Studio Code】ほぼテキストエディタとして使っている私の設定を晒してみます。

ちょっとカッコつけて楽にPC触りたいとかいう不純な動機で2か月ほどVS Codeを触ってみました。

たまにショートカットキーが反応しないような気はするものの、色分けや入力支援のおかげで感触は上々です。
<2019/05/14追記>
(→「IME」をオフにして「英文入力モード」にしたら使えるようになりました!!)

本来はプログラミングに使うためのエディタですが、Markdownのおかげで現状ほぼ只のテキストエディタと化している私の設定内容を公開してみたいと思います。

拡張機能プラグイン)の追加

VS Codeの設定

Japanese Language Pack for Visual Studio Code

初期設定は全編英語です。日本人なので、まずは何も考えず日本語化します。

インストール後、言語の設定変更が必要です。
詳しい手順はページ下方のリンク先をご参照頂ければと思います。

Settings Sync

GitHubのアカウントを使ってほかの端末とVSCodeの設定を同期できます。
設定ファイルの中身(settings.jsonなど)をGistにアップロード/ダウンロードすることで、データを管理します。

詳しい手順は、ページ下方のリンク先をご参照ください。

※同期完了後のメッセージ。
同期完了後のメッセージ

Material Theme

配色テーマとアイコンテーマがいっぱい。

インストール後、テーマの選択が必要です。
直接setting.jsonで設定する場合は、ページ下方をご参照ください。

現在は以下の設定で使っています。

  • 配色テーマ
    Material Theme Palenight High Contrast
    Material Theme Palenight High Contrast

  • ファイルアイコンのテーマ
    VSCode Icons
    vscode-icons

入力支援

Bracket Pair Colorizer

カッコ(Bracket)を階層ごとに色分けしてくれます。
Bracket Pair Colorizer

Path Intellisense

ファイルのパスを入力補完してくれるようになります。
Path Intellisense

Trailing Spaces

行末にある余計なスペースを強調表示します。
Trailing Spaces

設定の変更(settings.json

配色テーマ・ファイルアイコン

前述のMaterial Themeを適用させます。

"workbench.colorTheme": "Material Theme Palenight High Contrast",
"materialTheme.accent": "Pink",
"workbench.iconTheme" : "vscode-icons"

2行目の"materialTheme.accent": "Pink"は反映されてない気がする…

VSCode全体の表示

タイトルバー

プロジェクトルートからの相対パスを表示してもらいます。

"window.title":
   "${dirty}${appName}${separator}${rootName}${separator}${activeEditorMedium}",

${dirty}で、ファイルの変更を保存していないときに●記号がつきます。

  • Visual Studio Code - 02_Documents - 03_Languages\Javascript\test1.js

ミニマップ
"editor.minimap.enabled": false,
サイドバーの位置
"workbench.sideBar.location": "left",

エクスプローラ

バッジの有無

機能が分からないのでとりあえずコメントアウトで解除しています…

// Controls if file decorations should use badges.
// "explorer.decorations.badges": false,
ファイルの並び順
"explorer.sortOrder"    : "filesFirst",
 
// 検索欄
"search.showLineNumbers": true,
"search.sortOrder": "fileNames",

エディタ・ウィンドウ

カーソル

カーソル(縦棒|タイプ)の太さを調整します。

"editor.cursorWidth": 2,
フォント

<2020/09/23追記>
フォントNoto Sansは只今試用中…
Roboto MonoMotoyaLMaru W3 monoに変更しました。

"editor.fontFamily"   :
  "'Roboto Mono', 'MotoyaLMaru W3 mono', 'HackGen', 'Consolas', 'Meiryo UI'",
"editor.fontSize"     : 16,
// Enables font ligatures(合字、記号の結合)
"editor.fontLigatures": true,
文字の間隔

letterSpacingで横方向、lineHeightで縦方向です。

"editor.letterSpacing": 0.5,
"editor.lineHeight"   : 20,
インデント

スペース派です。

"editor.renderIndentGuides": true,
"editor.tabSize"           : 2,
"editor.tabCompletion"     : "on",
目安線

「●●文字目」を配列で指定します。

"editor.rulers": [60, 70, 80],
ファイルの最終行

最終行に改行の追加を強制

  • 言語ごとの改行コードと合わせて設定します。
  • Windowsの場合は、何かとトラブルの元なのでfalseでも良いかもしれません…
"files.insertFinalNewline": true,

Markdown

プレビュー

本当は自動で左側に置けるともっと嬉しいな。

"markdown.preview.breaks"    : true,
"markdown.preview.fontFamily":
  "'Roboto Mono', 'MotoyaLMaru W3 mono', 'Meiryo UI', sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', 'Droid Sans', 'Ubuntu'",
"markdown.preview.fontSize"  : 16,
"markdown.preview.lineHeight": 1.3,

HTML

タグの自動フォーマット関連です。
あんまり理解してなくて長くなってしまうので、詳しくはページ下方のリンク先をご参照ください。

<2020/09/23追記>
その後、設定を色々変更したので、以下は新しい情報に修正してあります。

フォーマッタの選択

既定のHTMLフォーマッタを有効/無効にします

"html.format.enable": true,

Prettierはうまく使えなかったので、VSCode純正のもので…

再フォーマット禁止
  • タグ

    • カンマ区切りで指定します。
    • nullと設定した場合は、リンク先にあるものが既定値になります。
"html.format.unformatted": "span, img",
  • コンテンツ(本文など)

    • カンマ区切りで指定します。
    • nullと設定した場合は、既定値の<pre>タグを表します。
"html.format.contentUnformatted": "p, span, a, pre, code, textarea",
セクションごとの区切り

直前に改行を1つ入れるタグを指定します。

  • カンマ区切りで指定します。
  • nullと設定した場合は、既定値の"head, body, /html"を表します。
"html.format.extraLiners":
  "html, body, header, footer, script, style, title, h1, h2, h3, h4, h5, h6, form, table, /div",
インデント

<head>セクションと<body>セクションをインデントします。

"html.format.indentInnerHtml" : true,
"html.format.indentHandlebars": true, // 書式設定とインデント{{###foo}}および{{/foo}}。
属性の折り返し

タグの属性ごとに区切って折り返しするか等を設定します。

"html.format.wrapLineLength": 70,     // 上限
"html.format.wrapAttributes": "auto", // 上限を超えたら折返し
改行
  • 要素の前にある既存の改行を保持するか

    • タグの内側やテキストに対しては機能しません。
"html.format.preserveNewLines": true,
  • 連続する改行をいくつまで保持するか

    • 無制限にするには、nullを指定します。
"html.format.maxPreserveNewLines": 1,

Gitまわり

"git.defaultCloneDirectory": "D:\\02_Documents",
"git.branchSortOrder"      : "committerdate",
"sync.gist"                : "xxxxxxxxxx",

参照

以下のページを参考にさせて頂きました。

  • 日本語化

  • 配色関連

  • 設定の保存、同期

  • 拡張機能、設定

後記

Web開発等はやっていないとは言え、現状ほとんどブログかプログラミング問題の下書きにしか使っていないので宝の持ち腐れ感もりもりです。

ただのテキストエディタとして使うなら、わざわざ選択するメリットは「画面を暗く出来て目に優しい!」くらいかもしれませんが、難しいことナシに好みに合わせて色々カスタマイズ出来るのが楽しいですね。

あと、プログラマーさん達による「初心者向け」の情報が多いのもポイント高し。

また、Node.jsが使えると自分でも拡張機能を作れるみたいです。
機能本体はどの言語でもOKのようで、JavaScriptでも出来るそうなのでノンプログラマーでもチャンスはあるかもしれません。

私はまだアイディアや技術力が無いので実際やるかどうかは別ですが…いつか出来たら楽しいかも。

先延ばしっぱなしですが、ノンプログラマーとしてはVBAGASも近々試してみたいと思います。

【Google Analytics × Slack】GASを使ってレポートをSlackに自動送信させてみた。

この2週間ほど、ブログを多めに書いてみました。
時間はまちまちだけど、ほぼ毎日エディタ画面に向かっています。

このブログを作った頃にとりあえずGoogle Analyticsは入れていたのですが、大してアクセスは無いだろうし、性格的にも絶対気にし過ぎてしまうので見ないように見ないようにして放置していました。
そもそも、ページビューとセッションの違いとかもよく分からないし…

でも、1年経ったし記事が増えてきたらやっぱり気になってきたので、少しずつ読み方を勉強しつつ、こちら↓の記事*1を参考にSlack用のGoogle Analyticsのレポートを作成してみました。

あああー、これで一喜一憂の日々の始まりだー!

完成イメージ

今回は、こんな感じでメッセージが投稿されるように設定していきます*2

※文字が小さかったので差し替えました。
メッセージの完成イメージ

準備

Spreadsheet

1. Google Analyticsのアドオンを追加

参照記事にならってGoogle Analyticsを使います。

使い方やメトリクス、ディメンションの中身は隣IT様を見ていただくとして、入れてみたデータはこちら。

Daily Report 流入別セッション数※1 記事別ページビュー
Start Date※2 ブログ開始日 ブログ開始日 =TODAY()-1
End Date※2 =TODAY()-1 =TODAY()-1 =TODAY()-1
Metrics ga:pageviews,
ga:sessions,
ga:pageviewsPerSession,
ga:users,
ga:bounceRate,
ga:avgSessionDuration
ga:sessions ga:pageviews,
ga:users,
ga:sessions
Dimensions ga:date ga:date,
ga:channelGrouping
ga:pageTitle,
ga:pagePath
Order -ga:date -ga:date -ga:pageviews

メトリクスを多少並び替えて、ソートも追加しました。

※補足

  1. 流入別セッション数」はSlackには送っていませんが、ご参考まで…
  2. Start DateとEnd Dateは、将来的に手修正とかで数値がブレてしまわないように、別シートに分けておいて「絶対参照」としています。
2. ダウンロードのスケジュールを設定

3つとも下記のように設定しています。

頻度 実行時刻
1日1回 早朝(4時~5時)
3. コンテナバインドスクリプトを作成

ファイル名はAnalyticsReportとしました。

4. ライブラリを追加

先人の知恵に感謝です!

  • SlackAppライブラリ
    Libraryキー:M3W5Ut3Q39AaIwLquryEPMwV62A3znfOO

  • Momentライブラリ
    Libraryキー:MHMchiX6c1bwSqGM1PZiW_PxhMjh3Sh48

Slack

1. アクセストークンを取得

こちらの「2.3 Slack API Tokenの取得」を参考に、トークンを取得しておきます。

2. プロパティ・サービスでSlackのトークンを登録

先ほど作成したスクリプトを開いて、スクリプト・プロパティ(もしくはユーザー・プロパティ)にトークンをSLACK_ACCESS_TOKENとして登録します。
登録方法は、アクセストークンの参照記事(上記)をご覧いただければ…

スクリプト

コード

一部未完成(後述)ですが、ひとまずレポートは作成出来たのでokということにします。

スクリプトファイルは1.と2.に分けて作成しました。

1. メイン(メッセージの作成)
メッセージをまとめる
function SendAnalyticsReportToSlack() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  
  // レポート
  var dailySheet   = ss.getSheetByName('DailyReport');
  var reportValues = dailySheet.getDataRange().getValues();
  
  const YESTERDAY_INDEX = 16;
  var yesterdayRow = YESTERDAY_INDEX - 1;
  var report       = createAnalyticsReport(reportValues[yesterdayRow]);
  
  // 記事別ランキング
  var rankingSheet  = ss.getSheetByName('記事別ページビュー');
  var rankingValues = rankingSheet.getDataRange().getValues();
  
  var rankingMessage = createRanking(rankingValues);
  
  // メッセージをまとめる
  var message = ':male-construction-worker: < おはようございまーす。昨日の成績ですよー' + '\n'
              + report + '\n'
              + '\n'
              + rankingMessage;
  
  //Slackにポスト
  const POST_CHANNEL_NAME   = 'xxxxxxxx'; // 投稿先のチャンネル名
  const DISPLAYED_USER_NAME = 'xxxxxxxx'; // botの名前(表示用)
  postMessage(message, POST_CHANNEL_NAME, DISPLAYED_USER_NAME);
}
メッセージのレポート部分を生成する
/**
 * Slack投稿用メッセージのレポート部分を生成する
 *
 * @param {Array} DailyReportの1次元配列[
 *   ga:date, ga:pageviews, ga:sessions, ga:pageviewsPerSession, ga:users, ga:bounceRate, ga:avgSessionDuration
 *   ]
 */
var createAnalyticsReport = function(rowValues) {
 // 読みやすいように値を加工
  var gaDate                = Moment.moment(rowValues[0]).format('YYYY/MM/DD (ddd)');
  var gaPageviewsPerSession = Number(rowValues[3]).toFixed(2);
  var gaBounceRate          = Number(rowValues[5] * 100).toFixed(1);
  var gaAvgSessionDuration  = Number(rowValues[6]).toFixed(1);
  
  var report = '*▼' + gaDate + '*' + '\n'
             + '```'
             + 'Pageviews             : ' + rowValues[1]          + ' views'         + '\n'
             + 'Sessions              : ' + rowValues[2]          + ' sessions'      + '\n'
             + 'Pageviews/Session     : ' + gaPageviewsPerSession + ' pages/session' + '\n'
             + 'Users                 : ' + rowValues[4]          + ' people'        + '\n'
             + 'BounceRate            : ' + gaBounceRate          + ' %'             + '\n'
             + 'Session Duration(Avg.): ' + gaAvgSessionDuration  + ' sec.'
             + '```';
              
  return report;
}
メッセージのランキング部分を生成する
/**
 * Slack投稿用メッセージのランキング部分を生成する
 *
 * @param {Array} 記事別ランキングの2次元配列
 *   [ga:date][ga:pageTitle, ga:pagePath, ga:pageviews, ga:users, ga:sessions]
 */
var createRanking = function(values) {
  
  var emojis = [
    ':one:', ':two:',   ':three:', ':four:', ':five:',
//  ':six:', ':seven:', ':eight:', ':nine:', ':keycap_ten:'
  ];
  var ranking = '*▼デイリーランキング*' + '\n';
  
  for(var i = 0; i < 5; i++) {
    const FIRST_DATA_ROW = 15;
    var tempRow          = FIRST_DATA_ROW + i;
  
    var tmp = emojis[i] + values[tempRow][0].replace(' - ゆるオタクのすすめ', '') + '\n'
            + 'https://yuru-wota.hateblo.jp' + values[tempRow][1] + '\n'
            + '```'
            + values[tempRow][2] + ' pv, '
            + values[tempRow][3] + ' people, '
            + values[tempRow][4] + ' sessions.'+ '\n'
            + '```' + '\n'
            + '\n';
    ranking += tmp;
  }
  return  ranking;
}
2. Slackにポスト
投稿を行う
/**
 * メッセージをSlackに投稿する
 *
 * @param {string} 作成したSlackメッセージ
 * @param {string} 投稿先のチャンネル名
 * @param {string} 投稿に表示するユーザー名
 */
function postMessage(m, channelId, userNameForDisplay) {
  var accessToken = PropertiesService.getScriptProperties().getProperty('SLACK_ACCESS_TOKEN');
  
  // SlackAppインスタンスの取得
  var slackApp = SlackApp.create(accessToken);
  
  slackApp.postMessage(
    channelId,
    m,
    {username: userNameForDisplay}
  );
}

トリガーを設定

Spreadsheetの更新後にSendAnalyticsReportToSlack関数が実行されるように設定しています。

頻度 実行時刻
1日1回 午前6時~7時

完了。

これで設定完了です。

補足

スクリプトファイルの分け方

ちょっと長くなってしまっているので、メッセージの生成とSlackへのアクセスは「別のアクション」と判断して、一応スクリプトファイルを分けて書いています。

でも、今回の用途だと2.のファイルにアクションを追加することは無さそう*3です。
それなら1つのファイルにしてしまっても良いかもしれません。

記事修正の副作用

ちょっと脱線ですが、今回のレポートは先日のPowerShellの記事をリライトしながら作成していたのです。

書きながら調べる途中で、記事投稿後にURLやタイトルを修正したら別物として集計されてしまうことも知りました。まじですかスカ!*4

タイトルはともかく、URLも最近結構いじりましたよ…
その後もちょこちょこGoogleから変更前のページにアクセスがあって「変だな」とは思ってたのですよ…
試しに検索してみたら旧URL残ってるし。

この間にアクセス頂いていた方、申し訳ありません。そしてありがとうございます…!
この記事も読んで頂いているかは分かりませんが…

Search Consoleなるもので申請出来ることは分かったものの、反映されるかは保証が無いみたいなので、とりあえず旧URLから新記事に誘導する形で対応させていただきます。

疑問

Slack APIの使い方

今回は以前個人的に作ったBotを流用したのでトークンを取得していますが、メッセージを自動送信するだけならIncoming Webhooksで充分で、わざわざ認証しなくても出来るみたいです。
なんでも、送信先のURLさえ分かれば何処でも誰でも使えるとかで。

と言っても、そもそも私自身が違いをよく分かってない気がしますが…
Slackのドキュメントも読んでみたのですが私の英語力では解読できませんでした…(悲)

関数の引き数

各シートからgetValues()してから各関数に渡しています。
でも、その後シートの操作は特に無いのです。
可読性的には、変数ssを直接渡すのとどっちが良いのでしょうかね…?

桁揃え

レポートの数値が左揃えでちょっとカッコ悪いので、桁を揃えたい(願望)。

Google先生に聞いてみたところ、「半角スペース(\u0020)でエスケープ」というところまでは理解しました。
String型に変換して連結してみたのですが、Logger.Log()には反映されるのに送信メッセージに反映されませんでした…なんで…?

■メモ

/* --------------↓メモ↓-------------- */
// 余裕が出来たら、レポート↑を桁揃えしたい
function showSpace() {
  var num = 1;
  Logger.log('【\u0020%s】', num);
  Logger.log('【%s\u0020】', num);
  
  num = String(1);
  Logger.log('【\u0020%s】', num);
  Logger.log('【%s\u0020】', num);
}
/* --------------↑メモ↑-------------- */

これだと結果は同じなんだけどな…

噂?

1日50PVをキープ出来たら上位20%っていう記事を見たけどホント…?
途中で辞めちゃう人が多いとかの統計マジックではなくて…????

どっちにしても私にはずっと遠い話ですけども…しばらく目標に…

感想

隣ITの記事を見ながら作業してると数字の違いに少し悲しくなってしまうのですが笑、この2週間ほどでPVも見てくださっている方も徐々に増えているみたいで結構嬉しいです。皆様ありがとうございます!!

問題は、これがいつまで続けられるかですが…!
毎日コンスタントに更新するのは結構大変だなぁと思いました。

PCに向かう習慣はともかく、「日々情報収集して、その日のうちに更新して」っていう、このスピード感が結構たいへん。
ライターさんってすごい。

「無駄にこだわる」悪い癖*5が消せたら良いのですけど、それ以外の部分で効率化も考える必要がありそうです。
これも鍛えたら強くなれるのでしょうか…

注釈

*1: 本当は同じ連載の2記事目をリンク先にさせて頂きたかったのですが、何故かカードが反映されないみたいです…連載目次からさかのぼってご覧いただければと思います。

*2: 数値がしょっぱいけど晒しちゃえ。

*3: あるとすればスラッシュコマンドとかで検索出来るようにするくらい?

*4: by モーニング娘。
モーニング娘。 『まじですかスカ!』 (MV)

「まじで」まで打ったら予測変換で出てきた(驚)Windows優秀ですねぇ。

*5: それでクォリティが上がっていれば良いけど、現実はそうでもない…

【おまけ】PowerShellで出来ることを調べてみよう

前回までの記事で、コマンドでパッケージ管理を始める手順をお伝えしました。
【PowerShell】パッケージ・プロバイダからパッケージを貰おう - ゆるおたノート

ただ、これだけでも今後の工数をかなり減らせるのですが、パッケージをインストールするということは、いつかはアンインストールしたくなる可能性もありますよね。

それに、他にもどんなコマンドがあるか気になりませんか?

というワケで、ここまでの記事中でご紹介しきれなかったコマンドを調べる方法を「おまけ」としてご説明します。
良かったらご覧ください。

PowerShellで使えるコマンド

コマンド一覧を確認する

ここまで当連載を読んでいただいた方ならもうお分かりかもしれませんが、PowerShellで使えるコマンドは、以下の1行で一覧を呼び出せます。

get-Command

これを実行すると、ダーッとPowerShellで使えるコマンドの一覧が表示されます。

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Alias           Add-AppPackage                                     2.0.1.0    Appx
Alias           Add-AppPackageVolume                               2.0.1.0    Appx
Alias           Add-AppProvisionedPackage                          3.0        Dism
 
# (超長いので中略)
 
Function        Z:
Filter          more
Cmdlet          Add-AppxPackage                                    2.0.1.0    Appx

英語やPC操作の得意な方であれば、これを使えば「PowerShellで何ができるか?」は何となく分かるでしょう。

コマンドの意味を知る方法

ズバリ、公式に聞こう

もちろん、英語もPCも得意ではない方でも、内容を知る手段はあります。
ご安心ください。

Windows社から、公式ドキュメント*1が公開されています。
PowerShell ドキュメント - PowerShell | Microsoft Docs

TOPページにあるReferenceというページで、各コマンドの詳細を確認することができます。
<2020/10/01追記>
その後ドキュメントが全体的にリニューアルされ、リファレンス*2PowerShell モジュール ブラウザーというページに移行したようです。
PowerShell モジュール ブラウザー - PowerShell | Microsoft Docs

こちらで各コマンドの詳細を確認することができます。
辞書や他のWebサイトと組み合わせて活用しましょう。

ただし!
このサイトが用意した日本語表記だと、ちょっと読みづらいことがあります…(これは個人の感想ですが…)
できれば、一旦ドキュメント上で「英語表記」をオフにして、ブラウザ側の翻訳機能で日本語に変換してから使うことをオススメします。

検索のコツ

オプションを使って、結果を絞り込む

パッケージの検索(過去記事)と同じように、コマンドにオプションを指定して情報を絞り込むこともできます。

例えば、「パッケージ」に関するコマンドを調べるときには、こんなコマンドが使えます。

get-Command -Name *-Package*

-Packageという文字列を含むコマンドは何があるか教えて~」という意味になります。
パッケージに関するコマンドをいっぺんに聞いている状態です。
ワイルドカードについては、過去記事をご参照ください。

こちらを実行すると、下記のような結果が戻ってきます。

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Cmdlet          Find-Package                                       1.0.0.1    PackageManagement
Cmdlet          Find-PackageProvider                               1.0.0.1    PackageManagement
Cmdlet          Get-Package                                        1.0.0.1    PackageManagement
Cmdlet          Get-PackageProvider                                1.0.0.1    PackageManagement
Cmdlet          Get-PackageSource                                  1.0.0.1    PackageManagement
Cmdlet          Import-PackageProvider                             1.0.0.1    PackageManagement
Cmdlet          Install-Package                                    1.0.0.1    PackageManagement
Cmdlet          Install-PackageProvider                            1.0.0.1    PackageManagement
Cmdlet          Register-PackageSource                             1.0.0.1    PackageManagement
Cmdlet          Save-Package                                       1.0.0.1    PackageManagement
Cmdlet          Set-PackageSource                                  1.0.0.1    PackageManagement
Cmdlet          Uninstall-Package                                  1.0.0.1    PackageManagement
Cmdlet          Unregister-PackageSource                           1.0.0.1    PackageManagement

インストールせずにパッケージの保存のみ行うSave-Packageや、アンインストールするUninstall-Packageなど、色々コマンドがあることが分かります。

困ったら聞いてみる

このように、困ったら「何が出来るのか」を直接PowerShellに質問してみることで、私たちからの指示の選択肢を増やすこともできます。

多少コツは要りますが、これが対話モード(過去記事)の便利なところですね!

その上で、辞書や先述のリファレンスを使ってコマンドの意味を調べていくと、より具体的な指示が出せるようになるはずです。

実は…

当連載記事では、分かりやすさのために敢えて「コマンド」という言葉を使ってきましたが、通常、PowerShell用のコマンドはコマンドレットと呼びます。
先ほどのコマンド一覧で出てきたCmdletも、その略語です。

他のブログやWebサイトではそのように呼んでいるものもあるかと思いますので、良かったら頭の片隅に置いておいてください。

まとめ

PowerShellのコマンドレットは結構たくさんあり、CUIひとつで色々出来ることはお分かりいただけたかなと思います。

私もまだほんの一部しか勉強できていませんが、時間のある時にでもゆっくりさらっと眺めておくと効率化のアイディアが浮かんでくるかもしれません。
便利なワザがあったら、是非教えてください。

それでは!

このシリーズについて

黒い画面(CUI)でPCを操作するテクニックをご紹介。

以下の方を対象に、Windows10のPowerShellを使ってパッケージを導入する手順をお送りしています。

プログラムには少し興味があって仕事でVBA触ったりはするけど、
『あの』黒い画面はちょっと怖くて触れない…という方。

疑問点・不明点がありましたら、コメント欄で頂ければと思います!

連載目次

  1. 【PowerShell】プログラムのプの字も知らない事務職が、黒い画面と格闘した話。 - ゆるおたノート
  2. 【PowerShell】超簡単にソフトを取り込む準備を始めよう - ゆるおたノート
  3. 【PowerShell】「そもそもパッケージって何?」っていうあなたに、少し説明します。 - ゆるおたノート
  4. 【PowerShell】コマンドを使ってパッケージ・プロバイダをインストールしよう - ゆるおたノート
  5. 【PowerShell】パッケージ・プロバイダからパッケージを貰おう - ゆるおたノート
  6. 当記事【おまけ】PowerShellで出来ることを知って、もう1つだけレベルを上げよう - ゆるおたノート

注釈

*1: ここで言う「ドキュメント」とは、windows社の用意した公式の説明書のようなものです。

*2:一般に「参照先」などの意味で使われる単語ですが、ここではプログラミング言語の辞書と思ってください。

【PowerShell】パッケージ・プロバイダからパッケージを貰おう

前回は、PowerShellを使ったパッケージ・プロバイダのインストール手順をご紹介しました。 【PowerShell】コマンドを使ってパッケージ・プロバイダをインストールしよう - ゆるおたノート

これで、ソフトウェアを入れる準備は完了しています。
このままアプリケーションのパッケージを入れちゃいましょう。

パッケージのダウンロードとインストール

パッケージの検索。

まずは、入れたいソフトがパッケージとして配布されているかを確認します。
今回は自分のPC内ではなく外部のネットワークから検索するので、今度はgetではなくfindを使います。

それを踏まえて、下記のように入力してみましょう。

find-Package -Name *** # ←***の部分に好きなソフトの名前を入れてみてください。

検索したパッケージが公開されていたら、一覧に出力されます。
試しに、私もEvernoteで検索してみました。

Name                           Version          Source           Summary
----                           -------          ------           -------
evernote                       6.17.7.8474      chocolatey       A cross-platform, freemium app designed fo...

evernoteというパッケージが1件ヒットしました。

chocolateyというパッケージ・プロバイダで、evernoteなるパッケージのver. 6.17.7.8474が配信されているようです。
※バージョンは執筆・編集時点のものです。

今回は、これをダウンロードしてみます。
以下、必要に応じてEvernoteの部分を欲しいソフトウェアの名前で読み替えてお試し下さい。

パッケージをダウンロード。

検索してヒットしたら、最近の更新状況を確認してみましょう。
Google先生Chocolatey パッケージ名と検索して、Chocolatey Galleryを開いてみてください。

こちらはEvernoteのパッケージ(ver. 6.17.7.8474)のページです。
最新のEvernoteパッケージ

英語表記ですが、赤枠のところを確認すると、最新の更新は半月ほど前の2019年4月19日に承認されたようです。

続いて画面を下の方にスクロールして、このパッケージについてのコメントを読んでみます。
これも英語ですが、現在のバージョンについて特に異常の報告は挙がってなさそうなので、このままダウンロードしたいと思います。

それでは早速、PowerShellさんに戻って以下のように依頼します。

# プロバイダー名は省略可
install-Package Evernote -ProviderName chocolatey

こちらのコマンドは、chocolateyという名前のプロバイダから配布している、Evernoteというパッケージをインストールして」という意味です。

このようにオプションとして-ProviderName以下を付け加えることで、使用するパッケージ・プロバイダを指定してダウンロードすることができます。
パッケージを検索したら同じ名前のものが複数のプロバイダから配信されている、といった場合に有効です。

もちろんコレは省略することも可能なので、数が少ないときはあえて書かなくても問題は無いでしょう。

…インストール?

ところで勘の良い方は気付いているかもしれませんが、今やりたいのはダウンロードなのに、コマンドはinstallです。

…何故だと思いますか?

パッケージの操作では、パッケージ・プロバイダの場合とは違いinstall-Packageダウンロードからインストールまで一気に行うコマンド*1だからです。

そういうワケで、実行したらそのままインストールまで進んでしまうことも頭の片隅に入れて操作しましょう。
悪意のあるコンテンツが入っていないとも限らないので…

パッケージをインストール。

さて、先ほどのコマンドを実行するとこのように↓ポップアップが出ます。

パッケージは、信頼済みとマークされていないパッケージ ソースから取得されています。
'chocolatey' からソフトウェアをアンインストールしますか?
[Y] はい(Y)  [A] すべて続行(A)  [N] いいえ(N)  [L] すべて無視(L)  [S] 中断(S)  [?] ヘルプ (既定値は "N"): a 'と入力すればOK

何故か「信頼済みとマークされていないパッケージ ソース」とか「アンインストールしますか?」とか言われるのですが、これは誤訳みたいです。
なかなか怖いこと言いますよねぇ…

そこで、ここは違和感をグッとこらえてa(すべて実行)を選択して、実行します。
その後はインストールが終わるまで少し待ってください。
パッケージの容量によって異なりますが、数秒~数分程かかります。

インストールが完了したら、「デスクトップ」のショートカットかスタートメニューの「最近追加した項目」に表示されていると思うので、確認しましょう。

なぜわざわざこんなことを言うのかと言うと、
動作が不安定らしく、ダウンロード出来てないのにエラーメッセージすら出ないこともあるからです…

念には念で、コマンドでも確認してみましょう。
ねんねん ねんね ねんねんねん ねねん♪*2

get-Package -Name Evernote

こんな風に出ました。

Name                 Version          Source                           ProviderName
----                 -------          ------                           ------------
evernote             6.17.7.8474      C:\Program Files (x86)\Everno... chocolatey

CドライブのProgram Files (x86)というフォルダに、chocolateyを使ってインストール出来ています!

これで大丈夫!やったー!!

もう1度復元ポイントを作成

最後に実行ポリシーをRestrictedに戻して、成功した記念にもう1度復元ポイントも作成しておきましょう。

転ばぬ先の杖ということで!

ここまで出来たら、パッケージのインストールは完了です!
お疲れさまでした!!!

そういえば…

このPCに入っているのは?

パッケージ・プロバイダの時と同じように、名前を指定せずに実行すると「このPC内のパッケージ全て」という情報を取得することも出来ます。

get-Package

パッケージ・プロバイダと違って数が多いのでさすがにちょっと時間かかりますが、こんな感じに出ます。
(ここに個人情報は無いと思いたい。。。)
私のPCに入っているパッケージ一覧

スペルがあやふやな時は?

パッケージ名は英語なので、スペルや名前がうろ覚えなこともありますよね。
そんな時も大丈夫です。

試しに、今度はGoogle Chromeを探してみましょう*3
下記を入力して実行してみてください。

find-Package -Name *chrome

ナントカchromeのパッケージが山ほどヒットします。
さっき以上に時間がかかるかと思います。

Name                           Version          Source           Summary
----                           -------          ------           -------
plex-chrome                    2.12.8.002018... chocolatey       Plex Chrome extension
adblockpluschrome              1.12.4           chocolatey       Chrome extension that blocks advertising while you browse.
addtoany-chrome                3.0.4            chocolatey       Chrome extension to add share options to MANY popular sites.
amazon-assistant-chrome        10.1610.8.1201   chocolatey       Amazon Assistant Chrome extension
 
# (長いので以下略)

chromeの前に付いているワイルドカードと言います。
トランプのジョーカーみたいなもので、「何が入ってもいいよ」という意味です。

つまり、このコマンドでは「ナントカchromeって名前のパッケージが存在するか確認して~」とお願いしています。

ワイルドカードは入れる位置や数も自由なので、例えばchrome**c*ro*eのようにも使えます。
まぁ、後者はちょっと極端な例ですが、割とザックリで探せるのです。

メジャーなソフトウェアであれば、工夫して探せば恐らく何かしらのパッケージは見つけられるはずです。
プロバイダやワイルドカードを組み合わせて、上手に検索しましょう。

おわりに

いかがだったでしょうか。

覚えることは少ないけれど、結構できることが多いPowerShell
パッケージ管理はその内のほんの一部ですが、黒い画面への恐怖心を少しでも減らすことができたなら幸いです。

一連の記事は可能な限り噛み砕いたつもりではありますが、読みづらいところ、誤り等があればぜひご指摘ください。

また、本連載で紹介した以外にも、シェルコマンドとか素敵なテクニックが色々あるので、興味のある方は勉強してみてください*4

シェル系の言語は事務職向けの説明が少ないのでちょっと難しいかもしれません*5が、これを乗り越えたらドヤ顔で帰れます!
一緒に事務員ノンプログラマーになりましょう!

それでは、長くなりましたがここまでお付き合いいただきありがとうございました!

このシリーズについて

黒い画面(CUI)でPCを操作するテクニックをご紹介。

以下の方を対象に、Windows10のPowerShellを使ってパッケージを導入する手順をお送りしています。

プログラムには少し興味があって仕事でVBA触ったりはするけど、
『あの』黒い画面はちょっと怖くて触れない…という方。

疑問点・不明点がありましたら、コメント欄で頂ければと思います!

次回

【おまけ】PowerShellで出来ることを知って、もう1つだけレベルを上げよう - ゆるおたノート

連載目次

  1. 【PowerShell】プログラムのプの字も知らない事務職が、黒い画面と格闘した話。 - ゆるおたノート
  2. 【PowerShell】超簡単にソフトを取り込む準備を始めよう - ゆるおたノート
  3. 【PowerShell】「そもそもパッケージって何?」っていうあなたに、少し説明します。 - ゆるおたノート
  4. 【PowerShell】コマンドを使ってパッケージ・プロバイダをインストールしよう - ゆるおたノート
  5. 当記事【PowerShell】パッケージ・プロバイダからパッケージを貰おう - ゆるおたノート
  6. 【おまけ】PowerShellで出来ることを知って、もう1つだけレベルを上げよう - ゆるおたノート

注釈

*1: ダウンロードだけのコマンドは今のところ見つけられませんでした…
<2019/05/04 追記>
save-Commandというコマンドがありました。
【おまけ】PowerShellで出来ることを知って、もう1つだけレベルを上げよう - ゆるおたノート

*2: by こぶしファクトリー
▼参考
https://www.youtube.com/watch?v=960j3j-XR4g

*3:Chromeくらい分かるよ!」というツッコみは胸にしまっていただけたら…いい例が見つからなかったのです…

*4: 今回の連載では、分かりやすさを重視して敢えてコマンドと表現していますが、PowerShellの世界ではこれをコマンドレットと呼びます。
勉強の際は、あらかじめ覚えておくと少し幸せになれるかもしれません。

詳しくは*1のリンク先(おまけ記事)をご参照ください。

*5: 少なくとも私には難しいです。。。●iitaとか●iitaとか●iitaとか…

【備忘録】初めてのLTで出来たこと・出来なかったこと

実は、プレゼンの類は今まで一切やったことがありません
もともと機会がほとんど無かったのもありますが、必要になる場面は避けるようにして生きてきました。

ただ、この1年で多くのプレゼン、LTを拝見するたびに「私もやってみたい」と思うようになりまして。
ノンプロ研in仙台を開催するタイミングでちょうどLTもご指名を頂いたので、深夜にセコセコとスライドを作成しました。

チャレンジした結果は案の定ボロボロでしたが、イベントの運営と同じくらい、良い収穫がありました。
この点についても、少し振り返りたいと思います。

もっとやりたかったこと

時間を「正しく」測る

前回の記事で触れましたが、時間の計算が全く出来てませんでした…
喋りながら、時計見ながら、残り時間計算しながら、…。
同時進行は結構難しいですね。

音楽を暗譜するのと同じように、構成・セリフは身体で覚えて、時間を見る余裕を残すしかなさそうです。
練習嫌いにはツラいけど仕方ない…

たまに話しかける・相手の反応を見る

発表するからには、ちょっとは興味を持ってもらいたいのが人情(だと思います)。
それに加えて発表中は基本的に部屋が暗くなるので、一方的な話は聞いている側が眠くなってしまいますよね。

スライドの説明をするので精一杯で、現場がどんな状況だったか私はあまり覚えていないのですが、出来れば「双方向」で話を進めたかったなと思いました。

時間測りつつアドリブ入れるのはもっと難しそうだけど…

ゆっくり笑顔で話す

私の声は低く暗めで、ただでさえボソボソ聞こえがち。
それを意識して、発表中はつとめて明るく話しました。
いや、明るく話した「つもり」でした。

後日、自分の発表の様子を動画で観ると、思ったよりキョドらず対応は出来ていたものの、声は想像以上にボソボソ。結構凹みました。

意識してもコレなので、たぶん表情も暗かっただろうし、客席(?)の皆様からも暗い人に見えたことでしょう…
そして普段はもっとヤバいはず…私も普段から明るく喋りたい…

やって良かったこと

発表の様子を録画or録音する

今回は「ノンプロ研」のイベントということで、主宰のタカハシさんが録画してくださっていました(ありがとうございます!)。

本当は、自分でも録音するためにスマホを傍らに置いていたのです。
しかし、アプリは起動したのに録音がONになっていませんでした…発表後、絶望。何やっとんじゃ…

冷静になってから自分を客観的に観るのは、とても勉強になります
のように、あとで自分の様子も観察できます。
見られるからには、多少セルフ・プロデュースも必要でしょうし(一番苦手)。

恐らく会場や内容によっては録画も録音もNGなこともあるかとは思いますが、可能な限り記録を残して改善の材料にしていきたいと思います。

「いらすとや」は使いやすい

みんな大好き「いらすとやさん」、ちょっと前にも話題になってましたね。
海外の人に日本の機械学習の研究についての意見を聞いたら『スライドのイラストに統一感がある』と言われて察した話 - Togetter

このブログでもしょっちゅうお世話になっています。笑

検索のコツはまだ完全には掴めていないですが、何回かワードを変えてみるとだいたい使いたいものに出会えます
当日も本番15分前で1枚だけ追加しましたが、キャッチ―な画像で非常に助かりました。

ただ、↑の記事にあるように「イラストのせいで内容が入ってこないし不愉快」という方もいるみたいなので、ほどほどに…

文字は大きく・少なく・短く

当日は、スライド120枚分を20分ほどでお話しました。
前日に指摘されて知りましたが、この分量は一般的にも多かったみたいです…
時間オーバーの元凶でした(ごめんなさい)。

今回はグラフや表などを使うポイントが無かったので「ほぼ文字」のスライドでしたが、むかーし学校で習ったのを思い出して、「読ませない」ことを意識して作りました。
一覧的なページを除いて、出来るだけパっと見で内容を把握出来るように。

具体的には、以下の3つです。

  • 文字を大きく*1
  • 1文を短く
  • 1行を短く

パラパラ漫画*2まではいきませんが、話はテンポよく進められたかなと思います。

まとめ

喋りと進行は下手くそです。
でも、スライドを書くのは割と好きだと思いました。自己満足ですが。

今まで見てきたものを参考に、「どう書いたら読みやすいかな?」「どう話したら面白いかな?」と想像しながら作るのは、結構楽しかったです。

すぐには出来ないかもしれませんが、ノンプログラマーのLT(や、もくもく会など)の機会をもっと増やしていけたらなと思いました。

最後に

ここまで何回かノンプロ研のお話をさせて頂きましたが、今月をもってノンプロ研を退会させていただきました。
今まで先延ばししていた問題が色々いっぺんにやってきてしまって、続けられなくなってしまいました…

ほとんどの方に直接お会いできなかったし、GW明けにやりたいこともあったのに…遂行できず申し訳ありません…

ただ、コミュニティ内外でどんどんシステムを造り、そして変えていく皆さんを眺めていて、私自身はとても勉強になりました。
先日もあっという間にHPが増えたみたいで…すごい。

「こういう方々が社会を変えていくんだなぁ…」と思います。

また、ノンプロ研がキッカケで作ったこのブログも、休み休みではあったけどなんとか1年間続きました。
某チャンネルのリーダーであるもりさん、コミュニティ主宰のタカハシさん、そしてチャンネルの皆さん、ありがとうございました!
ブログはもちろん今後も続けていきたいと思います。頑張ります。

そして、ハナから人を否定せず、何歳になっても新しいことを受け容れ吸収していく姿勢、これがノンプロ研で得た一番のモノだと思います。
プログラミング以上に大事ですよね。私も大事にします!

これまでほぼ1年間、大変お世話になりました。

のんびりしているうちにガンガン知らない方が増えていって内心ビビっていましたが、色んなことにチャレンジするキッカケをいただいて、とても楽しく過ごしました。
この調子で100人超えて令和の一大組織になられることをお祈りしています。*3

ありがとうございました!

連載目次

  1. 【雑記】人が怖いのに何故かイベントの運営を買って出て思ったこと - ゆるおたノート
  2. 【備忘録】初めてイベント運営してみて出来たこと・出来なかったこと - 事前準備編 - ゆるおたノート
  3. 【備忘録】初めてイベント運営してみて出来たこと・出来なかったこと - 当日・後日編 - ゆるおたノート
  4. 当記事【備忘録】初めてのLTで出来たこと・出来なかったこと - ゆるおたノート

注釈

*1:一般に、「1文字は30px以上」、「1枚は3行まで」が読み易いそうです。スクリーンの大きさと席との距離にもよるかと思いますが、参考まで。

*2:このLTの少し後に、まさにパラパラ漫画みたいなテンポでLTされた方がいて驚きました。笑
内容もすごく面白くて…!私には到底出来ないけど、何故か悔しいです。

*3:その時は「ドヤ顔おじさん」になります。

【備忘録】初めてイベント運営してみて出来たこと・出来なかったこと - 当日・後日編

前回、イベントの事前準備について感じたことを書きました。
【備忘録】初めてイベント運営してみて出来たこと・出来なかったこと - 事前準備編 - ゆるおたノート

記憶が薄れないうちに、本日はその続きで当日・後日編をお送りします。
あくまで何も知らない初心者の経験として、ご覧いただければ幸いです。

それでは、今回は反省からスタート。

当日編

やれば良かったこと

進行をタイマー(と頭)に入れておく

こちらが私の中で1番大きい反省です…
流れは頭に入っていたはずが、時間の認識が甘く…

LTでは、持ち時間10分のところ、発表だけで20分も話してしまって。
時計を見ながら話していたつもりでしたが、計算を誤っていたみたいです。時計の意味…

タイムキープすべき側がコレではダメダメですね、申し訳ありません…
今回はデジタル時計を使っていたので、次回はアナログ時計や視覚的なタイマーの使用も検討します。

時計は合わせておく

タイムキープにも関わるところですね。

もくもく会MacBookとWindowsPCの2台体制で参加しました。
MacBookは最近までしばらく弟に貸していたんですが、久しぶりに触らせてもらったら、電波時計をオフにして地味に15分ズラされてました笑
(恐らく最近ハマってるらしいオンラインゲームのためかとは思うんですけど、まぁそれは置いておいて。)

時間になるまではMacの時計を見つつ参加者の方とお話をしていたので、知らずに使っていたら危うく進行が遅れるところでした。
コレはもくもく会の時だったのでまだ良かったですが、発表の時だったらさらに恐ろしいことに…!

Macを使うなら、ケーブルかアダプタも用意しておく

先ほどのMacBookを、スクリーン投影用にするつもりでした。
しかし、手元に余っていたHDMIケーブルは持って行ったものの、肝心なアダプタの準備を忘れていました…
とりあえずWindowsの方を投影用に変更して事なきを得ましたが、しばらくMacに触っていなかったので、もくもく中はキーボードやエディタが使いづらいのなんのって。

ほとんどの場合、PCをプロジェクターに繋ぐ時は接続用のケーブルや変換アダプタが必要になります。
大抵は貸し出しサービスもありますが、「その大体がHDMIケーブルで、あってもminiD-Sub 5ピンかVGAだけ」という状況で、Thunderboltが使える貸し会議室は1室だけでした(私調べ)。

普段からMacBookを持ち歩く人であれば常備している方も多いかと思いますが、たまに使うレベルの方は、ディスプレイ用のケーブル(もしくはアダプタ)も忘れないように気を付けてください。

やって良かったこと

早めに着く

これは、タカハシさんのお陰です。
特に3/9はLTもあったので結構緊張していましたが、PCをプロジェクターに繋いで、流れを確認して、少し談笑して…とやっているうちに結構落ち着きました。

ただ、話している時間に前述のような作業も少しできたかなと思うので、お話の時間とは別に、それ用の時間を作っておいても良かったかなと思います。

スクリーン投影用マシンを用意する

主にもくもく会の場合ですが、自分の作業用PCとは別にスクリーンに映すための専用PC(orタブレットがあると、余裕をもって作業が出来ます。

1人だけ1番前で作業するのもちょっと落ち着かないので。笑
あと、リモートでつなぐ場合は、スクリーン用PCからカメラを使うことで、自分を含めて全員の顔を映せますね。

ただし、少なくとも荷物が2kg以上増えてしまうので、結構重いです。
両日とも家から徒歩で15分ほどの距離でしたが、あまり体力が無い方なので、運ぶだけでもグッタリでした…

私のように徒歩で会場に向かう方は、自分の体力と相談して考えましょう。

後日編

やれば良かったこと

「継続性」を考える

タカハシさんもよく仰っていることですが、これが大事だなと痛感しました。

というのも、会場探しは個人的にも割と骨の折れる時間でした。
「交通費は気にしないで」と言われていましたが、持ち前の(?)頑固さが働いてしまって、わざわざ歩いて回っていました*1

普段使わない路地に入ったりも出来て結構楽しかったのですが、仕事帰りや休日に歩き回るのは、体力的には「ちょっと失敗したな」と思いました。
人より疲れやすいのもあるので、いつも以上に自分の体力管理が必要ですね。
もちろん、普段から運動している方であれば、そんな心配は不要と思いますけど…!

また、私事ですが経済的な問題もあります…
「継続性の担保」には、自分があらゆる意味で「健全」である必要がありますね。

やって良かったこと

出来事は早めに記録しておく

1回目は「後でゆっくり書けばいいや~」とのんびりしていたんですが、瞬く間に時間が経って2回目(もくもく会)が来てしまい…
そんなわけで2回目は、眠い目をこすりながらすぐに記録しました。
たとえそれがキモいポエムであったとしても…

人間はすぐ忘れるもので、その時あったことなんてあっという間に忘却の彼方です。
印象に残ったセリフはあっても、その周辺の話とかは詳細な記憶が出て来なくなります、悲しいことに…

だから、本当は出来たらこの記事もすぐにアップすべきかなとは思います。
これは次回の反省ですね。

でもまぁ、アイディアなんかはゆっくりのんびりしてる時に浮かぶって言うし、そこはこだわらなくても良いのかな…?

後記

最初は「仕事ではないし、ゆるくのんびりできればいいや」と高を括っていました。
でも実際にやってみると、当然そんな甘々な考えでは到底出来ませんでした。
ひとつひとつ、考えることが山ほどあるんですね…

しみじみ、テキパキ動ける人ってカッコイイ。
脳みそ分けてもらうことは出来ないので、せめて爪の垢を煎じて飲みたい気持ちです。

この体験を活かして次に出来ることは何だろう?
ポンコツな自分が貢献できることは?

そんなことを考えていたら「起業すれば?」なんて言葉も(たぶん冗談で)いただきましたが、そんな機会や能力はきっと私には無いんだろうな。
でも、「自分の身の回りがひとつのコミュニティ」だとすると、いつかポンコツも集えば文殊の知恵になるんでしょうかね。

この先どうしましょ…
考えがまとまってないので、とりとめのないことを記して、筆をおくことにします。

次回は、番外編としてLTの反省を書いてみたいと思います。
引き続きゆるくお付き合いいただければ幸いです。

連載目次

  1. 【雑記】人が怖いのに何故かイベントの運営を買って出て思ったこと - ゆるおたノート
  2. 【備忘録】初めてイベント運営してみて出来たこと・出来なかったこと - 事前準備編 - ゆるおたノート
  3. 当記事【備忘録】初めてイベント運営してみて出来たこと・出来なかったこと - 当日・後日編 - ゆるおたノート
  4. 【備忘録】初めてのLTで出来たこと・出来なかったこと - ゆるおたノート

注釈

*1:タクシーは1人では乗ったことがないので恥ずかしい、バス停も近くに無くて歩いた方が早い、というのもありましたが…