2012-01-09 17 views
7

Sto iniziando l'interfaccia utente da Maya. Se l'interfaccia utente non è stata chiusa, eseguire nuovamente l'interfaccia utente bloccherà completamente Maya (con l'errore "Event Loop è già in esecuzione")PyQt - come rilevare e chiudere l'interfaccia utente se è già in esecuzione?

Chiudere manualmente l'interfaccia utente prima di rieseguire lo script impedirà il blocco. Ma immagino che non sia davvero pratico.

C'è un modo per rilevare se esiste già l'interfaccia utente che sto tentando di eseguire? E la forza possibile lo chiude?

+0

probabilmente un duplicato di http://stackoverflow.com/questions/5006547/qt-best-practice-for-a-single-instance-app-protection –

risposta

15

Ci sono un paio di soluzioni C++ piuttosto semplici date here.

Ho portato uno di questi a PyQt e ho fornito uno script di esempio di seguito. La soluzione C++ originale è stata divisa in due classi, poiché la funzione di messaggistica potrebbe non essere necessaria.

UPDATE:

Migliorato lo script in modo che utilizzi i segnali di nuovo stile e funziona sia con python2 e 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

Se eseguo quello con la messaggistica, ottengo sempre questo errore : 'QLocalSocket :: connectToServer: Connection rifiutato 'qualche idea su come risolverlo? – Jeena

+0

@Jeena. Sto ancora lavorando per me su Linux con PyQt-4.10. – ekhumoro

+0

Oh, allora era solo un po 'stranezza casuale, dopo un riavvio del mio computer stasera sembra funzionare bene adesso, grazie per il ritorno però! – Jeena

8

Nel caso in cui qualcuno vuole correre @ekhumoro soluzione python3 non c'è bisogno di fare alcuni aggiustamenti alle operazioni sulle stringhe, io condividere la mia copia in cui si stava lavorando Python 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_()) 

Esempio cli chiamate, supponendo che il nome dello script è "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" 

(e qui è chiamata wihout primo parametro, quindi un messaggio semplicemente non sarà inviato)

python SingleInstanceApp.py

Spero che possa aiutare qualcuno.

Problemi correlati