Embarrassing...released version 0.40 with about 6 bugs that I found within a 10 minute play around with the program. For shame!
Anyway, that takes care of that, I hope. Version 0.40.1 released Had another bug report about damn unicode errors - thought I'd fixed those! oh dear
Tuesday, 18 May 2010
Sunday, 16 May 2010
Whyteboard 0.4 released
New whyteboard released, bringing a whole new bunch of features.
New features:
- Highlighter tool
- Improvements to selection tool
- UI changes
- Better shape viewer
- PDF Cache viewer
- bug fixes and misc. changes/improvements - see changelog for further details
Enjoy. As always, report any bugs/issues/suggestions.
New features:
- Highlighter tool
- Improvements to selection tool
- UI changes
- Better shape viewer
- PDF Cache viewer
- bug fixes and misc. changes/improvements - see changelog for further details
Enjoy. As always, report any bugs/issues/suggestions.
Monday, 3 May 2010
The Law of Demeter
I've been learning a lot about refactoring over the past few days, and have made an effort to put these practices into effect. One interesting concept is the Law of Demeter - classes should know as little about their interacting classes as possible. This means restricting access on instance variable, and method arguments.
Suppose we have an instance variable of type B inside A. Class B contains a reference to class C. The Law of Demeter states we should not access properties of C through B; instead we should tell B on how to interact with C.
Here's a before/after example of some code from Whyteboard I've modified, involving selecting a shape using the Select tool:
In this code, we see the Select tool (a "model" is manipulating the canvas directly, when it really shouldn't be doing so. We can delegate this to the canvas itself, through the GUI.
We can now see that the appropriate classes are performing their correct responsibilities. The model sends out a message to indicate something's changed, without caring how the event is handled, and the GUI is updating its controls and menus. The GUI delegates a method call to the canvas which updates itself.
There are no longer many "access levels" (this.that.other.do_something) as all operations are on variables within a small scope. This is better code that's easier to test.
Suppose we have an instance variable of type B inside A. Class B contains a reference to class C. The Law of Demeter states we should not access properties of C through B; instead we should tell B on how to interact with C.
Here's a before/after example of some code from Whyteboard I've modified, involving selecting a shape using the Select tool:
class Select:
def check_for_hit(self, shape, x, y):
"""
Sees if a shape is underneath the mouse coords, and allows the shape to
be re-dragged to place
"""
found = False
handle = shape.handle_hit_test(x, y) # test handle before area
if handle:
self.handle = handle
found = True
elif shape.hit_test(x, y):
found = True
if found:
self.canvas.overlay = wx.Overlay()
self.shape = shape
self.dragging = True
self.offset = self.shape.offset(x, y)
if self.canvas.selected:
self.canvas.deselect()
self.canvas.selected = shape
shape.selected = True
pub.sendMessage('shape.selected', shape=shape)
return found
class Gui:
# bind a handler for shape.selected
pub.subscribe(self.shape_selected, 'shape.selected')
def shape_selected(self, shape):
"""
Shape getting selected (by Select tool)
"""
x = self.canvas.shapes.index(shape)
self.canvas.shapes.pop(x)
self.canvas.redraw_all() # hide 'original'
self.canvas.shapes.insert(x, shape)
shape.draw(self.canvas.get_dc(), False) # draw 'new'
ctrl, menu = True, True
if not shape.background == wx.TRANSPARENT:
ctrl, menu = False, False
self.control.transparent.SetValue(ctrl)
self.menu.Check(ID_TRANSPARENT, menu)
In this code, we see the Select tool (a "model" is manipulating the canvas directly, when it really shouldn't be doing so. We can delegate this to the canvas itself, through the GUI.
class Select:
def check_for_hit(self, shape, x, y):
"""
Sees if a shape is underneath the mouse coords, and allows the shape to
be re-dragged to place
"""
found = False
handle = shape.handle_hit_test(x, y) # test handle before area
if handle:
self.handle = handle
found = True
elif shape.hit_test(x, y):
found = True
if found:
self.shape = shape
self.dragging = True
self.offset = self.shape.offset(x, y)
pub.sendMessage('shape.selected', shape=shape)
return found
class Gui:
# bind a handler for shape.selected
pub.subscribe(self.shape_selected, 'shape.selected')
def shape_selected(self, shape):
"""
Shape getting selected (by Select tool)
"""
self.canvas.select_shape(shape)
ctrl, menu = True, True
if not shape.background == wx.TRANSPARENT:
ctrl, menu = False, False
self.control.transparent.SetValue(ctrl)
self.menu.Check(ID_TRANSPARENT, menu)
class Canvas:
def select_shape(self, shape):
"""Selects the selected shape"""
self.overlay = wx.Overlay()
if self.selected:
self.deselect_shape()
self.selected = shape
shape.selected = True
x = self.shapes.index(shape)
self.shapes.pop(x)
self.redraw_all() # hide 'original'
self.shapes.insert(x, shape)
shape.draw(self.get_dc(), False) # draw 'new'
We can now see that the appropriate classes are performing their correct responsibilities. The model sends out a message to indicate something's changed, without caring how the event is handled, and the GUI is updating its controls and menus. The GUI delegates a method call to the canvas which updates itself.
There are no longer many "access levels" (this.that.other.do_something) as all operations are on variables within a small scope. This is better code that's easier to test.
Subscribe to:
Posts (Atom)