3

I want only one instance of my app to be running at each time. but when the user attempts to open it the second time, I want the first window to be brought to the front (it could be just minimized or minimized to the corner of the taskbar and the user doesn't know how to open it)

I have this code that does the detection job and it doesn't allow the second instance. I have trouble with the part that it has to open the original window. I have commented out some of my attempt.

import sys
from PyQt4 import QtGui, QtCore 
import sys

class SingleApplication(QtGui.QApplication):
    def __init__(self, argv, key):
        QtGui.QApplication.__init__(self, argv)
        self._activationWindow=None
        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().toLocal8Bit().data())
    def isRunning(self):
        return self._running

    def activationWindow(self):
        return self._activationWindow

    def setActivationWindow(self, activationWindow):
        self._activationWindow = activationWindow

    def activateWindow(self):
        if not self._activationWindow:
            return
        self._activationWindow.setWindowState(
            self._activationWindow.windowState() & ~QtCore.Qt.WindowMinimized)
        self._activationWindow.raise_()
        self._activationWindow.activateWindow()

class Window(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.label = QtGui.QLabel(self)
        self.label.setText("Hello")
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.label)

if __name__ == '__main__':

    key = 'random _ text'

    app = SingleApplication(sys.argv, key)
    if app.isRunning():
        #app.activateWindow()
        sys.exit(1)

    window = Window()  
    #app.setActivationWindow(window)
    #print app.topLevelWidgets()[0].winId()
    window.show()

    sys.exit(app.exec_())
Kiarash
  • 7,378
  • 10
  • 44
  • 69

2 Answers2

1

I've made this work on Windows using the win32 api (I'm not entirely sure, but there are probably equivalent calls on macos/unix).

Add the following import to your application,

import win32gui

set the window title to a fixed name (instead of doing this, you could store its whndl in the shared memory)

window = Window()  
window.setWindowTitle('Single Application Example')
window.show()

and then change your activateWindow method to something like the following:

def activateWindow(self):
    # needs to look for a named window ...
    whndl = win32gui.FindWindowEx(0, 0, None, "Single Application Example")

    if whndl is 0:
        return #couldn't find the name window ...

    #this requests the window to come to the foreground 
    win32gui.SetForegroundWindow(whndl) 
goran
  • 411
  • 4
  • 6
  • I believe there should be pyqt solution for this which would work in between different operating systems. – Kiarash Aug 20 '13 at 03:04
  • A pure Qt solution exists for this as http://qt.gitorious.org/qt-solutions/qt-solutions/trees/master/qtsingleapplication. Instead of using win32 api calls, they create a QLocalServer. This class makes it possible to accept incoming local socket connections. Essentially, they listen for incoming connections on this socket, do some verification and then accept messages such as "bring to front". It looks like this has been ported at http://stackoverflow.com/questions/8786136/pyqt-how-to-detect-and-close-ui-if-its-already-running – goran Aug 20 '13 at 12:27
  • but in this case I don't want to really communicate. I just want a qt version of your idea. using qt to get the wid and opening it again. I made try but I can't make it work. – Kiarash Aug 20 '13 at 14:58
0

You might be interested by the solutions proposed here

For instance, I would try:

app = SingleApplication(sys.argv, key)
if app.isRunning():
    window = app.activationWindow()
    window.showNormal()
    window.raise()
    app.activateWindow()
    sys.exit(1)

window = Window()  
app.setActivationWindow(window)
window.setWindowFlags(Popup)
window.show()
Boris Dalstein
  • 7,015
  • 4
  • 30
  • 59
  • @TatakaiWasumi Thanks for the info. I'm only using Qt with C++, so I don't actually know. I'm leaving this here as it may still be useful to someone, feel free to edit if you find a fix. – Boris Dalstein Jan 19 '17 at 21:09