c7391.htm

来自「GUI Programming with Python」· HTM 代码 · 共 962 行 · 第 1/2 页

HTM
962
字号
<HTML><HEAD><TITLE>Drawing on Painters and Canvases</TITLE><METANAME="GENERATOR"CONTENT="Modular DocBook HTML Stylesheet Version 1.72"><LINKREL="HOME"TITLE="GUI Programming with Python: QT Edition"HREF="book1.htm"><LINKREL="UP"TITLE="Creating real applications with PyQt"HREF="p4627.htm"><LINKREL="PREVIOUS"TITLE="Conclusion"HREF="x7388.htm"><LINKREL="NEXT"TITLE="QCanvas"HREF="x7601.htm"></HEAD><BODYCLASS="CHAPTER"BGCOLOR="#FFFFFF"TEXT="#000000"LINK="#0000FF"VLINK="#840084"ALINK="#0000FF"><DIVCLASS="NAVHEADER"><TABLESUMMARY="Header navigation table"WIDTH="100%"BORDER="0"CELLPADDING="0"CELLSPACING="0"><TR><THCOLSPAN="3"ALIGN="center">GUI Programming with Python: QT Edition</TH></TR><TR><TDWIDTH="10%"ALIGN="left"VALIGN="bottom"><AHREF="x7388.htm"ACCESSKEY="P">Prev</A></TD><TDWIDTH="80%"ALIGN="center"VALIGN="bottom"></TD><TDWIDTH="10%"ALIGN="right"VALIGN="bottom"><AHREF="x7601.htm"ACCESSKEY="N">Next</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="CHAPTER"><H1><ANAME="CH16">Chapter 21. Drawing on Painters and Canvases</A></H1><DIVCLASS="TOC"><DL><DT><B>Table of Contents</B></DT><DT><AHREF="c7391.htm#AEN7412">Working with painters and paint devices</A></DT><DT><AHREF="x7601.htm">QCanvas</A></DT><DT><AHREF="x7875.htm">Conclusion</A></DT></DL></DIV><P>Constructing windows out of predefined    widgets is all very nice, but the real exciting stuff occurs when    you have to leave the beaten track and push the pixels around    yourself. This happens when you want to create diagram editors,    drawing applications, games (especially games!), complex page    layout engines (like an html renderer), charts, and plots.</P><P>Python and PyQt form a good basis for this    kind of work, because Qt already has two very powerful and    optimized drawing engines: the <TTCLASS="CLASSNAME">QCanvas</TT>    class and the <TTCLASS="CLASSNAME">QPainter</TT>.    <TTCLASS="CLASSNAME">QCanvas</TT> is extremely useful if your drawing    can be composed from separate elements, and if you want to be able    to track events that occur on those individual elements, such as    mouse clicks.</P><P><TTCLASS="CLASSNAME">QPainter</TT> offers finer    control of what you put on screen, at the cost of losing control    over the individual elements that make up the drawing.    <TTCLASS="CLASSNAME">QPainter</TT> is more suited to drawing charts,    bit-mapped drawings and plots. If you want real plotting power,    you should investigate PyQwt, which is introduced in     <AHREF="a8743.htm">Appendix B</A></P><P>Both <TTCLASS="CLASSNAME">QCanvas</TT> and    <TTCLASS="CLASSNAME">QPainter</TT> are very powerful. In fact, they    are even used in assembling software used to create animated    films. Animation means a lot of intensive work for your computer,    though, and Python can not always cope&#8212;even on the most    modern machines. In such cases, it is quite easy to replace the    Python class with a Qt-based C++ object (you won't have to    translate your whole Python application). See <AHREF="a8834.htm">Appendix C</A>    for information on the wrapping of new C++ objects with sip.</P><DIVCLASS="SECT1"><H1CLASS="SECT1"><ANAME="AEN7412">Working with painters and paint devices</A></H1><P>In <AHREF="c2591.htm">Chapter 10</A> we introduced      <TTCLASS="CLASSNAME">QPainter</TT>, for creating our little      scribbling example application, <TTCLASS="FILENAME">event1.py</TT>.      In this section we will take a closer look at the organization      and use of the PyQt painting mechanism.</P><P>Painting in PyQt involves the cooperation      of two classes: a <TTCLASS="CLASSNAME">QPainter</TT> and a      <TTCLASS="CLASSNAME">QPaintDevice</TT>. The first is a very      efficient abstraction of various drawing operations. It provides      the brushes and the letterpress, so to speak. The second      provides the &#8216;paper' on which to draw.</P><P>There are four subclasses of      <TTCLASS="CLASSNAME">QPaintDevice</TT>:      <TTCLASS="CLASSNAME">QWidget</TT>, <TTCLASS="CLASSNAME">QPicture</TT>,      <TTCLASS="CLASSNAME">QPixMap</TT> and      <TTCLASS="CLASSNAME">QPrinter</TT>.</P><P>You use hand-coded painting with a      <TTCLASS="CLASSNAME">QPainter</TT> object on a      <TTCLASS="CLASSNAME">QWidget</TT> to determine the look of a widget.      The place for the painting code is in re-implementations of the      <TTCLASS="FUNCTION">paintEvent()</TT> function.</P><P><TTCLASS="CLASSNAME">QPicture</TT> is a kind      of event recorder: it records every      <TTCLASS="CLASSNAME">QPainter</TT> action, and can replay them. You      can also save those actions to a platform independent file. This      is useful if you want to implement rolling charts with a limited      replay functionality (although <SPAN><ICLASS="EMPHASIS">I</I></SPAN> would      prefer to save the underlying data and reconstruct the chart      every time). You cannot alter anything in the sequence of events      once it is recorded. Starting with Qt 3,      <TTCLASS="CLASSNAME">QPicture</TT> has become quite powerful, with      the ability to load and save industry standard      <TTCLASS="FILENAME">.svg</TT> files - the scalable vector graphics      format.</P><P>Painting on a      <TTCLASS="CLASSNAME">QPixMap</TT> is extraordinarily useful.      Painting is always a bit slow, especially if it is done line by      line, dot by dot, and character by character. This can result in      visible lag or flickering if you paint directly on an exposed      <TTCLASS="CLASSNAME">QWidget</TT>. By first painting the complete      drawing on a <TTCLASS="CLASSNAME">QPixMap</TT> object, and then      using the <TTCLASS="FUNCTION">bitBlt()</TT> function to move the      picture in one swoop the the widget, you will avoid this      flickering. <TTCLASS="CLASSNAME">bitBlt()</TT> really      <SPAN><ICLASS="EMPHASIS">is</I></SPAN> fast.</P><P>Finally, being able to paint on a      <TTCLASS="CLASSNAME">QPrinter</TT> object means that anything you      can draw on-screen can also be printed. However, printing is      still quite a difficult subject &#8212; even if PyQt can      generate your PostScript for you, you still have to layout      everything yourself. You cannot, for instance, send the contents      of a <TTCLASS="CLASSNAME">QSimpleRichText</TT> widget to a printer      just like that... We'll discuss the basics of printing in      <AHREF="c8100.htm">Chapter 24</A>.</P><DIVCLASS="SECT2"><H2CLASS="SECT2"><ANAME="CH16PAINTINGEXAMPLE">A painting example</A></H2><P>There is little we can do using        <TTCLASS="CLASSNAME">QPainter</TT> and        <TTCLASS="CLASSNAME">QPaintDevices</TT> in our        <SPANCLASS="APPLICATION">Kalam</SPAN> project &#8212; but after        long and hard thinking I thought a rolling chart that counts        how many characters the user types per minute might be a nice,        although completely useless (and possibly frustrating) except        for <SPAN><ICLASS="EMPHASIS">real</I></SPAN> productivity freaks.</P><DIVCLASS="EXAMPLE"><ANAME="AEN7480"></A><P><B>Example 21-1. typometer.py - A silly type-o-meter that keeps a          running count of how many characters are added to a certain          document and shows a chart of the typerate...</B></P><PRECLASS="PROGRAMLISTING">"""typometer.pyA silly type-o-meter that keeps a running count of how many characters thereare in a certain document and shows a chart of the count..."""import sys, whrandomfrom qt import *FIVE_SECONDS = 1000 * 5 #  5 seconds in milli-secondsAVERAGE_TYPESPEED = 125 # kind of calibrationBARWIDTH = 3TRUE=1FALSE=0          </PRE></DIV><P>No surprises here&#8212;just some        declarations. I like to work with names instead of magic        numbers, and to conform to practice in other programming        languages, those names are in all-caps, even though they are        not constants.</P><PRECLASS="PROGRAMLISTING">class TypoGraph(QPixmap):    """ TypoGraph is a subclass of QPixmap and draws a small graph of    the current wordcount of a text.    """    def __init__(self, count, w, h, *args):        apply(QPixmap.__init__, (self, w, h) + args)        self.count = count        self.maxCount = AVERAGE_TYPESPEED        if count != 0:            self.scale = float(h) / float(count)        else:            self.scale = float(h) / float(AVERAGE_TYPESPEED)        self.col = 0        self.fill(QColor("white"))        self.drawGrid()      </PRE><P>The general design of this chart drawing        code consists of two parties: a specialized pixmap, descended        from <TTCLASS="CLASSNAME">QPixmap</TT>, that will draw the chart        and keep track of scrolling, and a widget that show the chart        and can be used everywhere where you might want to use a        widget.</P><P>In the constructor of        <TTCLASS="CLASSNAME">TypoGraph</TT>, the specialized        <TTCLASS="CLASSNAME">QPixMap</TT>, certain initial variables are        set. One point of attention is scaling. The chart will have a        certain fixed vertical size. It is quite possible that the        plotted values won't fit into the available pixels.</P><P>This means that we have to scale the        values to fit the pixels of the chart. This is done by        arbitrarily deciding upon a maximum value, and dividing the        height of the chart by that value. Any value greater than the        maximum will go off the chart, but if you can type more than        125 characters in five seconds, you deserve to fly off the        chart!</P><P>Because the scaling can be smaller than        one but greater than zero, we need to use        <SPAN><ICLASS="EMPHASIS">float</I></SPAN> numbers for our scale. Floats are        notoriously slow, but believe me, your computer can handle        more floats than you can throw at it per second, so you won't        feel the penalty for not using integers.</P><P>Finally, we fill the pixmap with a        background color (white in this case) and draw a nice        grid:</P><PRECLASS="PROGRAMLISTING">    def drawGrid(self):        p = QPainter(self)        p.setBackgroundColor(QColor("white"))        h = self.height()        w = self.width()        for i in range(1, h, h/5):            p.setPen(QColor("lightgray"))            p.drawLine(0, i, w, i)      </PRE><P>This is the first encounter with        <TTCLASS="CLASSNAME">QPainter</TT>. The basic procedure for        working with painter objects is very simple: you create a        painter for the right paintdevice. Here the paintdevice is        <TTCLASS="VARNAME">self</TT> &#8212; our specialized        <TTCLASS="CLASSNAME">QPixMap</TT>. After having created the        <TTCLASS="CLASSNAME">QPainter</TT> you can mess about drawing        lines, setting colors or throwing more complex shapes on the        paper. Here, we draw four lines at equal distances using a

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?