Python(pywin32)でWordを操作する[7] - 既存文書を開く/閉じる(Documents.Open(), Document.Close())

#概要

Documents.Open(), Document.Close()を利用して既存文書を開いて閉じます。
そのほかのテーマは以下です。

- [コレクションに対して「名前」でアクセスする](#コレクションと名前付きオブジェクトについて)
- [名前付き引数に気を付ける](#名前付き引数に気を付ける)


#利用する文書について

Word文書が安全かどうか保証できないので適宜用意してください。

そこそこ量があり、章・節を使った構造的文書(例えば、設計書・仕様書)がほしかったので、ここではローカルにあった(Visual Studioインストールで入った)「C# Language Specification Version 5.0」(CSharp Language Specification.docx)のコピーを利用しています。

![charp.png](https://qiita-image-store.s3.amazonaws.com/0/34777/1e659368-f25f-2fe7-d6aa-ed2492d3e2dd.png)

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

![word_obj__.png](https://qiita-image-store.s3.amazonaws.com/0/34777/dd4cdb6b-21df-5a81-6849-d17df2a5d5a9.png)

|オブジェクト名|プロパティ/メソッド|説明|リンク|
|:---|:---|:---|:---|
|Documents|- |Documentのコレクション|[msdn][docs_msdn]|
|         |Open() |文書を開く|[msdn][docs_open_msdn]|
|Document |-      |Documents内の1つの文書|[msdn][doc_msdn]|
|         |Close()|指定した文書を閉じる|[msdn][doc_close_msdn]|


#コード

qtconsoleの確認結果を示していきますが、前回同様テンプレ的な処理は省略します。
↓の「★なんらかの処理★」部分に記述しているイメージで読んでください。

```python
import win32com.client

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

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


★なんらかの処理★


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

##文書を開く

Documents.Open()を利用して、C:\tmp\CSharp Language Specification.docxにあるファイルを開きます。ファイル名を絶対パスで指定して開いてみます。

```python
In [54]: doc=Application.Documents.Open(r"C:\tmp\CSharp Language Specification.docx")
```

pathlibを利用して相対パス的に開いてみます。

```python
In [57]: import pathlib

In [58]: cd c:\tmp
c:\tmp

In [59]: p=pathlib.Path(".").joinpath("CSharp Language Specification.docx")

In [60]: p.resolve()
Out[60]: WindowsPath('C:/tmp/CSharp Language Specification.docx')

In [61]: doc=Application.Documents.Open(str(p.resolve()))
```

##文書を読み取り専用で開く

Documents.Open()を読み取り専用で開いてみます。引数のReadOnlyを利用します。
[名前付き引数に気を付ける](#名前付き引数に気を付ける)も参照ください。

[Documents.Open() - msdn][docs_open_msdn]
> 式 . Open( FileName, ConfirmConversions, **ReadOnly** , ・・・

|名前|必須 / 省略可能|説明|
|:---|:---|:---|:---|
|ReadOnly|省略可能|True:読み取り専用|


```python:方法1-名前付き引数
In [71] doc=Application.Documents.Open(FileName=r"C:\tmp\CSharp Language Specification.docx", ConfirmConversions=False, ReadOnly=True)
```

```python:方法2-位置引数
In [72] doc = Application.Documents.Open(r"C:\tmp\CSharp Language Specification.docx",False,True)
```

```python:方法3-位置引数+Noneで省略
In [73] doc=Application.Documents.Open(r"C:\tmp\CSharp Language Specification.docx",None,True)
```

##文書を閉じる

docで受け取ったDocumentオブジェクト参照からClose()メソッドを呼ぶだけです。

```python
In [74]: doc.Close()
```

ちなみに、Documentオブジェクトをdocで受け取るのはMustではないです。
[後述](#コレクションと名前付きオブジェクトについて)しますが、Documentオブジェクトは名前付きオブジェクトなので、名前(Name, FullNameと同じ文字列)でコレクションから取りだせます。

```python
#Open()で開いてNameを確認する
In [88]: Application.Documents.Open(r"C:\tmp\CSharp Language Specification.docx").Name
Out[88]: 'CSharp Language Specification.docx'

#Nameと同じ文字列を使って、Documentsコレクションからオブジェクトを取り出し、Close()を呼ぶ
In [89]: Application.Documents("CSharp Language Specification.docx").Close()
```

```python
#Open()で開いてFullNameを確認する
In [90]: Application.Documents.Open(r"C:\tmp\CSharp Language Specification.docx").FullName
Out[90]: 'C:\\tmp\\CSharp Language Specification.docx'

#FullNameと同じ文字列を使って、Documentsコレクションからオブジェクトを取り出し、Close()を呼ぶ
In [91]: Application.Documents("C:\\tmp\\CSharp Language Specification.docx").Close()
```

##変更を保存せずに文書を閉じる
Document.Close()を使って、変更を保存せずに文書を閉じてみます。
引数のSaveChangesの指定に定数(wdDoNotSaveChangesなど)を利用できますが、[事前準備][makepy_hunya]が必要なので、今回は直値で指定します。

[Document.Close() - msdn][doc_close_msdn]
> 式 . Close( **SaveChanges**, OriginalFormat, RouteDocument )

|名前|必須 / 省略可能|説明|
|:---|:---|:---|:---|
|SaveChanges|省略可能|文書の保存方法を指定。使用できる定数は、WdSaveOptions列挙のいずれか|

[WdSaveOptions列挙-msdn][wdsaveoptions-enumeration-word]

|名前|値|説明|
|:---|:---|:---|:---|
|wdDoNotSaveChanges|0|保留中の変更を保存しない|
|wdPromptToSaveChanges|-2|保留中の変更を保存するかどうかユーザーに確認|
|wdSaveChanges|-1|保留中の変更をユーザーに確認しないで自動的に保存|


引数なしのClose()と挙動の違いを見るために、文書を読み取り専用で開きます。

```python:
#文書を読み取り専用で開く : Open(ReadOnly=True)を呼ぶ
In [121]: doc=Application.Documents.Open(FileName=r"C:\tmp\CSharp Language Specification.docx", ConfirmConversions=False, ReadOnly=True)
```

手で適当に変更を加えます。

![henkou.PNG](https://qiita-image-store.s3.amazonaws.com/0/34777/a8803ec1-a33e-e582-6e07-803a7e4e5abb.png)


Close()を引数なしで呼びます。

```python:
#文書を閉じる : Close()を引数なしで呼ぶ
In [121]: doc.Close()
```

読み取り専用のファイルに変更を加えてしまったので、ファイル名を変更するようにダイアログができてしまいます。

![dialog.PNG](https://qiita-image-store.s3.amazonaws.com/0/34777/8aaf62d7-2fc3-7990-ffa8-15933494cca7.png)

キャンセルしてダイアログを抜けると以下の例外が発生します。

```python
In [122]: doc.Close()
---------------------------------------------------------------------------
com_error                                 Traceback (most recent call last)
<ipython-input-122-f62a2a61dece> in <module>()
----> 1 doc.Close()

com_error: (-2147352567, '例外が発生しました。', (0, 'Microsoft Word', 'このファイルは読み取り専用なので上書き保存できません。\n変更内容を保持するには、文書を別の名前で保存するか、別の場所に保存する必要があります。\r (C:\\...\\CSharp Language Specification....)', 'wdmain11.chm', 24635, -2146823133), None)
```

変更を保存せずに文書を閉じるために、Close(SaveChanges=0)を呼び出してみます。

```python
#変更を保存せずに文書を閉じる : Close(SaveChanges=0)を呼び出す。
In [123]: doc.Close(SaveChanges=0)
```

今度は、何事もなく閉じることができます。

![close.PNG](https://qiita-image-store.s3.amazonaws.com/0/34777/21fd66c4-2386-4501-0b73-ccd622f52860.png)


念のためもう一回開きます。

```python
In [124]: doc=Application.Documents.Open(FileName=r"C:\tmp\CSharp Language Specification.docx", ConfirmConversions=False, ReadOnly=True)
```

先ほど加えた変更が反映されてないことが確認できました。

![open_again.PNG](https://qiita-image-store.s3.amazonaws.com/0/34777/9b73767b-fa7c-f58e-8152-80f7cc91c1b7.png)



#コレクションと名前付きオブジェクトについて

[以前のコレクションの説明](https://qiita.com/int_main_void/items/a36cf348d6eedb31246f#word%E3%81%AE%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)

> ##Wordのコレクションについて
Wordのコレクションの各要素には、Documents(1)、Documents(2)、・・・と丸括弧でアクセスします。以下に注意が必要です
> 
- 角括弧ではないこと
- nは**1から始まる**こと
- Add()、Close()などの呼び出しやGUI操作(e.g.windowのクローズ操作)でコレクションの位置が変わる

名前付きオブジェクト(Nameプロパティを持っているオブジェクト)は、数値インデックス的なアクセスに加えて、「名前」(Nameプロパティ, FullNameプロパティと同じ文字列)を使ってアクセスできます。

[前回](https://qiita.com/int_main_void/items/6c3569dc242f843ca946)、Tasksコレクションで暗黙的に使っています。(TaskオブジェクトにNameプロパティがある)


#名前付き引数に気を付ける

pywin32だと **名前付き引数を使っても途中の引数が省略できない** ようです。(msdnの仕様を読んで位置を把握しないといけない)

[Documents.Open() - msdn][docs_open_msdn]
> 式 . Open( FileName, ConfirmConversions, **ReadOnly** , ・・・

|名前|必須 / 省略可能|説明|
|:---|:---|:---|:---|
|FileName|必須|文書の名前/パス|
|ConfirmConversions|省略可能|True:Word 形式ではない場合、ファイルの変換ダイアログを表示|
|ReadOnly|省略可能|True:読み取り専用|

ReadOlyの場合、FileNameとReadOlyの間にConfirmConversionsがあるので、ConfirmConversionsに何らかの値(True, False, None)を渡す必要があります。msdnで省略可能となっている部分は **本当に省略するのではなくNoneを渡す** と既定のデフォルトで動いてくれます。

VBAだとこのあたりいい感じに処理されているので以下のようなサンプルコードがよくあります。

[Documents.Open メソッドのサンプル](https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/documents-open-method-word#%E4%BE%8B)

```vb
Sub OpenDoc()
 Documents.Open FileName:="C:\MyFiles\MyDoc.doc", ReadOnly:=True
End Sub
```

再実装する場合は、明示的に名前付きで設定された引数の間に隠されている引数がないか、調査する必要があります。上記の例では、ConfirmConversionsがその隠された引数です。

VBScriptは、「名前付き引数自体をサポートしてない」＋「使わない引数に何も設定しなくてよい」(下記の例参照)ので、こちらも再実装する際に気を付けるポイントになります。おそらく多くのケースNoneを設定すればなんとかなると思います。

例：
[Officeファイルを読み取り専用で開くスクリプト - motoraku’s gists][OfficeOpenReadOnly.vbs]

> 
```vb:OfficeOpenReadOnly.vbs
'■読み取り専用で開く
Call objApp.Documents.Open(strFileName,,True)
```



#関連
[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)


#参考

- [オブジェクト モデル - MSDN][object_model]
- [PyWin32 Documentation - Tim Golden's Stuff][tim_pywin32_docs]
- [コレクションからオブジェクトを返す-名前付きオブジェクト-msdn][named-objects_msdn]
- [win32comで、makepyを使って COM(AcitveX)の定数をロードさせる手順について - ふにゃるん][makepy_hunya]
- [Win32OLE 活用法 【第 7 回】 ほかの言語での COM-Python の場合-Rubyist Magazine][com_python_ruby]

[com_python_ruby]:https://magazine.rubyist.net/articles/0009/0009-Win32OLE.html#python-%E3%81%AE%E5%A0%B4%E5%90%88

[makepy_hunya]:http://d.hatena.ne.jp/Wacky/20091011/1255240572

[named-objects_msdn]:https://msdn.microsoft.com/en-us/vba/word-vba/articles/returning-an-object-from-a-collection-word#named-objects


[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

[app_tasks_mdsn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/tasks-object-word
[tasks_count_mdsn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/tasks-count-property-word
[tasks_exists_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/tasks-exists-method-word
[tasks_exitwindows_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/tasks-exitwindows-method-word
[app_task_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/task-object-word
[task_name_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/task-name-property-word
[task_visible_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/task-visible-property-word

[task_winstate_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/task-windowstate-property-word
[task_top_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/task-top-property-word
[task_left_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/task-left-property-word
[task_width_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/task-width-property-word
[task_height_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/task-height-property-word

[task_activate_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/task-activate-method-word
[task_close_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/task-close-method-word
[task_move_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/task-move-method-word
[task_resize_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/task-resize-method-word
[task_sendwindowmessage_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/task-sendwindowmessage-method-word

[WdWindowState]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/wdwindowstate-enumeration-word
[docs_open_msdn]:https://msdn.microsoft.com/ja-jp/vba/word-vba/articles/documents-open-method-word

[doc_close_msdn]:https://msdn.microsoft.com/ja-jp/VBA/Word-VBA/articles/document-close-method-word
[wdsaveoptions-enumeration-word]:https://msdn.microsoft.com/ja-jp/VBA/Word-VBA/articles/wdsaveoptions-enumeration-word

[OfficeOpenReadOnly.vbs]:https://gist.github.com/motoraku/c9aef3c43843331b554f