Refactoring and New Elements

Apart from continuously improving previous UI elements, I have worked on three major ones in the past two weeks.

Refactoring

A huge portion of the module I am working on was refactored by another contributor to the library. After this was merged, I had to read through all the changes and modify all my code according to them. It took some time to get used to the changes but now coding new elements is a lot easier than before.

The base class for all the elements was refactored to implement some functions that were common to all or most elements, such as retrieving attributes like position, center and size of the elements.

Checkbox and RadioButtons

These elements have undergone major changes since the previous blog post.  The code for the same is available at https://github.com/nipy/dipy/pull/1559.

Option

The Option class, which is a set of a Button2D and a TextBlock2D, acts as a single option for check-boxes and radio buttons. It keeps a checked attribute to facilitate checking and unchecking in Checkbox and RadioButton classes.

Checkbox

The Checkbox class implements check-boxes, which is a set of Option objects, where multiple options can be checked at once. The toggle_check callback to the on_left_mouse_button_pressed event of the buttons handles the checking and unchecking of options.

def toggle_check(self, i_ren, obj, button):
    """ Toggles the checked status of an option.

    Parameters
    ----------
    i_ren : :class:`CustomInteractorStyle`
    obj : :class:`vtkActor`
    The picked actor
    button : :class:`Button2D`
    """
    event = []
    button.next_icon()
    for option in self.options:
        if option.button == button:
            option.checked = not option.checked
        if option.checked is True:
            event.append(option.label)
    i_ren.force_render()
    print(event)

RadioButton

The RadioButton class implements radio buttons, which is a set of Option objects, where only one option can be checked at once. It inherits from Checkbox class, with a different toggle_check function to uncheck all other options when one is clicked.


Range Slider

This is an addition to the already present LineSlider2D element, which is a line on which a handle slides. Each position of the handle represents some value.

I  have added two new classes as additions to this element.

The code for this is available at https://github.com/nipy/dipy/pull/1557

LineDoubleSlider2D

This element allows the user to have two handles on the same slider. These handles can slide on the track, while not crossing each other at any point.

This element is useful for setting a range for a parameter.  For example, in CT images, this can be used to select a mapping for some window of pixel values to values between 0-255 (Windowing).

RangeSlider

This element uses a LineDoubleSlider2D to select a range which restricts a LineSlider2D to move within that range.

This is done by updating the min_value or max_value of the LineSlider2D according to the positions of the handles of LineDoubleSlider2D.

def range_slider_handle_move_callback(self, i_ren, obj, slider):
    """ Actual movement of range_slider's handles.

   Parameters
    ----------
    i_ren : :class:`CustomInteractorStyle`
    obj : :class:`vtkActor`
        The picked actor
    slider : :class:`RangeSlider`
    """
    position = i_ren.event.position
    if obj == self.range_slider.handles[0].actors[0]:
        self.range_slider.set_position(position, 0)
        self.value_slider.min_value = self.range_slider.left_disk_value
        self.value_slider.update()
    elif obj == self.range_slider.handles[1].actors[0]:
        self.range_slider.set_position(position, 1)
        self.value_slider.max_value = self.range_slider.right_disk_value
        self.value_slider.update()
    i_ren.force_render()
    i_ren.event.abort() # Stop propagating the event.

This can be useful when a user wants to fine tune the value of a parameter by narrowing the permissible range for that parameter.


Scroll Bar

There is a pending PR by another user here that implements a ListBox, which shows a list of items that can be selected using mouse clicks. I was asked to add a scroll bar to it to extend the current existing scrolling capability through mouse wheel and buttons.

The most challenging part of this was finding a formula for the height of the bar and the amount of movement of the bar(in pixels) that should correspond to a unit scroll.

The code for this is available at https://github.com/karandeepSJ/dipy/commit/5c7295da5f20fd966a9f1995184d17f8d180473d

The next goal is to implement a FileSelectMenu that uses this ListBox to make a file dialog to browse the file structure and select a file.

Starting Off With Some Basic Widgets

After I thoroughly went through the code of the library I am working on during the Community Bonding period, I focused my attention to some basic widget implementations to get comfortable with writing code in VTK. After a video chat with my mentors during the first week, we came up with a list of tasks for me to complete to get my hands dirty.

Image Widget

My first widget was a simple image element which takes the image path and the required size as inputs. I started off with this because the code for this was pretty simple and most of it was already written in other classes.

Possible improvements to this include:

  1. Resizing the image with mouse.
  2. Changing the image by browsing the file system during display.

The code for this is available here : https://github.com/nipy/dipy/pull/1522

Checkboxes

I then moved on to implement a checkbox element. This was implemented as a composition of the Button2D and TextBox2D class objects. This was where I really started getting experience with VTK since I faced a lot of problems during implementation which made me go through a lot of documentation of the toolkit. It helped me find a few bugs in the classes that I used. This is what I finally made:

One issue I faced while implementing this was that the set of icons used in the Button2D class was a dictionary. This was a problem because we need a particular order for the icons in the buttons (Block followed by a Tick if checked) but dictionaries in python are randomly ordered, thus sometimes the buttons were initially ticks. After discussing with my mentors, we decided to change the dictionary to a list of tuples. The code for the same can be found here:  https://github.com/nipy/dipy/pull/1534

My mentors and I focused our attention to reviewing a pending PR in the repository. It is an important PR which refactors a huge portion of the code. I have decided to make a PR for checkbox after this PR and the one mentioned above are merged.

Plans for the Project

In a video call meeting, my mentors discussed what outcomes they expect from this project. They want me to move to 3D UI components ASAP, so I decided to complete the 2D portions in the next 2-3 weeks. One of the most important of the 2D widgets is a file select menu, which is not only an important component in itself, but can also enhance the functioning of other elements by allowing the user to control what is displayed.

Introduction

My name is Karandeep Singh Juneja and I am really excited to be working for DIPY this summer as a part of Google Summer of Code. I am a Computer Science undergraduate at IIIT Hyderabad.

About DIPY

DIPY is a free and open source software library for computational neuroanatomy and medical data science. It contains algorithms for diffusion magnetic resonance imaging (dMRI) analysis and tractography but also contains implementations of other computational imaging methods such as denoising and registration that are applicable to the greater medical imaging and image processing communities.

Project Information

dipy.viz provides many visualization capabilities. The goal of my project is to improve DIPY’s current User Interface widgets and create new futuristic ones so as to have a complete library from which users can build interactive applications.

Current Status of the library

Currently, dipy.viz implements some basic functionalities using VTK (Visualization Toolkit) in Python. The interactive tools already present are buttons, sliders, panels, text boxes, and file-select menus, all two dimensional.

My Project

I have planned to divide the project into two main parts:

  1. Improving and implementing 2D widgets: These new 2D widgets will include basic objects like checkboxes, radio buttons, progress bars, color picker, images, divider and combo boxes. There will also be containers like Adaptive Rectangles and stack panels for these widgets. The users would be allowed to change the basic styling like color, size, position, orientation and margins of these widgets at runtime. If time permits, I might experiment with adding some animations to them.
  2. Implementation of 3D widgets: One 3D widget already in progress is an orbital menu.