pygtk

PyGTK, Multithreading and progress bar

Multithreading in Python seems to be very simple. Let’s look at the example:

import threading
import time

global_var = 0

class ThreadClass(threading.Thread):
	def run(self):
		global global_var
		while True:
			print global_var

class ThreadClass2(threading.Thread):
	def run(self):
		global global_var
		while True:
			time.sleep(1)
			global_var += 1

t1 = ThreadClass()
t1.start()

t2 = ThreadClass2()
t2.start()

t1.join()
t2.join()

Thread 1 is printing the global_var and Thread 2 is incrementing it by 1, every second.

Recently I needed to use threads in PyGTK to display progress bar. Off course the state of progress bar was dependent on the other thread. It had to be updated after each subtask call. This is the code:

import threading
import time
import gtk

def init_progressbar():
    main_box = gtk.VBox()
    progressbar = gtk.ProgressBar()    
    progressbar_box = gtk.HBox(False, 20)
    main_box.pack_start(progressbar_box, False, False, 20)
    progressbar_box.pack_start(progressbar)    
    info_box = gtk.VBox()
    main_box.pack_start(info_box, False, False, 10)    
    info_label = gtk.Label("Running...")
    info_box.pack_start(info_label)
    return main_box, progressbar, info_label


def run_tasks(pb, info_label):
    task_list = ['task1', 'task2', 'task3', 'task4']
    task_no = 0
    for task in task_list:
        pb.set_fraction(float(task_no)/len(task_list))
        run_task(2, task, info_label)        
        task_no += 1
    pb.set_fraction(float(task_no)/len(task_list))
    info_label.set_text('Finished')


def run_task(delay, task, info_label):
    info_label.set_text(task + ' is running...')
    time.sleep(delay)   # simulation of running the task


win_pb, pb, info_label = init_progressbar()
win = gtk.Window()
win.set_default_size(400,100)
win.add(win_pb)
win.show_all()
win.connect("destroy", lambda _: gtk.main_quit())
t = threading.Thread(target=run_tasks, args=(pb,info_label))
t.start()
gtk.main()

And…it was not working. The progress bar (and the label) did not get updated after task calls. Why?

It was caused by the fact, that PyGTK is not thread safe. Fortunately we have function threads_init() in gobject module, which can handle it. We just need to call gobject.threads_init() before first thread creation. Corrected (working) code looks like that:

import threading
import time
import gobject
import gtk

def init_progressbar():
    main_box = gtk.VBox()
    progressbar = gtk.ProgressBar()    
    progressbar_box = gtk.HBox(False, 20)
    main_box.pack_start(progressbar_box, False, False, 20)
    progressbar_box.pack_start(progressbar)    
    info_box = gtk.VBox()
    main_box.pack_start(info_box, False, False, 10)    
    info_label = gtk.Label("Running...")
    info_box.pack_start(info_label)
    return main_box, progressbar, info_label


def run_tasks(pb, info_label):
    task_list = ['task1', 'task2', 'task3', 'task4']
    task_no = 0
    gobject.threads_init()
    for task in task_list:
        pb.set_fraction(float(task_no)/len(task_list))
        run_task(2, task, info_label)        
        task_no += 1
    pb.set_fraction(float(task_no)/len(task_list))
    info_label.set_text('Finished')


def run_task(delay, task, info_label):
    info_label.set_text(task + ' is running...')
    time.sleep(delay)   # simulation of running the task


win_pb, pb, info_label = init_progressbar()
win = gtk.Window()
win.set_default_size(400,100)
win.add(win_pb)
win.show_all()
win.connect("destroy", lambda _: gtk.main_quit())
t = threading.Thread(target=run_tasks, args=(pb,info_label))
t.start()
gtk.main()

I wanted also to be able to cancel program computation, by the cancel button. To do that I needed shared variable to be a status flag informing, whether computation should be cancelled or continued. It had to be accessible in task loop and cancel event.

In Python there are mutable and immutable objects. Mutable are passed by reference, but immutable – by value. More about that on this StackOverflow question. Boolean type is immutable, but there is a way around this: use list (mutable type) with one element (boolean variable). I know it is awful solution, but I did not find any better. If you know one, let me know!

This is the app with cancel button:

import threading
import time
import gobject
import gtk

def init_progressbar():
    main_box = gtk.VBox()
    progressbar = gtk.ProgressBar()    
    progressbar_box = gtk.HBox(False, 20)
    main_box.pack_start(progressbar_box, False, False, 20)
    progressbar_box.pack_start(progressbar)    
    info_box = gtk.VBox()
    main_box.pack_start(info_box, False, False, 10)    
    info_label = gtk.Label("Running...")
    info_box.pack_start(info_label)
    cancel_box = gtk.HBox()
    info_box.pack_start(cancel_box)
    cancel_button = gtk.Button("Cancel")
    cancel = [False]
    cancel_button.connect("clicked", cancel_counting, info_label, cancel)
    cancel_box.pack_start(cancel_button, False, False, 20)
    return main_box, progressbar, info_label, cancel


def run_tasks(pb, info_label, cancel):
    task_list = ['task1', 'task2', 'task3', 'task4']
    task_no = 0
    gobject.threads_init()
    for task in task_list:
        if cancel[0]:       
            pb.set_fraction(0)
            info_label.set_text('Canceled')
            return
        pb.set_fraction(float(task_no)/len(task_list))
        run_task(2, task, info_label)        
        task_no += 1
    pb.set_fraction(float(task_no)/len(task_list))
    info_label.set_text('Finished')


def run_task(delay, task, info_label):
    info_label.set_text(task + ' is running...')
    time.sleep(delay)   # simulation of running the task


def cancel_counting(widget, info_label, cancel):
    cancel[0] = True
    info_label.set_text("Cancelling...")


win_pb, pb, info_label, cancel = init_progressbar()
win = gtk.Window()
win.set_default_size(400,100)
win.add(win_pb)
win.show_all()
win.connect("destroy", lambda _: gtk.main_quit())
t = threading.Thread(target=run_tasks, args=(pb,info_label, cancel))
t.start()
gtk.main()

To make above code working on your machine you need to have Python and PyGTK installed. To find out the details, how to install Python and/or PyGTK check my Python jump start post.


Research Assistant Job – Project Sireum

Since January 2013 I am Master student at Kansas State University. I am also Research Assistant at SAnToS lab.

Bakar toolset

Since the beginning I am working on project Sireum. It is focused on toolset for analyzing Spark/Ada programs: Bakar. Spark is subset of Ada programming language extended by code contracts. More info here. Bakar contains two main tools: Alir (information flow analysis tool) and Kiasan (verification tool for code contracts).

The Kiasan tool is based on symbolic execution. Simply saying: it checks all possible code paths and discover possible issues (like variable overflow, side effects etc.). More info about Spark contracts can be found in Tokeneer Discovery Tutorial. Comprehensive description of Kiasan in contained in this paper.

Kiasan plugin for GPS

My first task was to integrate Kiasan tool (Java app) with GPS (GNAT Programming Studio).

I have never working on IDE plugins before. It was something totally new for me. That’s why I was excited.

Plugins for GPS are written in PyGTK (Python graphic library). First I needed to learn Python and PyGTK. Then I followed GPS documentation (especially chapters 16 and 18).

The sample flow of running Kiasan plugin is as follows:

  • right click on method (which you want to analyze) and choose ‘Run Kiasan’
  • handle this event and get method name and package name (will be needed to run Kiasan)
  • run external Java program (Kiasan.jar) from Python code (it has command line interface, so I just needed to execute subprocess.call method), which generates JSON report
  • parse JSON report and display it in the PyGTK window attached to the GPS
  • add code highlighting, dependent on chosen method or case (in report window by double-click)

GPS along with Kiasan plugin is available in Sireum distribution, which can be downloaded from http://www.sireum.org/download.

In the demo below I am showing how to install Sireum distribution and GPS with Kiasan plugin on Windows.

By the way: Sireum Project is Open Source on github!

Do I like GRA job? Yes. It is very nice experience. I am able to work on Mac. I didn’t have this opportunity before (I wouldn’t buy it soon probably). I was able to learn Python (and PyGTK). I learn Design by Contract. I learn how to create plugin for IDE (GNAT Programming Studio). We have continues integration server – Jenkins: I learn how to manage it. I explore the World of ‘High Assurance Software’ and Ada programming language, which is most common in such environment. It is totally different than working as software developer in some company.

Recently I started working on second SAnToS’ project: Medical Device Coordination Framework. Another interesting thing. But I will tell you about it in another post.

Interesting fact: Ada programming language was named after Ada Lovelace (1815–1852), who is credited as being the first computer programmer.


Python jump start

In my current job (Research Assistant at SAnToS lab) I had to learn Python. I was very happy because of that. It gaves me an opportunity to get familiar with one of the most popular programming languages nowadays.

I was very lucky to find awesome Google’s Python Class by Nick Parlante. It is great! If you want to start programming with Python or just learn it for fun, start with this tutorial!

As a supplement to above course you can read some more detailed tutorial. I went through two: Learn Python The Hard Way and Tutorial from Python documentation. However if you already know some other programming language(s), your should learn during development. Python contains almost all common features of programming languages such as if/else, loops, exceptions, functions, classes etc. I said ‘almost’, because there is e.g. no switch instruction. However to check things like that there is very well written documentation. It contains a lot of examples. The main difference between other popular languages like (C, C# or Java) and Python is that there is no semicolons. We use colons and indentation instead.

if number > 0:
  print "This is natural number."
else:
  print "This is not natural number."

Python is dynamic, strongly typed programming language. It means type checking occurs during the run time, instead of compilation time. Programming in Python is a real pleasure. Sometimes you can explicitly put your mind into the code. That is because of high level of abstraction. E.g. file operations are so simple and intuitive. You do not need to remember any StreamReaders or BufferedReaders and bunch of functions for simple I/O operations. Below example reads content of file.

f = open('file.txt')
f.read()
f.close()

Cool feature is the possibility to call functions explicitly on string. Like that:

"jakub".upper()

There is a lot of implemented (widely used) functions in Python. As a comparison, let’s see how to reverse words in a sentence using C, Java and Python.

In C:

void reverse_words(char *sentence)
{
   char *start = sentence;
   char *end = sentence;

   while (*end != '\0') {
      ++end;
   }
   --end;

   reverse_chars(start, end);
   
   while (*start != '\0') {
      for (; *start != '\0' && *start == ' '; start++) ;
      for (end=start; *end != '\0' && *end != ' '; end++) ;
      --end;
      reverse_chars(start, end);
      start = ++end;
   }
}

void reverse_chars(char *left, char *right)
{
   char temp;

   while( left < right) {
      temp = *left;
      *left = *right;
      *right = temp;
      ++left;
      --right;
   }
}

In Java:

public string ReverseWords(string sentence)
{
  string[] words =  sentence.split(" ");
  string rev = "";
  for(int i = words.length - 1; i >= 0 ; i--)
  {
    rev += words[i] + " ";
  }
  return rev;
}

In Python:

def reverse_words(sentence):
  return " ".join(reversed(sentence.split(" ")))

That’s why Python is good for Rapid Development.

I am also using PyGTK (graphic library for Python) in my work. There is a great tutorial Python GTK on youtube! PyGTK requires very less code than e.g. C# to create some simple application. We do not to have tons of generated code when we start. We create application from scratch. Look at below Hello World example.

import pygtk
pygtk.require('2.0')
import gtk

class HelloWorld:
    def hello(self, widget, data=None):
        print "Hello World"

    def destroy(self, widget, data=None):
        gtk.main_quit()

    def __init__(self):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.connect("destroy", self.destroy)
        self.window.set_border_width(10)
        self.button = gtk.Button("Hello World")
        self.button.connect("clicked", self.hello, None)
        self.window.add(self.button)
        self.button.show()
        self.window.show()

    def main(self):
        gtk.main()

if __name__ == "__main__":
    hello = HelloWorld()
    hello.main()

The result is the window with button ‘Hello World’. When you click the button, then ‘Hello World’ will be printed on console. All of that with 22 lines of code (I do not count white lines).

Hello Python

If you don’t know python yet, I encourage you to try it. Programming in python requires a little bit different way of thinking. It also allows you to look at the programming from the different perspective.

Python installation is easy on all operating systems and you can find it in google. To install PyGTK in Windows you can use all in-one installer. There is also all-in-one installer for Mac. PyGTK is included in most Linux distributions, so you won’t need to install it if you are using Linux.