前回、pythonnet
を使って .NET + b-PACを扱いました。
ただ、いろいろと辛かったため、今回はpywin32
を使ったCOM経由でb-PACを操作してみました。
結論から言うと、イベント処理も含めて、以下のようなC#でできたことはPython + pywin32でも実装できました。
ブラザーのラベルプリンタまわりを操作する b-PAC SDK を使ってみた - メモ的な思考的な
今回の目次です。
- 環境
- b-PAC SDKのCOMオブジェクトを作る
- プリンタ情報の取得
- メディア(ラベル)情報の取得
- 印刷できるかどうか
- 印刷
- b-PACの定数や列挙型について
- PrintedEventイベントについて
- ソースコード
- その他参考
環境
virtualenv環境は前回と同じものを使っていますが、再掲しておきます。
D:\sandbox>mkdir bpac_python D:\sandbox>cd bpac_python D:\sandbox\bpac_python>virtualenv -p c:\python35-32\python.exe env D:\sandbox\bpac_python>env\Scripts\activate # pipだとエラーになるので、easy_installを使う (env) D:\sandbox\bpac_python>easy_install https://sourceforge.net/projects/pywin32/files/pywin32/Build%20220/pywin32-220.win32-py3.5.exe/download
b-PAC SDKのCOMオブジェクトを作る
同じくCOM経由となるVB Scriptのサンプルを見ると、ProgIDはbpac.Document
でした。
pywin32ではCOMオブジェクトを作る方法として、
win32com.client.Dispatch("ProgID")
win32com.client.DispatchEx("ProgID")
win32com.client.gencache.EnsureDispatch ("ProgID")
の3つの方法がありました。
Dispatch()とDispatchEx()の違いは以下にあり、DispatchEx()
を使うのが良さそうでした。
- python - Using pywin32, what is the difference between Dispatch and DispatchEx? - Stack Overflow
- Tim Golden's Python Stuff: Start a new instance of a COM application
また、DispatchEx()とEnsureDispatch()の違いは以下にありました。
- Tim Golden's Python Stuff: Generate a static COM proxy
- Tim Golden's Python Stuff: Win32 How Do I...?
今回、DispatchEx()とEnsureDispatch()のどちらで書こうかと思いましたが、
doc = win32com.client.gencache.EnsureDispatch ("bpac.Document") #=> TypeError: This COM object can not automate the makepy process - please run makepy manually for this object
EnsureDispatch()ではエラーとなりました。
よって、今回はDispatchEx()
を使って
doc = win32com.client.DispatchEx("bpac.Document")
COMオブジェクトを生成しました。
プリンタ情報の取得
C#では、doc.Printer.GetInstalledPrinters()
を使ったため、今回もこれを使います。
ただ、GetInstalledPrintersの後ろに()
を付けると、
printers = doc.Printer.GetInstalledPrinters();
#=> TypeError: 'tuple' object is not callable
のようなエラーとなりました。
pywin32版では、メソッドに引数がない場合
printers = doc.Printer.GetInstalledPrinters
#=> エラーなし
()
無しにすれば良いようです。
あとはC#版と同じようにして、
printers = doc.Printer.GetInstalledPrinters for p in printers: support = "Yes" if self.doc.Printer.IsPrinterSupported(p) else "No" status = "Online" if self.doc.Printer.IsPrinterOnline(p) else "Offline" print("{name} - Support: {support}, Status: {status}" .format(name=p, support=support, status=status)) #=> Brother QL-720NW - Support: Yes, Status: Online
プリンタ情報を取得できました。
メディア(ラベル)情報の取得
使うものはC#版と同じです。
printers = doc.Printer.GetInstalledPrinters for p in printers: doc.SetPrinter(p, False) id = doc.Printer.GetMediaId name = doc.Printer.GetMediaName msg = "Label - {id} : {name}".format(id=id, name=name) if name else "No Media" print(msg) #=> Label - 259 : 62mm
印刷できるかどうか
印刷できるかどうかは、
- サポートされているプリンタがあること
- プリンタがオンラインであること
- メディア(ラベル)がセットされていること
をすべて満たせば良いと考えました。
printers = doc.Printer.GetInstalledPrinters for p in printers: doc.SetPrinter(p, False) if doc.Printer.IsPrinterSupported(p) \ and doc.Printer.IsPrinterOnline(p) \ and doc.Printer.GetMediaName: return p return ""
印刷
使うものはC#版と同じです。
# `enabled_printer`は、印刷可能なプリンタを指す doc.SetPrinter(enabled_printer, False) # 印刷で使うラベルテンプレート # Pythonスクリプトと同じディレクトリに置く前提 dir = os.path.abspath(os.path.dirname(__file__)) lbx_path = os.path.join(dir, "test.lbx") hasOpened = doc.Open(lbx_path) if not hasOpened: print("指定されたラベルを開けませんでした") return # デフォルトは各ラベルでカットなので、 # 最後のラベルでカットするように変更 doc.StartPrint("", 0x04000000) for i in range(0, 3): # テンプレートのテキストオブジェクトへの値設定 doc.GetObject("Content").Text = "No.{}".format(i) # テンプレートのバーコードオブジェクトへの値設定 doc.SetBarcodeData(self.doc.GetBarcodeIndex("Barcode"), i); doc.PrintOut(1, 0x04000000); doc.EndPrint doc.Close
b-PACの定数や列挙型について
上記の印刷コード例では、C#ではPrintOptionConstants.bpoCutAtEnd
を使っていたところが、pywin32ではdoc.StartPrint("", 0x04000000)
のような16進数表記を使っています。
pywin32でCOMの定数や列挙型を扱う時はwin32com.client.constants
を使います。
- win32comで、makepyを使って COM(AcitveX)の定数をロードさせる手順について - ふにゃるん
- Using COM Constants - Quick Start to Client side COM and Python
ただ、b-PACで試してみたところ、
print(win32com.client.constants.PrintOptionConstants.bpoCutAtEnd) #=> AttributeError: PrintOptionConstants
エラーとなりました。
b-PACのヘルプで列挙型を見ると、列挙型の他に16進数の表記もありました。そのため、16進数表記で使ったところ正常に動作しました。
PrintedEventイベントについて
b-PAC SDKの更新情報を見たところ、
2014/10/15 スクリプト言語(VBScript、JScript等)でPrintEventメソッドに対応しました。
とあり、また、b-PAC 3.1 APIのヘルプには
delegate void bpac::PrintedEvent (LONG status, VARIANT value) 印刷時、イベントハンドラー イベントハンドリングはVisualBasic6.0, VisualC++6.0, VB Script, JScriptには対応しておりません。 これらの言語ではコールバック設定にてイベントを処理してください。
と書かれていました。
他に、pywin32でイベントの存在を確認したところ、
print(win32com.client.getevents("bpac.Document")) # => <class 'win32com.gen_py.90359D74-B7D9-467F-B938-3883F4CAB582x0x1x3.IPrintEvents'>
となるなど、b-PAC 3.1 + pywin32版でもイベントを扱えそうでした。
pywin32でのイベントハンドリングについて調べたところ、以下の記事を見つけました。とても参考になりました。
2007-09-13 [skype][python] イベントハンドリング - やっとむでぽん
今回は、そこで紹介されていた以下の2パターンを試してみます。
- DispatchWithEvents
- getevents
DispatchWithEventでは実装できず
以下のようなソースを書いてみたところ、
class PrintEvents(object): def OnPrinted(self, status, value): print("status:{s} / value{v}".format(s=status, v=value)) ... doc = win32com.client.DispatchWithEvents("bpac.Document", PrintEvents) # => TypeError: This COM object can not automate the makepy process - please run makepy manually for this object
というエラーになりました。
エラーメッセージにあるmakepy.py
ファイルは、D:\Sandbox\bpac_python\env\Lib\site-packages\pywin32-220-py3.5-win32.egg\win32com\client\
ディレクトリの中にありました。
そこでmakepy.pyファイルを
(env) D:\Sandbox\bpac_python\env\Lib\site-packages\pywin32-220-py3.5-win32.egg\win32com\client>python makepy.py
のように実行すると、Select Libraryウィンドウが表示され、Brother b-PAC 3.1 Type Library [1.3]
などが含まれていました。
次に、以下を参考に-i
オプションを使い、詳細な情報を取得します。
Generated Python COM Support
(env) D:\Sandbox\bpac_python\env\Lib\site-packages\pywin32-220-py3.5-win32.egg\win32com\client>python makepy.py -i "Brother b-PAC 3.1 Type Library" Brother b-PAC 3.1 Type Library {90359D74-B7D9-467F-B938-3883F4CAB582}, lcid=0, major=1, minor=3 >>> # Use these commands in Python code to auto generate .py support >>> from win32com.client import gencache >>> gencache.EnsureModule('{90359D74-B7D9-467F-B938-3883F4CAB582}', 0, 1, 3)
と表示されました。
画面の表示通りに変更を加えてみましたが、
from win32com.client import gencache gencache.EnsureModule('{90359D74-B7D9-467F-B938-3883F4CAB582}', 0, 1, 1) doc = win32com.client.DispatchWithEvents("bpac.Document", PrintEvents) # => TypeError: This COM object can not automate the makepy process - please run makepy manually for this object
エラー内容は変わりませんでした。
そのため、DispatchWithEvent()
による実装は諦めました。
geteventsでは実装できた
geteventsの場合は、
class PrintEvents(win32com.client.getevents('bpac.Document')): def OnPrinted(self, status, value): # def Printed(self, status, value): => 動作しない print("status:{s} / value{v}".format(s=status, v=value))
のようなイベントハンドリングのためのクラスを用意します。
なお、上記参考ページにもありましたが、イベントハンドラ用のメソッドはOn
を先頭に付けて作成します。b-PACの場合も、On無しだとイベントが動作しませんでした。
最終的なメソッド名は、b-PACのヘルプにある「印刷完了イベントハンドリング&コールバック」のVBScript/JScriptサンプルの関数名がPrinted
だったことから、OnとPrintedを組み合わせたOnPrinted
としました。
あとは、以下を参考にイベントを待つようにtime.sleep()
を使って
Consuming COM events in Python - Stack Overflow
... # イベントハンドラの追加 handler = PrintEvents(doc) self.doc.StartPrint("", 0x04000000) ... self.doc.Close # Printedイベントが動作するのを確認するため、少々待つ import time time.sleep(5)
としたところ、印刷完了後コマンドプロンプト上にstatus:0 / value128
が表示されました。
ソースコード
GitHubに上げました。
thinkAmi-sandbox/bpac_python-sample
pywin32_ver.py
が、pywin32を使った場合のソースコードとなります。
その他参考
pywin32のドキュメントについて
探してみましたが、公式っぽいのは見当たりませんでした。
いくつかドキュメントへのリンクが示されていたので、それらを貼っておきます。
- python win32 extensions documentation - Stack Overflow
- PyWin32 - おなかすいたWiki!
- win32com extension documentation/API - Python
- win32com.client documentation? - Google グループ
HowToっぽいのもありました。
Tim Golden's Python Stuff: Win32 How Do I...?
b-PACの説明にある、CCIという単語の意味について
手元のb-PAC環境を3.0から3.1に上げるにあたり、b-PACのFAQを読んでいたところ、CCI
という単語が出てきました。
[Q] クライアントのb-PACをバージョンアップするにはどうしたらいいですか?
[A] 弊社が提供するCCIでクライアントのPCに上書きインストールするか、b-PAC SDK同梱のマージモジュールを組み込んだインストーラーを作成してクライアントのPCにインストールしてください。
特に説明もなくCCIと出てきたので、単語の意味について調べてみたところ、英語版に情報がありました。
[Q] How can I upgrade b-PAC on a client PC?
[A] Install the provided Client Ccmponent Installer (CCI) on the client PC. Or create an installer using the merge module included in the b-PAC SDK and install it on the client PC.
Operation | Labelling SDK (b-PAC) FAQ | FAQs | Developer Center | Brother
再配布用のクライアントインストーラーパッケージのようでした。