PyGTK: Allow only one instance of your application
In the post “Python: Allow only one instance of your application” I presented a method that works great for non GUI applications. It work’s fine for GUI applications too but if the application is already running the second instance will just quit. What if the application instance’s window is hidden by other window ?
In this case the best behavior is: if the GUI application is already running, the second application raise it and then quit. This provides a better user experience.
I read about gtk.gdk.Window.raise_() method. I don’t know why but it’s working (at least in my case) !!! The application’s window remains behind other windows.
The only working functions that I found, was:
- gtk.Window.present_with_time(): works all the time but it does not activate the window
- gtk.Window.present: it does not works all the time (ex: it does not works if the application shows a modal dialog on the main window), but when it does it activate the window too
The best way is to call them both. In the worst case the window is raised but is not activate.
The problem with this functions is that it must be called in the application instance, because I didn’t find a way to create a gtk.Window for an external xid (only gtk.gdk.Window). So when the second application starts it must inform the “only” application instance to raise it’s window.
There are different methods to comunicate between applications and I will not discuss them here. A simple and “lazy” solution is to create a file. The application instance will check from time to time if the file exists. If the file exists raise it’s window. We can can write some information in this file so that the application instance can use it.
This is my “Hello World !” application instance: gtkapplicationinstance.py. This application checks every second if it needs to raise itself.
import os import os.path import time import pygtk pygtk.require('2.0') import gtk import gobject #class used to handle one application instance mechanism class GTKApplicationInstance: #specify the base for control files def __init__( self, base_control_file, raise_cmd = '' ): self.pid_file = base_control_file + '.pid' self.raise_file = base_control_file + '.raise' #remove raise_file is already exists try: os.remove( self.raise_file ) except: pass self.check( raise_cmd ) self.startApplication() #check if the current application is already running def check( self, raise_cmd ): #check if the pidfile exists if not os.path.isfile( self.pid_file ): return #read the pid from the file pid = 0 try: file = open( self.pid_file, 'rt' ) data = file.read() file.close() pid = int( data ) except: pass #check if the process with specified by pid exists if 0 == pid: return try: os.kill( pid, 0 ) #this will raise an exception if the pid is not valid except: return #exit the application print "The application is already running !" #notify raise try: file = open( self.raise_file, 'wt' ) file.write( raise_cmd ) file.close() except: pass exit(0) #exit raise an exception so don't put it in a try/except block #called when the single instance starts to save it's pid def startApplication( self ): file = open( self.pid_file, 'wt' ) file.write( str( os.getpid() ) ) file.close() #called when the single instance exit ( remove pid file ) def exitApplication( self ): try: os.remove( self.pid_file ) except: pass #check if the application must to be raised #return None if no raise needed, or a string command to raise def raiseCommand( self ): retVal = None try: if os.path.isfile( self.raise_file ): file = open( self.raise_file, 'rt' ) retVal = file.read() file.close() os.remove( self.raise_file ) except: pass return retVal #Hello World simple window class HelloWorld: def __init__( self, appInstance ): self.appInstance = appInstance # create Hello World window self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window.connect("destroy", gtk.main_quit) self.window.set_border_width(100) self.label = gtk.Label("Hello World !") self.window.add(self.label) self.label.show() self.window.show() #check every second if the application needs to raise gobject.timeout_add( 1000, self.checkRaiseApplication ) def checkRaiseApplication( self ): raiseCmd = self.appInstance.raiseCommand() if raiseCmd is None: #no raise needed return True #raise the window self.window.present_with_time( int(time.time()) ) self.window.present() return True if __name__ == '__main__': #create application instance appInstance = GTKApplicationInstance( '/tmp/mygtkapp' ) #Hello World application print "Start MyGTKApp" helloWord = HelloWorld( appInstance ) gtk.main() print "End MyGTKApp" #remove pid file appInstance.exitApplication()

November 4th, 2008 at 1:05 pm
[...] « PyGTK: Allow only one instance of your application [...]