Python(pywin32)でWordを操作する[5] - ドキュメントをファイルに保存する、Wordのオプション変更


#概要

Document.SaveAs2(), Document.Save()を使って、ドキュメントをファイルに保存します。
最後、横道にそれてWordのオプション「既定のローカルファイルの保存場所」を変更します。

次回は、[「特定のタイトルが付いているウィンドウを閉じる - Qiita」](https://qiita.com/int_main_void/items/1cd864010e944b07185e)をpywin32で焼き直しします。

#今回利用するWordオブジェクト

![word_obj.png](https://qiita-image-store.s3.amazonaws.com/0/34777/74d1e357-3c3c-2f72-1ba0-ad7bb9f68fca.png)

|オブジェクト名|プロパティ/メソッド|説明|リンク|
|:---|:---|:---|:---|
|Document |-      |Documents内の1つの文書|[msdn][doc_msdn]|
|         |SaveAs2()|指定した文書を新しい名前または形式で保存|[msdn][doc_saveas2_msdn]|
|         |Save()  |指定した文書を保存|[msdn][doc_save_msdn]|
|         |Name|ファイル名のみ|[msdn][doc_name_msdn]|
|         |FullName|ファイル名を含むパス(フルパス)|[msdn][doc_fullname_msdn]|
|         |Path |ファイル名を含まないパス|[msdn_en-us][doc_path_msdn_en-us], [msdn_ja-jp][doc_path_msdn_ja-jp]|

msdnのPathの機械翻訳はマズイ部分があるので、en-usのほうを参照したほうがいいです。


#コード
qtconsoleの確認結果を示していきます。
↓の「★なんらかの処理★」部分に記述しているイメージで読んでください。

```python
import win32com.client

#Wordを起動する : Applicationオブジェクトを生成する
Application=win32com.client.Dispatch("Word.Application")

#Wordを画面表示する : VisibleプロパティをTrueにする
Application.Visible=True

#新規ドキュメントを作成する : Documents.Add()する。
doc=Application.Documents.Add()

#適当な文字列を書き込む : Content.Textに文字列を入れる
doc.Content.Text="あいうえおかきくけこ"

★なんらかの処理★


#Wordを終了する　: Quitメソッドを呼ぶ
Application.Quit()
```


##ファイルを保存する

タイトルバーに"文書 1"と表示されている状態で、
![shinki1.png](https://qiita-image-store.s3.amazonaws.com/0/34777/c0896705-03be-58bc-23ea-eedfb13bc9b8.png)

Name, FullName, Pathを確認してみます。

```python
In [241]: doc.Name
Out[241]: '文書 1'

In [242]: doc.FullName
Out[242]: '文書 1'

In [243]: doc.Path
Out[243]: ''
```

Document.SaveAs2()を使って、"test1"という名前を付けて保存します。

```python
In [246]: doc.SaveAs2("test1")
```

画面ではタイトルバーに"test1.docx"と表示された状態になります。
![test1.png](https://qiita-image-store.s3.amazonaws.com/0/34777/3f336d5b-d699-24f4-37e5-c85e3dff850e.png)

ファイル名だけ指定したので、どこにあるのかがわかりません。
どこに保存されたのでしょうか・・・Name, FullName, Pathを確認してみます。

```python
In [247]: doc.Name
Out[247]: 'test1.docx'

In [248]: doc.FullName
Out[248]: 'C:\\Users\\xxxxx\\Documents\\test1.docx'

In [249]: doc.Path
Out[249]: 'C:\\Users\\xxxxx\\Documents'
```

"C:\Users\xxxxx\Documents\test1.docx"に保存されたことがわかります。

保存する処理自体はここまでです。


##Wordのオプション「既定のローカルファイルの保存場所」を変更

保存の動作について少し深堀りして調べてみます。

ファイルの保存先はカレントディレクトリが考慮されているのでしょうか？
Cドライブ直下にtestというフォルダを作成して試してみましょう。

```python
In [260]: cd c:\test
C:\test

In [261]: pwd
Out[261]: 'C:\\test'
```

新しいDocumentを追加、名前を付けて保存してみます。

```python
#新しいDocumentを追加: Documents.Add()する
In [264]: doc2=Application.Documents.Add()

#名前を付けて保存 : Document.SaveAs2("test2")する
In [266]: doc2.SaveAs2("test2")
```

フルパスを確認してみると・・・

```python
#フルパスを確認 : Document.FullNameを見る
In [267]: doc2.FullName
Out[267]: 'C:\\Users\\xxxxx\\Documents\\test2.docx'
```

C:\testになりませんでした。カレントディレクトリは全く考慮されてないようです。

どうやら[「既定のローカルファイルの保存場所」][word_defalut_file]の設定を見ているようです。
![kitei.png](https://qiita-image-store.s3.amazonaws.com/0/34777/40b84d1b-71ed-34b1-1195-ecc984cb994d.png)

つまり、ファイル名だけだと「既定のローカルファイルの保存場所にあるファイル」を指すことになります。これは、「保存」だけでなく、「開く」でも同じです。

Wordは「手でできることは **たいてい** スクリプトできる」ように作られているので、このWordのオプション「既定のローカルファイルの保存場所」も取得・変更できるんじゃないでしょうか？

調べてみると、[DefaultFilePathプロパティ][app_options_msdn]と[WdDefaultFilePath列挙型][enum_WdDefaultFilePath_msdn]でできそうです。

DefaultFilePathプロパティの引数にwdDocumentsPath(0)を設定して呼び出してみます。

```python
In [271]: Application.Options.DefaultFilePath(0)
Out[271]: 'c:\\users\\xxxxx\\documents'
```
↑「既定のローカルファイルの保存場所」が取得できました。

設定もできるでしょうか？

[msdn][app_options_msdn]のVBScript例のように代入してみます。

> ```vb
Options.DefaultFilePath(wdDocumentsPath) = "C:\Documents"
```

↓

```python
In [273]: Application.Options.DefaultFilePath(0)="C:\test"
  File "<ipython-input-273-a8a37401d38a>", line 1
    Application.Options.DefaultFilePath(0)="C:\test"
                                                    ^
SyntaxError: can't assign to function call
```
あ、関数呼びだしへの代入と解釈されてしまいました。

じゃあ無理なんでしょうか。。。

いや、[調べてみる][set_default_filepath]と、pywin32は「プロパティだけどメソッドみたいなやつ」も考慮していて、それ用のメソッドが生成されるようです。

DefaultFilePathの場合、SetDefaultFilePathメソッドが生成されています。

```python
In [290]: dir(Application.Options)
Out[290]: 
['CLSID',
 'DefaultFilePath',
 'SetDefaultFilePath', #<---これ
・・・
```

SetDefaultFilePathを使って、設定してみましょう。

```python
In [291]: Application.Options.SetDefaultFilePath(0, "c:\\test")

In [292]: Application.Options.DefaultFilePath(0)
Out[292]: 'c:\\test'
```
設定できました。

画面から確認も確認してみましょう。
![defautl_file_path_after.png](https://qiita-image-store.s3.amazonaws.com/0/34777/cc3da0de-13e8-71b1-b02c-ced5e79a3925.png)

変更できています。

#「手でできることはたいていスクリプトできる」・・・じゃあ、スクリプトでできないことって？

※全部はわかりませんが・・・

[開発タブ](https://www.wordvbalab.com/word/3572/)→コード→Visual Basicと手操作でVBE(Visual Basic Editor)を起動できます。

しかし、[Application.VBE][app_vbe_msdn]はスクリプトからアクセス(プログラム的なアクセス)できないので、スクリプトからは起動できません。

```python
In [272]: Application.VBE
---------------------------------------------------------------------------
com_error                                 Traceback (most recent call last)
...
com_error: (-2147352567, '例外が発生しました。', (0, 'Microsoft Word', 'Visual Basic Project へのプログラム的なアクセスは信頼されません。', 'wdmain11.chm', 25548, -2146822220), None)
```

#関連
[Python(pywin32)でWordを操作する[1] - Wordオブジェクトモデル](https://qiita.com/int_main_void/items/16cede139bd64514b810)
[Python(pywin32)でWordを操作する[2] - Wordを起動／終了する](https://qiita.com/int_main_void/items/8776cc4b9b05478273b3)
[Python(pywin32)でWordを操作する[3] - 新規ドキュメント作成](https://qiita.com/int_main_void/items/a36cf348d6eedb31246f)
[Python(pywin32)でWordを操作する[4] - 文字列を入力/取得/削除する](https://qiita.com/int_main_void/items/f135d321262d9de05efc)
[Python(pywin32)でWordを操作する[5] - ドキュメントをファイルに保存する、Wordのオプション変更](https://qiita.com/int_main_void/items/b8549c92b33ea0f7d63b)
[Python(pywin32)でWordを操作する[6] - 特定のタイトルが付いているウィンドウの操作](https://qiita.com/int_main_void/items/6c3569dc242f843ca946)
[Python(pywin32)でWordを操作する[7] - 既存文書を開く/閉じる(Documents.Open(), Document.Close())](https://qiita.com/int_main_void/items/af46e7fe40f528b0b5cd)
[Python(pywin32)でWordを操作する[8] - 段落単位の文字列取得, 統計(ページ数, 段落数,etc)取得](https://qiita.com/int_main_void/items/7c5bc0aab52d8bfadd6e)

#参考
- [新規ファイルの既定の保存先を「デスクトップ」にしたい  [aotenブログ]木村幸子の知って得するマイクロソフトオフィスの話][word_defalut_file]
- [Word VBAによるドキュメント一括処理の第一歩！文書ファイルを開く方法と閉じる方法][word_default_file2]
- [python - Set parameterized COM property - Stack Overflow][set_default_filepath]


- [オブジェクト モデル - MSDN][object_model]
- [PyWin32 Documentation - Tim Golden's Stuff][tim_pywin32_docs]
- [VOV(VBA, OLE, VBScript)による自動操作 - スクリプト系プログラミング言語ファンのじたばた][vov]
- [COM(Component Object Model)についてふわっと学習していく その１ 導入編 - Qiita][qiita_com]
- [VBScriptでWordファイルのページ数一覧を作ってみる - Segmentation Fault][macro_list]
- [Office Space  Microsoft Word 文書でテキストを検索、置換する][vbscript_word_search]
- [みんなのワードマクロ][macro]

[word_defalut_file]:http://ciao.aoten.jp/ciao/2016/03/post-d958.html
[word_default_file2]:https://tonari-it.com/word-vba-document-open-close/


[set_default_filepath]:https://stackoverflow.com/questions/45252880/set-parameterized-com-property
[app_options_msdn]:https://msdn.microsoft.com/en-us/vba/word-vba/articles/options-defaultfilepath-property-word
[enum_WdDefaultFilePath_msdn]:https://msdn.microsoft.com/en-us/vba/word-vba/articles/wddefaultfilepath-enumeration-word

[doc_saveas2_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/document-saveas2-method-word
[doc_save_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/document-save-method-word
[docs_save_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/documents-save-method-word
[app_vbe_msdn]:https://msdn.microsoft.com/en-us/vba/word-vba/articles/application-vbe-property-word

[quit_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/application-quit-method-word
[visible_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/application-visible-property-word
[object_model]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/object-model-word-vba-reference
[qiita_com]:https://qiita.com/mintcandy/items/979a4a2669c8fc1f4f2f
[tim_pywin32_docs]:http://timgolden.me.uk/pywin32-docs/index.html
[vov]:http://www.dastec.org/vov/index.htm
[macro]:https://www.wordvbalab.com/
[macro_list]:https://www.wordvbalab.com/page-2266/
[vbscript_word_page]:http://www.segmentation-fault.xyz/entry/2017/11/25/222859
[vbscript_word_search]:https://technet.microsoft.com/ja-jp/library/ee692875.aspx

[rng_delete_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/document-content-property-word
[rng_text_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/range-text-property-word
[doc_rng_msdn]:https://msdn.microsoft.com/ja-jp/VBA/Word-VBA/articles/document-range-method-word

[rng_start_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/range-start-property-word

[rng_end_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/range-end-property-word
[doc_content_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/document-content-property-word

[rng_insaft_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/range-insertafter-method-word

[rng_insbfr_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/range-insertbefore-method-word

[inserting-text-in-a-document]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/inserting-text-in-a-document
[app_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/application-object-word
[docs_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/documents-object-word
[doc_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/document-object-word
[paras_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/paragraphs-object-word
[para_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/paragraph-object-word
[sens_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/sentences-object-word
[wrds_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/words-object-word
[chrs_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/characters-object-word
[rng_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/range-object-word
[rng_txt_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/range-text-property-word
[para_rng_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/paragraph-range-property-word
[docs_count_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/documents-count-property-word
[docs_open_mdsn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/documents-open-method-word
[doc_close_mdsn]:https://msdn.microsoft.com/ja-jp/VBA/Word-VBA/articles/document-close-method-word
[doc_fullname_msdn]:https://msdn.microsoft.com/ja-jp/VBA/Word-VBA/articles/document-fullname-property-word
[doc_name_msdn]:https://msdn.microsoft.com/ja-jp/VBA/Word-VBA/articles/document-name-property-word
[doc_activate_msdn]:https://msdn.microsoft.com/ja-jp/VBA/Word-VBA/articles/document-activate-method-word
[doc_path_msdn_ja-jp]:https://msdn.microsoft.com/ja-jp/VBA/Word-VBA/articles/document-path-property-word
[doc_path_msdn_en-us]:https://msdn.microsoft.com/en-us/VBA/Word-VBA/articles/document-path-property-word
[docs_add_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/documents-add-method-word
[app_activedoc_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/application-activedocument-property-word
[doc_saveas2_mdsn]:https://msdn.microsoft.com/ja-jp/VBA/Word-VBA/articles/document-saveas2-method-word
[doc_activate_msdn]:https://msdn.microsoft.com/ja-jp/VBA/Word-VBA/articles/document-activate-method-word