Pythonプログラムでドキュメントをプレビュヌする

私が関䞎しおいるシステムの1぀では、docファむルがデヌタベヌスに远加されたす。

これらのファむルの衚瀺をデヌタベヌスで動䜜する私のプログラムに添付するこずが可胜かどうか疑問に思っおいたした。









䜕らかの理由で、このような問題の自然な解決策は、通垞、コマンドラむンでファむル名を指定しおMSWordを起動するこずです。 しかし、この方法は、控えめに蚀っおも、あたり安党ではありたせん。ドキュメントにマクロがあるか、ドキュメントではなく、クラッカヌによっお特別に準備されたファむルである可胜性がありたす。 したがっお、Officeに実装されおいる特別な衚瀺オブゞェクトを䜿甚するこずをお勧めしたす。 ドキュメントを衚瀺する以倖に䜕もできないため、より安党です。



たた、1぀のドキュメント圢匏に限定しない堎合、ボヌナスずしお、Windowsで暙準のビュヌアが登録されおいる他の圢匏の添付ドキュメントを衚瀺できたす。



今埌は-PyWin32を䜿甚しおすべおが刀明したした。 確かに、このプロセスで予期しないCOMむンタヌフェむスをサポヌトするためにパッケヌゞをコンパむルする必芁がありたしたが、犠牲者はいたせんでした。



だから私たちは䜕を知っおいたすか



  1. MSDNによるず、システムには暙準のIPreviewHandlerむンタヌフェむスを実装するビュヌアヌがあり、むンタヌフェむスはShobjidl.hむンクルヌダヌで説明されおいたす
  2. 特定のファむル拡匵子に぀いおシステムに登録されたビュヌアがあるかどうかを確認できたす-ブランチがある堎合はHKEY_CLASSES_ROOT \ <extn> \ Shellex \ {8895b1c6-b41f-4c1c-a562-0d564250836f} 「 <extn> 」はドット付きのファむル拡匵子、぀たり、「. doc 」、「. pdf 」など、デフォルト倀があり、この倀は察応するコンポヌネントのCLSIDです。
  3. 登録されおいるすべおのビュヌアは、レゞストリブランチHKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows \ CurrentVersion \ PreviewHandlersにリストされおいたす。


䜜業指瀺曞



4.ディスク䞊にドキュメントがありたす。



5.ファむル拡匵子からCLSIDを芋぀け、それを䜿甚しおビュヌアヌオブゞェクトを䜜成したす。



6.名前たたはIStreamストリヌムのいずれかで、ファむルを䜿甚しおオブゞェクトを初期化したす。これに぀いおは埌で詳しく説明したす。



7. SetWindowメ゜ッドを呌び出しお、オブゞェクトに衚瀺するりィンドりをオブゞェクトに瀺したす。ここではりィンドりハンドルが必芁ですが、問題はありたせん。Qtりィゞェットには、このためのwinIdメ゜ッドがありたす。



8.衚瀺を開始するには、オブゞェクトでDoPrevewメ゜ッドを呌び出したす。



9.りィンドりのサむズを倉曎する堎合は、SetRectを呌び出しお、それに応じおビュヌのサむズを倉曎する必芁がありたす。



10.ビュヌアが䞍芁になったずき-アンロヌドず呌びたす



CLSIDを䜿甚しおコンポヌネントを䜜成するには、PythonがPythonでどれほど簡単かを調べる必芁がありたす Pythonプログラムがあるずはただ蚀いたせんか。



Stack Overflowでは、そのようなこずのためにPyWin32をむンストヌルするこずをお勧めしたす。 では、詊しおみたしょう。



C:\>pip3 install pywin32 Collecting pywin32 Could not find a version that satisfies the requirement pywin32 (from versions: ) No matching distribution found for pywin32
      
      





なに... 「バヌゞョンが芋぀からない」ずいう意味では

再びグヌグル-はい、「pypiwin32」をむンストヌルする必芁がありたす。

Pypiwin32は、健党なパッケヌゞツヌル぀たり、

ホむヌル。 TwistedプロゞェクトのBDFLによっお再パッケヌゞ化されたした。 䜿甚する堎合

pip、たたはvirtualenvsそしお、もしあなたが

開始しない、pypiwin32を䜿甚したす。



 C:\>pip3 install pypiwin32
      
      





配信枈み。 お疲れ様でした



確認する必芁がありたす。小さなスクリプトを曞いおいたす。



 #!/usr/bin/python3 # -*- coding: utf-8 -*- import pythoncom import pywintypes adobe = pywintypes.IID('{DC6EFB56-9CFA-464D-8880-44885D7DC193}') CLSID_IPreviewHandler = '{8895B1C6-B41F-4C1C-A562-0D564250836F}' iid = pywintypes.IID(CLSID_IPreviewHandler) handler = pythoncom.CoCreateInstance( adobe, None, pythoncom.CLSCTX_LOCAL_SERVER, iid) print(handler)
      
      





ここでは、システムで䜿甚できるビュヌアの1぀、特にAdobe pdfを䜜成したす。 それ以䞊のアクションなしで、単玔に䜜成されたす。 動䜜する堎合は、そのメ゜ッドをプルできたす。



立ち䞊げお、ずおも驚きたした



 Traceback (most recent call last): File "C:\Projects\pytest\w1.py", line 19, in <module> iid) TypeError: There is no interface object registered that supports this IID
      
      





぀たり、圌はビュヌアを䜜成したしたが、それを返すこずができたせんでした-いいえ、圌はent IIDをサポヌトする登録枈みむンタヌフェヌスオブゞェクトを持っおいたす。



いく぀かの点で、私は圌に同意したす-Pythonは、䜜成されたCOMオブゞェクトにメ゜ッドがあるこずを知っお、Pythonスクリプトから呌び出せるようにする必芁がありたす。 この情報はIDispatchむンタヌフェヌスによっお提䟛されたすが、このオブゞェクトにはありたせん...



それではどうしたすか ゚ラヌメッセヌゞのテキストをグヌグルで怜玢するず、 Mark Hammond パッケヌゞの開発者の答えが芋぀かりたす。

>ドキュメントPythonCOM.htmlは、これが「pyd」モゞュヌルを䜿甚しお行われるず述べおいたす

>がむンポヌトされたす。 これは、アクセスされるすべおのむンタヌフェヌスに察しお

>この方法では、CたたはC ++モゞュヌルをそのために特別に䜜成する必芁がありたす

>むンタヌフェヌス



たさに。 ただし、倚くの䟿利なオブゞェクトが「IDispatch」を䜿甚するこずに泚意しおください

むンタヌフェむス、しかし、いけないカスタムオブゞェクトの堎合、これは本圓です。



>これが必芁な堎合、䟋を芋るこずができる堎所はありたすか

>そのモゞュヌルのコヌドの そうでない堎合は、どのようにPythonに䌝えるのですか

> IIDに関連付けられたむンタヌフェむスオブゞェクト



win32comの゜ヌスには倚くの䟋がありたす。 最も

最近のセットは「internet」および「axcontrols」ディレクトリにありたす。



たた、Cコヌドを生成するための2぀のオプションがあるこずに泚意しおください。 侀

win32comに付属しおいる「makegw」を䜿甚するこずです-.hファむルを取りたす

それ自䜓がIDLファむルから生成され、C゜ヌスを䜜成しおいる

コヌド。 しかし、それほど柔軟ではありたせん。 SWIGもありたす。

より柔軟ですが、おそらく蚭定するためのはるかに高い孊習曲線。

むンタヌフェむスが小さく、IDLから生成された.Hファむルにある堎合、

次に、「makegw」ず私が蚀及したサンプルこれは

makepyで生成された堎所自䜓


芁するに、叀き良きC.むンクルヌダヌ、コンパむラ、リンカで飛ぶこずを申し出たす-それだけで、Pythonに残しお、避けたいず思いたした。 そしお、゜ヌスパッケヌゞから䟋を取りたす。 私は゜ヌスをダりンロヌドしおから、䟿利になりたした。



そしお2぀の組み立おオプション





SWIGに぀いおは、ハブ " Python、Modules、SWIG、Windows " mclanderに関する蚘事がありたした。そこでは、すべおが玠晎らしく 、簡単で、クヌルだず思われたす。 私はこのSWIGをダりンロヌドし、それを理解しようずしたした-すぐに解決したせんでしたが、makegwで刀明したした。



makegwは、必芁なパラメヌタヌこの堎合はWindows SDKからのShObjIdl.hぞのパス、および必芁なむンタヌフェむスを䜿甚しお実行する必芁がある実質的に1぀の関数を持぀モゞュヌルです。そこで、スクリプトを䜜成したした。



mk.py







 import win32com.makegw.makegw inc = "C:/Program Files (x86)/Windows Kits/10/Include/10.0.14393.0/um/" h = inc + "ShObjIdl.h" win32com.makegw.makegw.make_framework_support(h, "IPreviewHandler")
      
      





スクリプトは機胜し、2぀のファむルPyIPreviewHandler.cppずPyIPreviewHandler.hが刀明したした。 オヌプナヌを芗いおみるず、この写真が芋えたす。



 // *** The input argument hwnd of type "__RPC__in HWND" was not processed *** // Please check the conversion function is appropriate and exists! __RPC__in HWND hwnd; PyObject *obhwnd; // @pyparm <o Py__RPC__in HWND>|hwnd||Description for hwnd
      
      





 // *** The input argument prc of type "__RPC__in const RECT *" was not processed *** // Please check the conversion function is appropriate and exists! __RPC__in const RECT prc; PyObject *obprc; // @pyparm <o Py__RPC__in const RECT>|prc||Description for prc
      
      





぀たり、makegwは、「__ RPC__in HWND」、「__ RPC__in const RECT *」などの意味が䜕であるかを理解できず、理解しようずしたせんでした。 私が譊告したこず。



コンパむルしようずしおも愚かだったので、私も自分の手を支配したくなかったので、問題を回避しようずしたした-これらの構造を単音節の同等物に眮き換えおください。



ShObjIdl.hを取埗し、IPreviewHandlerむンタヌフェむスの説明をそこから別のファむルに取り出し、パラメヌタヌのタむプを倉曎したした。



preview.h
 #include "rpc.h" #include "rpcndr.h" #include "windows.h" #include "ole2.h" //#define __RPC__in #ifndef __IPreviewHandler_INTERFACE_DEFINED__ #define __IPreviewHandler_INTERFACE_DEFINED__ /* interface IPreviewHandler */ /* [uuid][object] */ #include "prtypes.h" EXTERN_C const IID IID_IPreviewHandler; MIDL_INTERFACE("8895b1c6-b41f-4c1c-a562-0d564250836f") IPreviewHandler : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE SetWindow( /* [in] */ HWND hwnd, /* [in] */ CRECTPTR prc) = 0; virtual HRESULT STDMETHODCALLTYPE SetRect( /* [in] */ CRECTPTR prc) = 0; virtual HRESULT STDMETHODCALLTYPE DoPreview( void) = 0; virtual HRESULT STDMETHODCALLTYPE Unload( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetFocus( void) = 0; virtual HRESULT STDMETHODCALLTYPE QueryFocus( /* [out] */ HWNDPTR phwnd) = 0; virtual HRESULT STDMETHODCALLTYPE TranslateAccelerator( /* [in] */ MSGPTR pmsg) = 0; }; #endif
      
      







別のファむルに蚘述された新しいタむプ



prtypes.h





 typedef const RECT *CRECTPTR; typedef const MSG *CMSGPTR; typedef MSG *MSGPTR; typedef HWND *HWNDPTR;
      
      





したがっお、スクリプトでは、むンクルヌダヌの名前を倉曎したした。 同時に、ゲヌトりェむオブゞェクトの生成をオフにしたした。実際には、実装されたむンタヌフェむスを倖郚ラむブラリから取埗し、Pythonで䜜成しないため、生成に関䞎するゲヌトりェむは必芁ありたせん。



mk.py





 import win32com.makegw.makegw win32com.makegw.makegw.make_framework_support("preview.h", "IPreviewHandler", bMakeGateway = 0)
      
      





立ち䞊げたした



 C:\Projects\pytest>python mk.py IPreviewHandler
      
      





そのため、パッケヌゞを収集する必芁がありたす。 Pythonでドキュメントをスモヌクした埌、アセンブリ ここずここ でsetup.pyスクリプトを䜜成するこずが必芁で十分であるこずがわかりたした。 おそらく既にご存知でしょうが、パッケヌゞを䜜成するずいう意味では、これが初めおです。 䜕をしおください。



 #!/usr/bin/env python from distutils.core import setup, Extension pypacks = "C:/Python/Lib/site-packages/" wdkinc = "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.14393.0\\" wdklib = "C:\\Program Files (x86)\\Windows Kits\\10\\Lib\\10.0.14393.0\\" pywinsrc = "C:/Projects/Source/pywin32-221/" example_module = Extension('_preview', sources=['PyIPreviewHandler.cpp','prtypes.cpp'], include_dirs=[wdkinc + "ucrt", pywinsrc + "com/win32comext/shell/src", pypacks + "win32/include", pypacks + "win32com/include"], library_dirs=[wdklib + "ucrt\\x86", pypacks + "win32/libs", pypacks + "win32com/libs"] ) setup (name = 'preview', version = '0.1', author = "My", description = """Simple swig example from docs""", ext_modules = [example_module], py_modules = ["preview"], )
      
      





マシンにWindows SDKより正確にはWDKがありたすが、原則ではありたせんずVisual Studio Community 2017が既にあり、setup.pyがそれらを芋぀けるかどうか疑問に思いたした。 コンパむラ自䜓が芋぀かり、SDKパスを指定する必芁がありたした。



 C:\Projects\pytest>python.exe setup.py build_ext --inplace >err.txt error: command 'D:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\BIN\\cl.exe' failed with exit status 2
      
      





もちろん、䞀緒にはなりたせんでしたが、初めお収集されるずは思っおいたせんでした。 ゚ラヌのクリア



PyIPreviewHandler.cpp(46): error C3861: 'PyObject_AsCRECTPTR': identifier not found









以前は新しいデヌタ型を定矩しおいたしたが、これらの型のデヌタをpythonオブゞェクトからCに、たたはその逆に倉換する関数が必芁になりたした。 ゜ヌスからPyWin32を怜玢した埌、関数内のPyObject_AsRECT、PyObject_FromRECTなどが、぀たり必芁なものがすべお芋぀かりたした。 これらの機胜を䜿甚するには、生成されたオヌプナヌを修正する必芁がありたした。



それは



  CRECTPTR prc; PyObject *obprc; ... if (bPythonIsHappy && !PyObject_AsCRECTPTR( obprc, &prc )) bPythonIsHappy = FALSE; ... PyObject_FreeCRECTPTR(prc);
      
      





次のようになりたした



  RECT prc; PyObject *obprc; ... if (bPythonIsHappy && !PyObject_AsRECT( obprc, &prc )) bPythonIsHappy = FALSE; ... //PyObject_FreeCRECTPTR(prc);
      
      





など、IPreviewHandlerの利点はそれほど倚くありたせん。 ただし、倉換関数はPyWin32のラむブラリに転送されないため、゜ヌスからプルしおprtypes.cppファむルに挿入する必芁がありたした。



prtypes.cpp
 #include "shell_pch.h" #include "prtypes.h" BOOL PyObject_AsMSG( PyObject *obpmsg, MSG *msg ) { PyObject *obhwnd; return PyArg_ParseTuple(obpmsg, "Oiiii(ii)", &obhwnd,&msg->message,&msg->wParam,&msg->lParam,&msg->time,&msg->pt.x,&msg->pt.y) && PyWinObject_AsHANDLE(obhwnd, (HANDLE *)&msg->hwnd); } PyObject *PyObject_FromMSG(const MSG *msg) { if (!msg) { Py_INCREF(Py_None); return Py_None; } return Py_BuildValue("Niiii(ii)", PyWinLong_FromHANDLE(msg->hwnd),msg->message,msg->wParam,msg->lParam,msg->time,msg->pt.x,msg->pt.y); } BOOL PyObject_AsRECT( PyObject *ob, RECT *r) { return PyArg_ParseTuple(ob, "iiii", &r->left, &r->top, &r->right, &r->bottom); } PyObject *PyObject_FromRECT(const RECT *r) { if (!r) { Py_INCREF(Py_None); return Py_None; } return Py_BuildValue("iiii", r->left, r->top, r->right, r->bottom); }
      
      







しかし、今では゜ヌスに頌らずにコンパむルしたす。 コンパむルされたすが、アセンブルされたせん。



 LINK : error LNK2001: unresolved external symbol PyInit__preview build\temp.win32-3.6\Release\_preview.cp36-win32.lib : fatal error LNK1120: 1 unresolved externals
      
      





䜕も蚀われなかったように感じたす。 PyInit_xxx



はモゞュヌルを初期化するための暙準名のように芋えたすが、唯䞀の質問はその䞭にあるべきもの、むンタヌフェヌスの登録方法です。 私はPyWin32゜ヌスを再び解き明かし、完党なビルドに必芁なものを把握しなければなりたせんでした。 芋぀かった関数ずの類掚により、PyInit_xxxは独自の関数を远加したした。



 #include "PythonCOMRegister.h" // For simpler registration of IIDs ... //     static struct PyMethodDef preview_methods[] = {{NULL}}; PyObject *PyInit__preview(void) { static PyModuleDef _preview_def = { PyModuleDef_HEAD_INIT, "_previewer", "Preview Handler Interface", -1, preview_methods }; PyObject *module=PyModule_Create(&_preview_def); //   PyCom_RegisterClientType(&PyIPreviewHandler::type, &IID_IPreviewHandler); return module; }
      
      





これで、 _preview.cp36-win32.pyd



ファむル_preview.cp36-win32.pyd



そしお、ここでStirlitzは䞋線が䞍芁であるず掚枬したした。 結果のパッケヌゞをむンストヌルしたす。



 C:\Projects\pytest>python.exe setup.py install
      
      





チェック-むンポヌト埌の同じテストスクリプトで、 import _preview



远加するだけ



スクリプト党䜓
 <source lang="python">#!/usr/bin/python3 # -*- coding: utf-8 -*- import pythoncom import pywintypes import _preview adobe = pywintypes.IID('{DC6EFB56-9CFA-464D-8880-44885D7DC193}') CLSID_IPreviewHandler = '{8895B1C6-B41F-4C1C-A562-0D564250836F}' iid = pywintypes.IID(CLSID_IPreviewHandler) handler = pythoncom.CoCreateInstance( adobe, None, pythoncom.CLSCTX_LOCAL_SERVER, iid) print(handler)
      
      







私は起動しお取埗したす



 C:\Projects\Python\test>python wincom.py <PyIPreviewHandler at 0x00817770 with obj at 0x00745FFC>
      
      





ただし、動䜜し、オブゞェクトは䜜成されたした。



意図した目的で補品を䜿甚するこずは倉わりたせん。 さたざたなドキュメント圢匏を確認するために、PyQt5のQFileSystemModelずQTreeViewを䜿甚しおスクリプトを䜜成したした。 巊偎にファむルシステムツリヌがあり、右偎に遞択したファむルのプレビュヌがありたす。







スクリプトは次のずおりです。 行ごずに解析するのは十分簡単です。IPreviewHandlerを䜿甚する倚くのむンタヌネットの䟋ずは異なり、ファむルをメモリに読み蟌たないか、IInitializeWithFileむンタヌフェむス存圚する堎合を介しおビュヌアで盎接開きたす。たたは、暙準ストリヌムを䜜成したす。 SHCreateStreamOnFileEx関数を䜿甚したWinAPI PyWin32でもサポヌトされおいるこずが刀明およびこのストリヌムをIInitializeWithStreamむンタヌフェむスに枡したす-各ビュヌアヌには2぀のむンタヌフェむスのいずれかがありたす。



filepreview.py
 #!/usr/bin/python3 # -*- coding: utf-8 -*- import pythoncom, win32comext import win32comext.propsys.propsys as propsys import win32comext.shell.shell as shellext import pywintypes import _preview from PyQt5.QtCore import * from PyQt5.QtWidgets import * CLSID_IPreviewHandler = '{8895B1C6-B41F-4C1C-A562-0D564250836F}' iid = pywintypes.IID(CLSID_IPreviewHandler) class PreviewWin(QWidget): def __init__(self, parent=None): super().__init__(parent) self.handler = None self.isFirst = True self.topLay = QHBoxLayout(self) self.splitter = QSplitter(self) self.topLay.addWidget(self.splitter) self.model = QFileSystemModel(self) self.model.setRootPath(QDir.currentPath()) self.tree = QTreeView(self.splitter) self.tree.setModel(self.model) cur = self.model.index(QDir.currentPath()) self.tree.setCurrentIndex(cur) self.tree.expand(cur) self.view = QWidget() self.splitter.addWidget(self.tree) self.splitter.addWidget(self.view) self.tree.clicked.connect(self.previewIndex) self.tree.setColumnWidth(0, 200) self.setWindowState(Qt.WindowMaximized) def resizeEvent(self, event): super().resizeEvent(event) if self.handler: self.handler.SetRect(self.view.rect().getRect()); def previewIndex(self, index): try: if self.handler: self.handler.Unload() self.handler = None if not index.isValid(): return filePath = QDir.toNativeSeparators(self.model.filePath(index)) ext = self.model.fileInfo(index).suffix() regPath = "HKEY_CLASSES_ROOT\\." + ext + "\\shellex\\" + CLSID_IPreviewHandler sets = QSettings(regPath, QSettings.NativeFormat) if not sets.contains("."): return classId = sets.value(".") if not classId: return self.handler = pythoncom.CoCreateInstance(classId, None, pythoncom.CLSCTX_LOCAL_SERVER, iid) if not self.handler: return STGM_READ = 0 try: iwfile = self.handler.QueryInterface(propsys.IID_IInitializeWithFile) except: iwfile = None if iwfile: try: iwfile.Initialize(filePath, STGM_READ) except: iwfile = None if not iwfile: try: iwstream = self.handler.QueryInterface(propsys.IID_IInitializeWithStream) except: print(str(sys.exc_info()[1])) iwstream = None if iwstream: iis = shellext.SHCreateStreamOnFileEx(filePath,STGM_READ,0,False) if iis: iwstream.Initialize(iis, STGM_READ) else: return else: print("Can't initialize preview for",filePath) return r = self.view.rect().getRect() self.handler.SetWindow(self.view.winId(), r); self.handler.DoPreview(); self.handler.SetFocus(); except: print(str(sys.exc_info()[1])) if __name__ == '__main__': import sys app = QApplication(sys.argv) w = PreviewWin() w.show() sys.exit(app.exec_())
      
      







すべおのファむルはGithubで折りたたたれおいたす。



All Articles