2021年8月31日火曜日

Pythonでフォルダに入れたExcelファイルを拡大設定し2部ずつ印刷する方法

職場で毎日している作業をスクリプト化して少しでも楽しようと画策。

今回は毎晩行っている「会社のシステムからA4サイズで出力されるXLS形式の台帳のファイルをA3に拡大して2部ずつ印刷する」作業をPythonのスクリプト化してみた。

「A4で見りゃいいじゃん」という話なのだが現場ではA3じゃないと困ると言うし、システムの方にはA3で出力する機能が無い。そのためこれまでは、

  1. ファイルを出力する。(全部で最大12枚あり)
  2. ファイルを開き印刷ダイアログでA3拡大印刷に設定
  3. 設定したファイルを2部印刷
これを出力したファイルごとに行っていたが面倒くさい。眠くなる。
1の作業は自動化が面倒くさそうなので、とりあえず2と3を自動化しようと考えた。

なお、今回はPywin32+win32api+win32printingを使っているのでWindows専用です。
また、Python自体やpywin32、win32api、win32printingなどのインストール方法などは、他に説明している方が多いので割愛。

スクリプトは以下の通り

import win32com.client
import win32api
import win32print
import glob
import os
import argparse
import time

copies = 2
printername = win32print.GetDefaultPrinter()

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('dir_path', help='designate dir path')
    args = parser.parse_args()

    pathlist = sorted(glob.glob(args.dir_path + '/*.xls'))

    xlApp = win32com.client.Dispatch("Excel.Application")
    xlApp.Visible = True

    for path in pathlist:
        wb = xlApp.Workbooks.Open(path)
        ws = wb.Worksheets("00")
        ws.Activate()
        ws.PageSetup.Zoom = 130
        ws.PageSetup.PaperSize = 8
    ws.PrintOut(1, 1, copies, False, printername)
        wb.Save()
        wb.Saved = True
        wb.Close()

    xlApp.Quit()

#    for path in pathlist:
#        for copy in range(copies):
#            win32api.ShellExecute(0, "print", path, printername, ".", 0)
#            time.sleep(5) 

if __name__ == '__main__':
    main()

使い方は、システムから出したXLSファイルを適当なフォルダに収め、そのフォルダを引数にしてこのスクリプトを実行すれば、フォルダ内ファイルを開いて設定して保存し印刷してくれる。

以下、作業でつまづいたポイント。

  • 当初はopenpyxlを使おうかと考えたけど、openpyxlはXLSX形式しか使えない。使い勝手もあってpywin32を選択。
  • pywin32を使いexcelでファイルを開いて拡大設定して保存までは比較的スムーズに行ったがプリントアウトが出来ない。ググってもPDFに出力する話はよく引っかかるが印刷する話はなかなか出てこない。「PrintOut()」メソッドで印刷できるような記事もあったが上手く行かなかったため、当初、印刷部分はPowerShellのスクリプトを使って行う方法を取った。→9/4追伸 PrintOut()メソッドで印刷が出来るようになった!!
  • PowerShellで「フォルダ内のファイルを名前順に読み込んで印刷」するスクリプトを組み、上手くいったので、そのスクリプトの最初に拡大設定するPythonスクリプトを実行する一行を入れて実行したら成功。これで良いかと思っていたら翌日使えなくなる。
  • Pythonスクリプトの方のwin32comでExcelを開く辺りが動かずPowerShellで見るとAttributionErrorになっている。散々ググってどうやら「C:\Users\<username>\AppData\Local\Temp\gen_py」が邪魔してるらしく、それを削除したらまたExcelが開くようになった。(詳しくはわからないが、どうやらPowerShellで組んだ方のスクリプトで使ったオブジェクトをきちんとクローズして開放していなかったためExcelのプロセスが残りまくり、それがPythonでExcelを開くのを邪魔していたらしい。オブジェクト名を同じにしていたのも原因っぽいが確証はない。)
  • PowerShellで印刷に使っていた手法がShellExecute(右クリックメニューで印刷を選ぶのをコマンドラインでやるみたいなもの)で行っていたので、それをPythonで出来ないか調べて、win32apiで出来ることが判明。
  • ShellExecuteでprintするためにはプリンター名を調べる必要があるのでwin32printでデフォルトプリンタ名を調べる方法を見つけそれを組み込んだら、Pythonスクリプトだけで印刷できるようになったが、ファイル名順にならず一部順番が乱れる。どうも、ファイルの開く閉じるや、PCからプリンタに印刷を送信した際のプリンタ側の反応のタイムラグのせいで、キューがずれるらしい。ファイルの印刷指示を行うたびにtime.sleep()で5秒程度の待ち時間を入れたら上手く印刷できるようになった。

とりあえずこれで、当初の目的を行うスクリプトが完成したので、ボチボチ使っていきながら、できれば上で書いた「システムからファイルを出力し」の部分も自動化して組み合わせられないか模索していこうと思う。

【9/4追伸】

PrintOutメソッドでの印刷が出来た。

()内の記述法が問題で、VBA等の場合は要素の省略が出来たが、Python&pywin32の場合は上のソースに書いた5つの要素を書いたら動いた。順番に(印刷初めのページ,印刷終わりのページ,印刷部数,印刷前にプレビューするか,プリンター名)。
こちらだと、ShellExecuteで問題になったタイミングがおかしくて印刷順番が狂ったりするのが無くスムーズに印刷される。(上手く行かなくなった時のために、ShellExecuteの部分もコメントアウトして残しておく。)

【参考】

0 件のコメント:

コメントを投稿