Languages/Python/PyKDE Knotify Tutorial: Difference between revisions

    From KDE TechBase
    (i18n tamplate)
    (9 intermediate revisions by 4 users not shown)
    Line 1: Line 1:
    {{Template:I18n/Language Navigation Bar|Development/Languages/Python/PyKDE_Knotify_Tutorial}}
     


    {{TutorialBrowser|
    {{TutorialBrowser|
    Line 24: Line 24:
    We start with a minimal KDE Appliciation ([http://techbase.kde.org/Development/Languages/Python/Using_PyKDE_4 Using PyKDE4]):
    We start with a minimal KDE Appliciation ([http://techbase.kde.org/Development/Languages/Python/Using_PyKDE_4 Using PyKDE4]):


    <code python>
    <syntaxhighlight lang="python">
    #! /usr/bin/python
    #! /usr/bin/python
    # -*- coding: utf-8 -*-
    # -*- coding: utf-8 -*-
    Line 52: Line 52:
          
          
         app = KApplication ()
         app = KApplication ()
    </code>
    </syntaxhighlight>


    == Job class ==
    == Job class ==
    The first step is to have a look to the [http://api.kde.org]. The intersting on is:
    The first step is to have a look to the [http://api.kde.org]. The intersting on is:
    * [http://api.kde.org/pykde-4.1-api/kdecore/KJob.html KJob(KDE 4.1-PyKDE4)]- unfortunately this documention is only available for KDE 4.1. So we have also a look to the actual C++ Doumentation
    * [http://api.kde.org/pykde-4.3-api/kdecore/KJob.html KJob(KDE 4.3-PyKDE4)]- unfortunately this documention is only available for KDE 4.3. So we have also a look to the actual C++ Doumentation
    * [http://api.kde.org/4.x-api/kdelibs-apidocs/kdecore/html/classKJob.html KJob(KJob 4.x-C++API)]
    * [http://api.kde.org/4.x-api/kdelibs-apidocs/kdecore/html/classKJob.html KJob(KJob 4.x-C++API)]


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


    <code python>
    <syntaxhighlight lang="python">
    from PyQt4.QtCore import QObject,QTimer,QString
    from PyQt4.QtCore import QObject,QTimer,QString
    from PyKDE4.kdecore import KJob
    from PyKDE4.kdecore import KJob
    Line 81: Line 81:
             self.i=0
             self.i=0
            
            
             #start the actual work in another thread - this function has to terminate, before the work is done
             #start the actual work in another thread
             QTimer().singleShot( 0, self.doWork )
            #this function has to terminate, before the work is done
             QTimer().singleShot(0, self.doWork)
                                                                                                                                                    
                                                                                                                                                    
         def doWork(self):             
         def doWork(self):             
          #the actual work                                         
            #the actual work                                         
          try:             
            try:             
                 #if we are killed or suspended just return                                           
                 #if we are killed or suspended just return                                           
                 if self.error() or self.isSuspended():                     
                 if self.error() or self.isSuspended():                     
                     return
                     return
             except RuntimeError:
             except RuntimeError:
                 #if this class is killed before this functions is called a RuntimeError will raise
                 #if this class is killed before a RuntimeError will raise
                 return
                 return


    Line 97: Line 98:
             self.i+=1
             self.i+=1


             #fortunately we have made a peace of work -> show this to everybody
             #fortunately we have made a peace of work  
            #-> show this to everybody
             KJob.setPercent(self,self.i*4)
             KJob.setPercent(self,self.i*4)
            
            
    Line 106: Line 108:
                 return  
                 return  
             #just go to sleep for 1000ms than go on
             #just go to sleep for 1000ms than go on
             QTimer().singleShot( 1000, self.doWork )
             QTimer().singleShot(1000, self.doWork)
          
          
         def doSuspend(self):
         def doSuspend(self):
    Line 122: Line 124:
             #return True for signaling that we support killing
             #return True for signaling that we support killing
             return True
             return True
    </code>
    </syntaxhighlight>
     
    ===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:
    <syntaxhighlight lang="python">
    job=MyJob(app)
    job.start()
    </syntaxhighlight>
     
    ===We want to know when the jobs done?===
    Create a '''SLOT''' to the '''result'''-signal:
    <syntaxhighlight lang="python">
    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'
       
        #for terimation the complete application
        sys.exit()
     
    job.result.connect(handleResult)
    </syntaxhighlight>
     
    Now we also add '''app.exec_()''' at the end of the main part, to prevent the program to stop before the job is stopped.
     
    ===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:
     
    <syntaxhighlight lang="python">
    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)
    </syntaxhighlight>
     
    === Setting the job title and description ===
    Now you have the running job and a user can see nice progressbar. However, there is no visible information what job is currently running and what it is doing. To change it we have to emit signal '''KJob.description''' in our method '''MyJob.doWork''':
     
     
    <syntaxhighlight lang="python">
        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)
            self.description.emit(self, "Our Splendid Job", ("Source","counter"), ("Destination","dummy"))
         
            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)
    </syntaxhighlight>
     
    The parameters of this signal are the sending job (anything else than '''self''' doesn't make sense here), job title, and two tuples consising of two strings arbitrary that can be used to denote some job details. (consider them as a key-value pair).
     
    === Conclusion ===
    Here is the [[Development/Languages/Python/PyKDE_Knotify_Tutorial/MyJob.py|full code]] of this example.
     
    Is is very easy to create a Job and make it visible to normal users.

    Revision as of 17:39, 10 March 2016


    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):

    #! /usr/bin/python
    # -*- 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'
        
        #for terimation the complete application
        sys.exit()
    
    job.result.connect(handleResult)
    

    Now we also add app.exec_() at the end of the main part, to prevent the program to stop before the job is stopped.

    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)
    

    Setting the job title and description

    Now you have the running job and a user can see nice progressbar. However, there is no visible information what job is currently running and what it is doing. To change it we have to emit signal KJob.description in our method MyJob.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)
            self.description.emit(self, "Our Splendid Job", ("Source","counter"), ("Destination","dummy"))
           
            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)
    

    The parameters of this signal are the sending job (anything else than self doesn't make sense here), job title, and two tuples consising of two strings arbitrary that can be used to denote some job details. (consider them as a key-value pair).

    Conclusion

    Here is the full code of this example.

    Is is very easy to create a Job and make it visible to normal users.