From e4edeaceb5496c1f947fc8be1195a95dc169bed6 Mon Sep 17 00:00:00 2001 From: "Denilson M. Amorim" Date: Sat, 14 Nov 2015 20:12:19 -0300 Subject: [PATCH 1/2] Squash: Single instance and pop up old instance --- src/bitmessagemain.py | 2 +- src/bitmessageqt/__init__.py | 66 +++++++++++++++++++++++++++++++++++- src/singleton.py | 16 +++++---- 3 files changed, 76 insertions(+), 8 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index bf1d74f2..78e1e903 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -143,7 +143,7 @@ class Main: shared.daemon = daemon # is the application already running? If yes then exit. - thisapp = singleton.singleinstance() + thisapp = singleton.singleinstance("", daemon) # get curses flag curses = False diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 1819628b..ba2015f4 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -41,6 +41,7 @@ try: from PyQt4 import QtCore, QtGui from PyQt4.QtCore import * from PyQt4.QtGui import * + from PyQt4.QtNetwork import QLocalSocket, QLocalServer except Exception as err: print 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download it from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\' (without quotes).' @@ -52,6 +53,7 @@ try: except AttributeError: print 'QtGui.QApplication.UnicodeUTF8 error:', err + def _translate(context, text): return QtGui.QApplication.translate(context, text) @@ -3901,8 +3903,70 @@ class UISignaler(QThread): sys.stderr.write( 'Command sent to UISignaler not recognized: %s\n' % command) + +app = None +myapp = None + +class MySingleApplication(QApplication): + """ + Listener to allow our Qt form to get focus when another instance of the + application is open. + + Based off this nice reimplmentation of MySingleApplication: + http://stackoverflow.com/a/12712362/2679626 + """ + + # Unique identifier for this application + uuid = '6ec0149b-96e1-4be1-93ab-1465fb3ebf7c' + + def __init__(self, *argv): + super(MySingleApplication, self).__init__(*argv) + id = MySingleApplication.uuid + + self.server = None + self.is_running = False + + socket = QLocalSocket() + socket.connectToServer(id) + self.is_running = socket.waitForConnected() + + # Cleanup past crashed servers + if not self.is_running: + if socket.error() == QLocalSocket.ConnectionRefusedError: + socket.disconnectFromServer() + QLocalServer.removeServer(id) + + socket.abort() + + # Checks if there's an instance of the local server id running + if self.is_running: + # This should be ignored, singleton.py will take care of exiting me. + pass + else: + # Nope, create a local server with this id and assign on_new_connection + # for whenever a second instance tries to run focus the application. + self.server = QLocalServer() + self.server.listen(id) + self.server.newConnection.connect(self.on_new_connection) + + def __del__(self): + if self.server: + self.server.close() + + def on_new_connection(self): + global myapp + if myapp: + myapp.appIndicatorShow() + +def init(): + global app + if not app: + app = MySingleApplication(sys.argv) + return app + def run(): - app = QtGui.QApplication(sys.argv) + global myapp + app = init() change_translation(l10n.getTranslationLanguage()) app.setStyleSheet("QStatusBar::item { border: 0px solid black }") myapp = MyForm() diff --git a/src/singleton.py b/src/singleton.py index ee5c3077..f3124424 100644 --- a/src/singleton.py +++ b/src/singleton.py @@ -3,22 +3,26 @@ import sys import os import errno -import tempfile +import shared from multiprocessing import Process - class singleinstance: """ - Implements a single instance application by creating a lock file based on the full path to the script file. + Implements a single instance application by creating a lock file at appdata. This is based upon the singleton class from tendo https://github.com/pycontribs/tendo which is under the Python Software Foundation License version 2 """ - def __init__(self, flavor_id=""): + def __init__(self, flavor_id="", daemon=False): import sys self.initialized = False - basename = os.path.splitext(os.path.abspath(sys.argv[0]))[0].replace("/", "-").replace(":", "").replace("\\", "-") + '-%s' % flavor_id + '.lock' - self.lockfile = os.path.normpath(tempfile.gettempdir() + '/' + basename) + self.daemon = daemon; + self.lockfile = os.path.normpath(os.path.join(shared.appdata, 'singleton%s.lock' % flavor_id)) + + if not self.daemon: + # Tells the already running (if any) application to get focus. + import bitmessageqt + bitmessageqt.init() if sys.platform == 'win32': try: From 2add3f6bc865bddaa2f75dfb99819337c6b73b7d Mon Sep 17 00:00:00 2001 From: "Denilson M. Amorim" Date: Sun, 15 Nov 2015 11:08:48 -0300 Subject: [PATCH 2/2] Dont run twice if daemon too --- src/bitmessagemain.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 78e1e903..4d68eff1 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -44,7 +44,9 @@ from debug import logger # Helper Functions import helper_bootstrap import helper_generic - + +# singleton lock instance +thisapp = None def connectToStream(streamNumber): shared.streamsInWhichIAmParticipating[streamNumber] = 'no data' @@ -139,6 +141,8 @@ if shared.useVeryEasyProofOfWorkForTesting: class Main: def start(self, daemon=False): + global thisapp + _fixWinsock() shared.daemon = daemon