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

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

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

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

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

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

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

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

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

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

・・・

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

( ´ー`)フゥー...

有効数字 今度こそ大丈夫??

まだ変なとこありましたので,書き直してみた。

うーん。なんと面倒な。。気にせずやれればいいんですが(;´▽`A`` 固定小数点でやれない数値の扱いって面倒ですね。

できるだけシンプルな方法を今後も考えます。主目的がここじゃないせいで,トライアンドエラーでしかやってない(;´▽`A``

Function 有効数字表示(As Variant,As Long, 指数表示 As Boolean) As String
    Dim Val_ As String, Format_ As String
    If= 1 Then Format_ = "0e-0" Else Format_ = "0." & String(- 1, "0") & "e-0"
    Val_ = Format(CSng(), Format_)
    
    If 指数表示 = True Then
        有効数字表示 = Replace(Val_, "e", "×10^")
    Else
        Dim 数値部分, 桁部分, tmp, tmp2, tmp3, tmp4
        数値部分 = Left(Val_, InStr(Val_, "e") - 1)
        桁部分 = Mid(Val_, InStr(Val_, "e") + 1)
        有効数字表示 = CSng(数値部分 & "e" & 桁部分)
        If= 1 Then Exit Function
        
        If InStr(有効数字表示, "E") = 0 Then
            If InStr(有効数字表示, ".") > 0 Then tmp = Len(有効数字表示) - 1 Else tmp = Len(有効数字表示)
            tmp = Format(CSng(数値部分) / CSng(有効数字表示), "0e-0")
            tmp2 = Replace(数値部分, ".", "")
            tmp3 = Mid(tmp, InStr(tmp, "e") + 1)
            Select Case tmp3
                Case Is = 0
                    有効数字表示 = 数値部分
                Case Is > 0
                    If tmp = 1 Then
                        有効数字表示 = "0." & tmp2
                    Else
                        有効数字表示 = "0." & String(tmp3 - 1, "0") & tmp2
                    End If
                Case Is < 0
                    tmp4 = Len(有効数字表示)
                    If tmp4 <Then
                        有効数字表示 = 有効数字表示 & "." & String(- tmp4, "0")
                    End If
            End Select
            
        End If
        
    End If
End Function

ちまちま確かめていると,間違いに気づかないのでExcelにこのFunctionを放り込んでチェック

f:id:chemiphys:20181103103757p:plain

大丈夫そうだけどなぁ(;´▽`A``

有効数字 修正をはじめてます。

有効数字を処理する部分がまずそうだったので修正中です。

Function 有効数字表示(As Variant,As Long, 指数表示 As Boolean) As String
    Dim Val_ As String, Format_ As String
    If= 1 Then Format_ = "0" Else Format_ = "0." & String(- 1, "0") & "e-0"
    Val_ = Format(CSng(), Format_)
    
    If 指数表示 = True Then
        有効数字表示 = Replace(Val_, "e", "×10^")
    Else
        有効数字表示 = CSng(Left(Val_, InStr(Val_, "e") - 1) & "e" & Mid(Val_, InStr(Val_, "e") + 1))
        
        If InStr(有効数字表示, "E") = 0 Then
            Dim 数値スタート As Long, i As Long, 数値桁 As Long
            For i = 1 To Len(有効数字表示)
                If Mid(有効数字表示, i, 1) <> "0" And Mid(有効数字表示, i, 1) <> "." Then
                    数値スタート = i
                    Exit For
                End If
            Next
            
            数値桁 = Len(有効数字表示) - 数値スタート + 1
            If> 数値桁 Then
                If Len(有効数字表示) = 1 Then 有効数字表示 = 有効数字表示 & "."
                有効数字表示 = 有効数字表示 & String(- 数値桁, "0")
            End If
        End If
    End If
End Function


数値桁 = Len(有効数字表示) - 数値スタート + 1
If 桁 > 数値桁 Then
If Len(有効数字表示) = 1 Then 有効数字表示 = 有効数字表示 & "."
有効数字表示 = 有効数字表示 & String(桁 - 数値桁, "0")
End If

最後当たりのこの辺がうまくいっていません。
考え中。

追記

とりあえず,トライアンドエラーでいじってみました。

フローチャートを書いて根元から考えようとしないヤツですみません(;´▽`A``

Function 有効数字表示(As Variant,As Long, 指数表示 As Boolean) As String
    Dim Val_ As String, Format_ As String
    If= 1 Then Format_ = "0e-0" Else Format_ = "0." & String(- 1, "0") & "e-0"
    Val_ = Format(CSng(), Format_)
    
    If 指数表示 = True Then
        有効数字表示 = Replace(Val_, "e", "×10^")
    Else
        Dim 数値部分, 桁部分, tmp, tmp2
        数値部分 = Left(Val_, InStr(Val_, "e") - 1)
        桁部分 = Mid(Val_, InStr(Val_, "e") + 1)
        有効数字表示 = CSng(数値部分 & "e" & 桁部分)
        If= 1 Then Exit Function
        
        If InStr(有効数字表示, "E") = 0 Then
            If InStr(有効数字表示, ".") > 0 Then tmp = Len(有効数字表示) - 1 Else tmp = Len(有効数字表示)
            tmp = Format(CSng(数値部分) / CSng(有効数字表示), "0e-0")
            tmp = Mid(tmp, InStr(tmp, "e") + 1)
            tmp2 = Replace(数値部分, ".", "")
            Select Case tmp
                Case Is = 0
                    有効数字表示 = 数値部分
                Case Is > 0
                    If tmp = 1 Then
                        有効数字表示 = "0." & tmp2
                    Else
                        有効数字表示 = "0." & String(tmp - 1, "0") & tmp2
                    End If
                Case Is < 0
                    有効数字表示 = Left(tmp2, 1 - CLng(tmp)) & "." & Mid(tmp2, 2 - CLng(tmp))
                    If Right(有効数字表示, 1) = "." Then 有効数字表示 = Left(有効数字表示, Len(有効数字表示) - 1)
            End Select
            
        End If
        
    End If
End Function


イミディエイトウィンドウでいろいろやってみた感じはいけそうなんだけど・・,さすがに何度も間違ったのでまだ疑っています(;´▽`A``

あとあきらかに美しくないので,きちんと統合していこうと思います。