[Tutor] Correct use of model-view-controller design pattern

boB Stepp robertvstepp at gmail.com
Sun Jun 19 00:18:41 EDT 2016


On Sun, May 29, 2016 at 9:28 AM, Peter Otten <__peter__ at web.de> wrote:

> Here's a commandline example with two views (the circle-drawing routine has
> to be fixed)...

I have to admit that now that I have finally gotten around to your
code, I am left scratching my head.  My main issue is what am I aiming
at for a final CLI display of a circle?  This is not such a simple
thing!  First, what are the units for a circle's radius in a CLI?  If
it is 1 radius unit = 1 character, then your code is displaying too
many characters.  To me it seems more natural to use 1 character as
the basic unit of measurement.  But if that is done, then the output
looks more like a non-circular ellipse with the longer axis being
vertical.  On my Windows PowerShell for the font I am using, each
character space is 8 pixels wide and 14 pixels wide.  I suspect this
is why your code uses range(2*diameter) and column/2, to compensate
for the built in stretching in the vertical direction due to how
characters are displayed.  So my current best version of your code
modifies your distance function as well as your CircleImageView class.
I am not totally happy with it.  I think I am still missing something,
but it tries to adhere to 1 measurement unit = 1 character based on my
display's font.


> def dist(ax, ay, bx, by):
>     return ((ax-bx)**2 + (ay-by)**2)**.5

I replaced this with:

def dist(ax, ay, bx, by):
    # Each character is 8 pixels wide by 14 pixels high.
    width_px = 8
    height_px = 14
    return (((ax-bx)*width_px)**2 + ((ay-by)*height_px)**2)**0.5

I also flipped how you used ax, ay and bx, by as I associate rows with
y-coordinates and columns with x-coordinates.

>
> class CircleImageView(CircleView):
>
>     def changed(self, model):
>         self.update_radius(model.radius)
>
>     def update_radius(self, radius):
>         # FIXME
>         diameter = 2*radius
>         cx = cy = radius
>         for row in range(diameter):
>             for column in range(2*diameter):
>                 if dist(cx, cy, row, column/2) < radius:
>                     print("*", end="")
>                 else:
>                     print(" ", end="")
>             print()

And I replaced this (Renaming some identifiers to make it clearer to me) with:

class CircleImageView(CircleView):

    def changed(self, model):
        self.update_radius_in_chars(model.radius_in_chars)

    def update_radius_in_chars(self, radius_in_chars):
        diameter_in_chars = 2 * radius_in_chars
        cx = cy = radius_in_chars
        for row in range(diameter_in_chars + 1):
            for column in range(diameter_in_chars + 1):
                if dist(cx, cy, column, row) <= dist(cx, 0, 0, 0):
                    print("O", end="")
                else:
                    print(" ", end="")
            print()
I did a '+ 1' in the range statements since a character should be
displayed in the center of the circle from which the radius is
measured.  I used '<= dist(cx, 0, 0, 0)'.  First, if it is equal to
the radius, I feel a character should be displayed.  Second, I used
'cx' instead of 'cy' to keep the result from being stretched
vertically.  Finally, I felt I needed to use some form of absolute
distance calculation, so the distance function converts everything to
pixels, which is what everything ultimately is displayed in anyway on
a monitor.

Doing things this way, I feel my displayed character circles look
better as the radius increases.  Some edited (I just removed blank
lines.) sample output (I changed from asterisks to capital ohs):

  OOOOOOO
 OOOOOOOOO
OOOOOOOOOOO
 OOOOOOOOO
  OOOOOOO

Circle radius_in_chars is now 5
Model-View-Controler Demo
Enter an integer to set the circle radius
or 'grow' to increase the radius
or 'shrink' to decrease the radius
or 'add' to add another CircleValueView


radius or 'grow' or 'shrink' or 'add': 6

    OOOOO
  OOOOOOOOO
 OOOOOOOOOOO
OOOOOOOOOOOOO
 OOOOOOOOOOO
  OOOOOOOOO
    OOOOO


radius or 'grow' or 'shrink' or 'add': 13

         OOOOOOOOO
      OOOOOOOOOOOOOOO
    OOOOOOOOOOOOOOOOOOO
   OOOOOOOOOOOOOOOOOOOOO
  OOOOOOOOOOOOOOOOOOOOOOO
 OOOOOOOOOOOOOOOOOOOOOOOOO
 OOOOOOOOOOOOOOOOOOOOOOOOO
OOOOOOOOOOOOOOOOOOOOOOOOOOO
 OOOOOOOOOOOOOOOOOOOOOOOOO
 OOOOOOOOOOOOOOOOOOOOOOOOO
  OOOOOOOOOOOOOOOOOOOOOOO
   OOOOOOOOOOOOOOOOOOOOO
    OOOOOOOOOOOOOOOOOOO
      OOOOOOOOOOOOOOO
         OOOOOOOOO

I have to say, I did a lot of playing around on graph paper with a
pencil and a compass.  If I actually draw a circle with a compass and
fill in marks for a distance from the circle's center, I get a very
unsatisfactory result!

I still have more conceptual work to do on your code.  I still do not
fully get decorators.  In your code I get the impression that you are
trying to be more formal in how you are writing your classes so as to
have getter and setter methods, as well as having more explicit
separation between model, view and controller.

And I haven't made it to your tkinter version at all!

BTW, thank you very much for putting in such effort to give me TWO
versions of MVC code to explore!

boB


More information about the Tutor mailing list