[:en]Tales of a GeekTrotter[:fr]Récits d'un GeekTrotter[:ja]Tales of a GeekTrotter [:en]Binary Logbook[:fr]Carnet de bord binaire[:ja]ギークの旅行

10Mai/111

Python Qt4 recipe: QSingleApplication (PySide)Recette Python Qt4: QSingleApplication (PySide)

Thanks to Renato Filho who helped me in this thread on the Qt Forums, I made a simple QSingleApplication class for PySide that allows you to be sure your program will be started only once.

In addition to start the application only once, we can send the arguments of the later calls of your program to the first (and only remaining) instance.

The following video summarizes the features:

Grâce à Renato Filho qui m'a aidé sur les forums Qt, j'ai écris une classe QSingleApplication pour PySide qui permet de ne lancer votre programme qu'une seule fois.

En plus et surtout, cette classe permet de passer les arguments des nouvelles exécutions du programme à la première (et seule restante) exécution.

La vidéo suivante résume les fonctionnalités :

The idea is to use the QLocalServer / QLocalSocket classes to perform the check and communication between the instances.

First, we create a socket and try to connect to the server, registered with the application name.

        self.m_socket = QLocalSocket()
        self.m_socket.connected.connect(self.connectToExistingApp)
        self.m_socket.error.connect(self.startApplication)
        self.m_socket.connectToServer(self.applicationName(), QIODevice.WriteOnly)

If the connection works, it means a server has already been registered, so the application has already been started and we try to connect to that existing application.
If we the new call has been made with arguments, we send them to the already running application (the first one only in this example, adapt to your needs for all).
If the new call has been made without any arguments, we notice the user that the program is already running and we quit.

    def connectToExistingApp(self):
        if len(sys.argv)>1 and sys.argv[1] is not None:
            self.m_socket.write(sys.argv[1])
            self.m_socket.bytesWritten.connect(self.quit)
        else:
            QMessageBox.warning(None, self.tr("Already running"), self.tr("The program is already running."))
            # Quit application in 250 ms
            QTimer.singleShot(250, self.quit)

If the connection fails, then it means this is the first instance of the application so we start the application and register a server listening to future applications.

    def startApplication(self):
        self.m_server = QLocalServer()
        if self.m_server.listen(self.applicationName()):
            self.m_server.newConnection.connect(self.getNewConnection)
            self.mainWindow.show()
        else:
            QMessageBox.critical(None, self.tr("Error"), self.tr("Error listening the socket."))

You can retrieve the complete source code at Gitorious: qSingleApplication.py

L'idée est d'utiliser QLocalServer / QLocalSocket pour effectuer les vérifications et communiquer entre les exécutions du programme.

Primo, on crée un socket et on essaie de se connecter au serveur enregistré avec le nom du programme.

        self.m_socket = QLocalSocket()
        self.m_socket.connected.connect(self.connectToExistingApp)
        self.m_socket.error.connect(self.startApplication)
        self.m_socket.connectToServer(self.applicationName(), QIODevice.WriteOnly)

Si la connexion réussie, ça veut dire qu'il y avait déjà un serveur qui tournait, donc que le programme est déjà lancé.
On va donc essayer de se connecter à ce programme.
Si le nouvel appel du programme a été fait sans paramètres, on affiche une alerte comme quoi le programme est déjà lancé et on quitte.
Si par contre on a des paramètres au nouvel appel, on les envoit au programme qui tourne déjà.

    def connectToExistingApp(self):
        if len(sys.argv)>1 and sys.argv[1] is not None:
            self.m_socket.write(sys.argv[1])
            self.m_socket.bytesWritten.connect(self.quit)
        else:
            QMessageBox.warning(None, self.tr("Already running"), self.tr("The program is already running."))
            # Quit application in 250 ms
            QTimer.singleShot(250, self.quit)

Si la connexion échoue, cela signifie que c'est la première fois qu'on lance le programme, donc on le lance ainsi qu'un serveur qui écoute les éventuels futurs lancements de ce même programme.

    def startApplication(self):
        self.m_server = QLocalServer()
        if self.m_server.listen(self.applicationName()):
            self.m_server.newConnection.connect(self.getNewConnection)
            self.mainWindow.show()
        else:
            QMessageBox.critical(None, self.tr("Error"), self.tr("Error listening the socket."))

Vous pouvez télécharger le code complet sur Gitorious: qSingleApplication.py