6

I know the question is a bit vague. I'll try to explain better below:

My app (python/gtk) is mostly an indicator. Using this indicator you can choose to show/hide the main window.

When I try to open a new instance of the app I made it so it would check if the app is already running. If so, it would abort trying to run the app.

Now I want to adjust it, so before aborting I want it to bring the already active app's main window to the foreground. Even if this window isn't opened at the moment.

So I believe my question is: How can I get a (global?) variable/instance of my already active app from my new app? (so I can bring my main window to the foreground)

EDIT:

I just found this method in the api: GtkWindow - set_startup_id().

This says: Normally, startup identifier is managed automatically and you should only use this function in special cases like transferring focus from other processes.

So this means it should be possible to bring focus to a window from another process. But how would I get this id? And how would I use this id to bring it to the foreground?

3 Answers 3

1

Lets start by saying that there are lots and lots of ways. You normally setup a token/identifier when the program starts, so later instances can look for the existence of that token.

I will describe one way which uses dbus.

Overview:

When starting a program, it can register itself on the session dbus under a unique name (e.g. "org.nicklemaire.myprogram"). Further instances of the program can check if such an accesspoint is already registered, and if so, tell the program what to do via this dbus access point (e.g. get focus, open a website, play a song). The last part is probably necessary when you want behaviour similar to "firefox askubuntu.com", which opens this page in a new tab in an already running instance.

Code:

#!/usr/bin/env python                                                                                                                           
import sys                                                                                                                                      
import gtk                                                                                                                                      
import dbus                                                                                                                                     
import dbus.service                                                                                                                             
from dbus.mainloop.glib import DBusGMainLoop                                                                                                    
from multiprocessing import Process                                                                                                             

class MyDBUSService(dbus.service.Object):                                                                                                       
    def __init__(self):                                                                                                                         
        bus_name = dbus.service.BusName('org.nicklemaire.myprogram', bus=dbus.SessionBus())                                                     
        dbus.service.Object.__init__(self, bus_name, '/org/nicklemaire/myprogram')                                                              

    @dbus.service.method('org.nicklemaire.myprogram', in_signature='s')                                                                         
    def startup(self, arg):                                                                                                                     
        print "got the following parameter from another instance:", arg                                                                         

def call_instance():                                                                                                                            
    try:                                                                                                                                        
        bus = dbus.SessionBus()                                                                                                                 
        programinstance = bus.get_object('org.nicklemaire.myprogram',  '/org/nicklemaire/myprogram')                                            
        bus = dbus.SessionBus()                                                                                                                 
        programinstance = bus.get_object('org.nicklemaire.myprogram',  '/org/nicklemaire/myprogram')                                            
        startup = programinstance.get_dbus_method('startup', 'org.nicklemaire.myprogram')                                                       
        try:                                                                                                                                    
            arg = sys.argv[1]                                                                                                                   
        except IndexError:                                                                                                                      
            arg = ""                                                                                                                            
        startup(arg)                                                                                                                            
        print "Another instance was running and notified."                                                                                      
    except dbus.exceptions.DBusException:                                                                                                       
        exit(-1) # process had an error                                                                                                         

if __name__ == "__main__":                                                                                                                      
    p = Process(target=call_instance)                                                                                                           
    p.start() 
    p.join()                                                                                                                                    
    if p.exitcode > 0: # process had an error                                                                                                   
        DBusGMainLoop(set_as_default=True)                                                                                                      
        myservice = MyDBUSService()                                                                                                             
        gtk.main()

Test:

Open a terminal and run the program: myprogram.py . It will not terminate because we currently want to have it running and waiting for a second instance to start.

Now do this: open another terminal and run the program again, this time with an additional argument myprogram.py askubuntu.com. It should print: "Another instance was running and notified." While in the first terminal, you should get an output similar to this: "got the following parameter from another instance: askubuntu.com"

The other part of your question: raising a program is described here: https://stackoverflow.com/questions/9054462/how-do-i-raise-a-window-that-is-minimized-or-covered-with-pygobject

Basically, you have to call mywindow.present() in the startup method.

1

Thanks to xubuntix's great answer, I've made a module which makes it simple:

"""
Allow an application to activate a running instance of itself instead of
starting another instance.
"""

import sys

import gtk
import dbus.service
from dbus.mainloop.glib import DBusGMainLoop

def _get_path(app_id):
    return '/' + app_id.replace('.', '/')

def listen_for_activation(app_id, window):
    """
    Listen for 'activate' events. If one is sent, activate 'window'.
    """
    class MyDBUSService(dbus.service.Object):                                                                                                       
        def __init__(self, window):
            self.window = window

            bus_name = dbus.service.BusName(app_id, bus=dbus.SessionBus())                                                     
            dbus.service.Object.__init__(self, bus_name, _get_path(app_id))

        @dbus.service.method(app_id)                                                                         
        def activate(self):
            print "The process was activated by another instance."
            self.window.present()

    DBusGMainLoop(set_as_default=True)
    _myservice = MyDBUSService(window)

def activate_if_already_running(app_id):
    """
    Activate the existing window if it's already running. Return True if found
    an existing window, and False otherwise.
    """
    bus = dbus.SessionBus()
    try:
        programinstance = bus.get_object(app_id, _get_path(app_id))
        activate = programinstance.get_dbus_method('activate', app_id)
    except dbus.exceptions.DBusException:
        return False
    else:
        print "A running process was found. Activating it."
        activate()
        return True
    finally:
        bus.close()

def test():
    APP_ID = 'com.example.myapp'

    activated = activate_if_already_running(APP_ID)
    if activated:
        sys.exit(0)

    w = gtk.Window()
    b = gtk.Button("Hello!")
    b.set_size_request(200, 200)
    w.add(b)
    w.connect('delete-event', gtk.main_quit)
    w.show_all()

    listen_for_activation(APP_ID, w)

    gtk.main()

if __name__ == '__main__':
    test()

Please refer the following gist for any further revisions in the code above:

0
0

Not sure if this is the ID your looking for:

Open System Monitor, either by going to the Applications, pressing CTRL+ALT+DEL or by typing in terminal gnome-system-monitor.

Go to View tab, on the top bar. Select All Processes and Dependencies. Go to Edit tab, on the top bar, and open Preferences.

On the Processes tab, under Information Fields, select ID.

Then try to find you program in the list. Good luck!

2
  • I think what he means is how you can make an application so that trying to start a second instance just switches to the first instance. E.g. if something tries to open Firefox, it goes to a new tab in an existing window, rather than creating a new window.
    – Thomas K
    Jul 16, 2012 at 21:49
  • I don't need the process ID. I'm trying to get the functionality that Thomas describes. I'm thinking I'll be needing sockets for this to work. But I've got no experience with it yet. But thanks for commenting :) Jul 17, 2012 at 11:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .