NASA-DEVELOP / VOCAL

Visualization of CALIPSO (VOCAL). A CALIPSO Cross Cutting tool for visualizing data
http://nasa-develop.github.io/VOCAL/
Other
38 stars 14 forks source link

Free draw throws exception #92

Closed Syntaf closed 8 years ago

Syntaf commented 8 years ago
[2016-03-09 10:17:59,290] [   DEBUG] --- Plotted point at (732475.03116, 8.98922)... (shape.py:317)
[2016-03-09 10:17:59,290] [   DEBUG] --- Drawing line from plot... (shape.py:319)
[2016-03-09 10:17:59,490] [    INFO] --- Plotting point at 732475.03124, 9.77775... (manager.py:272)
[2016-03-09 10:17:59,490] [   DEBUG] --- Plotted point at (732475.03124, 9.77775)... (shape.py:317)
[2016-03-09 10:17:59,490] [   DEBUG] --- Drawing line from plot... (shape.py:319)
[2016-03-09 10:17:59,588] [   DEBUG] --- Polygon labeled for draw... (shape.py:502)
[2016-03-09 10:17:59,588] [   DEBUG] --- Creating polygon from points... (shape.py:330)
[2016-03-09 10:17:59,589] [    INFO] --- Drawing polygon... (shape.py:97)
[2016-03-09 10:17:59,710] [    INFO] --- Plotting point at 732475.03125, 11.47931... (manager.py:272)
[2016-03-09 10:17:59,710] [   DEBUG] --- Plotted point at (732475.03125, 11.47931)... (shape.py:317)
[2016-03-09 10:18:00,436] [    INFO] --- Plotting point at 732475.03124, 13.80339... (manager.py:272)
[2016-03-09 10:18:00,437] [   DEBUG] --- Plotted point at (732475.03124, 13.80339)... (shape.py:317)
[2016-03-09 10:18:00,437] [   DEBUG] --- Drawing line from plot... (shape.py:319)
[2016-03-09 10:18:00,880] [    INFO] --- Plotting point at 732475.03120, 13.96939... (manager.py:272)
[2016-03-09 10:18:00,880] [   DEBUG] --- Plotted point at (732475.03120, 13.96939)... (shape.py:317)
[2016-03-09 10:18:00,881] [   DEBUG] --- Drawing line from plot... (shape.py:319)
[2016-03-09 10:18:01,309] [    INFO] --- Plotting point at 732475.03121, 11.56231... (manager.py:272)
[2016-03-09 10:18:01,309] [   DEBUG] --- Plotted point at (732475.03121, 11.56231)... (shape.py:317)
[2016-03-09 10:18:01,309] [   DEBUG] --- Drawing line from plot... (shape.py:319)
[2016-03-09 10:18:02,618] [    INFO] --- Plotting point at 732475.03125, 11.56231... (manager.py:272)
[2016-03-09 10:18:02,618] [   DEBUG] --- Plotted point at (732475.03125, 11.56231)... (shape.py:317)
[2016-03-09 10:18:02,618] [   DEBUG] --- Drawing line from plot... (shape.py:319)
[2016-03-09 10:18:02,720] [   DEBUG] --- Polygon labeled for draw... (shape.py:502)
[2016-03-09 10:18:02,720] [   DEBUG] --- Creating polygon from points... (shape.py:330)
[2016-03-09 10:18:02,720] [    INFO] --- Drawing polygon... (shape.py:97)
Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Anaconda2\lib\lib-tk\Tkinter.py", line 1536, in __call__
    return self.func(*args)
  File "C:\Users\Grant\Documents\GitHub\vocal\calipso\tools\toggleablebutton.py", line 164, in toggle
    s.untoggle()
  File "C:\Users\Grant\Documents\GitHub\vocal\calipso\tools\toggleablebutton.py", line 75, in untoggle
    self.__destructor()
  File "C:\Users\Grant\Documents\GitHub\vocal\calipso\polygon\manager.py", line 74, in clear_lines
    self.__current_list[-1].clear_unfinished_data()
  File "C:\Users\Grant\Documents\GitHub\vocal\calipso\polygon\shape.py", line 82, in clear_unfinished_data
    if self.__can_draw() != -1:
  File "C:\Users\Grant\Documents\GitHub\vocal\calipso\polygon\shape.py", line 496, in __can_draw
    b1 = tuple_to_nparray(self.__coordinates[-1])
IndexError: list index out of range
kdottiemo commented 8 years ago

Seems to happen when one has drawn shapes, THEN deleted them, THEN started drawing again.

Syntaf commented 8 years ago

It seems there are a number of causes for this bug.

In the case log in the OP, I drew the polygon then attempted to click a button. It must be something related to when a shape is finished drawing.

The traceback is during the deconstruction and cleanup after a polygon is drawn, maybe something is being deleted? Or we're trying to delete an empty shape etc..

Syntaf commented 8 years ago

I want to clarify this bug since we all had some interest in it @kdottiemo and @Smewhen (actually we ran into it way back when @stapleCamel) , and also I have it referenced in a logger.warning so it would be useful to explain why this happened.


First some useful background information:

Tkinter itself does not provide any sort of means of creating a group of toggled buttons, meaning only one button may be active (i.e. sunk inwards) while the rest must be raised and inactive. Because of this we designed a custom wrapper around a button which maintains a static internal state across all instances of the object, and allows us to create a toggleable gui (which are the buttons you see in the toolbar).

When a button is toggled, some function passed to the wrapper is invoked and the state is set to toggled, when the button is either clicked again or another button is clicked, the wrapper invokes a pseudo destructor before activating the next button. You can see this in our declaration of these types of buttons

free_draw_button = \
    ToggleableButton(self.__root, self.lower_button_frame, image=self.free_draw_img, width=30, height=30)
free_draw_button.latch(target=self.__canvas, key='button_press_event', 
                       command=self.__parent.get_shapemanager().plot_point, cursor='tcross',
                       destructor=self.__parent.get_shapemanager().clear_lines)

When the free draw button is clicked, it invokes a function which targets self.__canvas , looks for some key event to happen, and calls command when that event is parsed. It also supports a custom cursor. The desctuctor is invoked when the button is untoggled. Remember how this bug only occured when clicking another button after creating a shape? It makes sense that the desctructor is the root cause. If you look into clear_lines:

def clear_lines(self):
    """
    Clear any existing lines or unfilled shapes when the 'Free Draw' button
    is unpressed. This is fix a bug that is caused by polygons not being
    finished but corrupting future shapes.
    """
    if self.__current_plot == Plot.baseplot:
        return
    self.__current_list[-1].clear_unfinished_data()
    self.__canvas.show()

you notice it simply clears unfinished data from the last shape within current_list, but that's exactly the problem. After we draw a shape, that shape is populated and an empty shape is appended to the end of current_list.

# portion of code which draws a shape if lines intersect
if event.xdata and event.ydata:
    logger.info('Plotting point at %.5f, %.5f' % (event.xdata, event.ydata))
    check = self.__current_list[-1].plot_point(event, self.__current_plot,
                                               self.__figure, ShapeManager.outline_toggle)
    if check:
        self.__current_list[-1].set_tag(self.generate_tag())
        self.__current_list.append(Shape(self.__canvas)) # appends new object! current_list[-1] is now an empty Shape
        self.__canvas.show()

So what happens when we create a shape then try to untoggle the button? It's going to attempt to clear unfinished data from that new empty shape.

The fix is to simply check if the shape being cleared is empty, and skip the clearing if so.