Closed Syntaf closed 8 years ago
Seems to happen when one has drawn shapes, THEN deleted them, THEN started drawing again.
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..
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.