Excel (VBA)

Excel VBAに関するフォーラムです。
  • 解決済みのトピックにはコメントできません。
このトピックは解決済みです。
質問

 
(Windows 10全般 : Excel 2016)
画像ファイルの相対パスを取得させるマクロについて。
投稿日時: 18/12/28 10:39:18
投稿者: アイス

はじめまして、こんにちは。
vbaはまだまだ初心者です。画像ファイル(jpg形式)の参照をするために相対パスを取得させるコードを書きたいのですがどうすればいいかわからなかったので、方法をお尋ねしたく書き込みをさせて頂きます。
 
シート1のA列としましょう。A1のセルにタイトル行として、画像の参照とあります。そしてA2以降に現在のところ絶対パスが入るようなコードを作っていました。ユーザーフォームにテキストボックスを1つ、コマンドボタンを2つ用意し(実際にはほかにもテキストボックスがありますが、質問内容とは関係無いと思いますので省略します)、以下のようなコードを絶対パスを参照させるためのコマンドボタンへ書いています。
 
 Private Sub CommandButton2_Click()
 Dim Dialog_Title As String
 Dim FilePath As Variant
 
 
 Dialog_Title = "イラストのファイルを選択して下さい。"
 
 FilePath = Application.GetOpenFilename(, , Dialog_Title)
 
    If FilePath = flse Then 'パスが選択されないとキャンセル
        Exit Sub
         
    Else: TextBox3.Value = FilePath 'テキストボックスにパスを表示
         
    End If
     
 
 End Sub
 
 
これで、コマンドボタンをクリックすればファイルを選択できるようになり、絶対パスを取得したい画像ファイルをダブルクリックすればテキストボックスへ一旦絶対パスが記述されます。そしてそのほかのテキストボックスへ必要事項をすべて記述した時にもう1つのコマンドボタンをクリックすれば、絶対パスがシート1のA列へと追加されていく(コピーされる)コードができました。
 
しかし、問題が発生しました。このコードを作った時には、絶対パスと相対パスのことについて知らなかったのです。私が今作ろうとしているものは、私のPCだけで操作するのではなく、他のPCでも操作することがあります。そのため、絶対パスでは画像が表示できない(パスから画像を表示させるコードはできています)ことにようやく気付きました。
 
フォルダ、ファイルの現在の置き方(?)ですが、上のようなコードなどを記述しているブックがとあるフォルダー内にあります。そして画像ファイルがブックと同じフォルダー内に置かれています。
しかし、画像のファイルは、それらが入ったフォルダを用意してまとめたいと思っています。
そのため、とあるフォルダにブックが1つ、画像ファイルが入れられたフォルダが1つあるといったイメージでしょうか。
 
この状態で絶対パスではなく、相対パスを取得するマクロのコードが知りたいです。
それともう1点お伺いしたいのですが、相対参照する場合は画像の入ったフォルダ内で、画像ファイルの位置が別の画像ファイルの位置と入れ替わるとうまく参照できないでしょうか?画像ファイルは必要な時に次々に追加していき、追加したファイルのパスをブックのマクロで取得してもらい・・・という流れで考えているのです。このように追加された時に画像ファイルの位置関係がずれたり、あるいは並び替えで位置関係がずれたりした場合は別の画像が参照されるようになってしまうのでしょうか?相対パスについて詳しくなくて申し訳ございません。
 
ご回答頂ければ幸いです。よろしくお願い致します。

回答
投稿日時: 18/12/28 13:37:47
投稿者: Suzu

こんにちは。
 
VBAは、絶対パスで動作させます。
 
ですので、相対パス の取得は用意されていません。
 
 
当該動作をするコードを内包するワークブックが、仮に C:\DATA\Sumple.xlsm だとします。
 
この時の、「C:\DATA」は、ThisWorkBook.Path にて取得可能です。
画像が、 C:\DATA\Photo にあるのであれば、 『ThisWorkBook.Path & "\Photo"』で参照できますよね。
 
ThisWorkBook.Path & "\Photo" は、相対パスの様にも見えますよね。
まぁ、コーディングの仕方次第と言ったところでしょう。
 
 
画像の取り込みについてですが、
使用している コマンドで、
・ブック内に取りこまれている (画像ファイル内臓)
・必要に応じて、元の 画像ファイルのパスを参照しに行き再表示を行う (画像リンク)
の動作に違いが出ます。
 
ワークシートオブジェクト.Pictures.Insert : 画像リンク
ワークシートオブジェクト.Shapes.AddPicture : 画像ファイル内臓
 
の様になります。
 
この時も、参照はあくまでも、絶対パスでの参照です。
 
相対パスにしたいのであれば、そうなる様に(見せかける)コーディングを行いましょう。

回答
投稿日時: 18/12/28 13:55:39
投稿者: WinArrow
投稿者のウェブサイトに移動

運用するPCごとに絶対パスは、異なる・・・当たり前のことです。
 自ブックのフォルダと画像ファイルの入っているフォルダの関係を、
どのように取得すできるか?
ってことですか?
  
なんのヒントもなしに、自動で取得するには、かなりハードルが高いでしょう。
  
例えば、「デスクトップ」とか「マイドキュメント」とか
 に画像ファイルを保存する取り決めをすれば、
GetOpenFilenameメソッドで実行する前に準備することができます。
要は、GetOpenFilenameメソッド実行時に表示されるダイアログに準備したフォルダを指定できるということです。
  
こんなことで、相対パスを指定することの回答になっていますか?

回答
投稿日時: 18/12/28 14:49:22
投稿者: Suzu

引用:
ワークシートオブジェクト.Pictures.Insert : 画像リンク
ワークシートオブジェクト.Shapes.AddPicture : 画像ファイル内臓

 
説明が不十分でした。これは、当方の環境の場合です。
 
ワークシートオブジェクト.Pictures.Insert は、Officeのバージョンによっては、
エクセルファイルに画像ファイルが内臓されるコマンドになっています。
 
 
【画像ファイルを挿入する】
https://www.moug.net/tech/exvba/0120020.html

回答
投稿日時: 18/12/28 17:33:39
投稿者: WinArrow
投稿者のウェブサイトに移動

>とあるフォルダにブックが1つ、画像ファイルが入れられたフォルダが1つある
 この状況が前提ならば、
 本マウロブックが入ったフォルダと、
 画像ファイルが入れられたフォルダの関係を
次のように決めれば、自ブックのPATHから画像ファイルのフォルダーを
 アクセスできます。
  
---親フォルダ---自ブック
       ┗画像ファイルが入ったフォルダ--画像ファイル多数
  
Dim myPATH As String
   
     myPATH = Thisworkbook.Path
     ChDir myPATH & "\画像ファイルフォルダ"
     JpgFilePath = Aplplication.GetOpenFilename("画像ファイル,"*.jpg")
   

投稿日時: 18/12/28 19:48:58
投稿者: アイス

皆様、ご回答ありがとうございます。
 
皆様がおっしゃって下さったことをもとに今からいろいろと試してみます。うまくいくとよいのですが・・・。
 
うまくいけば、あるいは、再びつまづいたらまたご連絡させて頂きます。

投稿日時: 18/12/28 21:58:36
投稿者: アイス

WinArrow様が書いてくださっていたコードを少し書き換えて使用してみました。しかし、表示されたのは絶対パスであると思うのです・・・。
 
相対パスの取得は直接はできないのでしょうか?
絶対パスを取得して、それを書き換えて相対パスにする という方法しかないでしょうか?

回答
投稿日時: 18/12/28 23:15:19
投稿者: simple

なぜ相対パスが必要なんですか?
 
相対パスという概念は、何を基準にするかということが重要です。
どんな基準があってもそれに対応する相対パスが必要なんですか?
 
マクロがあるExcelBookがあるフォルダという特定の基準パスなんでしょう?
すでに回答は出ていると思いますよ。
何か、無い物ねだりをされている印象です。
 
なぜ相対パスが必要なのか、詳しく説明してもらえますか?

回答
投稿日時: 18/12/28 23:23:17
投稿者: simple

つまり、
JpgFilePath=Aplplication.GetOpenFilename("画像ファイル,"*.jpg")
で絶対パスがえられます。
表示などには、Dir(JpgFilePath)でファイル名を出してそれを表示すればいいし、
実際の取得には、JpgFilePathを使えばよいだけではないですか?
任意の基準に基づく一般的な相対パスを取得するアルゴリズムなど不要でしょう。

投稿日時: 18/12/28 23:33:18
投稿者: アイス

simple さんの引用:
なぜ相対パスが必要なんですか?
 
相対パスという概念は、何を基準にするかということが重要です。
どんな基準があってもそれに対応する相対パスが必要なんですか?
 
マクロがあるExcelBookがあるフォルダという特定の基準パスなんでしょう?
すでに回答は出ていると思いますよ。
何か、無い物ねだりをされている印象です。
 
なぜ相対パスが必要なのか、詳しく説明してもらえますか?

 
相対パスが必要な理由は、私以外のものが、私のPC以外で画像の追加、参照をするためです。マクロのあるブックを基準に相対パスを取得させたいと思っています。相対パスをべた打ちするのではなく、GetOpenFilenameなどを用いて、ファイルをクリックするだけで相対パスがテキストボックスに表示されるようにしたいのです。
ブックと同じ階層に画像のフォルダを用意して、そのフォルダ内に画像は入れてありますので、ブックを基準にして参照させればいいことはなんとなく理解できています。しかし、ファイルを選択した際に相対パスを入力するにはどうすればいいのかが分からないのです・・・。
相対パスについて、図書館の本を用いて調べてはみたのですが、私が手に取ったどの本にもほとんど相対パスについては載っていませんでした・・・。
 
vbaの勉強を始めて長くもありませんし、エクセルやPCにも特に詳しいというわけではないので、もしかしたらおっしゃっていただいていることが十分に理解できていないのかもしれません。
もしよろしければ、具体的なコードをご提示して頂けないでしょうか?

回答
投稿日時: 18/12/28 23:43:22
投稿者: simple

>しかし、ファイルを選択した際に相対パスを入力するにはどうすればいいのかが分からないのです・・・。
何のために、どこに入力するんですか?

投稿日時: 18/12/29 00:02:56
投稿者: アイス

>何のために、どこに入力するんですか?
絶対パスだと他のPCでは動作しないので、他のPCでも画像を表示できるようにです。
相対パスを表示する場所は一旦ユーザーフォームのテキストボックスに表示して、そのほか必要事項を記入すれば、コマンドボタンを押して、相対パスやその他の事項がセルへそれぞれ入力されるようにしています。
相対パスがまず出力されるのはユーザーフォームのテキストボックスですね。

回答
投稿日時: 18/12/29 00:03:15
投稿者: simple

    Dim JpgFilePath As String
    JpgFilePath = Application.GetOpenFilename("画像ファイル,*.jpg")
    Debug.Print Replace(JpgFilePath, ThisWorkbook.Path & "\", "")

キャンセルを考慮していませんが、こんな風にすれば相対パスは得られますけどねえ。

回答
投稿日時: 18/12/29 00:06:57
投稿者: simple

>絶対パスだと他のPCでは動作しないので、他のPCでも画像を表示できるようにです。
いや、固定した絶対パスを与えるんじゃなく、
Application.GetOpenFilenameを使うんでしょう?
それはそのPCで操作するときのものになるじゃありませんか。
他のPCでも支障ないはずだと思いますが。

投稿日時: 18/12/29 00:12:14
投稿者: アイス

simple さんの引用:
    Dim JpgFilePath As String
    JpgFilePath = Application.GetOpenFilename("画像ファイル,*.jpg")
    Debug.Print Replace(JpgFilePath, ThisWorkbook.Path & "\", "")

キャンセルを考慮していませんが、こんな風にすれば相対パスは得られますけどねえ。

 
ありがとうございます。実際にやってみましたが、なぜか表示されるのは、
C:\Users\〇〇〇(ユーザー名)\Desktop\ブックのあるフォルダ名\画像のあるフォルダ名\ファイル名.jpg
このような絶対パスの形でした・・・。私のやり方がまずいのでしょうか?

回答
投稿日時: 18/12/29 00:23:41
投稿者: simple

>とあるフォルダにブックが1つ、画像ファイルが入れられたフォルダが1つある
という前提になっているんですよね。
つまり、
・マクロがあるブックが保存されているフォルダの下に画像フォルダがあって、
・その中の画像をGetOpenFileNameで指定しているんですよね。
そこは間違いないですか?

回答
投稿日時: 18/12/29 00:32:59
投稿者: simple

D:\AAA\BBB\book1.xlsm が当該ブック
D:\AAA\BBB\GAZOU\gazou1.jpg とする。
 
ThisWorkbook.Pathは D:\AAA\BBB が返る。
 
D:\AAA\BBB\GAZOU\gazou1.jpgという絶対パスに対して、
D:\AAA\BBB\ を ""に置換すれば、
GAZOU\gazou1.jpgという相対パスが得られる。
 
そういう意味です。

投稿日時: 18/12/29 00:51:50
投稿者: アイス

simple さんの引用:
D:\AAA\BBB\book1.xlsm が当該ブック
D:\AAA\BBB\GAZOU\gazou1.jpg とする。
 
ThisWorkbook.Pathは D:\AAA\BBB が返る。
 
D:\AAA\BBB\GAZOU\gazou1.jpgという絶対パスに対して、
D:\AAA\BBB\ を ""に置換すれば、
GAZOU\gazou1.jpgという相対パスが得られる。
 
そういう意味です。

 
ブックのパス、画像ファイルのパスをコピーして確認してみました。
画像ファイルのパスはブックのパスと比べて、画像ファイルがあるフォルダ名が付いています。それ以前の文字列は同じです。ということは、合ってますよね?フォルダの中にブックと画像ファイルのフォルダが並列しています。画像ファイルのフォルダには画像ファイルが存在しています。
 
先ほど書いてくださったコードの直後に
TextBox1.Value = jpgFilePath
と記述していますが、やはり表示されるのはD:\AAA\BBB\GAZOU\gazou1.jpgこの形のパスでした・・・。

回答
投稿日時: 18/12/29 07:52:06
投稿者: simple

Debug.Print Replace(JpgFilePath, ThisWorkbook.Path & "\", "")
の意味を理解していますか?
 
Replace(JpgFilePath, ThisWorkbook.Path & "\", "")
は具体例で説明しましたよね。
文字列の置換です。
JpgFilePathという絶対パスの文字列のなかの、
ThisWorkbook.Path & "\" という文字列を
"" という空文字列に置換する、という処理です。
 
置換した文字列そのものです。
JpgFilePathは置換されていませんよ。
 
Debug.Print というのは、
「イミディエイトウインドウ」という
デバッグ用のウインドウに表示しなさい、
という命令です。
 
実際に結果を確認してください、というのが私の意図でした。
 
テキストボックスに表示するなら、
Debug.Print Replace(JpgFilePath, ThisWorkbook.Path & "\", "")
の代わりに、
TextBox1.Value = Replace(JpgFilePath, ThisWorkbook.Path & "\", "")
とすればよいでしょう。
 
これでどうでしょうか?
内容を理解せずに単にコピーペイストするだけだと悲しい結果になりますよ。

回答
投稿日時: 18/12/29 08:08:23
投稿者: simple

画像フォルダ名も各人が任意につけられるとしたら、相対ファイルといえども障害のもとになりますね。
 
また、画像がどのくらいあるのか知りませんが、
画像フォルダ名を逐一つけて表示されるのもうっとおしくないですか?
単に画像ファイル名だけを取り扱えばいいんじゃないですか?
 
また、すでに書いたように、
@Dir(GetOpenFileNameで取得したフルパス)でファイル名が得られるし、
AThisWorkBook.Path & "\" & 画像フォルダ名 & ファイル名 とすることでフルパスが得られるわけで、
相対ファイルという考えに振り回される必要もないですよ。
なお、これらはすでに皆さんから提示されている話です。

投稿日時: 18/12/29 10:12:53
投稿者: アイス

 Debug.Printとはイミディエイトウィンドウに表示するものなのですね。
 Replaceと直後にあったので、jpgFilePathが書き換えられているものとばかり思ってしまっていました。
 
TextBox1.Value = Replace(JpgFilePath, ThisWorkbook.Path & "\", "")
こちらのコードで相対パスは取得することができました。
 
画像を表示するのは、ユーザーフォームのイメージに表示させるようにと思っていました。
LoadPictureメソッドを用いていましたが、こちらは絶対パスでなくては表示できないのですね。相対パスでは、「パスが不正です」というエラーが出てしまいました。
 
画像を表示するだけの人と、画像の登録もできる人の2パターン存在するのですが、画像を登録する際には相対パスで記述しておいて、画像を表示するマクロの直前に絶対パスに変換するといった操作が必要でしょうか?

投稿日時: 18/12/29 10:27:08
投稿者: アイス

追記致します。
 
LoadPictureを用いて、相対パスでも画像の表示ができると思っていたために、おかしなことになっていました。ハイパーリンクで試したところ、やはり相対は正しく取得できていました。
 
となると、相対パスを基に絶対パスへ一時的に変更して、画像の表示を行う必要がありますかね?

回答
投稿日時: 18/12/29 10:34:01
投稿者: simple

カレントフォルダが「相対パスの基準となるフォルダ」と一致していれば、
相対パスを指定するだけでLoadPictureを使って取得ができます。
カレントフォルダを変更するには ChDir を使います。
ChDir ステートメントでヘルプを参照してください。
(ちなみに、CurDirでカレントフォルダが確認できます。これもヘルプで調べてください)
 
# 相対パス相対パスと連呼するからには、そうしたことは理解できているものと思っていましたが。

回答
投稿日時: 18/12/29 11:12:57
投稿者: WinArrow
投稿者のウェブサイトに移動

「絶対パス」と「相対パス」の概念が理解されていないようです。
 
あなたがいう、他PCと自PC(開発用PC)のパスが違うのは、
単に「絶対パス」が違う・・・だけで、それを「相対パス」云々とは言いません。
 
「相対パス」は、何かを基準として、目的のファイルなり、フォルダなりが
基準との関係を指すものです。
 

投稿日時: 18/12/29 12:58:00
投稿者: アイス

 
相対パスではなぜかLoadPictureで画像を表示することができません。カレントフォルダを基準にして相対パスは取っているのですが・・・。
 
画像を表示する直前に、画像の入ったフォルダ名以降のみをMID関数を用いて取り出し、あとはThisWorkbbok.Path & MID関数で絶対パスを毎回表示させるようにしました。
 
Dim txt As String
 
txt = ws1.Range("B" & i).Value
ws1.Range("B" & i).Value = ThisWorkbook.Path & Mid(txt, InStr(txt, "\画像のあるフォルダ名") - 1)
 
 
これで、解決できました。ちなみに、コマンドボタンをクリックした時に上のコードを実施し、そのあと続けてユーザーフォームを表示(画像の表示)をしています。変数のiは最終行までを指していて、for i 構文でループさせています。
 
相対パスで直接画像を表示できればいいのですが、どうもうまくいかないので、このようにしました。
シート1のB列を毎回書き換えるのでデータが増えれば増えるほど重くはなってしまいますが、1万を超えるようなデータではなく多くても千くらいのパスしか記入しないと思いましたので、このように致しました。
 
ご教授して頂いた皆様、本当にありがとうございました。

回答
投稿日時: 18/12/29 13:19:39
投稿者: simple

念のためコードを示します。

    Dim 基準フォルダ As String
    
    基準フォルダ = ThisWorkbook.Path
    ChDrive 基準フォルダ
    ChDir 基準フォルダ
これで、基準となるフォルダをカレントフォルダに設定できると思います。
(ドライブがまたがっているケースも想定しています。)
画像へのアクセスの都度する必要はなく、
UserForm_Initializeのなかで一度だけ実行すればよいでしょう。
こうしておけば、GetOpenFileNameのデフォルト表示は基準となるパスにもなります。
 
# もっとも、カレントフォルダを変更するとファイルの保存などで思わぬ予想外のことが
# 起きる懸念もありますので、神経を使うかもしれません。
# いったん絶対パスに変換(基準となるフォルダを頭につける)して
# 絶対パスでLoadPictureするのが間違いは少ないと思います。
 
要するに、人にどう示すか、です。
コード側で背後でやりくりすれば良いのです。
もし画像フォルダ名が統一されているなら、
画像ファイル名だけを表示することにすればスッキリします。
裏で、基準フォルダと画像フォルダを補足すればよいだけです。
 
私は以上とします。

回答
投稿日時: 18/12/29 15:12:23
投稿者: WinArrow
投稿者のウェブサイトに移動

>相対パスではなぜかLoadPictureで画像を表示することができません。
 
投稿日時: 18/12/28 13:37:47 の Suzu さんレス
>VBAは、絶対パスで動作させます。
>
>ですので、相対パス の取得は用意されていません。
 
当たり前だと考えている人には、
どのような形で指定すると「相対パス」になるのか??
わかりません。

投稿日時: 18/12/29 15:46:53
投稿者: アイス

>基準フォルダ = ThisWorkbook.Path
この回答を拝読してこの方法があったかとおもいました。VBAにもっと触れて、コーディングに慣れていこうと思います。
 
私の考えていた相対パスとは、
.\フォルダ名\ファイル名.拡張子
でした。ブックを基準にして1つ下の階層に画像が用意されていたので、相対パスはこの形でした。
 
simple様、WinArrow様、最後までありがとうございました。
 
解決済みにさせて頂きます。本当にありがとうございました。重ねてお礼申し上げます。