I'm trying to implement a cross-platform python interface for a third-party API (Antidote), so that it can communicate with a PyQt application.
Firstly, for Linux here's the working D-Bus interface; https://gitlab.com/-/snippets/2151173
Simply put, we create a DBus server (class AdaptateurAntidote) which relay all calls to another class (ImplementationAntidote), which in turn communicates with the GUI widgets.
Then, below is a tentative for the COM interface (notice that the method differ between the two API);
Similarly a COM server is created (AdaptateurAntidote), which receives calls from Antidote's API. However, due to the nature of COM objects, the adapter class is never instantiated, and won't accept the ImplementationAntidote as its parent attribute.
For instance, if "parent" is declared in _public_attrs_, this;
Results in;
Finally, instead of using Dispatch(), I've tried;
So I could split the COM objects properties in a class, and handle the events in another. However, this gives;
Firstly, for Linux here's the working D-Bus interface; https://gitlab.com/-/snippets/2151173
Simply put, we create a DBus server (class AdaptateurAntidote) which relay all calls to another class (ImplementationAntidote), which in turn communicates with the GUI widgets.
Then, below is a tentative for the COM interface (notice that the method differ between the two API);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
#!/usr/bin/python3 import pythoncom import sys import winreg import win32com.client import win32com.server.register import win32com.server.util from win32com.server import localserver from pathlib import Path from PyQt5 import QtCore, QtWidgets class MainWindow(QtWidgets.QMainWindow): def __init__( self , app): super ().__init__() self .body = QtWidgets.QTextEdit( "Texte à corrigée." ) self .setCentralWidget( self .body) self .antidote = ImplementationAntidote( self , self .body) def closeEvent( self , event): super ().closeEvent(event) sys.exit( 0 ) # Kills the hanging server in QThread class ImplementationAntidote(QtCore.QObject): def __init__( self , parent: QtCore.QObject, body: QtWidgets.QTextEdit): super ().__init__() self .parent = parent self .body = body # self._register() self ._launch() self .worker = ServerThread() self .serverThread = QtCore.QThread() self .serverThread.started.connect( self .worker.run) self .worker.moveToThread( self .serverThread) self .serverThread.start() # this should renew a timer until local com server and antidote are both ready # ## QtCore.QTimer.singleShot( 1000 , self ._connect) @staticmethod def readRegistry(path: str , name: str ): key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path, 0 , winreg.KEY_READ) value, regtype = winreg.QueryValueEx(key, name) winreg.CloseKey(key) return value def _connect( self ): version = self .readRegistry(r "Software\Druide informatique inc.\Antidote" , "VersionAPI" ) version = min ([ 2.0 , float (version)]) self .server = win32com.client.Dispatch( "Correcteur.Antidote" ) self .antidote = win32com.client.Dispatch( "Antidote.ApiOle" ) self .antidote.LanceOutilDispatch2( self .server, "C" , "", str (version)) # Server, outil, langue, version API def _launch( self ): antidoteFolder = self .readRegistry(r "Software\Druide informatique inc.\Antidote" , "DossierAntidote" ) antidotePath = Path(antidoteFolder) / "Antidote.exe" QtCore.QProcess.startDetached( str (antidotePath), [ "-activex" ]) def _register( self ): win32com.server.register.UseCommandLine(AdapteurAntidote) class EventHandler: pass class ServerThread(QtCore.QObject): def __init__( self ): super ().__init__() def run( self ): pythoncom.CoInitialize() localserver.serve([ "{D390AE78-D6A2-47CF-B462-E4F2DC9C70F5}" ]) # ## Hangs on quit pythoncom.CoUninitialize() # ## Never called class AdapteurAntidote: _reg_clsctx_ = pythoncom.CLSCTX_LOCAL_SERVER _reg_clsid_ = "{D390AE78-D6A2-47CF-B462-E4F2DC9C70F5}" # pythoncom.CreateGuid() _reg_progid_ = "Correcteur.Antidote" _reg_verprogid_ = "Correcteur.Antidote.1" _reg_class_spec_ = "AntidoteCOM.AdapteurAntidote" _public_methods_ = [ "ActiveApplication" , "ActiveDocument" , "DonneDebutSelection" , "DonneFinSelection" , "DonneIdDocumentCourant" , "DonneIdZoneDeTexte" , "DonneIdZoneDeTexteCourante" , "DonneIntervalle" , "DonneLongueurZoneDeTexte" , "DonneNbZonesDeTexte" , "DonnePolice" , "DonneTitreDocCourant" , "RemplaceIntervalle" , "SelectionneIntervalle" ] def DonneIdDocumentCourant( self ): print ( "DonneIdDocumentCourant" ) def DonneIdZoneDeTexte( self ): print ( "DonneIdZoneDeTexte" ) def DonneIdZoneDeTexteCourante( self ): print ( "DonneIdZoneDeTexteCourante" ) def DonneIntervalle( self ): print ( "DonneIntervalle" ) def DonneLongueurZoneDeTexte( self ): print ( "DonneLongueurZoneDeTexte" ) def DonneNbZonesDeTexte( self ): print ( "DonneNbZonesDeTexte" ) def DonnePolice( self ): print ( "DonnePolice" ) def DonneTitreDocCourant( self ): print ( "DonneTitreDocCourant" ) def RemplaceIntervalle( self ): print ( "RemplaceIntervalle" ) def ActiveDocument( self ): print ( "ActiveDocument" ) # self.parent.activeDocument() def ActiveApplication( self ): print ( "ActiveApplication" ) # self.parent.activeDocument() def DonneDebutSelection( self ) - > int : print ( "DonneDebutSelection" ) # return self.parent.donneDebutSelection() def DonneFinSelection( self ) - > int : print ( "DonneFinSelection" ) # return self.parent.donneFinSelection() def SelectionneIntervalle( self , debut: int , fin: int ): print ( "SelectionneIntervalle" ) # self.parent.selectionneIntervalle(debut, fin) if __name__ = = '__main__' : app = QtWidgets.QApplication([]) gui = MainWindow(app) gui.show() app. exec () |
For instance, if "parent" is declared in _public_attrs_, this;
1 |
self .server.parent = self |
Error:TypeError: must be real number, not ImplementationAntidote.
If "parent" is not declared;Error:AttributeError: 'Correcteur.Antidote.parent' can not be set.
Settings a global variable or setting a "parent" attribute into AdaptateurAntidote also does not work, as it always return its default value (None).Finally, instead of using Dispatch(), I've tried;
1 |
self .server = win32com.client.DispatchWithEvents( "Correcteur.Antidote" , EventHandler) |
Error:TypeError: This COM object can not automate the makepy process - please run makepy manually for this object
Documentation is scarce and I'm stuck !