Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Pywin32 et POO : Créer une classe héritant de "Dispatch" ?

+1 vote

Actuellement en train de m'amuser à contrôler Excel via COM en utilisant pywin32, j'aimerai structurer mon code en créant une classe.

from win32com.client.gencache import EnsureDispatch
from win32com.client import constants

class Excel(object) :
    def __init__(self) :
        xl = EnsureDispatch("Excel.Application")
        self = xl
        self.DisplayAlerts = False
        dir = os.path.dirname(__file__)
        self.Workbooks.Open(os.path.join(dir,"ClientOle.xla"))
        self.Visible = True

xl=Excel()
print(xl.Visible)

Je comprend pourquoi mon code ici ne fonctionne pas, "xl = EnsureDispatch("Excel.Application")" ne fait que créer une référence vers l'objet généré par EnsureDispatch.
Seulement, après avoir passé plus d'une heure dans le code source du module pywin32, impossible de trouver comment faire hériter ma classe de EnsureDispatch. Je suis juste incapable de trouver comment instancier tout ça proprement.

demandé 6-Jul-2015 par Zara

Pour plus de clarté dans la question, je te renvois vers les points si-dessous ===============================================

-Commence par nous montrer réelement le message d'erreur, on en saura un peu plus

-Présente en quelques mots à quoi sert le module que tu importes histoire qu'on en sache un peu plus

-Explique clairement ce qu'est censé faire :

EnsureDispatch("Excel.Application")

Le message d'erreur en lui même n'a que peu d'intérêt, il m'a juste fait comprendre que je ne copiais pas l'objet xl dans self, seulement que je créais une référence.

Pywin32 permet de contrôler les applications Windows via COM, si je ne dis pas de bêtise c'est assimilable à un lien OLE.

Comme l'a dit JC,

EnsureDispatch("Excel.Application")

renvoi un objet qui permet de contrôler l'application Excel via ses méthodes.
Ce que j'ai cru comprendre, c'est que l'objet est généré dynamiquement et que je ne peux pas en hériter de façon classique.

1 Réponse

+1 vote
 
Meilleure réponse

Ton

    self = xl

est audacieux :)

Je ne suis pas sous windows donc je ne peux pas réellement tester.

Si EnsureDispatch avait était une classe, tu aurais pu faire quelque chose du genre

class Excel(EnsureDispatch) :
    def __init__(self) :
       super(EnsureDispatch, self).__init__("Excel.Application")
       # rest of code

Sauf que EnsureDispatch est une fonction. Tu ne peux pas faire hériter ta classe comme ca.

Par contre tu peux faire un object proxy , cf l'article dédié chez S&M avec pleins de bons examples.

répondu 6-Jul-2015 par jc (2,704 points)

Merci beaucoup, ça fonctionne très bien !

J'ai voulu essayer d'ajouter des attributs propres à ma classe Excel, sans succès.
J'ai compris que je devais modifier getattribute et setattr, mais je ne comprend pas ce que je dois y implémenter.

class Excel(object) :
    def __init__(self, obj) :
        # L'objet passé en paramètre est
        # sauvegardé dans un attribut.
        # On le fait en utilisant 
        # object.__setattr__, qui est le
        # __setattr__ du parent, et non directement
        # en faisant self._obj = obj
        # afin d'éviter une boucle infinie car
        # nous écrasons __setattr__ plus bas.
        object.__setattr__(self, "_obj", obj)
        obj.DisplayAlerts = False
        self.rep = os.path.dirname(__file__)

    # On écrase les méthodes magiques __getattribute__
    # (qui est appelée quand on faire self.nom_attribut), 
    # __delattr__ (qui est appelée quand on fait 
    # del self.nom_attribut) et __setattr__ (qui est 
    # appelée quand on fait self.nom_attribut = truc)
    def __getattribute__(self, name):
        try :
            return getattr(object.__getattribute__(self, "_obj"), name)
        except AttributeError :
            #que mettre ?
    def __delattr__(self, name):
        delattr(object.__getattribute__(self, "_obj"), name)
    def __setattr__(self, name, value):
        try :
            setattr(object.__getattribute__(self, "_obj"), name, value)
        except AttributeError :
            #que mettre ?

En effet, si j'essaie de définir un nouvel attribut, le programme lance l'exception AttributeError.

ok il semble que tu ne puisse pas affecter de nouveaux attributs a ton objet proxyfié.
(certainement a cause du fait qu'il provienne d'un composant COM)

Une solution (peut être un peu bourrine) : si le setattr fail sur l'objet proxyfié, tu fais un setattr sur self.

def __getattribute__(self, name):
    try :
        return getattr(object.__getattribute__(self, "_obj"), name)
    except AttributeError :
        #  essaie d'obtenir l'attribut a partir de l'instance de Excel quand pas trouvé sur l'objet proxifié
        return object.__getattribute__(self, name)

def __setattr__(self, name, value):
    try :
        setattr(object.__getattribute__(self, "_obj"), name, value)
    except AttributeError :
        # affecte les nouveaux attributs a l'instance de Excel
        object.__setattr__(self, name, value)

Super ça fonctionne très bien !
Merci beaucoup !

Juste pour compléter, j'ai également modifié le delattr comme suit :

    def __delattr__(self, name):
    try :
        delattr(object.__getattribute__(self, "_obj"), name)
    except AttributeError :
        object.__delattr__(self, name)

@Zara

Si la réponse te vas, clique sur la coche en dessous des votes pour l'accepter ;)

Dès que je rentre à la maison, je viens seulement de m'inscrire et le bouton n'apparaît pas sur mon téléphone...

...