インストールレスプログラミング( ´ー`)

VBA , JavaScript , HTAなど 365アプリはインストール必要ですが、仕事に無いケースはほぼないから(・_・;)

JScript mp3の再生について 書き残し

前回のタイマーで一番時間がかかった点の記録。

JavaScriptでmp3を鳴らすのはとても簡単だったのに,JScriptの場合はそうでなかった点。

というかそれに関する記述を探すのがとても大変でした。なので書き残し。

ちなみにわたしはDreamweaverを使ってJavascriptの学習をしています。。


Javascriptの場合はAudioオブジェクトがあるのでかなり楽でした。

        var pu = new Audio();
        pu.src = "pu.mp3";
        pu.volume = 0.5;
        pu.play();

これだけで音が鳴らせます。volumeについては0~1の間で設定。ほんとにすごく簡単。

でも,このままではHTAで,つまりJScriptではと言っていいんでしょうか,鳴らせません。

そこでやってるのが

      var pu= new ActiveXObject("MediaPlayer.MediaPlayer");
      pu.Volume=-10000; //音量を0に
      pu.FileName = "pu.mp3";
      pu.Volume=0;  //音量最大に  -10000~0 規定値 -600
      pu.play();

Createobjectに相当するやつなんでしょう。これで似たようにかける。

Audioタグを使ったり,ライブラリを使う方法は難しそうでめんどくさそうで 見送りました。

こちらの問題点はFileNameを設定したタイミングかな,そこで音がいきなりなります。

なので一度Volumeを最小に相当する-10000にして,適当なタイミングで音量をもどして使っています。

ひたすら検索しまくって探してたどりついたので,元記事がもうわかりません。。

見つけたら貼ります。

ここを除けばけっこうあっさりできてた気がしますので,ほんと苦労した内容でした。

年が明けてしばらく経ちますが。

のんびりしてたらもう5日も経ってました。

最近はVBAからは離れていますが,仕事が始まればまたデータ処理で思い出すんだと思います。

VBAからはすこし離れていますが,しばらく前に興味を持ったJavaScriptの勉強をしているところです。

全然わからない → 他人のコードを切り貼りしたり ちょこっとずつ付け足していくとなんとなく物が作れるようになってきました。

とても役に立ちそうな言葉だと思いますし,ブラウザを基本とすれば大概いろいろつくれそうなものなんだなぁというところまでは来ています。

でも,ローカルのファイルの操作となると File API とかいうやつを使わないといけないんだなぁ 面倒だなぁと思いながらそれでもいろいろといじってると,

HTA に出会いました。HTML Applications ってやつですね。

Windows10では少なくとも使えるみたいです。現在の職場ではOSはWindows10なので,少なくとも数年はいける。

JavaScript自身は HTML5CSS とセットでたぶんずーっと使えそうな予感ですので,そちら側はブラウザを元とするものについてはいけそうです。

HTAになると,微妙に JScriptとかVBScriptになり,ちょこっとずつ仕様が変わるので,思った通りにいかないことも多々ありますが,

 ローカルファイルにアクセスできる
 セキュリティに関して制限が甘い(警告類があんまりでない)
 htmlで気軽に書いてるのになんか立派なアプリケーションぽく見える
 もちろんインストールとかいらない

など,私にとってはかなりの魅力を持ったもののようです。

数年戦えますし,JavaScriptの学習にも十二分にもなりますので,OfficeのVBAJavaScriptHTAで遊び続けてものにしようと思います。

JQueryもローカルにライブラリ落としとけば私の職場の環境でもつかえそうです。JQueryまで理解すればたぶん本職ではないプログラマには十分かなぁと思ったり。。

本はひたすら買い漁りました。かなり出費してしまいましたが,長く使える知識になってくれると思っています。

さて,作ったものを載せてみます。

元はPowerPointVBAで作って授業中に使っていたタイマーと同じものなんですが,PowerPointってExcelのThisworkbookに相当するものがないので,他のスライドショーとの共存が難しいです。

基本はOneNote主体で授業を構成しているので,それでもいいんですが,PowerPointも使いたいよなぁと常々思っていました。

最初はブラウザで動くものをJavascriptで作り,満足していたんですが,表示の面でツールバーとかがいろいろ残るので不満が残るものが最初にできました。

CSSJavascriptがフルに使えるという意味でそちらを優先するものも今後いろいろ試してはみますが,タイマーはコンパクトであってほしい。そこは譲れない。

なので,いろいろな制限をのむ形でHTAで作ってみました。 メモ帳に張り付けて 文字コードUTF-8で保存します。拡張子はhtaで。。
音もなるように作ってるんですが,音を鳴らすためにはどこからかmp3を拾ってきて 同じフォルダに pu.mp3 start.mp3 end.mp3 とリネームして入れてください。

www.kurage-kosho.info
こちらのページなどの音がおすすめかも。クレジット表示なしでも使っていいですよ,と書かれていますので,コンパクトさ優先の私にはありがたいです。

アプリケーションを聞いてきたら Microsoft HTML アプリケーションホスト みたいなやつを選んでください。

<!DOCTYPE HTML>
<html lang="ja">
<HTA:APPLICATION SCROLL="no" NAVIGABLE="yes" CAPTION="yes" />

<head>
  <title></title>
  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
  <!--CSS-->
  <style type="text/css">
    a.Link{
      text-decoration: none;
      display: block;
      width: 100%;
      height: 100%;
    }
    a.Timer{
      text-decoration: none;
      font-size:160px;
      color:red;
      display: block; 
      line-height: 1.1;
      font-family:"Comic Sans MS", "cursive";
      text-align:center;
    }
    a:hover{background-color:yellow;}
    table {
      border-collapse: collapse
    }
    table td{
      text-align:center;
      width:150px;
      height:50px;
      border:1px solid black;
      font-size: 40px;
    }
  </style>
  <!--JavaScript記述-->
  <script type="text/javascript">
    var spanMyTime;//時間 を 表示 する SPAN
    var spanlog;
    var time0;
    var timer;//経過時間のTimer
    var timeval;
    var pu= new ActiveXObject("MediaPlayer.MediaPlayer");
      pu.Volume=-10000; //音量を0に
      pu.FileName = "pu.mp3";
    var endSound= new ActiveXObject("MediaPlayer.MediaPlayer");
      endSound.Volume=-10000; //音量を0に
      endSound.FileName="end.mp3";
    var startSound=new ActiveXObject("MediaPlayer.MediaPlayer");
      startSound.Volume=-10000;
      startSound.FileName="start.mp3";
    //初期処理(各種DOMを取得)
    function init() {
      timer=null;
      resizeTo(480, 290);
      document.body.style.overflow = "hidden";
      spanMyTime = document.getElementById("mytime");
      spanlog=document.getElementById("log");
      var time = ['10:00', '8:00', '7:00', '6:00', '5:00','4:00', '3:00', '2:00', '1:00', '0:30', '0:20','0:10'];
      var timesec = ['600', '480', '420', '360', '300', '240', '180', '120', '60', '30', '20','10'];
      var html = ['<table>'];
      for (var c = 0; c < 4; c++) {
        html.push('<tr>');
        for (var r = 0; r < 3; r++) {
          html.push('<td><a class="Link" href="#" onclick="startTimer(' + timesec[c+4*r] + ',true)">');
          html.push(time[c + 4 * r]);
          html.push('</a></td>');
        }
      }
      spanMyTime.innerHTML = html.join('');
      spanlog.innerHTML="";
    }

    function startTimer(tm,flg) {
      pu.Volume=0;  //音量最大に  -10000~0 規定値 -600
      pu.stop();
      endSound.Volume=0;  //音量最大に
      endSound.stop();
      startSound.Volume=0;
      startSound.play();
      timeval = tm;
      if (flg==true) time0=tm;
      spanlog.innerHTML="";
      // 既に 開始 し て いる 場合 は、 何 も し ない
      if (timer!= null) {
          stopTimer();
          return;
      }
      spanMyTime.innerHTML = '<a class="Timer"  href="#" onclick="stopTimer()">'+ timeConv(timeval) +'</a>';
      timer = setInterval(interval, 1000);
    }
    function stopTimer() {
      if (timer!= null) {
          clearInterval(timer);
      }
      // timer に null を 代入 する
      timer = null;
      spanMyTime.innerHTML = '<a class="Timer"  href="#" onclick="startTimer('+timeval+',false)">' + timeConv(timeval) + '</a>';
      spanlog.innerHTML='<a href="#" onClick="init()"> BACK </a>    <a href="#" onClick=startTimer('+time0+',false)> ReStart ' + timeConv(time0) + '</a>';
    }
    function interval() {
      // 経過 時間 を 表示
      timeval--;
      spanMyTime.innerHTML = '<a class="Timer"  href="#" onclick="stopTimer()">' + timeConv(timeval) + '</a>';
      if (timeval == 0) {
        clearInterval(timer);
        endSound.play();
        timer=null;
        spanlog.innerHTML='<a href="#" onClick="init()"> BACK </a>    <a href="#" onClick=startTimer('+time0+',false)> ReStart ' + timeConv(time0) + ' </a>';
        spanMyTime.innerHTML='<a class="Timer"  href="#" onclick="startTimer('+time0+')">' + timeConv(timeval) + '</a>';
      } else {
        if (timeval % 5 == 0) pu.play();
        if (timeval < 5) pu.play();
      }
    }
    function timeConv(tm) {
      var fun;
      var byou;
      fun = Math.floor(tm / 60);
      byou = ("0" + tm % 60).slice(-2);
      return fun + ":" + byou;
    }
  </script>
<!--JavaScript ここまで-->
</head>
<body onload="init()">
    <span id="mytime"></span>
    <span id="log"></span>
</body>
</html>

ちなみに,音を鳴らすたびに微妙に時間はずれていきますので 10分のを回すと音によるのかもしれませんが3秒くらいずれました。

気になる方は使えません。私は気にならないのでたぶん使います。

あとはBase64とかいうやつで文字列に直せば音もソースに組み込めるんでしょうか,そんな知識はないのでやってはいませんが,いろいろと奥は深いんでしょうね。。

今後もいろいろ試してみようと思っているところです。 

部品づくり 図形がどのセルの上にあるか

imihitoさんに教えていただいた application.caller TopLeftCell BottonRightCell これらはとても便利そう。

注意すべきは,自分で作り自分だけが使うときは問題ないけど少しだけ図形がずれる,ということがあると想定外の不具合はありそうです。


Altキー押しながらセルの上にきっちり図形を置くと,TopLeftCellは想定通り動きますが,BottomRightCellは右下のセルを返します。

自分の想定ではTopLeftCellのほうを使えばいい,となりますが,ほんのちょこっとずらされてしまうだけでだめになるはず。
f:id:chemiphys:20181119102419p:plain
なので,図形の真ん中の座標で判定させてみるテストをしてみました。

Sub test()
Dim Sht As Worksheet: Set Sht = ActiveSheet
Dim Shp As Shape: Set Shp = Sht.Shapes(Application.Caller)

Dim CenterX As Long, CenterY As Long
CenterX = Shp.Left + Shp.Width / 2
CenterY = Shp.Top + Shp.Height / 2

Dim Rng As Range: Set Rng = Sht.Range(Shp.TopLeftCell.Address & ":" & Shp.BottomRightCell.Address)
Dim r As Range
For Each r In Rng
    If CenterX >= r.Left And CenterX < r.Left + r.Width And CenterY >= r.Top And CenterY < r.Top + r.Height Then
        MsgBox r.Address
        Exit For
    End If
Next

f:id:chemiphys:20181119102432p:plain

負荷を減らすべく,評価対象のセルの数を少なくできてたらいいな。

もしこれらで取得するセルを利用する場合は,セルを間違ったら大変なことになるはずなので,基本的には図形名等にセルアドレスを忍ばせて,それを利用することで想定外は無くすべきだと考えますが,次善の策としてはかなり有用だなぁと思います。

それにしてもCallerプロパティはいいですね。これは今後コード書くときに重宝する気がしています。

メモ

Excelで図形を作ってOnActionを使ってみようとしていたら,次の記事を見つけました。

Excel VBA を学ぶなら moug モーグ | 即効テクニック | OnActionで実行するプロシージャに引数を渡す


図形にマクロをつけて,引数もつけれると(;´▽`A``

これはいい。

軽くテストしてみました。

Sub test()
    Dim Sht As Worksheet: Set Sht = ActiveSheet
    Dim Rng As Range: Set Rng = Sht.Range("c5:e8")

    Dim c As Range
    For Each c In Rng
        With Sht.Shapes.AddLabel(msoTextOrientationHorizontal, c.Left, c.Top, c.Width, c.Height)
            .Name = c.Address(False, False) & "_BTN"
            .OnAction = "'test2 """ & .Name & """ '"
            '.OnAction = "'test2 ""印刷""'"
        End With
    Next
    
End Sub


Sub test2(Str As String)
    MsgBox Str
End Sub

セルのシングルクリックイベントの代わりに図形をのせているので,各セルのleft,top,width,heightを使って図形を書き,それにOnActionをのっける。

セルのアドレスを捕まえることができるなら,いろいろとやれるのでそのためのものです。

testマクロを動かすと,
f:id:chemiphys:20181116220018p:plain
セルの上に図形をきれいにはりつけます。
無色透明にするなら .Fill.Visible=msoFalse をすればいいのかな。

これを実行すると,そのセルを選んだら(そこに貼り付けられている図形をクリックしたら)
f:id:chemiphys:20181116221039p:plain

このように,ちゃんとアドレスにしたがった値を返せます。

これはきっといいものな気がします。

onActionでマクロを貼り付けられた図形を動かすのはちょっと面倒なので,その点もいい感じ。

シートに貼る形のActiveXのリストボックスが,解像度が変わったり,タブレットで縦横表示が切り替わり続けると容易に表示が崩れたりと役立たずだったので,


リストボックス風なものを自分で実装したりしていました。
それを実現するためのいい手段が手に入った気がします。

追記 さらにメモ

d.hatena.ne.jp

のコメントのところから抜粋

参考にさせて頂き、助かりました。
第二引数の指定方法が分かりましたので、例示しておきます。
Sub AAAA(str1 As String, str2 As String)
.OnAction = "'AAAA ""引数1"", ""引数2""'"

きっといつか使う

さらにさらに追記

まぁこれも面白いわけですが,Powerpointの時もそうでしたが,セルに書く必要ないじゃないか という根本的なところにいきますね。。
セルにこだわらずに,加えた図形群でリストボックスぽいものをやる方法を考えていこう。

トグル入力のコード Excel VBA

ブログ名前詐欺中です。 今取り組んでることのための部品。

キーボードを使わずタブレットだけで作業をする。

複雑なインターフェースは準備できない。 でも簡単な入力方法を実装したい。

いつも思いつくのは,お風呂に入ってるときや,帰宅,出勤時。

考えを変えるタイミングって大事ですね。

さて,とても単純なヤツデス。

Sub クリック入力(Add As String, Min As Long, Max As Long)
    Dim Rng As Range: Set Rng = ActiveSheet.Range(Add)
    Rng.Value = Rng.Value + 1
    If Rng.Value > Max Then Rng.Value = Min
End Sub

Sub Sht1_E5()
    Call クリック入力("e5", 0, 5)
End Sub

Sub Sht1_E6()
    Call クリック入力("e6", 0, 1)
End Sub

(´▽`) '`,、'`,、 ミジカイ

f:id:chemiphys:20181114224804p:plain


私は雑な人間ですが,さすがに,上のSSはわざとちょっとずれてるのをスルーしています。

E5,E6のセル上にボタンにするための透明な図形をのせて,それらに Sht1_E5 Sht1_E6をマクロ登録しています。

動画とらないで大丈夫ですよね。。あまりにも短いコードですから。。

クリックするたびに数値が1ずつ増え,最大値を超えると最小値になるよ,というコードです。

Excelのリスト入力は便利なんですけど,キーボードやマウスが使えない時ってあれってかなりちっちゃい。

その時のためのインターフェースとして考えてみました。明日実装してみよう。。

毎年この時期はデータ処理。。

教員をしていると,入試の時期が必ず来ます。

そろそろ,また今年の分の調整をし始めないといけません。

思えば,様々なアドバイスをいただき始めたのも,データ処理をどうするか,という話の頃から。

とてもありがたいことです。以前とはコードの書き方もけっこう変化しました。

そして,私はいつも同じ付近に考え方を戻して,いろんなことを気分でやるので,今回はある意味ふりだしに戻った感が否めません。

ですが,VBA組める同僚はほとんど出会わない職場ですので,他の人でも見てわかる,とか,自分が中身を忘れてもみればわかる・・・

そのようなものを求めるのもしょうがないのかなぁと思います。

infoment.hatenablog.com

Infomentさんのブログで配列のソートに取り組まれています。

PowerPointでものづくりをするときは否応なしにそっち方向でやっていかないといけないので,とても楽しみに様子を見させてもらってます。

わたしは,とりあえず今年は一時ワークシートを作ってそこで作業する,という方向に戻して考えようということにしました。

Stopでもそのあたりに入れておけば,ソートの途中経過など確認しやすいから,というのもありますね。

とりあえずてきとーに組んだのがこちら。Excel VBAです。

Function ソート(ソース As Variant, ParamArray Keys()) As Variant
    
    Dim Data As Variant, 行数 As Long, 列数 As Long
    If TypeName(ソース) = "Range" Then
        行数 = ソース.Rows.Count
        列数 = ソース.Columns.Count
        Data = ソース.Value
    Else
        行数 = UBound(ソース, 1)
        列数 = UBound(ソース, 2)
        Data = ソース
    End If
    
    Dim TempSht As Worksheet
    Set TempSht = ThisWorkbook.Worksheets.Add
    
    Dim s As Worksheet, No As Long, flg As Boolean
    Do
        No = No + 1
        flg = False
        For Each s In ThisWorkbook.Worksheets
            If s.Name = "作業用一時シート" & Format(No, "00") Then flg = True
        Next
    Loop Until flg = False
    TempSht.Name = "作業用一時シート" & Format(No, "00")

    Dim SObj As Sort, TempRng As Range, i As Long, j As Long
    Set TempRng = TempSht.Range(TempSht.Cells(1, 1), TempSht.Cells(行数, 列数))
    TempRng.Value = Data

    Set SObj = TempSht.Sort
    With SObj
        .SortFields.Clear
        .SetRange TempRng
        .Header = xlYes
        For i = 0 To UBound(Keys) - 1 Step 2
            .SortFields.Add Key:=TempRng.Cells(1, Keys(i)), Order:=Switch(Keys(i + 1) = True, xlAscending, Keys(i + 1) = False, xlDescending)
        Next
        .Apply
    End With
    
    ソート = TempRng.Value
    
    Application.DisplayAlerts = False
        TempSht.Delete
    Application.DisplayAlerts = True
    
    
End Function

Function 抽出(ソース As Variant, flg As Variant, ParamArray Keys()) As Variant
    Dim i As Long, j As Long, k As Long, l As Long, Flg2 As Boolean
    Dim 行数 As Long, 列数 As Long, tmp() As Variant
    
    行数 = UBound(ソース, 1)
    列数 = UBound(ソース, 2)
    
    If flg = True Then
        ReDim tmp(1 To 行数, 1 To UBound(Keys) + 1)
        For i = 1 To UBound(ソース, 1)
            k = 1
            For l = 0 To UBound(Keys)
                tmp(i, k) = ソース(i, Keys(l))
                k = k + 1
            Next l
        Next i
    Else
        ReDim tmp(1 To 行数, 1 To 列数 - UBound(Keys) - 1)
        k = 1
        For j = 1 To UBound(ソース, 2)
            Flg2 = True
            For l = 0 To UBound(Keys)
                If j = Keys(l) Then Flg2 = False
            Next
            If Flg2 = True Then
                For i = 1 To UBound(ソース, 1)
                    tmp(i, k) = ソース(i, j)
                Next
                k = k + 1
            End If
        Next
    End If
    
    抽出 = tmp
End Function

Sub テスト()
    Dim ret As Variant
    ret = 抽出(ソート(Sheet1.Range("a1").CurrentRegion, 8, True, 5, True), True, 5, 6, 7, 9)
    
    Sheet4.Cells(1, 1).Resize(UBound(ret, 1), UBound(ret, 2)).Value = ret
    Stop
End Sub

Sub テスト2()
    Dim a As Variant: a = Sheet1.Range("a1").CurrentRegion.Value
    Dim b As Variant: b = 抽出(a, False, 2, 3)
    Stop
End Sub

自分用メモが主のブログでもありますので,説明とかいろいろと省きまくりなのはスルーしてほしいところです。

一時シートをつくり,そこにデータを放り込んでソートオブジェクトにおまかせ,というやつです。

あと,ソートの結果の一部を利用したり,または,ほとんど利用するんだけど,数列分は消したいという作業をよくやるので,抽出する関数も用意しています。

Sheet1のデータを取り込んで,一部抽出したり,ソート結果をさらに抽出する,という作業を テスト や テスト2
とかでやってみてますが,見た目にはまともに動いてそうでした。。

結局,速いアルゴリズムが優れたソートを自分はあまり実装できず,できてバブルソートだったりするので,

ExcelならSortオブジェクト使い倒してしまえ,という気分で作ったものです。

いくつキー与えてもやってくれるし,このマクロ組んでたら,Sortにcustomorderが使えるというのに今更ながらに知って驚いたり,

やっぱりいろいろと気楽にやってみるもんだなぁと思ったところです。。


--
わたしがいる学校ではそろそろ文化祭。

同僚に突然仕事を振られて,準備期間ほんのちょっとなのに 販売で使うシステムを組まされたりしています。

かなり以前に,小さなテキストメモみたいなものを吐き出して共通に見れるフォルダに放り込み,それをデータとして使う,,というシステムを作ったことがありました。

きちんとルール作りをすれば,データはその1KB程度のテキストがたくさん存在するだけであり,Excelが固まってもデータはほとんど消えることなく,

システムも小さくて済む。とても気楽に多人数でデータを扱えるシステムができている気がしています。

まさかこんなところで日の目を見るとは思わないものづくりでしたが,いつどんなものが役に立つかわからないものだなぁと思う。

思いつきは役に立とうが立つまいが一度は形にして,蓄積していけばいつか時間がないときにささっと対応できるなぁ,と2年ほど前の自分をほめつつ,

極めて短い準備期間なので,不具合をとても不安にも思いつつ作業しているところです。。

ひと段落しています。(VBAコード一切ありません)

とりあえず,作った教材を紹介する,という機会が終わりました。

仕事で人にものを説明することを日々やっている割に,やはり緊張するものですね。

また,PowerPoint2016のノートPCとPowerPoint2013のタブレットで動作確認をし,さらに直前に,

説明する電子黒板と同じ状態であろう,隣の部屋の電子黒板で動くことを確認していたのに,

実際説明するとき(PowerPoint2013でした),途中でマクロが止まってΣ(・ω・ノ)ノ!びっくりしました。

こういうとこは,さすが自分だな・・(;´▽`A``と思う次第。。

疑問なのは,普段スライドショーの完遂が優先され,コードエラーを直接吐かず沈黙するPowerPoint

エラーを吐いて止まったこと。。

どういう状態だったのか興味があります。確かめようはありませんが。。

いい経験ですね。やはり,動かす機械で確認をとっておかないといけないです。

一部,思い通りにいかなかったとはいえ,その部分はどちらかといえば枝葉の部分で主幹部分ではなかったので,

軽くスルーしてしまいました。

自分の適当さをそこでも感じたところです。

・・・

終わったし,何かまた何か作ってみよう。何に手を出そうか。。

( ´ー`)フゥー...