1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
|
Drawing
=======
In WPY, only a CScrollView object can be drawn on. The only other view
available is a CEditView, and a CEditView does not receive an OnDraw() message.
Instead, it manages its own drawing internally. Other drawable views
such as CView may be added in the future, but for now CScrollView is
generally useful, since the scroll bars can be turned off if you don't
need them. Note that dialog boxes can not be drawn to, although adding
static controls (like text) achieves the same result.
All drawing in wpy is done to a "device context", an object representing
the screen, a printer or a bitmap. A device context or "DC" is associated
with each view object you have. A DC contains a number of drawing tools
such as pens, brushes and fonts. All drawing methods use these tools. For
example, if you draw a line, the current pen is used. When you draw to a DC,
the system prepares the DC for the type of output device (screen, printer, etc.)
and it uses the drawing tools you have selected for that DC.
Although a wpy device context is based on a Windows DC, drawing in wpy is
much simpler. A wpy DC is not shared with any other application or object,
so it is not necessary to replace drawing tools with the original tools in order
to return the DC to its original configuration. When running on Windows,
the system supplies this logic as required. Also, any tools selected into
a device context stay there until replaced, so it is often unnecessary to
keep replacing tools. Windows would return a DC with default tools for each
call.
Drawing in wpy is done in response to the view's OnDraw(self, DC) message.
You must always override this view method to display your view of the document.
When OnDraw(self, DC) is called, the whole view has already been erased. So
your OnDraw() method must re-draw the whole view by calling the drawing methods
of the DC. An OnDraw() message is sent when the view is
first displayed. After that, the WPY system will refresh the screen
as required using the objects you have drawn to the view, and OnDraw()
will never be called again, except:
1) OnDraw() will be called whenever there are no drawn objects available
and the WPY system needs to update the screen. Your OnDraw() should
return promptly if there is nothing to draw.
2) If you call the view's method InvalidateRect(), the entire view
is erased and OnDraw() will be called again. But you should really
call the document's UpdateAllViews() instead of InvalidateRect().
UpdateAllViews() will call OnUpdate() for each view, and the base
class OnUpdate() calls InvalidateRect(). Using UpdateAllViews()
instead of InvalidateRect() is better because it is easier to
add selective updates, and it is easier to add views.
If you have a view method OnPrint(self, DC, pagenum), then it will be called
when you are printing (see print.txt). If there is no such method, then
the same OnDraw() mechanism is used when drawing to a printer. If you need
to distinguish between the screen and a printer from within OnDraw(),
check the DC.wpyIsPrinting attribute. Although the
DC is a printer DC, the same code will draw to the printer just as it draws
to the screen. But you must be careful to draw in a device-independent way.
For example, check the sizes of drawn objects as you go along instead of
assuming they have the size they would have on the screen. Remember that
screens have about 70 pixels per inch, but printers have 300 (or 600).
So layout must be done using attributes of the DC.
If you change an attribute of a DC the change will persist, and each time you
are given a DC it will have the tools last assigned to it. For example, if you
change the pen in the DC with a call to SelectObject(), the next OnDraw()
message will have a DC with the changed pen. This makes it easy to fix a pen,
brush, font etc. in a DC once and for all. But if you want to change a pen to
a different pen, you must call SelectObject() for each change. If you never
call SelectObject, a default drawing tool is used.
When you draw an object with DrawText(), LineTo() etc. the return value
is an instance of CDrawnText, CDrawnLine etc. which represents the object
as drawn on the view. You can change certain attributes of the object, for
example, text color, by assigning to the object's attributes. You can move
the object by assigning to the object's wpyLocX and/or wpyLocY. You must call
the view's Redraw() method for these changes to be visible. Using Redraw()
is ofter easier and faster than using InvalidateRect() to cause another
call to OnDraw().
The drawn objects have wpyLocX/Y and wpySizeX/Y and other attributes
depending on the object drawn. The wpyLocX/Y and wpySizeX/Y are the
bounding box for the drawn object, not the coordinates used to create
the object. For example, if you draw a rectangle, the location and size
are larger than the rectangle coordinates by half the pen width because
the outline is drawn centered on the coords.
The CScrollView methods GetDrawnObject() and GetDrawnObjectList() are
available to return the drawn objects given the view coordinates (x, y).
This may be used, for example, to respond to a mouse click over
a piece of text in a web browser. GetAllDrawnObjs() returns all
the drawn objects as a list.
It is sometimes necessary to draw other than in response to an OnDraw message.
For example, you must draw to the view directly to draw lines or boxes with the
mouse. In this case, call DC = view.GetDC() to return the DC associated with
the view. Then call the drawing methods of the DC as before. The tools
will be the same as the tools used for OnDraw(). You must call
view.ReleaseDC(DC) as soon as you are done drawing, and you must not
save a copy of DC for later use. You must call ReleaseDC before you exit
the current function. It is a "feature" of Windows that this DC is a
volatile pointer and you must call GetDC and ReleaseDC very frequently.
Since you will probably be drawing with the mouse to need this feature,
you will have a call to GetDC and ReleaseDC at the top and bottom of
OnMouseMove().
Updating the View
=================
The simplest drawing scheme for a WPY app is to draw the whole view in OnDraw()
and call UpdateAllViews() to erase the whole view when anything changes. If
OnPrint() is omitted, then your OnDraw() is also used for printing. This
works surprisingly well, since OnDraw() is only called once, and the WPY
system refreshes the screen as required. You should probably try this method
first and see if it is good enough.
Unfortunately it may prove to be too slow for large views with many drawn
items which require small updates. It may be necessary to update the
individual drawn items in the view when the document changes to achieve
acceptable speed. This is harder because while
the DC is available in OnDraw(), it is not available at an arbitrary point
where the change to the document is made. Also the sizes of the drawn objects
varies depending on the DC. For example, if a certain font is specified,
and the DC is a printer DC which lacks the font, then a different font will
be used instead. This is in addition to the fact that the resolutions are
different for the screen and the printer. So the pixel locations and
sizes for the drawn objects depend on the DC.
First recall that the document has the method UpdateAllViews() which should
be called when the document changes so that all views can re-draw themselves.
UpdateAllViews() calls OnUpdate() for each view. The base class OnUpdate()
calls InvalidateRect() which would erase the view. So first make sure
you are calling UpdateAllViews() when the document changes, and then
override the OnUpdate() method of the view to perform the update.
UpdateAllViews(self, sender, update) will call OnUpdate(self, sender, update)
for all views not equal to sender. This is intended to alert views
other than the sending view which presumably already redrew itself. Use
None for sender to cause OnUpdate() to be called for ALL views. The "update"
object is meant to contain hints about what changed so that the views do
not have to examine the document looking for changes. Any Python object
is acceptable, and tuples are often convenient as are instances. For
example, if a line of something in the document changed, the "update"
could be the line number, the line object or both. The default update
is None, which means that no update information is available.
WPY has a number of methods which enable you to alter the drawn items on
the view without access to the DC. The WPY system takes care of screen
updates itself after you have made changes. These methods are:
view::Redraw(self, obj) re-displays the drawn object after you have
altered its attributes, such as text, text color, size, location.
view::AddDraw(self, obj) adds a drawn object to the view. This is less
useful than calling the drawing method directly, but can be used to
copy drawn objects from a different view, for example, when making
a view which is a copy of another view.
view::DestroyDrawn(self, obj) removes an object from the view.
view::DestroyAllDrawn(self) erases the view. Not needed with OnDraw()
because the view is always erased before OnDraw() is called.
Using the above tools, there are (at least) two schemes for updating
less than all of the view. You will probably need to use a separate
OnPrint() method (instead of using OnDraw() for printing) to make things
simpler. A seperate OnPrint() is needed for multi-page printing anyway
so this hardly matters.
First, if the sizes of drawn objects are known without using the DC,
perhaps because sizes were saved from the call to OnDraw() or because
you are always drawing to the screen using its known resolution, then
you can just manipulate drawn objects directly with Redraw(),
DestroyDrawn(), etc.
If you really need the DC to get accurate drawn object sizes, then you can
use GetDC() and ReleaseDC() for the view within OnUpdate() to get the
DC for the view. Be sure an exception does not cause ReleaseDC() to
fail to be called. You will definitely need an OnPrint() for printing.
Even if you are using OnUpdate() to make updates to the screen, you still
need an OnDraw() which will re-draw the entire view. If
MyDraw(self, update, line_start, line_end) is your drawing utility which
draws lines in line_list[], then OnDraw should call it as follows:
def OnDraw(self, DC):
self.MyDraw(None, 0, len(line_list))
Note that an update of None (the default) means there is no specific
update information was available.
Make sure you do not add multiple identical drawn objects. Be sure to
destroy drawn objects as required. Multiple identical objects will
look OK on the screen, but will reduce efficiency.
class CDC
Class CDC represents a device context. See above. Device contexts are never
constructed by the user. They are made available as method arguments.
Instance Variables
wpyIsPrinting, int
0/1 if OnDraw() is called for printing. Only valid
within OnDraw().
wpySizeX/Y, int
The size of the display surface.
wpyOneMeter, int
The number of pixels in one meter.
wpyPrintPoint, float
The number of pixels in a printer's point ( 1/72 inch).
wpyTextColor, 3-tuple
The color to draw text, as the tuple (red, green, blue). The
colors have values from 0 to 255.
Methods
DrawText(self, text, x, y, width = 0, justify = "left")
The return value is a CDrawnText instance.
text, string
Text to draw.
x, y, int
The location to draw the text in pixels. The anchor is "nw", the
upper left corner.
width, int
If specified, the width in pixels for the text. If text exceeds
this width, it is broken into lines at a blank. Otherwise, the text can
be any length. A newline always breaks the text. If width is
specified, text is formatted within width. If width is not
specified, the returned size just bounds the text.
justify, one of "left", "right", "center"
How to display multiline text. Has no effect unless width is
entered too.
GetTextExtent(self, text)
string text
Return the size of the text as the tuple (width, height). Needed to
do text layout and drawing. Within view::OnDraw(), you must call this
with the device context given, as "DC.GetTextExtent()". This method
may also be called for a window, and it uses the window's device
context. Do not use "self.GetTextExtent()" within OnDraw().
LineTo(self, x, y)
The return value is a CDrawnLine instance.
x, y, int
The ending location of the line, and the new current location.
Draw a line using the current pen from the current location
(see MoveTo) to the specified location, and update the current
location.
MoveTo(self, x, y)
Change the current location to (x, y).
Arc(self, x, y, w, h, start, extent):
Draw an arc made of the ellipse in the rectangle x, y, w, h, starting
from the angle "start" counter clockwise through the angle "extent" in degrees.
The return value is a CDrawnArc instance.
Chord(self, x, y, w, h, start, extent):
Draw a chord made of the ellipse in the rectangle x, y, w, h, starting
from the angle "start" counter clockwise through the angle "extent" in degrees.
The return value is a CDrawnArc instance.
Circle(self, x, y, radius):
Draw a circle centered at (x, y) with the given radius.
The return value is a CDrawnEllipse instance.
Ellipse(self, x, y, w, h):
Draw an ellipse in the rectangle specified.
The return value is a CDrawnEllipse instance.
Pie(self, x, y, w, h, start, extent):
Draw a pie slice made of the ellipse in the rectangle x, y, w, h, starting
from the angle "start" counter clockwise through the angle "extent" in degrees.
The return value is a CDrawnArc instance.
Polygon(self, points):
Draw a closed polygon made of the points in the list "points"
consisting of tuples (x, y).
The return value is a CDrawnPolygon instance.
Rectangle(self, x, y, w, h):
The return value is a CDrawnRectangle instance.
x, y, w, h, int
Draw a rectangle with "w" width and "h" height at (x, y) using
the current pen and brush.
DrawImage(self, image, x, y)
The return value is a CDrawnImage instance.
image, a CImage instance.
The image object to draw.
x, y, int
The location to draw the image in pixels. The anchor is "nw", the
upper left corner.
SelectObject(self, object)
object, a drawing object such as a pen or font
Replace the current drawing object in the device context with the specified
new drawing object, and return the old object.
SetTextColor(self, color)
Change the color to draw text. "Color" is the tuple (r, g, b).
Return the previous color.
class CBrush
Class CBrush represents a brush, a drawing tool.
Instance Variables
wpyRed, wpyGreen, wpyBlue, int 0 to 255, readonly after Create()
The color of the brush.
Methods
__init__(self, rgb = (0, 0, 0))
Construct a brush with the color given. If the color is specified
as (), then the brush is a hollow brush and there is no fill color.
Create(self)
Create the brush using the underlying GUI. Return self.
class CPen
Class CPen represents a pen, a drawing tool.
Instance Variables
wpyWidth, int, readonly after Create()
The width of the pen in pixels.
wpyRed, wpyGreen, wpyBlue, int 0 to 255, readonly after Create()
The color of the pen.
wpyStyle, int, readonly after Create()
The style of the pen.
Methods
__init__(self, width = 1, rgb = (0, 0, 0), style = wpycon.PS_SOLID)
Construct a pen with the attributes given.
Create(self)
Create the pen using the underlying GUI. Return self.
Class CFont
Class CFont represents a font, a drawing tool.
Instance Variables
wpyFamily, wpyHeight, wpyWeight Equal to the __init__ arguments.
Methods
__init__(self, family, height = 0, weight = wpycon.FW_NORMAL)
family, string
The general type of font. It must be one of the strings:
"swiss" A proportional font without serifs such as Helvetica.
"roman" A proportional font with serifs such as Times Roman.
"modern" A fixed width font such as Courier.
The family string can be followed by "italic", as in "roman italic".
A feature to specify a more specific font will be added later, but the
"family" will still be required in case the more specific font is not
available.
height, int
The size of the font in printer's points. Or use zero to specify
a default size.
weight, int
The weight of the font; that is, how dark it is. For example, FW_NORMAL
and FW_BOLD are available on most systems.
Create(self)
Create the font using the underlying GUI. Return self.
GetTextExtent(self, text)
Return the text size on the screen of "text" as (sx, sy).
Bugs: The function used to map fonts to X fonts when using Tk is primitive.
The response of Tk when a non-existant font is requested is
also primitive.
Class CImage represents a color bitmap. It is not a drawing tool.
Instance Variables
wpySizeX/Y, int, readonly after Create()
The size of the bitmap in pixels.
Methods
__init__(self, filename = None)
Construct an image object, record the filename.
Create(self)
Create the image using the underlying GUI. Initialize the bitmap
from the filename (currently the only bitmap creation method).
The size of the bitmap is self.wpySizeX/Y. If there was an error
in creating the bitmap, the size is (0, 0). You should test the
size before using the CImage object.
The file formats currently available are whatever Tk supports for
Unix (currently GIF and PPM), and BMP, DIB, GIF and PPM on Windows.
The file name extension is used to decide what read method to use,
so the file name must end in one of the above extensions, ".gif" etc.
Drawn Object Classes
====================
Instances of these classes are returned by the drawing methods.
class CDrawnLine
Class CDrawnLine represents a line drawn by LineTo().
Instance Variables
wpyLocX, wpyLocY, wpySizeX, wpySizeY
The bounding box for the line.
wpyStartX, wpyStartY, wpyEndX, wpyEndY
The starting and ending points.
wpyPen
The pen in effect in the DC when the text was drawn.
The following attributes can be changed and then re-drawn with view::Redraw():
wpyPen
class CDrawnArc
Class CDrawnArc represents an arc, pie slice or chord.
class CDrawnEllipse
Class CDrawnEllipse represents an ellipse drawn by Ellipse() or
a circle drawn by Circle().
class CDrawnPolygon
Class CDrawnPolygon represents a polygon drawn by Polygon().
You may not change wpyLocX nor wpyLocY for this object.
Remove and redraw instead.
class CDrawnRectangle
Class CDrawnRectangle represents a rectangle drawn by Rectangle().
Instance Variables
wpyLocX, wpyLocY, wpySizeX, wpySizeY
The bounding box for the object.
wpyPen
The pen in effect in the DC when the text was drawn.
wpyBrush
The brush in effect in the DC when the text was drawn.
The following attributes can be changed and then re-drawn with view::Redraw():
wpyPen, wpyBrush
class CDrawnText
Class CDrawnText represents a piece of text drawn by DrawText().
Instance Variables
wpyLocX, wpyLocY, wpySizeX, wpySizeY
The bounding box for the text.
wpyText
The text drawn.
wpyTextColor
The text color in effect in the DC when the text was drawn as (r, g, b).
wpyFont
The font in effect in the DC when the text was drawn.
The following attributes can be changed and then re-drawn with view::Redraw():
wpyText, wpyFont, wpyTextColor
BUG: The effect of redrawing multi-line text is undefined.
GetIndexXY(self, view, x, y)
Return the index in self.wpyText (the text drawn) of the character
located closest to the point (x, y) in view coordinates. Use this
to figure out which character is located under the mouse, for example.
NOTE: This only works for a single line of text, as the system is
(currently) not aware of where multiline text was broken.
class CDrawnImage
Class CDrawnImage represents an image drawn by DrawImage().
Instance Variables
wpyLocX, wpyLocY, wpySizeX, wpySizeY
The bounding box for the image.
The following attributes can be changed and then re-drawn with view::Redraw():
None.
Class CView
These are the methods of the view classes which are used for drawing.
See the file view.txt.
InvalidateRect(self)
GetDrawnObject(self, x, y)
GetDrawnObjectList(self, x, y)
GetAllDrawnObjs(self)
Redraw(self, object)
DestroyDrawn(self, obj)
DestroyAllDrawn(self)
AddDrawn(self, obj)
|