h-yonesanのblog

VBAやプログラムについて日々勉強したことを書いています。

PowerPoint VBA 折れ線グラフを散布図に変更して、軸の書式設定を行う

きっかけ

先日PowerPointのスライド200枚の中にある折れ線グラフ約250個に対して、以下の作業をする機会があった。

各折れ線グラフに対して
(1) グラフの種類を折れ線から散布図に変更
(2) 横軸の最大値、最小値を設定

更に、一つのスライドに存在する折れ線グラフの数によって処理が異なり、
(1) グラフが1つの場合
・目盛り幅を4に設定

(2) グラフが2つ以上の場合
・目盛り幅を8に設定
・補助目盛り幅を4に設定
・補助目盛りの種類を目盛りと同じものに設定

サンプルコード

Sub Main()
    Dim s As slide
    Dim lineCharts As Collection, chart As Variant
    For Each s In ActivePresentation.Slides
        Set lineCharts = 折れ線グラフを取得(s) 
        For Each chart In lineCharts
            Call 折れ線グラフを散布図に変換(chart, lineCharts.Count)
        Next chart
    Next s
End Sub

Sub 折れ線グラフを散布図に変換(chart As Variant, chart_num As Long)
    Dim shp As Shape: Set shp = chart
    Dim xAxis As Axis: Set xAxis = shp.chart.Axes(1)
    shp.chart.ChartType = xlXYScatterLines
    xAxis.MinimumScale = 0
    xAxis.MaximumScale = 96
    If chart_num = 1 Then
        xAxis.MajorUnit = 4
    Else
        xAxis.MajorUnit = 8
        xAxis.MinorTickMark = xAxis.MajorTickMark
        xAxis.MinorUnit = 4
    End If
End Sub

Function 折れ線グラフを取得(s As slide) As Collection
    Dim ret As Collection: Set ret = New Collection
    Dim shp As Shape
    For Each shp In s.Shapes
        If shp.HasChart Then
            If shp.chart.ChartType = xlLineMarkers Then ret.Add shp 
        End If
    Next shp
    Set 折れ線グラフを取得 = ret
End Function

Excel VBAAccess VBAと勝手が異なり苦労した。
細かな処理は別プロシージャ化して、なるべくMainのプロシージャが簡潔になるようにした。
改善点は色々あると思うが、とりあえずやりたいことは出来たので、これでよしとしよう。

VBA 文字列の一部分を取得する関数を作る

きっかけ

いつも勉強させてもらっている伊藤先生のサイトで、面白い記事があった。
ByRef・参照渡しとはどう使うのか:エクセルマクロ・Excel VBAの使い方

サンプルコード

戻り値が配列の場合

Function 文字列から要素数2の配列を取得(ByVal code As String) As String()
    Dim ret(1) As String
    ret(0) = Mid(code, 1, 4)
    ret(1) = Mid(code, 5, 6)
    文字列から要素数2の配列を取得 = ret
End Function

イミディエイトウインドウでテストすると下記のようになる

? 文字列から要素数2の配列を取得("abcd012345")(0)
abcd
? 文字列から要素数2の配列を取得("abcd012345")(1)
012345

戻り値が文字列の場合

Function 2つに分割した文字列を取得(ByVal code As String, num As Long) As String
    Dim ret As String
    Select Case num
        Case 0: ret = Mid(code, 1, 4)
        Case 1: ret = Mid(code, 5, 6)
        Case Else: ret = "ERROR"
    End Select
    2つに分割した文字列を取得 = ret
End Function

なお、0,1以外が入力された場合は"ERROR"を返すようにした。
前半を0、後半を1、というのは分かりにくいな。。
仮引数名が「num」というのもいまいちだが、、

イミディエイトウインドウでテストすると下記のようになる

? 2つに分割した文字列を取得("abcd012345",0)
abcd
? 2つに分割した文字列を取得("abcd012345",1)
012345
? 2つに分割した文字列を取得("abcd012345",-10)
ERROR

AccessVBA フォームのFilterプロパティに設定する文字列を生成する関数

初めに

先日、レコードを一覧表示するフォームに検索用のテキストボックスを追加し、その入力内容でフィルタをかける、という機能を作った。
例えばこんな感じ

Me.Filter= "入社年 > 2015 and 部署 = A事業部 and 性別 = 男"
Me.Fiter=True

このFilterプロパティに設定する文字列
"入社年 > 2015 and 部署 = A事業部 and 性別 = 男"
を生成する関数を作った。
(正確に言うと自分の力では作れず、私の先生の力を大いに借りたのだが、、)

サンプルコード

Function Combine(condition1 As String, condition2 As String, condition3 As String) As String
    Combine = Join(Array(condition1, condition2, condition3), " and ")
End Function

これをイミディエイトウィンドウ上で実行すると
?combine("cond1","cond2","cond3")
cond1 and cond2 and cond3
となった。

今は引数の数が固定なので、任意の数の引数に対応するようにしたい。改良したのが次のコード。

Function Combine(ParamArray conditions()) As String
    Dim ret As String
    Dim i As Long
    For i = LBound(conditions) To UBound(conditions)
        ret = ret & " and " & conditions(i)
    Next i
    Combine = ret
End Function

これを実行すると
?combine("cond1","cond2")
and cond1 and cond2

?combine("cond1","cond2","cond3")
and cond1 and cond2 and cond3

と引数の数の変化には対応出来たが、先頭に" and "が入ってしまった。それを削除したのが次のコード

Function Combine(ParamArray conditions()) As String
    Dim ret As String
    Dim i As Long
    For i = LBound(conditions) To UBound(conditions)
        ret = ret & " and " & conditions(i)
    Next i
    Combine = Mid(ret, Len(" and ") + 1)
End Function

実行すると
?combine("cond1","cond2")
cond1 and cond2

?combine("cond1","cond2","cond3")
cond1 and cond2 and cond3

と先頭の" and "が削除された。更に区切り文字 " and "も引数にしたのが次のコード。

Function Combine(delimiter As String, ParamArray conditions()) As String
    Dim ret As String
    Dim i As Long
    For i = LBound(conditions) To UBound(conditions)
        ret = ret & delimiter & conditions(i)
    Next i
    Combine = Mid(ret, Len(delimiter) + 1)
End Function

実行すると
?combine(" and ","cond1","cond2","cond3")
cond1 and cond2 and cond3
となった。

ただし、今の関数だと引数が空文字列の場合
?combine(" and ","cond1","","cond3")
cond1 and and cond3
?combine(" and ","","cond2","cond3")
and cond2 and cond3

のように不要なandが含まれてしまう。引数に空文字列が与えられた場合(つまり検索条件の指定が無かった場合)スキップするように改良したのが次のコード

Function Combine(delimiter As String, ParamArray conditions()) As String
    Dim ret As String
    Dim i As Long
    For i = LBound(conditions) To UBound(conditions)
        If conditions(i) <> "" Then ret = ret & delimiter & conditions(i)
    Next i
    Combine = Mid(ret, Len(delimiter) + 1)
End Function

実行すると
?combine(" and ","cond1","","cond3")
cond1 and cond3
?combine(" and ","","cond2","cond3")
cond2 and cond3
と余計なandが入っていない。これで一応欲しい機能が実現出来たようだ。だが、きちんと動作確認するには単体テストが必要で、それは後日書こうと思う。

最後に

生まれて初めてブログを書いた。ブログを書くのがこんなに難しいとは思わなかった。文章を書く良い訓練になるので、今後も自分が勉強したことを少しずつ書いていこうと思う。
また、シンタックスハイライトが出来なくて困ったが、こちらの記事を見て解決した。ありがとうございます!
k01ken.hatenablog.com