class QSingleApplication(QApplication): def singleStart(self, mainWindow): self.mainWindow = mainWindow # Socket 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) 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) 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.")) def getNewConnection(self): self.new_socket = self.m_server.nextPendingConnection() self.new_socket.readyRead.connect(self.readSocket) def readSocket(self): f = self.new_socket.readLine() self.mainWindow.getArgsFromOtherInstance(str(f)) self.mainWindow.activateWindow() self.mainWindow.show()
def startApplication(self, first_start=True): self.m_server = QLocalServer() if self.m_server.listen(self.sock_file): if "--testnet" in sys.argv[1:]: print("Starting app... TESTNET MODE") else: print("Starting app...") self.appMain.run() else: if not first_start: print("Error listening the socket. App can't start!", file=sys.stderr) QTimer.singleShot(250, self.quit) return # remove the listener path file and try to restart app one more time print("Error listening the socket. Try to restart application...", file=sys.stderr) if sys.platform != 'win32': try: os.unlink(self.sock_file) except Exception, err: print(err, file=sys.stderr) QTimer.singleShot(250, lambda: self.startApplication(first_start=False))
class QSingleApplication(QApplication): # Signal sent when another instance is launched with arguments argsReceived = QtCore.Signal((str, )) # Start the application, # either as a server (first instance) or as a client # others clients only send the argv they have been given and exit def singleStart(self, mainWindow): print "singleStart() function from QSingleApp got called." self.mainWindow = mainWindow # Socket self.m_socket = QLocalSocket() # Connected, error are signals that are being emitted self.m_socket.connected.connect(self.connectToExistingApp) self.m_socket.error.connect(self.startApplication) self.m_socket.connectToServer(self.applicationName(), QIODevice.WriteOnly) # used for the very first instance to create the main window and start server def startApplication(self): self.m_server = QLocalServer() print "startApplication() function from QSingleApplication got called" # Returns bool (True on success). Tells server to listen for incoming connections on 'name' if self.m_server.listen(self.applicationName()): print "Server is listening .... startApplication() function from QSingleApp" self.m_server.newConnection.connect(self.getNewConnection) # After emitting a signal, connecting it to the UI class function self.argsReceived.connect(self.mainWindow.receive_args) self.mainWindow.show() else: QMessageBox.critical(None, self.tr("Error"), self.tr("Error listening the socket.")) # used by later instances to send argv to the main instance def connectToExistingApp(self): if len(sys.argv) > 1 and sys.argv[1] is not None: self.m_socket.write(sys.argv[1]) print "exiting new app A" self.m_socket.bytesWritten.connect(self.quit) else: print "exiting new app B" self.m_socket.write("--show") self.m_socket.bytesWritten.connect(self.quit) def getNewConnection(self): print "getNewConnection() was called" self.mainWindow.show() self.new_socket = self.m_server.nextPendingConnection() self.new_socket.readyRead.connect(self.readSocket) def readSocket(self): print "readSocket() function in QSingleApplication.py" f = self.new_socket.readLine() self.argsReceived.emit(str(f))
def startApplication(self): self.m_server = QLocalServer() print "startApplication() function from QSingleApplication got called" # Returns bool (True on success). Tells server to listen for incoming connections on 'name' if self.m_server.listen(self.applicationName()): print "Server is listening .... startApplication() function from QSingleApp" self.m_server.newConnection.connect(self.getNewConnection) # After emitting a signal, connecting it to the UI class function self.argsReceived.connect(self.mainWindow.receive_args) self.mainWindow.show() else: QMessageBox.critical(None, self.tr("Error"), self.tr("Error listening the socket."))
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."))
def _create_server(self, try_remove=True): """ Attempt to create a new local server and start listening. """ if not self._server: self._server = QLocalServer(self) self._server.newConnection.connect(self._new_connection) if self._server.isListening(): return True # If desired, remove the old server file. if try_remove: QLocalServer.removeServer(self._app_id) # Now, attempt to listen and return the success of that. return self._server.listen(self._app_id)
class QSingleApplication(QApplication): # Signal sent when another instance is launched with arguments argsReceived = QtCore.Signal((str,)) # Start the application, # either as a server (first instance) or as a client # others clients only send the argv they have been given and exit def singleStart(self, mainWindow): self.mainWindow = mainWindow # Socket 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) # used for the very first instance to create the main window and start server def startApplication(self): self.m_server = QLocalServer() if self.m_server.listen(self.applicationName()): self.m_server.newConnection.connect(self.getNewConnection) self.argsReceived.connect(self.mainWindow.receive_args) self.mainWindow.show() else: QMessageBox.critical(None, self.tr("Error"), self.tr("Error listening the socket.")) # used by later instances to send argv to the main instance 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: self.m_socket.write("--show") self.m_socket.bytesWritten.connect(self.quit) def getNewConnection(self): self.mainWindow.show() self.new_socket = self.m_server.nextPendingConnection() self.new_socket.readyRead.connect(self.readSocket) def readSocket(self): f = self.new_socket.readLine() self.argsReceived.emit(str(f))
class QSingleApplication(QApplication): sock_file = 'ryo_wallet_sock' if sys.platform == 'win32': sock_file = "\\\\.\\pipe\\%s" % sock_file elif sys.platform == 'darwin': sock_file = os.path.join(DATA_DIR, '.%s' % sock_file) else: sock_file = os.path.join(getSockDir(), sock_file) def singleStart(self, appMain): self.appMain = appMain # Socket self.m_socket = QLocalSocket() self.m_socket.connected.connect(self.connectToExistingApp) self.m_socket.error.connect( lambda: self.startApplication(first_start=True)) self.m_socket.connectToServer(self.sock_file, QIODevice.WriteOnly) def connectToExistingApp(self): # Quit application in 250 ms QTimer.singleShot(250, self.quit) print("App is already running.", file=sys.stderr) def startApplication(self, first_start=True): self.m_server = QLocalServer() if self.m_server.listen(self.sock_file): if "--testnet" in sys.argv[1:]: print("Starting app... TESTNET MODE") else: print("Starting app...") self.appMain.run() else: if not first_start: print("Error listening the socket. App can't start!", file=sys.stderr) QTimer.singleShot(250, self.quit) return # remove the listener path file and try to restart app one more time print("Error listening the socket. Try to restart application...", file=sys.stderr) if sys.platform != 'win32': try: os.unlink(self.sock_file) except Exception, err: print(err, file=sys.stderr) QTimer.singleShot(250, lambda: self.startApplication(first_start=False))
def createStatusBar(self): """ Create the status bar. """ statusbar = self.statusBar # line and column number self.lineColNumber = QLabel(statusbar) self.lineColNumber.setText("") statusbar.addPermanentWidget(self.lineColNumber) statusbar.socketServer = QLocalServer() statusbar.socketServer.removeServer(self.infolog.socket) statusbar.socketServer.listen(self.infolog.socket) statusbar.socketServer.newConnection.connect( self.setupStatusConnection) self.log.info('Ready')
class QSingleApplication(QApplication): """ This subclass of :class:`~PySide.QtGui.QApplication` ensures that only a single instance of an application will be run simultaneously, and provides a mechanism for new instances to send commands to the previously existing instance before the new instance closes. When running on Windows, QSingleApplication also listens for the ``WM_DWMCOMPOSITIONCHANGED`` message to allow styles utilizing Aero Glass to properly enable or disable Aero Glass on widgets when composition is enabled or disabled on a system-wide level. The application ID used to verify that the application is not already running can be customized by setting the ``app_id`` and ``session`` variables of the QSingleApplication instance to the desired strings before using :attr:`already_running` or calling :func:`ensure_single` or :func:`send_message`. .. note:: ``app_id`` and ``session`` will undergo additional processing and be converted into a :func:`UUID <uuid.uuid5>` before being utilized. If you *really* wish to use your own string, set ``_app_id``. """ if sys.version_info.major == 3: messageReceived = Signal([dict], [list], [bool], [int], [float]) else: messageReceived = Signal([dict], [list], [bool], [int], [long], [unicode], [float]) compositionChanged = Signal() # Public Variables session = None app_id = None # Lock State Variables _app_id = None _mutex = None _lockfile = None _server = None _already = None def __init__(self, *args, **kwargs): if not args: args = (sys.argv,) super(QSingleApplication, self).__init__(*args, **kwargs) #QApplication.__init__(self, *args, **kwargs) # During shutdown, we can't rely on globals like os being still available. if os.name == "nt": self._close_lock = self._close_mutex else: self._close_lock = self._close_lockfile # If this is Windows, then _aeroglass is imported already, so hook up # to that. # if _aeroglass: # _aeroglass.manager.attach(self) def __del__(self): """ Close the handle of our mutex if we have one, destroy any existing lock file, any gracefully close the QLocalServer. """ self._close_lock() self._close_server() ##### Event Filtering ##################################################### def winEventFilter(self, message): """ Extend the built-in event filtering to handle the WM_DWMCOMPOSITIONCHANGED message on Windows. """ if getattr(message, 'message', None) == WM_DWMCOMPOSITIONCHANGED: self.compositionChanged.emit() return QApplication.winEventFilter(self, message) ##### Lock Code ########################################################### @property def already_running(self): """ Whether or not the application is already running. """ if self._already is None: # Attempt to acquire a lock. self._acquire_lock() return self._already def ensure_single(self, message=None): """ Ensure that this is the only instance of the application running. If a previous instance is detected, send the provided message before raising a :class:`SystemExit` exception. If ``message`` is None, ``sys.argv[:1]`` will be used instead. Note that a false value *other* than None will result in no message being sent. """ if self.already_running: if message is None: message = sys.argv[1:] if message: self.send_message(message) exit() # Still here? Keep being awesome. ##### Private Lock Code ################################################### def _acquire_lock(self): """ Depending on the OS, either create a lockfile or use Mutex to obtain a lock. Also start the socket server. """ if not self._app_id: self._build_app_id() if os.name == 'nt': result = self._create_mutex() else: result = self._create_lockfile() if result: self._create_server() self._already = not result def _build_app_id(self): """ Create a unique application ID if necessary. """ if not self.app_id: path, binary = os.path.split(os.path.abspath(sys.argv[0])) # Build the first part of the app_id. self.app_id = 'qsingleapp-%s-%s' % (binary, path) # if isinstance(self.app_id, unicode): # self.app_id = self.app_id.encode('utf8') # Now, get the session ID. if not self.session: if os.name == 'nt': try: sid = '%X' % getsid(os.getpid()) except OSError: sid = os.getenv('USERNAME') else: sid = os.getenv('USER') if profile.profile_path: sid = '%s-%s' % (profile.profile_path, sid) # if isinstance(sid, unicode): # sid = sid.encode('utf8') self.session = sid self._app_id = str(uuid.uuid5(uuid.NAMESPACE_OID, self.app_id + self.session)) ##### Mutex Code ########################################################## def _close_mutex(self, CloseHandle=windll.kernel32.CloseHandle): """ If we have a mutex, try to close it. """ if self._mutex: CloseHandle(self._mutex) self._mutex = None def _create_mutex(self): """ Attempt to create a new mutex. Returns True if the mutex was acquired successfully, or False if the mutex is already in use. """ mutex_name = c_wchar_p(self._app_id[:MAX_PATH]) handle = windll.kernel32.CreateMutexW(None, c_bool(False), mutex_name) self._mutex = handle return not (not handle or GetLastError() == ERROR_ALREADY_EXISTS) ##### Lockfile Code ####################################################### def _close_lockfile(self, unlink=os.unlink, close=os.close): """ If a lockfile exists, delete it. """ if self._lockfile: unlink(self._lockfile) close(self._lockfd) self._lockfile = None self._lockfd = None def _create_lockfile(self): """ Attempt to create a lockfile in the user's temporary directory. This is one of the few things that doesn't obey the path functions of profile. """ lockfile = os.path.abspath(os.path.join(QDir.tempPath(), u'%s.lock' % self._app_id)) try: fd = os.open(lockfile, os.O_TRUNC | os.O_CREAT | os.O_RDWR) fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) os.write(fd, "%d\n" % os.getpid()) except (OSError, IOError) as e: if e.errno in (errno.EACCES, errno.EAGAIN): return False raise # We've got it. self._lockfd = fd self._lockfile = lockfile return True ##### The LocalServer ##################################################### def _create_server(self, try_remove=True): """ Attempt to create a new local server and start listening. """ if not self._server: self._server = QLocalServer(self) self._server.newConnection.connect(self._new_connection) if self._server.isListening(): return True # If desired, remove the old server file. if try_remove: QLocalServer.removeServer(self._app_id) # Now, attempt to listen and return the success of that. return self._server.listen(self._app_id) def _close_server(self): """ Close the local server. """ if self._server: try: if self._server.isListening(): self._server.close() except RuntimeError: pass self._server = None def _read_length(self, sock): if sock.bytesAvailable() < 4: return # Read the length. length = struct.unpack("!I", sock.read(4).data())[0] # If we don't have a length, just end now. if not length: sock.close() return # Set the next reader. if sock.bytesAvailable() == length: self._read_message(sock, length) else: sock.readyRead.disconnect() sock.readyRead.connect(partial(self._read_message, sock, length)) def _read_message(self, sock, length): if sock.bytesAvailable() < length: return #TODO: enter switch message = sock.readAll().data().decode() message = json.loads(message) sock.close() self.messageReceived.emit(message) def _new_connection(self): """ Accept a connection and read the message from it. """ sock = self._server.nextPendingConnection() if not sock: return sock.readyRead.connect(partial(self._read_length, sock)) def send_message(self, message, callback=None): """ Attempt to send a message to the previously running instance of the application. Returns True if the message is sent successfully, or False otherwise. Alternatively, if a callback is provided the function will return immediately and the boolean will be sent to the callback instead. """ message = json.dumps(message) if sys.version_info.major == 3: packedLen = struct.pack("!I", len(message)).decode() else: packedLen = struct.pack("!I", len(message)) message = packedLen + message # Create a socket. sock = QLocalSocket(self) # Build our helper functions. def error(err): """ Return False to the callback. """ callback(False) def connected(): """ Send our message. """ sock.writeData(message, len(message)) def bytesWritten(bytes): """ If we've written everything, close and return True. """ if not sock.bytesToWrite(): sock.close() callback(True) if callback: sock.error.connect(error) sock.connect.connect(connected) sock.bytesWritten.connect(bytesWritten) # Now connect. sock.connectToServer(self._app_id) if not callback: # Do things synchronously. connected = sock.waitForConnected(5000) if not connected: return False # Write it. sock.writeData(message, len(message)) # Wait until we've written everything. while sock.bytesToWrite(): success = sock.waitForBytesWritten(5000) if not success: sock.close() return False sock.close() return True
def _createServer(self): from PySide.QtNetwork import QLocalServer ret = QLocalServer(self.q) ret.newConnection.connect(self._onNewConnection) return ret