サポンテ 勉強ノート

サポンテの勉強ノート・読書メモなどを晒します。

ワークシート上の図を画像ファイルとして保存【Excel/VBA/Windows】

Excel を使う理由

 サポンテは Excel で簡単な図を書くことがあります。

 なぜ Excel を使うか?普及率が群を抜いて高いためです。サポンテは色々な会社で仕事しましたが、いまのところ 100% の会社でインストールされています。アプリのインストールを制限されている環境でも、これだけは確実にインストールされています。Visio などのドローソフトを導入する費用がなかったり、自分で勝手にソフトをインストールできない環境に置かれても、Excel が導入されていない企業はありませんでした。

Excel で描いた図をイメージファイルとして出力したい

 書いた図を、PNG などに出力したいことが、しばしば発生します。

続きを読む

ルビの入力って面倒ですよね 【Markdown/HTML/CotEditorスクリプト】

はじめに

 Markdown でブログ書いていても、読み仮名は HTML で書く必要があります。読み仮名、ときどき入れたくなります。「人智アントロポゾフィー」とか。しかし、いまいち手間なんですよね。

CotEditor のスクリプトを書いた

 ブログの編集に使っている CotEditor で入力しやすいように、AppleScriptスクリプトを書きました。

 せっかくなので共有します。よろしければ CotEditor のスクリプトフォルダに入れてご利用ください。

続きを読む

【コードジェネレータ】Excel などから TSV を貼り付けて、C# のオブジェクトリスト初期化コードを生成する

【コードジェネレータ】Excel などから TSV を貼り付けて、C# のオブジェクトリスト初期化コードを生成する

はじめに

 ソースコードとデータは切り離して管理すべきですが、それでもコード中にデータを埋め込みたいケースはしばしば発生します。テストコードとか。

 そのようなデータの元ネタとして、Excel や Numbers や Google Spreadsheet などの表計算ソフトや、ObjectBrowser などの RDBMS クライアントを用いたいという要件はあるかと思います。

 そのようなデータ元ネタは「コピー」すると、TSV 文字列としてクリップボードに格納されます。その TSV を「ペースト」してコードを生成してくれるジェネレータを JSFiddle で作りました。

コードジェネレータ

 左の入力欄に、サンプルのように TSV 文字列をコピペして、convert ボタンをクリックしてください。右側の入力欄に生成したコードが書き出されます。

 サンプル文字列がすでに入力されているので、convert ボタンをクリックするだけで動作を確認できます。

生成されるコード例

 以下のようなコードが出力されます。

var dataSrc = new[]{
    @"1  Apple   200",
    @"2  Banana  150",
    @"3  Citrus  300",
};

var data = dataSrc
    .Select(e => e.Split('   '))
    .Select(e => new
    {
        Id = e[0],
        Name = e[1],
        Price = e[2],
    });

foreach (var item in data)
{
    Console.WriteLine(item.ToString());
}

ソースコード

 念のため、ソースコードも載っけておきます。JSFiddle がサービス終了しちゃうかもしれませんからね。

 それに、オフラインで使いたいという要件もあるかもしれません(その場合、CDN のところは修正してください)。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        @media (prefers-color-scheme: dark) {
            body {
                background-color: #333;
                color: #fff;
            }
            textarea {
                background-color: #444;
                color: #fff;
            }
        }
    </style>
</head>
<body>
    <div id="app">
        <textarea name="" id="src" cols="30" rows="10" v-model="src" placeholder="Excel などからコピペしてください"></textarea>
        <button v-on:click="convert">convert</button>
        <textarea name="" id="result" cols="30" rows="10" v-model="result"></textarea>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                src: "Id\tName\tPrice\n1\tApple\t200\n2\tBanana\t150\n3\tCitrus\t300",
                result: "",
            },
            methods: {
                convert: function (e) {
                    var lines = this.src.split("\n");
                    
                    var ret = "var dataSrc = new []{\n";
                    for (let i = 1; i < lines.length; i++) {
                        ret += "    @\"";
                        ret += lines[i];
                        ret += "\",\n";
                    }
                    ret += "};\n\n";
                    
                    ret += "var data = dataSrc\n";
                    ret += "    .Select(e => e.Split('\t'))\n";
                    ret += "    .Select(e => new {\n";
                    var colnames = lines[0].split("\t");
                    for (let j = 0; j < colnames.length; j++) {
                        ret += "        " + colnames[j] + " = e[" + j + "],\n";
                    }
                    ret += "    });\n\n";
                    
                    ret += "foreach (var item in data)\n";
                    ret += "{\n";
                    ret += "    Console.WriteLine(item.ToString());\n";
                    ret += "}\n";
                    
                    this.result = ret;
                }
            }
        });
    </script>
</body>
</html>

おわりに

 以前なら、このようなコードは ExcelVBA で作成していました。

 でも Excel の無い環境もあるでしょうし、VBA だと共有したい時に配布しないといけないんですよね。共有したいツールが増えると、手間も増えます。

 そのような点から、JavaScript を使った Web サービスの方が、作り手の視点からも、使う側の視点からも嬉しい気がしてきました。Web サービスになっていれば URL をメールやチャットで送るだけですからね。JSFiddle や CodePen のような便利なサービスもありますし。

Git で外部の diff ツールを使う方法のまとめ

はじめに

 Git で差分比較を行う際に、外部ツールを使う方法です。

 基本的にどんなツールも、すでに誰かが書いていることが多いので、リンクの紹介が主です。

 下記にない場合は 「git diff <ツール名>」 で検索すれば、何かしら情報が出てくるでしょう。

 Mac の場合は以下がいろいろ詳しいですね。

 参考:Mac で使える git mergetool をいろいろ試してみる - 準備編 | そんなこと覚えてない

 Merge のことはあんまり考えていません。個人開発が多いためか、あんまりコンフリクトしないので。

続きを読む

アクティブブックのフォルダを開く

はじめに

 Excel を使っていて、編集中のファイル(アクティブワークブック)のフォルダを開きたいと思うことが、しばしばあります。

 サポンテは、作業環境をシンプルに保ちたく、今すぐに使わないウィンドウをずっと開いておくのに抵抗を感じます。エクスプローラのウィンドウも、必要なければすぐに閉じてしまいます。

 それは、後でまた開きたくなった時にすぐ開くことができれば問題ありません。

 そういうことでサポンテは、アクティブブックのフォルダを開くマクロをリボンに登録して使っています。このマクロ自体は、たいへん短いコード(実質一行)なので、記事を書いて公開するほどのものでもありませんでした。

SharePoint 対応

 ところが最近、SharePoint 上のファイルを直接開いて編集するということを頻繁にするようになりました。この場合も、ブラウザで SharePoint を開いてほしい。

続きを読む

CotEditor をストップウォッチとして使う

はじめに

 時間を計測するストップウォッチですが、計測するということは即ちどこかに記録するということで、いっそそのままテキストデータになっていると嬉しい、のではないか。

 ということで、CotEditor から実行できるスクリプトとしてストップウォッチがわりに使えるものを作ってみました。

使い方

  1. CotEditor で新規ドキュメントを開きます。
  2. はじめに、ストップウォッチをスタートさせるスクリプトを実行します。
  3. 次に、ラップタイムを記録するスクリプトを実行します。

実行結果

  1. スタートのスクリプトを実行すると、CotEditor で編集中のドキュメントの末尾に「00:00.000」と出力します。
  2. ラップタイムのスクリプトを実行すると、スタートからのラップタイムを、同じ形式の文字列で出力します。やはりドキュメントの末尾です。

実行結果例

ショートカット

 それぞれのスクリプトにショートカットを割り当てたい場合は、下記のスクリプトを保存する際、ファイル名に特別な記号を付与してください。

 例えば、スタートを [Command] + [Control] + [S] にしたい場合のファイル名の例は「StopWatch_Start.@^s.php」です。「@」が [Command] に、「^」が [Control] に該当します。他のモディファイアキーは CotEditor の「ヘルプ」メニューから、「CotEditorスクリプトガイド」→「スクリプトについて」を開いていただき、「CotEditorスクリプトのファイル名規則」を参照してください。

副作用

 スタートのスクリプトを実行した時に、ユーザの「書類」フォルダの下に、テキストファイルを一つ作成します。内容はスタートした時刻文字列です。ラップのスクリプトは、このファイルを読んで差分時間をラップタイムとして出力しています。このファイルは不要になったら消してください。ファイル名は(スクリプト内に書かれていますが)「com.hatenablog.saponote.StopWatch.txt」です。

スクリプト

 以下の PHP スクリプトを、上記のように好みのファイル名で保存していただき、実行権を与えて、CotEditor のスクリプトフォルダに格納してください。

 実行権を忘れないでくださいね。

StopWatch_Start.php

#!/usr/bin/php -q
<?php
// %%%{CotEditorXOutput=AppendToAllText}%%%

echo "00:00.000\n";
file_put_contents($_SERVER['HOME'] . '/Documents/com.hatenablog.saponote.StopWatch.txt', microtime(true), 0);

StopWatch_Lap.php

#!/usr/bin/php -q
<?php
// %%%{CotEditorXOutput=AppendToAllText}%%%

$start_time = file_get_contents($_SERVER['HOME'] . '/Documents/com.hatenablog.saponote.StopWatch.txt', false);
$lap_time = microtime(true) - $start_time;

list($sec, $millisec) = explode('.', $lap_time . '');
$m = floor($sec / 60);
$s = $sec % 60;

echo str_pad($m, 2, 0, STR_PAD_LEFT) . ':' .
     str_pad($s, 2, 0, STR_PAD_LEFT) . '.' .
     substr($millisec, 0, 3) . "\n";

おわりに

 タイムキーパー的な仕事をしている人には役に立ちそうな気がしますが...そのような仕事をしていて、CotEditor を使っていて、スクリプトにも明るい人...使う人、いるのでしょうか...。

 動作を確認したサポンテの Mac では PHP のバージョンは 7.3.11 でした。

編集中のHTMLに書かれたイメージファイルをQuickLook【CotEditor】

はじめに

 html をテキストエディタで書く人って、少数派だとは思うのですが。

 以前サポンテは html を手書きしていました。html を身につければ、高価なエディタを使わずともホームページを作成できるためです。

 macOS には、QuickLook という非常に便利な機能があります。

 html 編集中に、img タグに埋め込まれているイメージファイルを、ちょこっと確認したいことがあります。Finder に戻っても良いのですが、images ディレクトリが込み入ったところにあったりすると、少々面倒です。そんなときは、編集中のエディタから直接確認できれば便利です。

 CotEditor には、編集中の書類に適用できるスクリプトを簡単に追加できる機能があります。CotEditor と QuickLook のコマンドライン版である qlmanage を AppleScript で組み合わせれば、それが実現可能です。

動作イメージ

 編集中の html(に限りませんが)書類から見た相対パス文字列を選択しスクリプトを起動することで、該当の相対パスに存在するファイルを QuickLook で表示します。確認したら Space キーで閉じることができます。

スクリプト動作イメージ

スクリプト

 一部のスクリプト呆備録様より拝借しました。

ファイル、フォルダの存在確認 - 呆備録

 下記のスクリプトを拡張子 .scpt で保存して CotEditor のスクリプトフォルダに入れれば、CotEditor から直接 QuickLook できるようなります。

-- CotEditor にアクセスする
tell application "CotEditor"
    -- CotEditor が何らかのドキュメントを開いているかどうか確認する
    if (exists front document) then
        -- CotEditor の選択テキストを取得する
        set theSelection to contents of selection of front document
        -- テキストが選択されていたことを確認する
        if theSelection is not "" then
            set dirpath to path of front document
            set dirpath to quoted form of dirpath
            -- 最前面書類のパスを取得する
            set thePath to do shell script "dirname " & dirpath
            -- パスが取得できたことを確認する
            if thePath is not "" then
                -- ファイルが存在することを確認する
                if isExistItem(thePath & "/" & theSelection) of me then
                    -- QuickLook を開く
                    do shell script "qlmanage -p " & thePath & "/" & theSelection
                end if
            end if
        end if
    end if
end tell

on isExistItem(thePosixPath)
    -- 拝借元:https://oppara.hatenadiary.org/entry/20060209/p1
    try
        set theFilePath to thePosixPath as POSIX file
        set theAliasPath to theFilePath as alias
        return true
    on error
        return false
    end try
end isExistItem