2012-01-09 11 views
7

Estoy comenzando la UI desde Maya. Si la IU no se ha cerrado, ejecutar la IU nuevamente congelará Maya (con el error "Event Loop ya se está ejecutando")PyQt: ¿cómo detectar y cerrar la IU si ya se está ejecutando?

Cerrar manualmente la IU antes de volver a ejecutar la secuencia de comandos evitará que se congele. Pero supongo que eso no es realmente práctico.

¿Hay alguna forma de detectar si la interfaz de usuario que intento ejecutar ya existe? Y la posible fuerza cerrarlo?

+0

posiblemente un duplicado de http://stackoverflow.com/questions/5006547/qt-best-practice-for-a-single-instance-app-protection –

Respuesta

15

Hay un par de soluciones de C++ bastante simples dadas here.

He transferido uno de ellos a PyQt, y he proporcionado un script de ejemplo a continuación. La solución original de C++ se ha dividido en dos clases, porque la instalación de mensajería puede no ser necesaria.

ACTUALIZACIÓN:

mejorado el script para que utiliza señales de estilo nuevo y funciona tanto con python2 y python3.

# only needed for python2 
import sip 
sip.setapi('QString', 2) 

from PyQt4 import QtGui, QtCore, QtNetwork 

class SingleApplication(QtGui.QApplication): 
    messageAvailable = QtCore.pyqtSignal(object) 

    def __init__(self, argv, key): 
     QtGui.QApplication.__init__(self, argv) 
     self._memory = QtCore.QSharedMemory(self) 
     self._memory.setKey(key) 
     if self._memory.attach(): 
      self._running = True 
     else: 
      self._running = False 
      if not self._memory.create(1): 
       raise RuntimeError(self._memory.errorString()) 

    def isRunning(self): 
     return self._running 

class SingleApplicationWithMessaging(SingleApplication): 
    def __init__(self, argv, key): 
     SingleApplication.__init__(self, argv, key) 
     self._key = key 
     self._timeout = 1000 
     self._server = QtNetwork.QLocalServer(self) 
     if not self.isRunning(): 
      self._server.newConnection.connect(self.handleMessage) 
      self._server.listen(self._key) 

    def handleMessage(self): 
     socket = self._server.nextPendingConnection() 
     if socket.waitForReadyRead(self._timeout): 
      self.messageAvailable.emit(
       socket.readAll().data().decode('utf-8')) 
      socket.disconnectFromServer() 
     else: 
      QtCore.qDebug(socket.errorString()) 

    def sendMessage(self, message): 
     if self.isRunning(): 
      socket = QtNetwork.QLocalSocket(self) 
      socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly) 
      if not socket.waitForConnected(self._timeout): 
       print(socket.errorString()) 
       return False 
      if not isinstance(message, bytes): 
       message = message.encode('utf-8') 
      socket.write(message) 
      if not socket.waitForBytesWritten(self._timeout): 
       print(socket.errorString()) 
       return False 
      socket.disconnectFromServer() 
      return True 
     return False 

class Window(QtGui.QWidget): 
    def __init__(self): 
     QtGui.QWidget.__init__(self) 
     self.edit = QtGui.QLineEdit(self) 
     self.edit.setMinimumWidth(300) 
     layout = QtGui.QVBoxLayout(self) 
     layout.addWidget(self.edit) 

    def handleMessage(self, message): 
     self.edit.setText(message) 

if __name__ == '__main__': 

    import sys 

    key = 'app-name' 

    # send commandline args as message 
    if len(sys.argv) > 1: 
     app = SingleApplicationWithMessaging(sys.argv, key) 
     if app.isRunning(): 
      print('app is already running') 
      app.sendMessage(' '.join(sys.argv[1:])) 
      sys.exit(1) 
    else: 
     app = SingleApplication(sys.argv, key) 
     if app.isRunning(): 
      print('app is already running') 
      sys.exit(1) 

    window = Window() 
    app.messageAvailable.connect(window.handleMessage) 
    window.show() 

    sys.exit(app.exec_()) 
+0

Si ejecuto el mensaje de mensajería, siempre obtengo este error : 'QLocalSocket :: connectToServer: Connection se negó' ¿Alguna idea de cómo solucionarlo? – Jeena

+0

@Jeena. Todavía estoy trabajando en Linux con PyQt-4.10. – ekhumoro

+0

Oh, fue solo una rareza aleatoria, después de un reinicio de mi computadora esta noche parece que funciona bien ahora, ¡gracias por regresar! – Jeena

8

En caso de que si alguien quiere correr @ekhumoro solución con python3 hay necesidad de hacer algunos ajustes para las operaciones de cadena, voy a compartir mi ejemplar en el que estaba trabajando pitón 3.

import sys 

from PyQt4 import QtGui, QtCore, QtNetwork 

class SingleApplication(QtGui.QApplication): 
    def __init__(self, argv, key): 
     QtGui.QApplication.__init__(self, argv) 
     self._memory = QtCore.QSharedMemory(self) 
     self._memory.setKey(key) 
     if self._memory.attach(): 
      self._running = True 
     else: 
      self._running = False 
      if not self._memory.create(1): 
       raise RuntimeError(self._memory.errorString()) 

    def isRunning(self): 
     return self._running 

class SingleApplicationWithMessaging(SingleApplication): 
    def __init__(self, argv, key): 
     SingleApplication.__init__(self, argv, key) 
     self._key = key 
     self._timeout = 1000 
     self._server = QtNetwork.QLocalServer(self) 

     if not self.isRunning(): 
      self._server.newConnection.connect(self.handleMessage) 
      self._server.listen(self._key) 

    def handleMessage(self): 
     socket = self._server.nextPendingConnection() 
     if socket.waitForReadyRead(self._timeout): 
      self.emit(QtCore.SIGNAL('messageAvailable'), bytes(socket.readAll().data()).decode('utf-8')) 
      socket.disconnectFromServer() 
     else: 
      QtCore.qDebug(socket.errorString()) 

    def sendMessage(self, message): 
     if self.isRunning(): 
      socket = QtNetwork.QLocalSocket(self) 
      socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly) 
      if not socket.waitForConnected(self._timeout): 
       print(socket.errorString()) 
       return False 
      socket.write(str(message).encode('utf-8')) 
      if not socket.waitForBytesWritten(self._timeout): 
       print(socket.errorString()) 
       return False 
      socket.disconnectFromServer() 
      return True 
     return False 

class Window(QtGui.QWidget): 
    def __init__(self): 
     QtGui.QWidget.__init__(self) 
     self.edit = QtGui.QLineEdit(self) 
     self.edit.setMinimumWidth(300) 
     layout = QtGui.QVBoxLayout(self) 
     layout.addWidget(self.edit) 

    def handleMessage(self, message): 
     self.edit.setText(message) 

if __name__ == '__main__': 

    key = 'foobar' 

    # if parameter no. 1 was set then we'll use messaging between app instances 
    if len(sys.argv) > 1: 
     app = SingleApplicationWithMessaging(sys.argv, key) 
     if app.isRunning(): 
      msg = '' 
      # checking if custom message was passed as cli argument 
      if len(sys.argv) > 2: 
       msg = sys.argv[2] 
      else: 
       msg = 'APP ALREADY RUNNING' 
      app.sendMessage(msg) 
      print("app is already running, sent following message: \n\"{0}\"".format(msg)) 
      sys.exit(1) 
    else: 
     app = SingleApplication(sys.argv, key) 
     if app.isRunning(): 
      print('app is already running, no message has been sent') 
      sys.exit(1) 

    window = Window() 
    app.connect(app, QtCore.SIGNAL('messageAvailable'), window.handleMessage) 
    window.show() 

    sys.exit(app.exec_()) 

Ejemplo cli llamadas, suponiendo que el nombre del script es "SingleInstanceApp.py":

python SingleInstanceApp.py 1 
python SingleInstanceApp.py 1 "test" 
python SingleInstanceApp.py 1 "foo bar baz" 
python SingleInstanceApp.py 1 "utf8 test FOO ßÄÖÜ ßäöü łąćźżóń ŁĄĆŹŻÓŃ etc" 

(y aquí es llamar wihout primer parámetro, por lo que el mensaje simplemente no se enviarán)

python SingleInstanceApp.py

Espero que ayude a alguien.

Cuestiones relacionadas