pythonqtpyqtpyside

QtSingleApplication for PySide or PyQt


Is there a Python version of the C++ class QtSingleApplication from Qt Solutions?

QtSingleApplication is used to make sure that there can never be more than one instance of an application running at the same time.


Solution

  • Here is my own implementation. It has been tested with Python 2.7 and PySide 1.1.

    It has essentially the same interface as the C++ version of QtSingleApplication. The main difference is that you must supply an application unique id to the constructor. (The C++ version by default uses the path to the executable as a unique id; that would not work here because the executable will most likely be python.exe.)

    from PySide.QtCore import *
    from PySide.QtGui import *
    from PySide.QtNetwork import *
    
    class QtSingleApplication(QApplication):
    
        messageReceived = Signal(unicode)
    
        def __init__(self, id, *argv):
    
            super(QtSingleApplication, self).__init__(*argv)
            self._id = id
            self._activationWindow = None
            self._activateOnMessage = False
    
            # Is there another instance running?
            self._outSocket = QLocalSocket()
            self._outSocket.connectToServer(self._id)
            self._isRunning = self._outSocket.waitForConnected()
    
            if self._isRunning:
                # Yes, there is.
                self._outStream = QTextStream(self._outSocket)
                self._outStream.setCodec('UTF-8')
            else:
                # No, there isn't.
                self._outSocket = None
                self._outStream = None
                self._inSocket = None
                self._inStream = None
                self._server = QLocalServer()
                self._server.listen(self._id)
                self._server.newConnection.connect(self._onNewConnection)
    
        def isRunning(self):
            return self._isRunning
    
        def id(self):
            return self._id
    
        def activationWindow(self):
            return self._activationWindow
    
        def setActivationWindow(self, activationWindow, activateOnMessage = True):
            self._activationWindow = activationWindow
            self._activateOnMessage = activateOnMessage
    
        def activateWindow(self):
            if not self._activationWindow:
                return
            self._activationWindow.setWindowState(
                self._activationWindow.windowState() & ~Qt.WindowMinimized)
            self._activationWindow.raise_()
            self._activationWindow.activateWindow()
    
        def sendMessage(self, msg):
            if not self._outStream:
                return False
            self._outStream << msg << '\n'
            self._outStream.flush()
            return self._outSocket.waitForBytesWritten()
    
        def _onNewConnection(self):
            if self._inSocket:
                self._inSocket.readyRead.disconnect(self._onReadyRead)
            self._inSocket = self._server.nextPendingConnection()
            if not self._inSocket:
                return
            self._inStream = QTextStream(self._inSocket)
            self._inStream.setCodec('UTF-8')
            self._inSocket.readyRead.connect(self._onReadyRead)
            if self._activateOnMessage:
                self.activateWindow()
    
        def _onReadyRead(self):
            while True:
                msg = self._inStream.readLine()
                if not msg: break
                self.messageReceived.emit(msg)
    

    Here is a simple test program:

    import sys
    from PySide.QtGui import *
    from QtSingleApplication import QtSingleApplication
    
    appGuid = 'F3FF80BA-BA05-4277-8063-82A6DB9245A2'
    app = QtSingleApplication(appGuid, sys.argv)
    if app.isRunning(): sys.exit(0)
    
    w = QWidget()
    w.show()
    app.setActivationWindow(w)
    sys.exit(app.exec_())