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.