Languages/Python/PyKDE Knotify Tutorial

    From KDE TechBase
    Revision as of 23:50, 9 August 2010 by Hefee (talk | contribs) (→‎Job class: indent error)


    Development/Languages/Python/PyKDE_Knotify_Tutorial


    Python KNotify Tutorial
    Tutorial Series   Python
    Previous   None
    What's Next  
    Further Reading   Qt Signals and Slots in python, Introduction to PyQT4, Using PyKDE4

    Abstract

    The aim of this tutorial is to give a sample class to include Notifications and Jobs to your own python programms, so the user can see what is going on (for example a backup system, that tells you that is backuping right now). It assumes a basic working knowledge of Python and PyKDE4. For now only the Job part is mentioned, 'cause that is the only part I've created right now.

    Further plans

    To add the Notification part also to this tutorial.

    Instruction

    We start with a minimal KDE Appliciation (Using PyKDE4):

    1. ! /usr/bin/python
    2. -*- coding: utf-8 -*-

    if __name__ == "__main__":

       import sys
       
       from PyKDE4.kdecore import KCmdLineArgs, KAboutData, KLocalizedString, ki18n
       from PyKDE4.kdeui import KApplication    
       
       appName     = "default"
       catalog     = ""
       programName = ki18n ("default")  
       version     = "1.0"
       description = ki18n ("Default Example")  
       license     = KAboutData.License_GPL
       copyright   = ki18n ("(c) 2010 Sandro Knauß") 
       text        = ki18n ("none") 
       homePage    = "techbase.kde.org"
       bugEmail    = "[email protected]"
    
       aboutData   = KAboutData (appName, catalog, programName, version, description,
                                 license, copyright, text, homePage, bugEmail)
    


       KCmdLineArgs.init (sys.argv, aboutData)
       
       app = KApplication ()
    

    Job class

    The first step is to have a look to the [1]. The intersting on is:

    So it is easy to write a small Job-Class:

    from PyQt4.QtCore import QObject,QTimer,QString from PyKDE4.kdecore import KJob

    class MyJob(KJob):

       def __init__(self,parent=QObject()):
           KJob.__init__(self,parent)
           #We want to have a Suspenable and Killable Job
           self.setCapabilities(KJob.Capabilities(KJob.Suspendable|KJob.Killable))
    
           #Just a maximum Variable
           self.max=25
    
           #index
           self.i=0
    
       def start(self):
           #initalizing for work
           self.i=0
          
           #start the actual work in another thread
           #this function has to terminate, before the work is done
           QTimer().singleShot(0, self.doWork)
                                                                                                                                                  
       def doWork(self):             
           #the actual work                                         
           try:            
               #if we are killed or suspended just return                                           
               if self.error() or self.isSuspended():                    
                   return
           except RuntimeError:
               #if this class is killed before a RuntimeError will raise
               return
    
           #do a peace of hard work
           self.i+=1
    
           #fortunately we have made a peace of work 
           #-> show this to everybody
           KJob.setPercent(self,self.i*4)
          
           if self.i==self.max:
               #jeah we have done the bunch of work
               #send the result signal for showing, that we ended the work
               self.emitResult()
               return 
           #just go to sleep for 1000ms than go on
           QTimer().singleShot(1000, self.doWork)
       
       def doSuspend(self):
           #the surounding function from KJob makes the isSuspended() become True
           #returns True for signaling that this class supports to suspend
           return True
    
       def doResume(self):
           #start with work again
           QTimer().singleShot( 0, self.doWork )
           #return True for signaling that this class supports resuming
           return True
    
       def doKill(self):
           #return True for signaling that we support killing
           return True
    

    What is going on?

    • MyJob.__init__: First we create the class and initialize the counter i and the maximum max. To make is visible for others we set the Capacities, so this job is suspendable and killable.
    • MyJob.start: Here the pre working setting are made, we will se later for what we use this. After initalizing the Work we start the actual work by using a QTimer. The start method should start the asyncron.
    • MyJob.doWork: Do actual work and handle, if this job is killed or suspended.
    • MyJob.doSuspend/MyJob.doResume/MyJob.doKill: These function indicate that this feature is supported by this class.

    How to use?

    Just create a instance of this class and start it: job=MyJob(app) job.start()

    We want to know when the jobs done?

    Create a SLOT to the result-signal: def handleResult(job):

       #handleResult Function
       #it is called when the job is terminating
       if job.error():
          print 'error happend'
       else:
          print 'job has terminated successfully'
    

    job.result.connect(handleResult)

    Visual feedback

    Till now, a normal user doesn't see anything what is going on. To change it we have to register the Job to a JobTracker. Now a user will see a progressbar and a suspend and a stop button. So we modify the start method:

    from PyKDE4.kio import KIO ...

       def start(self):
           #register the Job to the JobTracker
           KIO.getJobTracker().registerJob(self)
           self.i=0
           QTimer().singleShot(0, self.doWork)
    

    Conclusion

    Here is the full code of this example.

    Is is very easy to create a Job and make it visible to normal users. Unfortunately I have problems with setting the heading and description, so this will be added in later versions.