ExcelVBA ブックが開かれた時に呼ばれる処理を書いてみたよ

やあ子供たち、今日はね、Excelを開いた途端に、自身のウィンドウサイズを所定の値にして、かつ、リボンが畳まれた状態になってくる、そんなVBAスクリプトを作ったので、そのやり方というか、自分用メモとして残します。
(※MS Office2019のみで動作を確認しております。)

Private Sub Workbook_Open()
    
    '本ブックが開かれた時に呼び出される固定関数の一つがこのWorkbook_Open()だ
    
    'リボンを畳まれた状態にする(トグルする効果があるようなので2回呼ぶ)
    Application.CommandBars.ExecuteMso "MinimizeRibbon"
    Application.CommandBars.ExecuteMso "MinimizeRibbon"
    
   'ウィンドウサイズを指定します
    Application.WindowState = xlNormal
    Application.ActiveWindow.Height = 380
    Application.ActiveWindow.Width = 400
    
End Sub

ちなみに以下コードは、VBAエディタ画面左のソースコード階層の中の、Sheetのとこではなく、「ThisWorkBook」となっているところにペーストすべきとされているので注意してくれ。Sheetのとこのソースコードにこの関数書いても動かないぞ。気をつけろ!
ほんとはウィンドウ位置も指定したいのだけど、ウィンドウがどっか行っちゃうんだよな。それはまた今度だ!
以上だよ、チャオ!

編集中のスライドに日付テキストボックスを挿入するPowerPointVBA

やあ子供たち。資料作ってるか。今日は今編集中のPowerPointのスライドの右上に、日付現在時刻テキストボックスを入れる便利スクリプトの紹介だよ。ボタンに紐づけてXLMファイルとして持っておけばいつでも使える便利ツールの出来上がりだ。
これはいろいろ調べて2時間くらいで出来たのだけどその中ではいろいろと勉強になった。

  • 現在編集中のスライドを取得するやり方
  • 便利なWith構文
  • WeekDay関数やWeekDayName関数
  • テキストボックスの挿入方法
  • スライドの幅の取得
  • フォント名の指定方法
  • With構文の中でもIf文などが普通に使えてしまうこと
  • 引数付きの関数は、UIから直接呼ぶことは出来ないこと

まそういったノウハウが、以下のコードを詰め込んであるのでよかったら参考にしてくれよな。
(※MS Office2019のみで動作を確認しております。)

Sub PPT_util_DateTag_common(Optional with_time As Integer)
  
    Dim ppApp As New PowerPoint.Application
    Dim ppPres As PowerPoint.Presentation
    Dim ppSlide As PowerPoint.Slide
    ' 現在編集中のスライドを取得する。
    Set ppPres = ppApp.ActivePresentation
    Dim slide_index
    slide_index = ppApp.ActiveWindow.Selection.SlideRange.SlideIndex
    Set ppSlide = ppApp.ActivePresentation.Slides(slide_index)
    Dim slide_width, box_width
    slide_width = ppPres.PageSetup.SlideWidth
    box_width = 220
    ' 日付ボックスをスライド右上に描画。
    Set shp = ppSlide.Shapes.AddShape(Type:=msoShapeRectangle, _
        Left:=slide_width - box_width, Top:=0, Width:=box_width, Height:=21)
    With shp.TextFrame2
        .TextRange.Text = Date & "(" & WeekdayName(Weekday(Date)) & ") "
        If (with_time > 0) Then
            .TextRange.Text = .TextRange.Text & Time
        End If
        .TextRange.Font.Name = "Meiryo UI"
        .TextRange.Font.NameFarEast = "Meiryo UI"
        .TextRange.Font.Size = 12
    End With
    ' 曜日ごとに色を変える
    Select Case Weekday(Date)
        Case 2
            shp.Fill.ForeColor.RGB = RGB(160, 30, 30) '月曜日
        Case 3
            shp.Fill.ForeColor.RGB = RGB(230, 180, 60) '火曜日
        Case 4
            shp.Fill.ForeColor.RGB = RGB(150, 200, 150) '水曜日
        Case 5
            shp.Fill.ForeColor.RGB = RGB(150, 120, 50) '木曜日
        Case 6
            shp.Fill.ForeColor.RGB = RGB(0, 20, 150) '金曜日
        Case Else
            shp.Fill.ForeColor.RGB = vbBlack
    End Select

End Sub

Sub PPT_util_DateTag()
    Call PPT_util_DateTag_common(0)
End Sub

Sub PPT_util_DateTagTime()
    Call PPT_util_DateTag_common(1)
End Sub

実際にどんな感じになるのかは、以下図をご参考下さい。
f:id:nurs:20211014115855p:plain
やーこれねー、どうよこれ。「私ずっとこういうの欲しかったの。」「私、ずっとこういうのが欲しかったんですっ!」って声がたくさん聞こえてきそうじゃないかこれ。おじさんはPowerPointファイルを日記代わりにしていて、テーマごとにあちこちに作ってるんだけど、この日付情報の入力を手動でずっとやってきたんだよね。いつか自動化したいなと思ってさ。
チャオ!

パワポのテキスト抽出VBA

パワーポイントファイルの中の、テキストを抽出するVBA。
フォントサイズが20以上のテキストを見つけたら、抽出。

Sub PPT_pdf()

   Dim ppt As New PowerPoint.Application
   Dim presen1 As PowerPoint.Presentation
   Dim save_path As String, file_name As String
   Dim Target As String
   
   'ターゲットファイルpptxを選択させて選択したファイルを開く
   Target = Application.GetOpenFilename("PowerPoint,*.pptx")
   If Target = "False" Then Exit Sub
   Set presen1 = ppt.Presentations.Open(Target, WithWindow:=MsoTriState.msoFalse)

   '結果出力用のテキストファイルを開く
   Dim fs As Object
   Dim stream As Object
   Set fs = CreateObject("Scripting.FileSystemObject")
   Set stream = fs.OpenTextFile("d:\extracted_text.txt", 2, True)
   
   '各スライドを探索し、フォントサイズが20以上のテキストがあれば
   '結果出力用のテキストに書出し
   For Each sl In presen1.Slides
     stream.Write "slide-" & sl.SlideNumber
     stream.WriteLine
     For Each shape1 In sl.Shapes
       If shape1.HasTextFrame Then
         If shape1.TextFrame.TextRange.Font.Size >= 20 Then
            Dim txt As String
            txt = shape1.TextFrame.TextRange.Text
            stream.Write txt
            stream.WriteLine
         End If
       End If
     Next
     stream.WriteLine
   Next
   stream.Close
   
End Sub

フォルダ内の全画像の縮小版を生成するPythonスクリプト

やあ子供たち。
今日はおじさんはフォルダ内の全画像の画像サイズを、ちょっと大きすぎたので小さくして、それで置き換えたいと思ったことがあった。じゃそういうのはpythonあたりで数行で作ってしまうのが楽でしょうと。というわけでそういうのを作ったからメモだ。
python の3.9あたりは入っているとして、まずは以下のような下準備をしたよ。

pip install opencv-python

続いて以下のようなコードを作成した。短く書けたね。短く書けると思ったからpythonにしたんだけど、それにしても短すぎるっっ!!!

import os
import cv2
import numpy as np
from pathlib import Path

input_path="d:/_melt2/"
for filename in os.listdir( input_path ):
  # ファイルではなくフォルダなどであれば飛ばす
  if not os.path.isfile(input_path+filename):
    continue
  
  print(filename,end=" ")
  img = cv2.imread(input_path+filename, cv2.IMREAD_COLOR)
  # 画像サイズを取得、表示
  height, width = img.shape[:2]
  print( str(width) + ", "+ str(height))
  # 画像リサイズを実行
  new_img = cv2.resize( img, dsize=None, fx=0.5,fy=0.5 )
  # 出力用フォルダがなければ作成
  Path(input_path+"out/").mkdir(parents=True, exist_ok=True)
  # リサイズした画像を出力用フォルダに同じファイル名の画像として出力
  cv2.imwrite(input_path+"out/"+filename, new_img)
  

はい、これは、input_pathに指定したフォルダ内にある全画像を、さらにその中にoutという名前のフォルダを作って、同じファイル名の縮小版を作成してくれるんだ。わかりやすくていいね。便利な場合もあるしね。いいなこういうシンプルで便利なのはすきだな。
いろいろ簡単に書けてシンプルに実装できる。しかもOpenCVがその裏にある。。本当にpythonて、、恐ろしい子
チャオ!

ユーザー入力文字列を識別子に使うのはやめよう

やあ子供たち。
ユーザーが入力した文字列を、IDに使おうなんて考えて痛い目にあったという話です。まーするわけないよね普通だったらね。でも惰性で作ってきてそうなっちゃったみたいな場合もあるわけで。そんなことしてたら、すくなくともこんな痛い目を見ましたという話。

すっかりユーザーが入力してくる文字列というものに注意を払ってないままに、入力結果を見てみると、例えば、30人に1人くらい「my name is "J"」という内容を打ち込んでくれたとしましょう。すると、以下のようなejs出力内容が、しれっとそのままブラウザに送信されてしまうことはごく当然の帰結として起こり得る、というか実際に起こってしまうことなのですが、

<body>
  // HTML部品を設置
  <h1 id="my name is &#34j&#34">"Hello HTML.&#34</h1>
  <script type="text/javascript">
    // プログラムを記述
    var h1 = document.getElementById("my name is &#34j&#34");// エラー
    //var h1 = document.getElementById("my name is \"j\"");// エラー発生せず
    h1.textContent = "OK. byebye";
  </script>
</body>

このコードは、ブラウザのgetElementById()の解釈で、エラーとなります(少なくともChromeで)。
ちなみにここで、アンパサンドシャープ34を、手動で修正して \" にすれば、エラーは発生しません。→何かしらの理由はあるのだろうがこれ以上深く考えたくない。となります。
ここでは素直に、「例えば、getElementById()は、アンパサンドシャープ34のような表記をうけつけないのだな」ということを学びましょう。わかりました。はい。そういうものなんですね。(アンパサンドシャープ34をそのまま半角でこの文章内に書いても、「"」となってしまう。ごめんね。)
そしてその学習の結果、獲得した教訓はこうでした。

  • ユーザーの入力には気をつけろってこと。
  • ユーザーが入力した文字列を何かの識別子には決して使わないこと。
  • getElementByIdの動作には何か理由があるのだろうがgetElementByIdは悪くないし、そこはもう深入りしない。

じゃっ、今日はこの辺で。
チャオ!

myHTMLテンプレート

何でもとりあえず書き始められる、HTMLテンプレート(随時更新)。
2022年1月現在あたりだと、jQueryの最新版は3.6.0、jQueryUIの最新版は1.13.0 あたりなのかも知れない。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <meta http-equiv="Pragma" content="no-cache">
  <meta http-equiv="Cache-Control"content="no-cache">
  <title>This is Tab TItle</title>
<style>
  /* スタイルシートコードを記述 */
</style>
</head>
<!-- スタイルシートコードについては、以下のように別ファイルからも取り込み可能。 
      その場合は.cssの中に<style></style>は必要ない
       <link rel="stylesheet" type="text/css" href="my.css" /> -->
<body>
  <!-- HTML部品設置を記述 -->
  <h1 id="hello">Hello HTML.</h1>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
  <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
  <!-- <script src="/socket.io/socket.io.js"></script> -->
  <script type="text/javascript">
    // ***javascriptのプログラムを記述***
    // var socket = io();
    window.addEventListener("DOMContentLoaded",function(){
    });
    // socket.on("hoge", ()=>{
    // });
  </script>
  <!-- プログラムは以下のように別ファイルからも取り込み可能。
  <script type="text/javascript" src="./my.js"></script>  
  -->
</body>
</html>

JavaScript:使わないと忘れそうなPromiseのthenチェーンについての備忘録(自分用)

やあ子供たち。今日はJavascriptのPromiseのthenチェーンについてのおじさんなりの備忘録だよ。あのな、なんだってそうなんだけどな、勉強したことや聞いた話ってのは頭の中でとにかく抽象化、つまり「ひとことで言うとこうだ!」みたいな感じで、それだけ覚えておくようにするんだよ。そうすれば他のことは全部きれいに忘れ去っても、これは何のことはない「ひとことで言うとこう」なんだからって、全部自動的にそこから芋蔓式に思い出せるんだね。例えば二次関数の解の公式を忘れても、いつだって平方完成すれば自分で公式を導けるようにね。
さて前置きが長くなったが今日はPromiseのthenチェーンについてだよ。まずはPromiseのおさらいから行こうか。
Promiseとはずばり、「任意の処理を、関数として自身の中に定義・保持したもの。定義した処理は、そのインスタンスに対してthen()を呼び出すことで、実行される。定義した処理の完了は、そのインスタンスに対してthen()を呼び出すことで、確認される。」だよ。これだけは今日覚えて帰ってくれ。これだけでもいいよ最低限な話すると。それくらい重要だぞこれは。そしてもう少し覚えて帰ってほしいことをいけばこうだ。→Promiseとは、「then()で実行されるが、このthenに、( func_resolve, func_reject ) という二つの関数を指定でき、前者は即ち、Promise定義時に指定する関数の一つ目の引数、後者は二つ目の引数として使われる。」だ。
さて、このthen()は、その返り値として、やはりPromiseを返すので、チェーンのように繋げることができるという。今日はね、このthenチェーンについてなんだけど、ひとことで覚えるとしたらね、こうだよ。「thenチェーンは、処理を同期的につなぐこともできるし、途中に非同期な処理をいれてつなぐこともできる」かな。つまり複数の処理を順番に行わせたいんだけど、この処理はすぐ終わるので同期的に次の処理(then呼び出し)にいかせたい。ということもできるし、途中のこの処理は時間かかるので非同期に実行させて、それが終わったら次(のthen呼び出し)、みたいなふうに、一連の複数の処理を、同期的だろうが、非同期的(「終わったら次みたいな」)だろうと、とにかく、指定した順番どおりに、前の処理が確実に終わってから次の処理、というやり方で実行させることができるというものだ。この、同期的につなぎたい、すぐ終わる処理を書きたい場合は、thenに指定する関数の中で単に「値」をreturnします。
以下の例を見てくれ。
thenチェーン、ケース①(同期処理を順番に実行させたい場合)

function promise_test_1()
{
  let ui = document.getElementById("ui");
  let prom1 = new Promise(function (resolv, reject) {
    var arr = [];
    arr.push(1);
    ui.textContent += "[" + arr + "]";
    setTimeout(() => { resolv(arr); }, 1000);
  });
  prom1.then(function (arr) {
    arr.push(2);
    ui.textContent += "[" + arr + "]";
    return arr;
  }).then(function (arr) {
    arr.push(3);
    ui.textContent += "[" + arr + "]";
    return arr;
  }).then(function (arr) {
    arr.push(4);
    ui.textContent += "[" + arr + "]";
    return;
  });
}

「then()はPromiseそのものを返すというわけなのに、このthenの中では値を返しているな。変な話だな?」なんて思ってはいけないよ。よく見てくれ。then()の返り値ではないよね。then()に指定している関数の返り値として値を返しているだけだろ、な、そうだろ。この結果はこうなるよ。以下の結果[1],[1,2][1,2,3],[1,2,3,4]が、いちどきに表示されるぞ。

●thenチェーン、ケース②(非同期処理なんだけどあくまで順番に実行させたい場合)
さて次は以下のコードだ。上述したように「いやすぐには終わらない、非同期に実行させたい処理を順番にやらせたいんだよ」という場合は、こういうふうに書いたりするわけだ。

function promise_test_2()
{
  let ui = document.getElementById("ui2");
    let prom1 = new Promise(function (resolv, reject) {
    var arr2 = [];
    arr2.push(1);
    ui.textContent += "[" + arr2 + "]";
    setTimeout(() => { resolv(arr2); }, 1000);
  });
  prom1.then(function (arr2) {
    return new Promise(function (resolv, reject) {
      arr2.push(2);
      ui.textContent += "[" + arr2 + "]";
      setTimeout(() => { resolv(arr2); }, 1000);
    });
  }).then(function (arr2) {
    return new Promise(function (resolv, reject) {
      arr2.push(3);
      ui.textContent += "[" + arr2 + "]";
      setTimeout(() => { resolv(arr2); }, 1000);
    });
  }).then(function (arr2) {
    arr2.push(4);
    ui.textContent += "[" + arr2 + "]";
    return;
  });
}

はい、このように、thenに指定する関数の中で、明示的に非同期処理を定義したPromiseを作成してそれを、returnしてあげるんですよ。
この場合、ブラウザ出力は以下のようになるよ。この場合は、いちどきに表示されるんじゃなくて、「[1]」が表示されてから1秒後に、「1,2」が表示され、またその1秒後に、「1,2,3」が表示され、またその1秒後に、「1,2,3,4」が表示されるぞ。

●thenチェーン、ケース③(②の確認)
じゃ最後、以下のコードではどうか。

function promise_test_3()
{
  let ui = document.getElementById("ui3");
  let prom1 = new Promise(function (resolv, reject) {
    var arr2 = [];
    arr2.push(1);
    ui.textContent += "[" + arr2 + "]";
    setTimeout(() => { resolv(arr2); }, 1000);
  });
  prom1.then(function (arr2) {
    return new Promise(function (resolv, reject) {
      arr2.push(2);
      ui.textContent += "[" + arr2 + "]";
      setTimeout(() => { resolv(arr2); }, 1000);
    });
  }).then(function (arr2) {
    return new Promise(function (resolv, reject) {
      arr2.push(3);
      ui.textContent += "[" + arr2 + "]";
      setTimeout(() => { resolv([7, 7, 7, 7]); }, 1000);
    });
  }).then(function (arr2) {
    arr2.push(4);
    ui.textContent += "[" + arr2 + "]";
    return;
  });
}

ちゃんとresolv()に引数として指定した値が次のthenに指定した関数の引数としてわたっているのがわかるだろ?そこが腑に落ちるように用意したサンプルなだけだよこの3つ目のサンプルはさ。もちろんこの場合も、[1]」が表示されてから1秒後に、「1,2」が表示され、またその1秒後に、「1,2,3」が表示され、またその1秒後に、file「7,7,7,7,4」が表示されるぞ。

最後に全ソースは以下のようだよ。

<html lang="ja">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache">
<head>
<style type="text/css">
#ui{ color: #a00; }
#ui2{ color: #0a0; }
#ui3{ color: #00a; }
</style>
</head>
<script type="text/javascript">

function promise_test_1()
{
  let ui = document.getElementById("ui");
  let prom1 = new Promise(function (resolv, reject) {
    var arr = [];
    arr.push(1);
    ui.textContent += "[" + arr + "]";
    setTimeout(() => { resolv(arr); }, 1000);
  });
  prom1.then(function (arr) {
    arr.push(2);
    ui.textContent += "[" + arr + "]";
    return arr;
  }).then(function (arr) {
    arr.push(3);
    ui.textContent += "[" + arr + "]";
    return arr;
  }).then(function (arr) {
    arr.push(4);
    ui.textContent += "[" + arr + "]";
    return;
  });
}

function promise_test_2()
{
  let ui = document.getElementById("ui2");
    let prom1 = new Promise(function (resolv, reject) {
    var arr2 = [];
    arr2.push(1);
    ui.textContent += "[" + arr2 + "]";
    setTimeout(() => { resolv(arr2); }, 1000);
  });
  prom1.then(function (arr2) {
    return new Promise(function (resolv, reject) {
      arr2.push(2);
      ui.textContent += "[" + arr2 + "]";
      setTimeout(() => { resolv(arr2); }, 1000);
    });
  }).then(function (arr2) {
    return new Promise(function (resolv, reject) {
      arr2.push(3);
      ui.textContent += "[" + arr2 + "]";
      setTimeout(() => { resolv(arr2); }, 1000);
    });
  }).then(function (arr2) {
    arr2.push(4);
    ui.textContent += "[" + arr2 + "]";
    return;
  });
}

function promise_test_3()
{
  let ui = document.getElementById("ui3");
  let prom1 = new Promise(function (resolv, reject) {
    var arr2 = [];
    arr2.push(1);
    ui.textContent += "[" + arr2 + "]";
    setTimeout(() => { resolv(arr2); }, 1000);
  });
  prom1.then(function (arr2) {
    return new Promise(function (resolv, reject) {
      arr2.push(2);
      ui.textContent += "[" + arr2 + "]";
      setTimeout(() => { resolv(arr2); }, 1000);
    });
  }).then(function (arr2) {
    return new Promise(function (resolv, reject) {
      arr2.push(3);
      ui.textContent += "[" + arr2 + "]";
      setTimeout(() => { resolv([7, 7, 7, 7]); }, 1000);
    });
  }).then(function (arr2) {
    arr2.push(4);
    ui.textContent += "[" + arr2 + "]";
    return;
  });
}

window.addEventListener("DOMContentLoaded",function(){

  promise_test_1();
  promise_test_2();
  promise_test_3();  

});
</script>
<body>
<h1 id="ui"> hello  </h1>
<h4>↑値をreturnした場合のthenチェーン。複数の処理を同期的に順番に処理。<br>return した値がそのまま次のthenに指定した関数の、引数となる。</h4>
<h1 id="ui2"> hello </h1>
<h4>↑明示的に作成したPromiseをreturnした場合のthenチェーン。複数の処理を非同期的に順番に処理。<br>return したPromiseの中でresolvの引数に指定した値がそのまま次のthenに指定した関数の、引数となる。</h4>
<h1 id="ui3"> hello </h1>
<h4>↑明示的に作成したPromiseをreturnした場合のthenチェーン。複数の処理を非同期的に順番に処理。<br>return したPromiseの中でresolvの引数に指定した値がそのまま次のthenに指定した関数の、引数となることを確かめてみたもの。</h4>
</body>
</html>

これをhello_promise.htmlファイルとして保存して、Chromeとかで実行してみてくれよな。
じゃ今日はこの辺で。チャオ!