What is a device context and how can I create one

There are ten subclasses of wx.DC in wxPython, split into the following three groups:

■ Contexts used for drawing to a screen

■ Contexts used for drawing to a location other than a screen

■ Contexts used to buffer a device context until you are ready to draw it to a screen

Screen-based device contexts

The first group represents device contexts that draw to the screen. You'd think there would only need to be one of these, but in fact wxPython provides four of them, depending on exactly where you need to draw, and exactly when you are doing the drawing.

Screen device contexts are meant to be created temporarily. This means you should only create them locally whenever you need one for drawing, and allow them to be garbage collected normally. You should never try to hold on to a screen device context as, say, an instance variable—this is unsafe and could lead to an unstable program.

Typically, you will draw to a window on the screen using either a wx.ClientDC or a wx.PaintDC. Which one you use depends on when you are performing the drawing. If you are drawing to the screen during a EVT_PAINT event handler you must use wx.PaintDC. At all other times, you must use wx.ClientDC. In fact, whenever you bind a handler to the EVT_PAINT event, you must create a wx.PaintDC object in the handler method even if you don't use it (not creating a wx.PaintDC can cause the platform to assume that the event wasn't fully handled and so it will send another event). The reason that the paint events require a different kind of device context is that the wx.PaintDC instances are optimized to only draw within the area of the window that is actually being refreshed during the redraw event, making redrawing faster.

You create a client or paint device context via a simple constructor whose one argument is the wxPython widget that you wish to draw upon—wx.ClientDC(win-dow) or wx.PaintDC(window). When you use these contexts, you will only be able to draw within the client area of that widget. This means that in a frame you will not be able to draw over the border, the title bar, or other decorations.

If you need to draw on the entire area of a frame including the borders and decorations, you should use a wx.WindowDC. You create a wx.WindowDC in the same way you would a wx.ClientDC—the constructor is wx.WindowDC(window). Like a wx.ClientDC, you should not create a wx.WindowDC during a paint event—the border-drawing behavior is not compatible with the clipping optimization of the paint device context.

Occasionally, you won't want to be limited to merely drawing to a single window, and you need the whole screen to be your canvas. In that case, you can use a wx.ScreenDC. Again, you should not create one of these during a paint event. The constructor takes no arguments (since you don't need to specify a window for the drawing to take place within)—wx.ScreenDC(). After that, you can use it just like any other device context. The images you draw are displayed on top of all of the windows in your display.

Non-screen device contexts

The second group of device contexts are used to draw to items other than a screen. This is sort of a grab bag of different kinds of things that can be treated as logically equivalent to a screen display.

■ wx.MetafileDC

■ wx.PostScriptDC

The first of these is wx.MemoryDC, which allows you to draw to a bitmap that is stored in memory and not being displayed. You create a wx.MemoryDC with a no argument constructor, wx.MemoryDC(), but you must associate it with a bitmap before using it. This is accomplished by calling the method SelectObject(bit-map) with an argument of type wx.Bitmap. Once that is done, you can draw to the memory device context, and the internal bitmap is changed. When you are done drawing, you can use the Blit() method to draw the bitmap to a window. We'll discuss memory device contexts in more detail in the following section.

The creation of MS Windows metafiles is simplified by using the wx.MetafileDC context. This context is only available on Windows systems—although it's hard to imagine all that much call for creating metafiles on other systems (frankly, it's hard to imagine there's all that much call for it on Windows systems, either). You create a metafile device context by passing a constructor with a file name, wx.MetafileDC(filename=""). If the filename is blank, the metafile is created in memory, but it's worth noting that there's not much you can do with it within wxPython. After creation, draw commands sent to this device context are written to the file. When you are done drawing, you can optionally call the method Close() which has no effect on the file itself, but does return a wxPython wx.Meta-file object. With the current version of wxPython, the only thing you can do with a wx.Metafile object is send it to the clipboard with the SetClipboard(width=0, height = 0) method.

Similar functionality that is of greater use across platforms is the wx.Post-ScriptDC object that creates Encapsulated PostScript files (.eps) files. You create a wx.PostScriptDC file from a print data object—wx.PostScriptDC(printData). The printData argument is of the type wx.PrintData, which will be discussed in chapter 17. Once created, the PostScript device context can be used like any other DC. The filename you save to can be set in the wx.PrintData object. So, you can save the .eps file as in the following example.

data = wx.PrintData() data.SetFileName("/tmp/test.eps") data.SetPaperId(wx.PAPER_LETTER) dc = wx.PostScriptDC(data) dc.StartDoc("") dc.DrawCircle(300,300, 100)

dc.EndDoc() # the file is written at this point

On Windows systems, you can access any Windows printer driver using the wx.PrinterDC. This class is also created from a print data object—wx.PrinterDC (printData). We'll discuss printing further in chapter 17.

Buffered device contexts

The third group of device contexts allows you to buffer a device context until you are ready to draw it to a screen.

■ wx.BufferedDC

■ wx.BufferedPaintDC

Buffering allows you to perform individual drawing commands to the buffer, and then draw them all to the screen in one shot. This prevents screen flicker when you are doing several redraws at once. As a result, buffering is a common technique when doing animation or other screen intensive drawing techniques.

There are two buffered device contexts in wxPython—wx.BufferedDC, which can be used to buffer any device context (but is normally used just with a wx.ClientDC), and wx.BufferedPaintDC, which is specifically designed to buffer a wx.PaintDC. The buffered contexts both work in essentially the same way, as something of a simplified wrapper around a memory device context. The wxBufferedDC constructor takes a device context and an optional bitmap as parameters— wx.BufferedDC(dc, buffer=None). On the other hand, wx.BufferedPaintDC takes a window and an optional bitmap—wx.BufferedPaintDC(dc, buffer=None). The dc argument is the device context where you want the drawing to end up when finished, for the wx.BufferedPaintDC window argument is used to create a wx.PaintDC internally and the buffer argument is a bitmap that is used as the temporary buffer. If the buffer argument is not specified, the device context creates its own bitmap internally. Once the buffered device context is created, you use it just as though it was the device context that you intend to have as the final drawing. Internally, the buffer context uses a memory device context and the bitmap to store the drawing. The shortcut is that you don't have to do anything in particular to get the buffer to draw to the real device context. It happens automatically. When the buffered device context is garbage collected (typically when the method ends and it drops out of scope), the C + + destructor function triggers the Blit() which draws the final contents of the buffer to the actual device context, with no further work needed on your part.

Was this article helpful?

0 0

Responses

  • Celendine
    How to create "Device Context"?
    8 years ago

Post a comment