PyQt - how to detect and close the user interface if it is already running?

I am launching the user interface from Maya. If the user interface has not been closed, starting the UI again completely freezes Maya (with the error "Event Loop is already running")

Manually closing the user interface before restarting the script will prevent it from freezing. But I think this is not very practical.

Is there a way to determine if there is a user interface that I am trying to run? And a possible force closes it?

+8
source share
3 answers

Here is a very simple PyQt5 solution using QLockFile :

from PyQt5 import QtCore, QtWidgets lockfile = QtCore.QLockFile(QtCore.QDir.tempPath() + '/my_app_name.lock') if lockfile.tryLock(100): app = QtWidgets.QApplication([]) win = QtWidgets.QWidget() win.setGeometry(50, 50, 100, 100) win.show() app.exec() else: print('app is already running') 

The Qt Wiki gave some fairly simple C ++ solutions that seem to no longer exist. I migrated one of them to PyQt and provided an example script below. The original C ++ solution was split into two classes, as a messaging tool might not be needed.

PyQt5 :

 from PyQt5 import QtWidgets, QtCore, QtNetwork class SingleApplication(QtWidgets.QApplication): messageAvailable = QtCore.pyqtSignal(object) def __init__(self, argv, key): super().__init__(argv) # cleanup (only needed for unix) QtCore.QSharedMemory(key).attach() 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): super().__init__(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(QtWidgets.QWidget): def __init__(self): super().__init__() self.edit = QtWidgets.QLineEdit(self) self.edit.setMinimumWidth(300) layout = QtWidgets.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_()) 

PyQt4 :

 # 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) # cleanup (only needed for unix) QtCore.QSharedMemory(key).attach() 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_()) 
+17
source

If someone wants to run @ekhumoro's solution with python3, you need to make minor adjustments to the string operations, I will share my copy where it runs 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_()) 

Examples of cli calls, assuming your script name is "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" 

(and the first parameter is called here, so the message just won’t be sent)

python SingleInstanceApp.py

Hope this helps someone.

+9
source

My solution is this:

 import sys from PyQt5.QtCore import QLockFile from PyQt5.QtWidgets import QApplication from PyQt5.QtWidgets import QMessageBox from window import MainWindow if __name__ == "__main__": try: app_object = QApplication(sys.argv) lock_file = QLockFile("app.lock") if lock_file.tryLock(): window = MainWindow() window.show() app_object.exec() else: error_message = QMessageBox() error_message.setIcon(QMessageBox.Warning) error_message.setWindowTitle("Error") error_message.setText("The application is already running!") error_message.setStandardButtons(QMessageBox.Ok) error_message.exec() finally: lock_file.unlock() 
0
source

All Articles