Hello my fellow GSoCers. Hope you all have very good time and regularly check for your blog feeds in terri.toybox.ca/python-soc/.

I just needed feedback for my tkinter based pip GUI application in order to improve it further. Your contribution as feedback can be very valuable and helpful to let me know what people may expect from this project. Let me tell you about this project :

We have made a preliminary version of GUI for PIP. This project is intended to provide a GUI version for “pip” ( target audience for the project : beginners in Python or people who are not familiar with command line).

How to install pip_tkinter?

Ensure that you have python3.4+ and python3-pip with latest version(8.1.2+)

Tkinter is mainly based on single-threaded event model. The mainloop(), callbacks, event handlers and raising tkinter exceptions are all handled in single thread. That is why, it happens quite often that tkinter GUI becomes unresponsive ( for the user or is considers to be unresponsive by the user : which is a bad user experience) when event handlers try to do long blocking operations. In order to avoid it and improve user experience, it is generally advised that long running background tasks should be shifted to other threads.

But, we have two choices to incorporate parallelism in application using Python:

Multithreading

Multiprocessing

But, Python’s multithreading module is notorious due to GIL in Python. ( Google about GIL, you will find about it). It prevents Python multithreaded applications from taking full advantage of multiple processors. I used multithreading module with my application, with no significant improvement in performance. However, if we have used basic multithreading like :

It was very easy to convert it into multiprocessing module by replacing ‘multithreading’ with ‘multiprocessing’. And also we have used the queues to send messages from processes running in secondary threads to tkinter mainloop() (primary thread), then those queues should be replaced multiprocessing safe queues : multiprocessing.Queue(). But, it is important to remember that only objects which can be pickled can be allowed to pass from process X to process Y. The general thumb rule I came to know about it was that generally all Python native objects like lists, tuples, strings, integers, dictionaries are picklable. But, complex classes or objects can’t be pickled. I found about it here.

We need to ask us questions to decided which objects can be pickled :

Can you capture the state of the object by reference (i.e. a function defined in __main__ versus an imported function)? [Then, yes]

Does a generic

__get_state__

__set_state__

rule exists for the given object type? [Then, yes]

Does it depend on a Frame object (i.e. rely on the GIL and global execution stack)? Iterators are now an exception to this, by “replaying” the iterator on unpickling. [Then, no]

Does the object instance point to the wrong class path (i.e. due to being defined in a closure, in C-bindings, or other __init__ path manipulations)? [Then, no]

This week I really went through a lot of codebases, docs and Python books to learn about unittest. It was really a difficult task to learn it because we need to think of our application in a different way. We need to think about possible use cases of different methods, verify their functionality and logic and check for exceptions. All this becomes difficult and tricky when we need to create unittests for tkinter application.

The main difficulty in tkinter application is that we can’t just normally check it’s functionality. The call to root.mainloop() is a blocking call. Once we call root.mainloop() in setUpclass() method of unittest.TestCase object it blocks the further execution of tests which is not desired.

Ok, then as a normal person you will try to run root.mainloop() in a different thread other than the main thread in which the unittests are made. This trick also fails as neither tkinter is very handy nor unittest module when it comes to multithreading. root.mainloop() can only be run in main thread, therefore the unittests fail when we attempt to run root.mainloop() in secondary thread.

Therefore, finally we can do one thing. Actually root.mainloop() is nothing but just a loop running which periodically checks for changes in GUI elements. We can simulate the functionality of root.mainloop() ourselves by calling root.update() method whenever we change the GUI element programatically. Yeah, one more thing any GUI event can be only injected into tkinter GUI through invoke() or generate_event() methods. They work exactly similar to user input.

We can have an alternate philosophy of testing our GUI applications. It can be applied to each and every GUI application. We can test Tkinter application without using Tkinter library. Basically, remove all the non-Tkinter code from the classes
that handle the GUI and shunt that to another class. This way, we can easily test the logic that is being implemented without actually using Tkinter.

I have planned to use both methods for testing as they represent different perspectives.

Recently, I got stuck with very new problem (for me) of updating GUI in Tkinter when long processes are needed to be run (like running a time-consuming loop, waiting for a process to complete and return or fetching something from URL). Actually, for processes requiring a long time to complete, the Tkinter blocks other GUI events. Because of it, updates to GUI element only happen when the process returns after completing execution.

I earlier didn’t know about the ‘thread safe’ property of python Tkinter. The main caveat here is that we can’t update GUI elements from multiple threads. Once main thread initiates the mainloop(), we can never use other thread to update the GUI. However, we can easily do background processes in other threads. But, here also we need to invoke a GUI function whenever the process in the background thread stops and returns the result. But, a GUI function can only be invoked through the thread executing mainloop().

Therefore, after reading some online resources on StackOverflow and other Python blogs, I came to know about one design pattern followed to solve this problem. Instead of invoking a GUI function, whenever the result is returned we need to maintain a shared queue. The contents of queue will be shared between the thread executing mainloop() and the thread running the background process. Whenever we need to return the result after the process ends in the background thread, we need to put the result in the queue. On the other side, the thread ( executing mainloop() ) needs to periodically check the contents of the shared queue. In my case, I couldn’t understand this concept of ‘shared queue’ just by reading about it. Therefore, let’s go through a small piece of code to understand it better.

These are some of the screenshots of the GUI application developed till now. Now, in a few days, I need to fix and give a final touch to these functionalities and write test modules for this GUI application.

Further work for next week are related to making this application multithreaded, implement advanced features and improve GUI experience. It is very necessary for me to complete the basic GUI application, so that I can get user feedback when this application is released with Python 3.6 in first week of July.