IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

PyQt Python Discussion :

Demande d'avis : empêcher la deuxième exécution d'un programme


Sujet :

PyQt Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Expert confirmé
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 486
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 486
    Billets dans le blog
    6
    Par défaut Demande d'avis : empêcher la deuxième exécution d'un programme
    Bonjour,

    Je cherchais une méthode pour empêcher un programme PyQt4 d'être lancé une seconde fois alors qu'il y avait déjà une instance en activité.

    J'ai cherché un code PyQt4 sur le web et je n'en ai pas trouvé. J'ai aussi trouvé des 'usines à gaz' en C++, difficiles à traduire en Python.

    Finalement, le plus simple a été de traduire en PyQt4 le code en C de la FAQ Qt: http://qt.developpez.com/faq/?page=q...ples-instances.

    Cela a donné le code suivant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    #! /usr/bin/python
    # -*- coding: utf-8 -*-
    # Python v2.7
     
    import sys
    from PyQt4 import QtCore, QtGui
     
    #############################################################################
    class Fenetre(QtGui.QMainWindow):
     
        #========================================================================
        def __init__(self, parent=None):
            super(Fenetre,self).__init__(parent)
     
     
    #############################################################################
    if __name__ == "__main__":
     
        # lancement du graphique
        app = QtGui.QApplication(sys.argv)
     
        # on prépare la création d'une zone partagée avec une clé quelconque
        # mais cette clé ne doit pas être utilisée par un autre programme!
        sharedMemory = QtCore.QSharedMemory(QtCore.QString(u"D6BB05B2919732D8F29045842531CAD23162E1B9"))
     
        # on essaye de créer cette zone partagée de taille 50 (quelconque):
        # si ce n'est pas possible, c'est qu'elle existe déjà, et donc
        # qu'elle a été créée par une instance précédente du même programme!
        if not sharedMemory.create(50):
            QtGui.QMessageBox.warning(None,
                             u"Vérification",
                             u"""
     
    Attention:
     
    ce programme est déjà en cours d'exécution
     
    utilisez la version déjà lancée!
     
                             """)
            sys.exit()
     
        # lancement de la fenêtre
        fen = Fenetre()
        fen.show()
        sys.exit(app.exec_())
    Le principe est très simple:

    1- on créé une clé (une chaine) unique pour le programme concerné

    2- on cherche à créer une zone de mémoire partagée avec cette clé

    3- si ça ne marche pas, c'est que la zone existe déjà avec la même clé, et donc qu'elle a déjà été créée par une instance précédente du programme.

    Pour créer une clé, il suffit que la chaine soit unique. Vous pouvez prendre un GUID comme proposé par la FAQ Qt, mais n'importe quelle chaine peut faire l'affaire. J'ai même essayé avec succès: "nimportequoipourvuquecamousse" . Plus sérieusement, si vous manquez d'inspiration, vous pouvez fabriquer une chaine de 40 caractères au hasard avec:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    from random import randint
    guidrand = lambda n=40, alphanum=u"0123456789ABCDEF":      ''.join([alphanum[randint(0,len(alphanum)-1)] for i in xrange(n)])
    print guidrand()
    D6BB05B2919732D8F29045842531CAD23162E1B9
    Et ça a l'air de marcher: le 1er lancement du programme se fait, mais le second lancement est empêché (avec message). Ceci à condition de lancer le programme normalement (en console ou un lanceur graphique), et pas à partir d'un outil de développement.

    Ça fonctionne de la même façon sous Windows et sous Linux (sous Mac OS_X, je ne sais pas).

    Avec cx_freeze sous Windows, ça marche aussi, même si dans certains cas, il faut attendre une minute pour que Windows fasse disparaitre la précédente zone mémoire partagée.

    Sous Windows, j'ai même essayé de "tuer" sauvagement la 1ère instance avec le gestionnaire des tâches, pour vérifier que la zone partagée était supprimée aussi: et oui!


    Mais alors, quel est mon problème? Il est ici: la méthode me parait tellement simple que j'ai un doute. Aussi, j'aimerais que plusieurs parmi vous essayent pour vérifier que ça marche dans tous les cas!. Il suffit de récupérer le code en copier-coller, et d'avoir déjà Python 2.x et PyQt4.

    Si ça marche, en plus de l'utiliser sur mes programmes, je le proposerai à la FAQ PyQt4.

    Merci d'avance.

    Tyrtamos

    [EDIT]: petite modif du code: en cas de détection d'échec, après la fenêtre du message d'erreur, il faut remplacer le simple sys.exit() par:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    app.quit()
    sys.exit(0)
    Dans ce cas, ça fonctionne très bien et sans attente avec cx_freeze!

  2. #2
    Membre Expert Avatar de pacificator
    Profil pro
    Inscrit en
    Août 2006
    Messages
    1 074
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 1 074
    Par défaut
    Bonjour Tyrtamos,

    as-tu essayé cette technique: Comment n'instancier qu'une seule fois un programme?

  3. #3
    Expert confirmé
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 486
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 486
    Billets dans le blog
    6
    Par défaut
    Bonjour pacificator,

    Merci de ta remarque. Oui, c'est la 1ère chose que j'ai essayée.

    Mais avec un fichier, j'ai la trouille bleue qu'un arrêt anormal de la 1ère instance empêche toute tentative ultérieure d'exécution du programme (même après un reboot!). Il faudra alors accéder au répertoire concerné pour effacer 'à la main' le fichier en question.

    Avec un drapeau sur un serveur, le reboot devrait marcher, mais c'est une solution un peu trop 'rustique' à mon goût.

    En fait, il faut que dans tous les cas, la fermeture, même anormale, de la 1ère instance fasse disparaitre le drapeau choisi. Et pour ça, il vaudrait mieux que ce soit l'OS qui s'en charge automatiquement. D'où ma proposition.

    Tyrtamos

  4. #4
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 753
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 753
    Par défaut
    Salut Tyrtamos,

    Le solution proposée par Pacificator crée un serveur qui écoute sur le port 50000. Comme on ne peut pas créer deux serveurs qui écoutent sur le même port, çà fonctionne modulo un délai d'attente au démarrage suivant.

    Par contre, cette technique devrait aussi fonctionner avec un fichier ouvert en écriture - qui par défaut n'est pas "partageable" sauf dans le cas "append".

    Les défauts d'une méthode via mémoire partagée sont:
    - pourquoi passer par Qt?
    - la mécanique sous jacente dépend de l'OS donc la portabilité de ce que tu fais avec devra être testée dans tous les environnements - même en passant par Qt.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  5. #5
    Expert confirmé
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 486
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 486
    Billets dans le blog
    6
    Par défaut
    Bonjour wiztricks,

    Merci de tes remarques.

    Pourquoi une solution Qt? Parce que je fais des programmes Qt et que je ne trouve pas de solutions générales.

    Je viens d'essayer la solution serveur, et effectivement, elle fonctionne. Mais elle n'a pas que des avantages:
    - il faut demander l'accord du parefeu
    - il faut supposer qu'il n'y a pas un serveur de port 50000 pour une autre raison
    - pour appliquer la méthode à plusieurs programmes, il va falloir leur attribuer un port différent (50001, 50002, etc...) et gérer la liste pour éviter les collisions.

    Quand à la solution "on ouvre un fichier en écriture sans le fermer", elle est astucieuse, mais elle ne marche pas. Je viens d'ouvrir 2 instances du même programme qui ont ouvert en écriture le même programme sans broncher. ??? C'est dommage parce que cette solution est très simple.

    En plus, dans les OS modernes, on ne peut plus écrire dans la zone d'installation des programmes. Il faut donc chercher une zone publique ou le home de l'utilisateur pour écrire dedans.

    Tyrtamos

  6. #6
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 753
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 753
    Par défaut

    cela aurait été trop simple!
    Jettes un œil à https://bitbucket.org/pchambon/python-rock-solid-tools et plus particulièrement au module rsfile
    - W
    PS: d'après la doc d'os.open le flags os.O_EXLOCK est disponible sur Windows et Unix. Une utilisation pourrait être (ici Python2.7 sous OSX).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    >>> flags = os.O_EXLOCK | os.O_RDWR | os.O_NONBLOCK
    >>> f = os.open('test.txt', flags)
    >>> f2 = os.open('test.txt', flags)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    OSError: [Errno 35] Resource temporarily unavailable: 'test.txt'
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  7. #7
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 753
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 753
    Par défaut
    Autres...

    Citation Envoyé par tyrtamos Voir le message
    En plus, dans les OS modernes, on ne peut plus écrire dans la zone d'installation des programmes. Il faut donc chercher une zone publique ou le home de l'utilisateur pour écrire dedans.
    Comme on souhaite utiliser créer un verrou EX sur la ressource "system wide" matérialisée par le fichier, il n'est pas indispensable d'ouvrir en écriture:
    >>> flags = os.O_EXLOCK | os.O_RDONLY | os.O_NONBLOCK
    Le fichier peut être un .dat quelconque dans les répertoires du code livré.
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

Discussions similaires

  1. Réponses: 3
    Dernier message: 28/10/2007, 19h53
  2. Demande d'avis sur un bouquin (Deitel)
    Par oodini dans le forum C++
    Réponses: 7
    Dernier message: 26/02/2005, 01h50

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo