Python(pywin32)でWordを操作する[6] - 特定のタイトルが付いているウィンドウの操作


#概要

[「特定のタイトルが付いているウィンドウを閉じる - Qiita」](https://qiita.com/int_main_void/items/1cd864010e944b07185e)をpywin32で焼き直して、もう少し深堀してみます。閉じる・移動・リサイズ・最前面表示・サインアウトをやります。

原因はよくわかりませんが、qtconsoleだとうまく実行できないため、.pyに保存して実行してください。
※Application.Tasks以下のメソッド・プロパティにアクセスすると制御が戻ってこなくなる。

Tasks.ExitWindows()を呼ぶと、**本当に突然サインアウトしてしまう** ので、注意してください。

次は、Documents.Open()を使った既存文書の開き方(+名前付き引数)について説明します。
(予想以上にDocuments.Openの分量が多くなったのでSentencesなどを使った文書の読み書きはそのあとでやります。)

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

![word_obj.png](https://qiita-image-store.s3.amazonaws.com/0/34777/8babe76b-d1ea-b771-f0d1-b973a5b9dc56.png)

|オブジェクト名|プロパティ/メソッド|説明|リンク|
|:---|:---|:---|:---|
|Tasks|-    |現在実行中のすべてのTaskのコレクション|[msdn][app_tasks_mdsn]|
|     |Count|コレクション内のTaskの数|[msdn][tasks_count_mdsn]|
|     |Exists()|指定されたTaskの存在を確認|[msdn][tasks_exists_msdn]|
|     |ExitWindows()|サインアウトする|[msdn][tasks_exitwindows_msdn]|
|Task |-    |実行されている 1 つのタスク(アプリケーション)を表すオブジェクト|[msdn][app_task_msdn]|
|     |Name|名前|[msdn][task_name_msdn]|
|     |Visible|ウィンドウの表示/非表示|[msdn][task_visible_msdn]|
|     |WindowState|ウィンドウの状態(最大/最小/標準)|[msdn][task_winstate_msdn]|
|     |Top|ウィンドウの垂直方向位置|[msdn][task_top_msdn]|
|     |Left|ウィンドウの水平方向位置|[msdn][task_left_msdn]|
|     |Width|ウィンドウの幅|[msdn][task_width_msdn]|
|     |Height|ウィンドウの高さ|[msdn][task_height_msdn]|
|     |Activate()|ウィンドウを最前面にする|[msdn][task_activate_msdn]|
|     |Close()|ウィンドウを閉じる(アプリケーションを終了する)|[msdn][task_close_msdn]|
|     |Move()|ウィンドウを移動する|[msdn][task_move_msdn]|
|     |Resize()|ウィンドウをサイズを変更する|[msdn][task_resize_msdn]|
|     |SendWindowMessage()|Windowsメッセージを送る|[msdn][task_sendwindowmessage_msdn]|

SendWindowMessage()は、Close()メソッドと等価なWM_CLOSEメッセージだけ紹介します。


#コード
原因はよくわかりませんが、qtconsoleだとうまく実行できないようです。
※Application.Tasks以下のメソッド・プロパティにアクセスすると制御が戻ってこなくなる。

今回は.pyを作成して適宜実行しています。(ファイル名は、wintask.pyにしています。)

↓の「★なんらかの処理★」部分に記述しているイメージで読んでください。

新しいインスタンスを作成しないとうまく動かないケースがあったので、今回は[win32com.client.DispatchEx](http://timgolden.me.uk/python/win32_how_do_i/start-a-new-com-instance.html)にしています。


```python:wintask.py
# -*- coding: utf-8 -*-
import win32com.client

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

★なんらかの処理★

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

##準備
以下をテキストエディタにコピペし、"target.hta"として保存してダブルクリックで実行します。
簡易アプリが起動するので、これを操作してみます。

```html:target.hta
<html>
<head>
<title>ターゲット</title>
</head>
<body>
これを操作します。
</body>
</html>
```

![target.png](https://qiita-image-store.s3.amazonaws.com/0/34777/1f293390-08e2-141d-b69d-0a62559a597b.png)

##タイトルが「ターゲット」のウィンドウを探す

Tasks.Exist("ターゲット")で「"ターゲット"というタイトルを持つウィンドウがあるか？」を確認できます。

```python
if Application.Tasks.Exists("ターゲット"):
    print("ターゲットが存在")
```

```console
> python wintask.py
ターゲットが存在
```

また、Tasks("ターゲット")で、そのウィンドウを操作するためのTaskオブジェクトにアクセスできます。

```python
if Application.Tasks.Exists("ターゲット"):
    print("ターゲット:"+Application.Tasks("ターゲット").Name)
```
```console
> python wintask.py
ターゲット:ターゲット
```

部分一致なので似たようなタイトルや同一タイトルの別ウィンドウもヒットしてしまうので、厳密性には欠けます。

```python
if Application.Tasks.Exists("ターゲット"):
    print("ターゲット:"+Application.Tasks("ターゲット").Name)

if Application.Tasks.Exists("ターゲ"):
    print("ターゲ:"+Application.Tasks("ターゲ").Name)

if Application.Tasks.Exists("ゲット"):
    print("ゲット:"+Application.Tasks("ゲット").Name)
```

```console
> python wintask.py
ターゲット:ターゲット
ターゲ:ターゲット
ゲット:ターゲット
```

##ウィンドウを閉じる

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

Tasks("ターゲット")でTaskオブジェクトを特定し、Close()メソッドを呼びます。
簡易アプリが終了してしまうので、手で再起動してください。

```python
# ウィンドウを閉じる
Application.Tasks("ターゲット").Close()
```

SendWindowMessage()でWM_CLOSE(0x0010)を送信しても同じ効果です。

```python
# ウィンドウを最前面にする
Application.Tasks("ターゲット").Activate()

import time
time.sleep(3)

# WM_CLOSE(0x0010)を送信する
Application.Tasks("ターゲット").SendWindowMessage(0x0010, 0, 0)
```

##ウィンドウを最前面にする
Activate()メソッドを呼ぶと最前面化します。

```python
# ウィンドウを最前面にする
Application.Tasks("ターゲット").Activate()
```

##ウィンドウの幅と高さを取得・設定する。

WidthとHeightを読むと現在の値を取得できます。

```python
if Application.Tasks.Exists("ターゲット"):
    w=Application.Tasks("ターゲット").Width
    h=Application.Tasks("ターゲット").Height
    print(f"width={w}, height={h}")
```

読み書き可能なプロパティなので設定もできます。(ウィンドウのサイズが変更されます)
Resize(Width,Height)メソッドで同等のことができます。

```python
# ウィンドウを最前面にする
Application.Tasks("ターゲット").Activate()

# 幅200pt x 高さ100pt のウィンドウにする
Application.Tasks("ターゲット").Width=200
Application.Tasks("ターゲット").Height=100

# 3秒待つ
import time
time.sleep(3)

# 幅100pt x 高さ200pt のウィンドウにする
Application.Tasks("ターゲット").Resize(100,200)
```

##ウィンドウの位置を取得・設定する。

TopとLeftを読むと現在の値を取得できます。読み書き可能なプロパティなので設定もできます。

```python
if Application.Tasks.Exists("ターゲット"):
    l=Application.Tasks("ターゲット").Left
    t=Application.Tasks("ターゲット").Top
    print(f"left={l}, top={t}")
```

TopとLeftに値を設定しても同じですが、Move(Left,Top)メソッドを使ってウィンドウを動かしてみます。
5秒間かけて(0,0)から(500,500)まで移動します。(左上から斜めに移動します。)

```python
import time
Application.Tasks("ターゲット").Activate()
if Application.Tasks.Exists("ターゲット"):
    for i in range(500):
        # Move(Left,Top)メソッドを使ってウィンドウを動かす。
        Application.Tasks("ターゲット").Move(i,i)
        time.sleep(0.01)
```

##ウィンドウの状態(最大/最小/標準)を取得・設定する。

WindowStateを読み書きすると、ウィンドウの状態(最大/最小/標準)を取得・設定できます。
設定できる値は下表の通り。

[WdWindowState列挙(msdn)][WdWindowState]

|名前|値|説明|
|:---|:---|:---|
|wdWindowStateMaximize|1|最大化|
|wdWindowStateMinimize|2|最小化|
|wdWindowStateNormal  |0|標準|


最大化→1秒待ち→最小化→1秒待ち→標準と遷移させてみます。

```python
import time
if Application.Tasks.Exists("ターゲット"):
    Application.Tasks("ターゲット").Activate()

    # ウィンドウを最大化する
    Application.Tasks("ターゲット").WindowState=1

    time.sleep(1)

    # ウィンドウを最小化する
    Application.Tasks("ターゲット").WindowState=2

    time.sleep(1)

    # ウィンドウを標準にする
    Application.Tasks("ターゲット").WindowState=0

    Application.Tasks("ターゲット").Activate()
```


##サインアウトする

Tasks.ExitWindows()を呼ぶとサインアウトします。なぜ、Wordにこのメソッドがあるのかわかりませんが、　**本当に突然サインアウトしてしまう** ので注意してください。

```python
# サインアウトする
Application.Tasks.ExitWindows()
```

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

[アプリのスクリーンショットをpngファイルにする - Qiita](https://qiita.com/int_main_void/items/66e74c1f63c6cc3c489f)

#参考

- [オブジェクト モデル - MSDN][object_model]
- [PyWin32 Documentation - Tim Golden's Stuff][tim_pywin32_docs]


[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